//------------------------------------------------------------
//
//-----------------------------------------------------------
class BotScripted2 expands ScriptedPawn;


// ----------------------------------------------------------------------
// state Attacking
//
// Kill!  Kill!  Kill!  Kill!
// ----------------------------------------------------------------------

State Attacking
{
     function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
     {
          local Pawn oldEnemy;
          local bool bHateThisInjury;
          local bool bFearThisInjury;

          if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury))
          {
               oldEnemy = Enemy;

               bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury);
               bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury);

               if (bHateThisInjury)
                    IncreaseAgitation(instigatedBy, 1.0);
               if (bFearThisInjury)
                    IncreaseFear(instigatedBy, 2.0);

               if (ReadyForNewEnemy())
                    SetEnemy(instigatedBy);

               if (ShouldFlee())
               {
                    SetDistressTimer();
                    PlayCriticalDamageSound();
                    if (RaiseAlarm == RAISEALARM_BeforeFleeing)
                         SetNextState('Alerting');
                    else
                         SetNextState('Fleeing');
               }
               else
               {
                    SetDistressTimer();
                    if (oldEnemy != Enemy)
                         PlayNewTargetSound();
                    SetNextState('Attacking', 'ContinueAttack');
               }
               GotoDisabledState(damageType, hitPos);
          }
     }

     function SetFall()
     {
          StartFalling('Attacking', 'ContinueAttack');
     }

     function HitWall(vector HitNormal, actor Wall)
     {
          if (Physics == PHYS_Falling)
               return;
          Global.HitWall(HitNormal, Wall);
          CheckOpenDoor(HitNormal, Wall);
     }

     function Reloading(DeusExWeapon reloadWeapon, float reloadTime)
     {
          Global.Reloading(reloadWeapon, reloadTime);
          if (bReadyToReload)
               if (IsWeaponReloading())
                    if (!IsHandToHand())
                         TweenToShoot(0);
     }

     function EDestinationType PickDestination()
     {
          local vector               distVect;
          local vector               tempVect;
          local rotator              enemyDir;
          local float                magnitude;
          local float                calcMagnitude;
          local int                  iterations;
          local EDestinationType     destType;
          local NearbyProjectileList projList;

          destPoint = None;
          destLoc   = vect(0, 0, 0);
          destType  = DEST_Failure;

          if (enemy == None)
               return (destType);

          if (bCrouching && (CrouchTimer > 0))
               destType = DEST_SameLocation;

          if (destType == DEST_Failure)
          {
               if (AICanShoot(enemy, true, false, 0.025) || ActorReachable(enemy))
               {
                    destType = ComputeBestFiringPosition(tempVect);
                    if (destType == DEST_NewLocation)
                         destLoc = tempVect;
               }
          }

          if (destType == DEST_Failure)
          {
               MoveTarget = FindPathToward(enemy);
               if (MoveTarget != None)
               {
                    if (!bDefendHome || IsNearHome(MoveTarget.Location))
                    {
                         if (bAvoidHarm)
                              GetProjectileList(projList, MoveTarget.Location);
                         if (!bAvoidHarm || !IsLocationDangerous(projList, MoveTarget.Location))
                         {
                              destPoint = MoveTarget;
                              destType  = DEST_NewLocation;
                         }
                    }
               }
          }

          // Default behavior, so they don't just stand there...
          if (destType == DEST_Failure)
          {
               enemyDir = Rotator(Enemy.Location - Location);
               if (AIPickRandomDestination(60, 150,
                                           enemyDir.Yaw, 0.5, enemyDir.Pitch, 0.5, 
                                           2, FRand()*0.4+0.35, tempVect))
               {
                    if (!bDefendHome || IsNearHome(tempVect))
                    {
                         destType = DEST_NewLocation;
                         destLoc  = tempVect;
                    }
               }
          }

          return (destType);
     }

     function bool FireIfClearShot()
     {
          local DeusExWeapon dxWeapon;

          dxWeapon = DeusExWeapon(Weapon);


          //MPBots EdGann KLUDGE: bReadyToFire doesn't seem to work right for Scripted Pawns
          // So I set it and execute ReadyToFire() automatically.

          dxWeapon.bReadyToFire=false;
          dxWeapon.ReadyToFire();

          if (dxWeapon != None)
          {
               if ((dxWeapon.AIFireDelay > 0) && (FireTimer > 0))
                    return false;
               else if (AICanShoot(enemy, true, true, 0.025))
               {

                    // MPBots EdGann: Changed from Fire to ClientFire.
                    Weapon.ClientFire(0);
                    FireTimer = dxWeapon.AIFireDelay;
                    return true;
               }
               else
                   return false;

          }
          else
              return false;
     }

     function CheckAttack(bool bPlaySound)
     {
          local bool bCriticalDamage;
          local bool bOutOfAmmo;
          local Pawn oldEnemy;
          local bool bAllianceSwitch;

          oldEnemy = enemy;

          bAllianceSwitch = false;
          if (!IsValidEnemy(enemy))
          {
               if (IsValidEnemy(enemy, false))
                    bAllianceSwitch = true;
               SetEnemy(None, 0, true);
          }

          if (enemy == None)
          {
               if (Orders == 'Attacking')
               {
                    FindOrderActor();
                    SetEnemy(Pawn(OrderActor), 0, true);
               }
          }
          if (ReadyForNewEnemy())
               FindBestEnemy(false);
          if (enemy == None)
          {
               Enemy = oldEnemy;  // hack
               if (bPlaySound)
               {
                    if (bAllianceSwitch)
                         PlayAllianceFriendlySound();
                    else
                         PlayAreaSecureSound();
               }
               Enemy = None;
               if (Orders != 'Attacking')
                    FollowOrders();
               else
                    GotoState('Wandering');
               return;
          }

          SwitchToBestWeapon();
          if (bCrouching && (CrouchTimer <= 0) && !ShouldCrouch())
          {
               EndCrouch();
               TweenToShoot(0.15);
          }
          bCriticalDamage = False;
          bOutOfAmmo      = False;
          if (ShouldFlee())
               bCriticalDamage = True;
          else if (Weapon == None)
               bOutOfAmmo = True;
          else if (Weapon.ReloadCount > 0)
          {
               if (Weapon.AmmoType == None)
                    bOutOfAmmo = True;
               else if (Weapon.AmmoType.AmmoAmount < 1)
                    bOutOfAmmo = True;
          }
          if (bCriticalDamage || bOutOfAmmo)
          {
               if (bPlaySound)
               {
                    if (bCriticalDamage)
                         PlayCriticalDamageSound();
                    else if (bOutOfAmmo)
                         PlayOutOfAmmoSound();
               }
               if (RaiseAlarm == RAISEALARM_BeforeFleeing)
                    GotoState('Alerting');
               else
                    GotoState('Fleeing');
          }
          else if (bPlaySound && (oldEnemy != Enemy))
               PlayNewTargetSound();
     }

     function Tick(float deltaSeconds)
     {
          local bool   bCanSee;
          local float  yaw;
          local vector lastLocation;
          local Pawn   lastEnemy;
          local float  surpriseTime;


          Global.Tick(deltaSeconds);
          if (CrouchTimer > 0)
          {
               CrouchTimer -= deltaSeconds;
               if (CrouchTimer < 0)
                    CrouchTimer = 0;
          }
          EnemyTimer += deltaSeconds;
          UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false);
          if ((Enemy != None) && HasEnemyTimedOut())
          {
               lastLocation = Enemy.Location;
               lastEnemy    = Enemy;
               FindBestEnemy(true);
               if (Enemy == None)
               {
                    SetSeekLocation(lastEnemy, lastLocation, SEEKTYPE_Guess, true);
                    GotoState('Seeking');
               }
          }
          else if (bCanFire && (Enemy != None))
          {
               ViewRotation = Rotator(Enemy.Location-Location);
               if (bFacingTarget)
                    FireIfClearShot();
               else if (!bMustFaceTarget)
               {
                    yaw = (ViewRotation.Yaw-Rotation.Yaw) & 0xFFFF;
                    if (yaw >= 32768)
                         yaw -= 65536;
                    yaw = Abs(yaw)*360/32768;  // 0-180 x 2
                    if (yaw <= FireAngle)
                         FireIfClearShot();
               }
          }
          //UpdateReactionLevel(true, deltaSeconds);
     }

     function bool IsHandToHand()
     {
          if (Weapon != None)
          {
               if (DeusExWeapon(Weapon) != None)
               {
                    if (DeusExWeapon(Weapon).bHandToHand)
                         return true;
                    else
                         return false;
               }
               else
                    return false;
          }
          else
               return false;
     }

     function bool ReadyForWeapon()
     {
          local bool bReady;

          bReady = false;
          if (DeusExWeapon(weapon) != None)
          {
               if (DeusExWeapon(weapon).bReadyToFire)
                    if (!IsWeaponReloading())
                         bReady = true;
          }
          if (!bReady)
               if (enemy == None)
                    bReady = true;
          if (!bReady)
               if (!AICanShoot(enemy, true, false, 0.025))
                    bReady = true;

          return (bReady);
     }

     function bool ShouldCrouch()
     {
          if (bCanCrouch && !Region.Zone.bWaterZone && !IsHandToHand() &&
              ((enemy != None) && (VSize(enemy.Location-Location) > 300)) &&
              ((DeusExWeapon(Weapon) == None) || DeusExWeapon(Weapon).bUseWhileCrouched))
               return true;
          else
               return false;
     }

     function StartCrouch()
     {
          if (!bCrouching)
          {
               bCrouching = true;
               SetBasedPawnSize(CollisionRadius, GetCrouchHeight());
               CrouchTimer = 1.0+FRand()*0.5;
          }
     }

     function EndCrouch()
     {
          if (bCrouching)
          {
               bCrouching = false;
               ResetBasedPawnSize();
          }
     }

     function BeginState()
     {
          StandUp();

          // hack
          if (MaxRange < MinRange+10)
               MaxRange = MinRange+10;
          bCanFire      = false;
          bFacingTarget = false;

          SwitchToBestWeapon();

          //EnemyLastSeen = 0;
          BlockReactions();
          bCanConverse = False;
          bAttacking = True;
          bStasis = False;
          SetDistress(true);

          CrouchTimer = 0;
          EnableCheckDestLoc(false);
     }

     function EndState()
     {
          EnableCheckDestLoc(false);
          bCanFire      = false;
          bFacingTarget = false;

          ResetReactions();
          bCanConverse = True;
          bAttacking = False;
          bStasis = True;
          bReadyToReload = false;

          EndCrouch();
     }

