// Zombie Monster for KF Invasion gametype
class NiceZombieBloat extends NiceZombieBloatBase;
#exec OBJ LOAD FILE=KF_EnemiesFinalSnd.uax
//----------------------------------------------------------------------------
// NOTE: All Variables are declared in the base class to eliminate hitching
//----------------------------------------------------------------------------
var class<FleshHitEmitter> BileExplosion;
var class<FleshHitEmitter> BileExplosionHeadless;
function bool FlipOver()
{
    return true;
}
// don't interrupt the bloat while he is puking
simulated function bool HitCanInterruptAction()
{
    if( bShotAnim )
    {
       return false;
    }
    return true;
}

function DoorAttack(Actor A)
{
    if ( bShotAnim || Physics == PHYS_Swimming)
       return;
    else if ( A!=none )
    {
       bShotAnim = true;
       if( !bDecapitated && bDistanceAttackingDoor )
       {
           SetAnimAction('ZombieBarf');
       }
       else
       {
           SetAnimAction('DoorBash');
           GotoState('DoorBashing');
       }
    }
}
function RangedAttack(Actor A)
{
    local int LastFireTime;
    if ( bShotAnim )
       return;
    if ( Physics == PHYS_Swimming )
    {
       SetAnimAction('Claw');
       bShotAnim = true;
       LastFireTime = Level.TimeSeconds;
    }
    else if ( VSize(A.Location - Location) < MeleeRange + CollisionRadius + A.CollisionRadius )
    {
       bShotAnim = true;
       LastFireTime = Level.TimeSeconds;
       SetAnimAction('Claw');
       //PlaySound(sound'Claw2s', SLOT_Interact); KFTODO: Replace this
       Controller.bPreparingMove = true;
       Acceleration = vect(0,0,0);
    }
    else if ( (KFDoorMover(A) != none || VSize(A.Location-Location) <= 250) && !bDecapitated )
    {
       bShotAnim = true;

       // Randomly do a moving attack so the player can't kite the zed
       if( FRand() < 0.8 )
       {
           SetAnimAction('ZombieBarfMoving');
           RunAttackTimeout = GetAnimDuration('ZombieBarf', 1.0);
           bMovingPukeAttack=true;
       }
       else
       {
           SetAnimAction('ZombieBarf');
           Controller.bPreparingMove = true;
           Acceleration = vect(0,0,0);
       }

       // Randomly send out a message about Bloat Vomit burning(3% chance)
       if ( FRand() < 0.03 && KFHumanPawn(A) != none && PlayerController(KFHumanPawn(A).Controller) != none )
       {
           PlayerController(KFHumanPawn(A).Controller).Speech('AUTO', 7, "");
       }
    }
}
// Overridden to handle playing upper body only attacks when moving
simulated event SetAnimAction(name NewAction)
{
    local int meleeAnimIndex;
    local bool bWantsToAttackAndMove;
    if( NewAction=='' )
       Return;
    bWantsToAttackAndMove = NewAction == 'ZombieBarfMoving';
    if( NewAction == 'Claw' )
    {
       meleeAnimIndex = Rand(3);
       NewAction = meleeAnims[meleeAnimIndex];
    }
    if( bWantsToAttackAndMove )
    {
      ExpectingChannel = AttackAndMoveDoAnimAction(NewAction);
    }
    else
    {
      ExpectingChannel = DoAnimAction(NewAction);
    }
    if( !bWantsToAttackAndMove && AnimNeedsWait(NewAction) )
    {
       bWaitForAnim = true;
    }
    else
    {
       bWaitForAnim = false;
    }
    if( Level.NetMode!=NM_Client )
    {
       AnimAction = NewAction;
       bResetAnimAct = True;
       ResetAnimActTime = Level.TimeSeconds+0.3;
    }
}
// Handle playing the anim action on the upper body only if we're attacking and moving
simulated function int AttackAndMoveDoAnimAction( name AnimName )
{
    if( AnimName=='ZombieBarfMoving' )
    {
       AnimBlendParams(1, 1.0, 0.0,, FireRootBone);
       PlayAnim('ZombieBarf',, 0.1, 1);

       return 1;
    }
    return super.DoAnimAction( AnimName );
}