Begin:
     if (Enemy == None)
          GotoState('Seeking');
     //EnemyLastSeen = 0;
     CheckAttack(false);

Surprise:
     if ((1.0-ReactionLevel)*SurprisePeriod < 0.25)
          Goto('BeginAttack');
     Acceleration=vect(0,0,0);
     PlaySurpriseSound();
     PlayWaiting();
     while (ReactionLevel < 1.0)
     {
          TurnToward(Enemy);
          Sleep(0);
     }

BeginAttack:
     EnemyReadiness = 1.0;
     ReactionLevel  = 1.0;
     if (PlayerAgitationTimer > 0)
          PlayAllianceHostileSound();
     else
          PlayTargetAcquiredSound();
     if (PlayBeginAttack())
     {
          Acceleration = vect(0,0,0);
          TurnToward(enemy);
          FinishAnim();
     }

RunToRange:
     bCanFire       = false;
     bFacingTarget  = false;
     bReadyToReload = false;
     EndCrouch();
     if (Physics == PHYS_Falling)
          TweenToRunning(0.05);
     WaitForLanding();
     if (!IsWeaponReloading() || bCrouching)
     {
          if (ShouldPlayTurn(Enemy.Location))
               PlayTurning();
          TurnToward(enemy);
     }
     else
          Sleep(0);
     bCanFire = true;
     while (PickDestination() == DEST_NewLocation)
     {
          if (bCanStrafe && ShouldStrafe())
          {
               PlayRunningAndFiring();
               if (destPoint != None)
                    StrafeFacing(destPoint.Location, enemy);
               else
                    StrafeFacing(destLoc, enemy);
               bFacingTarget = true;
          }
          else
          {
               bFacingTarget = false;
               PlayRunning();
               if (destPoint != None)
                    MoveToward(destPoint, MaxDesiredSpeed);
               else
                    MoveTo(destLoc, MaxDesiredSpeed);
          }
          CheckAttack(true);
     }