function PlayDyingSound()
{
    if( Level.NetMode!=NM_Client )
    {
       if ( bGibbed )
       {
           PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
           return;
       }

       if( bDecapitated )
       {
           PlaySound(HeadlessDeathSound, SLOT_Pain,1.30,true,525);
       }
       else
       {
           PlaySound(sound'KF_EnemiesFinalSnd.Bloat_DeathPop', SLOT_Pain,2.0,true,525);
       }
    }
}

// Barf Time.
function SpawnTwoShots()
{
    local vector X,Y,Z, FireStart;
    local rotator FireRotation;

    // check this from the very start to prevent any log spam
    if (Controller == none || IsInState('ZombieDying'))
        return;

    if (KFDoorMover(Controller.Target)!=none )
    {
       Controller.Target.TakeDamage(22,Self,Location,vect(0,0,0),Class'DamTypeVomit');
       return;
    }
    GetAxes(Rotation,X,Y,Z);
    FireStart = Location+(vect(30,0,64) >> Rotation)*DrawScale;
    if ( !SavedFireProperties.bInitialized )
    {
       SavedFireProperties.AmmoClass = Class'SkaarjAmmo';
       SavedFireProperties.ProjectileClass = Class'KFBloatVomit';
       SavedFireProperties.WarnTargetPct = 1;
       SavedFireProperties.MaxRange = 500;
       SavedFireProperties.bTossed = False;
       SavedFireProperties.bTrySplash = False;
       SavedFireProperties.bLeadTarget = True;
       SavedFireProperties.bInstantHit = True;
       SavedFireProperties.bInitialized = True;
    }
    // Turn off extra collision before spawning vomit, otherwise spawn fails
    ToggleAuxCollision(false);
    FireRotation = Controller.AdjustAim(SavedFireProperties,FireStart,600);
    Spawn(Class'KFBloatVomit',,,FireStart,FireRotation);
    FireStart-=(0.5*CollisionRadius*Y);
    FireRotation.Yaw -= 1200;
    spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
    FireStart+=(CollisionRadius*Y);
    FireRotation.Yaw += 2400;
    spawn(Class'KFBloatVomit',,,FireStart, FireRotation);
    // Turn extra collision back on
    ToggleAuxCollision(true);
}