Fire:
     bCanFire      = false;
     bFacingTarget = false;
     Acceleration = vect(0, 0, 0);

     SwitchToBestWeapon();
     if (FRand() > 0.5)
          bUseSecondaryAttack = true;
     else
          bUseSecondaryAttack = false;
     if (IsHandToHand())
          TweenToAttack(0.15);
     else if (ShouldCrouch() && (FRand() < CrouchRate))
     {
          TweenToCrouchShoot(0.15);
          FinishAnim();
          StartCrouch();
     }
     else
          TweenToShoot(0.15);
     if (!IsWeaponReloading() || bCrouching)
          TurnToward(enemy);
     FinishAnim();
     bReadyToReload = true;

ContinueFire:
     while (!ReadyForWeapon())
     {
          if (PickDestination() != DEST_SameLocation)
               Goto('RunToRange');
          CheckAttack(true);
          if (!IsWeaponReloading() || bCrouching)
               TurnToward(enemy);
          else
               Sleep(0);
     }
     CheckAttack(true);
     if (!FireIfClearShot())
          Goto('ContinueAttack');
     bReadyToReload = false;
     if (bCrouching)
          PlayCrouchShoot();
     else if (IsHandToHand())
          PlayAttack();
     else
          PlayShoot();
     FinishAnim();
     if (FRand() > 0.5)
          bUseSecondaryAttack = true;
     else
          bUseSecondaryAttack = false;
     bReadyToReload = true;
     if (!IsHandToHand())
     {
          if (bCrouching)
               TweenToCrouchShoot(0);
          else
               TweenToShoot(0);
     }
     CheckAttack(true);
     if (PickDestination() != DEST_NewLocation)
     {
          if (!IsWeaponReloading() || bCrouching)
               TurnToward(enemy);
          else
               Sleep(0);
          Goto('ContinueFire');
     }
     Goto('RunToRange');

ContinueAttack:
ContinueFromDoor:
     CheckAttack(true);
     if (PickDestination() != DEST_NewLocation)
          Goto('Fire');
     else
          Goto('RunToRange');

}

defaultproperties
{
}