simulated function Tick(float deltatime)
{
    local vector BileExplosionLoc;
    local FleshHitEmitter GibBileExplosion;

    Super.tick(deltatime);


    if( Role == ROLE_Authority && bMovingPukeAttack )
    {
       // Keep moving toward the target until the timer runs out (anim finishes)
       if( RunAttackTimeout > 0 )
       {
           RunAttackTimeout -= DeltaTime;

           if( RunAttackTimeout <= 0 )
           {
               RunAttackTimeout = 0;
               bMovingPukeAttack=false;
           }
       }

       // Keep the gorefast moving toward its target when attacking
       if( bShotAnim && !bWaitForAnim )
       {
           if( LookTarget!=none )
           {
               Acceleration = AccelRate * Normal(LookTarget.Location - Location);
           }
       }
    }
    // Hack to force animation updates on the server for the bloat if he is relevant to someone
    // He has glitches when some of his animations don't play on the server. If we
    // find some other fix for the glitches take this out - Ramm
    if( Level.NetMode != NM_Client && Level.NetMode != NM_Standalone )
    {
       if( (Level.TimeSeconds-LastSeenOrRelevantTime) < 1.0  )
       {
           bForceSkelUpdate=true;
       }
       else
       {
           bForceSkelUpdate=false;
       }
    }
    if ( Level.NetMode!=NM_DedicatedServer && /*Gored>0*/Health <= 0 && !bPlayBileSplash &&
       HitDamageType != class'DamTypeBleedOut' )
    {
       if ( !class'GameInfo'.static.UseLowGore() )
       {
           // remove self.
           BileExplosionLoc = Location;
           BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));

           if (bDecapitated)
           {
               GibBileExplosion = Spawn(BileExplosionHeadless,self,, BileExplosionLoc );
           }
           else
           {
               GibBileExplosion = Spawn(BileExplosion,self,, BileExplosionLoc );
           }
           bPlayBileSplash = true;
       }
       else
       {
           BileExplosionLoc = Location;
           BileExplosionLoc.z += (CollisionHeight - (CollisionHeight * 0.5));

           GibBileExplosion = Spawn(class'LowGoreBileExplosion',self,, BileExplosionLoc );
           bPlayBileSplash = true;
       }
    }
}
function BileBomb()
{
    BloatJet = spawn(class'BileJet', self,,Location,Rotator(-PhysicsVolume.Gravity));
}
function PlayDyingAnimation(class<DamageType> DamageType, vector HitLoc)
{
//    local bool AttachSucess;
    super.PlayDyingAnimation(DamageType, HitLoc);
    // Don't blow up with bleed out
    if( bDecapitated && DamageType == class'DamTypeBleedOut' )
    {
       return;
    }
    if ( !class'GameInfo'.static.UseLowGore() )
    {
       HideBone(SpineBone2);
    }
    if(Role == ROLE_Authority)
    {
       BileBomb();
//    if(BloatJet!=none)
//    {
//    if(Gored < 5)
//    AttachSucess=AttachToBone(BloatJet,FireRootBone);
//    // else
//    // AttachSucess=AttachToBone(BloatJet,SpineBone1);
//
//    if(!AttachSucess)
//    {
//    log("DEAD Bloaty Bile didn't like the Boning :o");
//    BloatJet.SetBase(self);
//    }
//    BloatJet.SetRelativeRotation(rot(0,-4096,0));
//    }
    }
}
simulated function ProcessHitFX()
{
    local Coords boneCoords;
    local class<xEmitter> HitEffects[4];
    local int i,j;
    local float GibPerterbation;
    if( (Level.NetMode == NM_DedicatedServer) || bSkeletized || (Mesh == SkeletonMesh))
    {
       SimHitFxTicker = HitFxTicker;
       return;
    }
    for ( SimHitFxTicker = SimHitFxTicker; SimHitFxTicker != HitFxTicker; SimHitFxTicker = (SimHitFxTicker + 1) % ArrayCount(HitFX) )
    {
       j++;
       if ( j > 30 )
       {
           SimHitFxTicker = HitFxTicker;
           return;
       }

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

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

       if( HitFX[SimHitFxTicker].bone == 'obliterate' && !class'GameInfo'.static.UseLowGore())
       {
           SpawnGibs( HitFX[SimHitFxTicker].rotDir, 1);
           bGibbed = true;
           // Wait a tick on a listen server so the obliteration can replicate before the pawn is destroyed
           if( Level.NetMode == NM_ListenServer )
           {
               bDestroyNextTick = true;
               TimeSetDestroyNextTickTime = Level.TimeSeconds;
           }
           else
           {
               Destroy();
           }
           return;
       }

       boneCoords = GetBoneCoords( HitFX[SimHitFxTicker].bone );

       if ( !Level.bDropDetail && !class'GameInfo'.static.NoBlood() && !bSkeletized && !class'GameInfo'.static.UseLowGore() )
       {
           //AttachEmitterEffect( BleedingEmitterClass, HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );

           HitFX[SimHitFxTicker].damtype.static.GetHitEffects( HitEffects, Health );

           if( !PhysicsVolume.bWaterVolume ) // don't attach effects under water
           {
               for( i = 0; i < ArrayCount(HitEffects); i++ )
               {
                   if( HitEffects[i] == none )
                       continue;

                     AttachEffect( HitEffects[i], HitFX[SimHitFxTicker].bone, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir );
               }
           }
       }

       if ( class'GameInfo'.static.UseLowGore() )
       {
           HitFX[SimHitFxTicker].bSever = false;

           switch( HitFX[SimHitFxTicker].bone )
           {
                case 'head':
                   if( !bHeadGibbed )
                   {
                       if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
                       {
                           DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
                       }
                       else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
                       {
                           DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
                       }
                       else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
                       {
                           DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
                       }

                         bHeadGibbed=true;
                     }
                   break;
           }

           return;
       }

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

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

               case LeftThighBone:
                   if( !bLeftLegGibbed )
                   {
                       SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
                       KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       bLeftLegGibbed=true;
                   }
                   break;

               case RightThighBone:
                   if( !bRightLegGibbed )
                   {
                       SpawnSeveredGiblet( DetachedLegClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
                       KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       bRightLegGibbed=true;
                   }
                   break;

               case LeftFArmBone:
                   if( !bLeftArmGibbed )
                   {
                       SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
                       KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;;
                       bLeftArmGibbed=true;
                   }
                   break;

               case RightFArmBone:
                   if( !bRightArmGibbed )
                   {
                       SpawnSeveredGiblet( DetachedArmClass, boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, GetBoneRotation(HitFX[SimHitFxTicker].bone) );
                       KFSpawnGiblet( class 'KFMod.KFGibBrain',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       KFSpawnGiblet( class 'KFMod.KFGibBrainb',boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, GibPerterbation, 250 ) ;
                       bRightArmGibbed=true;
                   }
                   break;

               case 'head':
                   if( !bHeadGibbed )
                   {
                       if ( HitFX[SimHitFxTicker].damtype == class'DamTypeDecapitation' )
                       {
                           DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false);
                       }
                       else if( HitFX[SimHitFxTicker].damtype == class'DamTypeProjectileDecap' )
                       {
                           DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, false, true);
                       }
                       else if( HitFX[SimHitFxTicker].damtype == class'DamTypeMeleeDecapitation' )
                       {
                           DecapFX( boneCoords.Origin, HitFX[SimHitFxTicker].rotDir, true);
                       }

                         bHeadGibbed=true;
                     }
                   break;
           }

           // Don't do this right now until we get the effects sorted - Ramm
           if( HitFX[SimHitFXTicker].bone != 'Spine' && HitFX[SimHitFXTicker].bone != FireRootBone &&
               HitFX[SimHitFXTicker].bone != LeftFArmBone && HitFX[SimHitFXTicker].bone != RightFArmBone &&
               HitFX[SimHitFXTicker].bone != 'head' && Health <=0 )
               HideBone(HitFX[SimHitFxTicker].bone);
       }
    }
}
simulated function HideBone(name boneName)
{
    local int BoneScaleSlot;
    local coords boneCoords;
    local bool bValidBoneToHide;

      if( boneName == LeftThighBone )
    {
       boneScaleSlot = 0;
       bValidBoneToHide = true;
       if( SeveredLeftLeg == none )
       {
           SeveredLeftLeg = Spawn(SeveredLegAttachClass,self);
           SeveredLeftLeg.SetDrawScale(SeveredLegAttachScale);
           boneCoords = GetBoneCoords( 'lleg' );
           AttachEmitterEffect( LimbSpurtEmitterClass, 'lleg', boneCoords.Origin, rot(0,0,0) );
           AttachToBone(SeveredLeftLeg, 'lleg');
       }
    }
    else if ( boneName == RightThighBone )
    {
       boneScaleSlot = 1;
       bValidBoneToHide = true;
       if( SeveredRightLeg == none )
       {
           SeveredRightLeg = Spawn(SeveredLegAttachClass,self);
           SeveredRightLeg.SetDrawScale(SeveredLegAttachScale);
           boneCoords = GetBoneCoords( 'rleg' );
           AttachEmitterEffect( LimbSpurtEmitterClass, 'rleg', boneCoords.Origin, rot(0,0,0) );
           AttachToBone(SeveredRightLeg, 'rleg');
       }
    }
    else if( boneName == RightFArmBone )
    {
       boneScaleSlot = 2;
       bValidBoneToHide = true;
       if( SeveredRightArm == none )
       {
           SeveredRightArm = Spawn(SeveredArmAttachClass,self);
           SeveredRightArm.SetDrawScale(SeveredArmAttachScale);
           boneCoords = GetBoneCoords( 'rarm' );
           AttachEmitterEffect( LimbSpurtEmitterClass, 'rarm', boneCoords.Origin, rot(0,0,0) );
           AttachToBone(SeveredRightArm, 'rarm');
       }
    }
    else if ( boneName == LeftFArmBone )
    {
       boneScaleSlot = 3;
       bValidBoneToHide = true;
       if( SeveredLeftArm == none )
       {
           SeveredLeftArm = Spawn(SeveredArmAttachClass,self);
           SeveredLeftArm.SetDrawScale(SeveredArmAttachScale);
           boneCoords = GetBoneCoords( 'larm' );
           AttachEmitterEffect( LimbSpurtEmitterClass, 'larm', boneCoords.Origin, rot(0,0,0) );
           AttachToBone(SeveredLeftArm, 'larm');
       }
    }
    else if ( boneName == HeadBone )
    {
       // Only scale the bone down once
       if( SeveredHead == none )
       {
           bValidBoneToHide = true;
           boneScaleSlot = 4;
           SeveredHead = Spawn(SeveredHeadAttachClass,self);
           SeveredHead.SetDrawScale(SeveredHeadAttachScale);
           boneCoords = GetBoneCoords( 'neck' );
           AttachEmitterEffect( NeckSpurtEmitterClass, 'neck', boneCoords.Origin, rot(0,0,0) );
           AttachToBone(SeveredHead, 'neck');
       }
       else
       {
           return;
       }
    }
    else if ( boneName == 'spine' )
    {
       bValidBoneToHide = true;
       boneScaleSlot = 5;
    }
    else if ( boneName == SpineBone2 )
    {
       bValidBoneToHide = true;
       boneScaleSlot = 6;
    }
    // Only hide the bone if it is one of the arms, legs, or head, don't hide other misc bones
    if( bValidBoneToHide )
    {
       SetBoneScale(BoneScaleSlot, 0.0, BoneName);
    }
}

State Dying
{
  function tick(float deltaTime)
  {
   if (BloatJet != none)
   {
    BloatJet.SetLocation(location);
    BloatJet.SetRotation(GetBoneRotation(FireRootBone));
   }
    super.tick(deltaTime);
  }
}
function RemoveHead()
{
    bCanDistanceAttackDoors = False;
    Super.RemoveHead();
}
function ModDamage(out int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, class<NiceWeaponDamageType> damageType, float headshotLevel, KFPlayerReplicationInfo KFPRI, optional float lockonTime){
    if(damageType == class 'DamTypeVomit' || damageType == class 'DamTypeBlowerThrower')
       Damage = 0;
    else
       Super.ModDamage(Damage, instigatedBy, hitlocation, momentum, damageType, headshotLevel, KFPRI);
}
static simulated function PreCacheStaticMeshes(LevelInfo myLevel)
{//should be derived and used.
    Super.PreCacheStaticMeshes(myLevel);
    myLevel.AddPrecacheStaticMesh(StaticMesh'kf_gore_trip_sm.limbs.bloat_head');
}
static simulated function PreCacheMaterials(LevelInfo myLevel)
{
    myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_cmb');
    myLevel.AddPrecacheMaterial(Combiner'KF_Specimens_Trip_T.bloat_env_cmb');
    myLevel.AddPrecacheMaterial(Texture'KF_Specimens_Trip_T.bloat_diffuse');
}
defaultproperties
{
    BileExplosion=Class'KFMod.BileExplosion'
    BileExplosionHeadless=Class'KFMod.BileExplosionHeadless'
    stunLoopStart=0.100000
    stunLoopEnd=0.600000
    idleInsertFrame=0.950000
    EventClasses(0)="NicePack.NiceZombieBloat"
    MoanVoice=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Talk'
    MeleeAttackHitSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_HitPlayer'
    JumpSound=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Jump'
    DetachedArmClass=Class'KFChar.SeveredArmBloat'
    DetachedLegClass=Class'KFChar.SeveredLegBloat'
    DetachedHeadClass=Class'KFChar.SeveredHeadBloat'
    HitSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Pain'
    DeathSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Death'
    ChallengeSound(0)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
    ChallengeSound(1)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
    ChallengeSound(2)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
    ChallengeSound(3)=SoundGroup'KF_EnemiesFinalSnd.Bloat.Bloat_Challenge'
    AmbientSound=Sound'KF_BaseBloat.Bloat_Idle1Loop'
    Mesh=SkeletalMesh'KF_Freaks_Trip.Bloat_Freak'
    Skins(0)=Combiner'KF_Specimens_Trip_T.bloat_cmb'
}
