////////////////////////////////////////////////////////////////////////////////
// cai_Support - Custom Combat AI - Support Mode
// By Deva Bryson Winblood. 11/2004
////////////////////////////////////////////////////////////////////////////////
#include "cai_inc_hos"
#include "nw_I0_generic"

int fnClassIsSpellCaster(int nPos,object oNPC)
{ // tell me if this class is a spell caster
  int nClass=GetClassByPosition(nPos,oNPC);
  int bRet=FALSE;
  switch(nClass)
  {
    case CLASS_TYPE_ARCANE_ARCHER:
    case CLASS_TYPE_BARD:
    case CLASS_TYPE_CLERIC:
    case CLASS_TYPE_DRUID:
    case CLASS_TYPE_FEY:
    case CLASS_TYPE_DRAGONDISCIPLE:
    case CLASS_TYPE_PALADIN:
    case CLASS_TYPE_PALEMASTER:
    case CLASS_TYPE_RANGER:
    case CLASS_TYPE_SHADOWDANCER:
    case CLASS_TYPE_SHIFTER:
    case CLASS_TYPE_SORCERER:
    case CLASS_TYPE_WIZARD: { bRet=TRUE; break; }
    default: { break; }
  }
  return bRet;
} // fnClassIsSpellCaster()

object fnGetBestTarget(int nRace)
{ // tell me who my best target is at the moment
  object oRet=OBJECT_INVALID;
  int nN;
  float fD;
  int nVal;
  object oOb;
  int nThis;
  int nC;
  nN=1;
  oOb=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,OBJECT_SELF,nN,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  fD=GetDistanceBetween(oOb,OBJECT_SELF);
  while(oOb!=OBJECT_INVALID&&fD<30.0)
  { // check all nearby targets
    nThis=0;
    nN++;
    if (fD<20.0) nThis++;
    if (fD<15.0) nThis++;
    if (fD<10.0) nThis++;
    if (fD<8.0) nThis++;
    if (fD<5.0) nThis++;
    fD=IntToFloat(GetCurrentHitPoints(oOb))/IntToFloat(GetMaxHitPoints(oOb));
    if (fD<0.51) nThis++;
    if (fD<0.31) nThis++;
    if (fD<0.11) nThis++;
    if (nRace==RACIAL_TYPE_UNDEAD)
    { // see if target is a cleric
      nC=GetClassByPosition(1,oOb);
      if (nC==CLASS_TYPE_CLERIC||nC==CLASS_TYPE_PALADIN) nThis=nThis+3;
      nC=GetClassByPosition(2,oOb);
      if (nC==CLASS_TYPE_CLERIC||nC==CLASS_TYPE_PALADIN) nThis=nThis+3;
      nC=GetClassByPosition(3,oOb);
      if (nC==CLASS_TYPE_CLERIC||nC==CLASS_TYPE_PALADIN) nThis=nThis+3;
    } // see if target is a cleric
    if (fnClassIsSpellCaster(1,oOb)) nThis++;
    if (fnClassIsSpellCaster(2,oOb)) nThis++;
    if (fnClassIsSpellCaster(3,oOb)) nThis++;
    if (nThis>nVal)
    { // new best target
      oRet=oOb;
      nVal=nThis;
    } // new best target
    oOb=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,OBJECT_SELF,nN,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    fD=GetDistanceBetween(oOb,OBJECT_SELF);
  } // check all nearby targets
  return oRet;
} // tell me who my best target is at the moment

void main()
{
   object oMe=OBJECT_SELF;
   object oTarget=GetAttackTarget();
   float fF;
   int nN;
   int bMUST=FALSE;
   int nMyRace=GetRacialType(oMe);
   object oBest=fnGetBestTarget(nMyRace);
   int bTargetChanged=FALSE;
   int nState=GetLocalInt(oMe,"nCAIState");
   if (oBest!=oTarget)
   {
     oTarget=oBest;
     bTargetChanged=TRUE;
   }
   if (GetIsObjectValid(oTarget))
   { // target is valid
     nState++;
     if (nState>4) nState=1;
     oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oMe,1,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
     if (GetIsObjectValid(oBest))
     { // there are enemies
       if (GetDistanceBetween(oMe,oBest)<5.1)
       { // enemy too close
         DetermineCombatRound(oBest);
       } // enemy too close
       else
       { // not close
         oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,1,CREATURE_TYPE_IS_ALIVE,TRUE);
         if (GetIsObjectValid(oBest))
         { // there are friends
           if (GetDistanceBetween(oMe,oBest)<10.1)
           { // friends are close
             nN=1;
             oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,d6(),CREATURE_TYPE_IS_ALIVE,TRUE);
             while((GetIsObjectValid(oBest)==FALSE||(GetDistanceBetween(oBest,oMe)<10.1&&GetDistanceBetween(oBest,oMe)!=0.0))&&nN<4)
             { // find friend
               nN++;
               oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,d6(),CREATURE_TYPE_IS_ALIVE,TRUE);
             } // find friend
             if (GetIsObjectValid(oBest)==FALSE) oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,1,CREATURE_TYPE_IS_ALIVE,TRUE);
             if (GetIsObjectValid(oBest))
             { // friend to help found
               nN=GetMaxHitPoints(oBest)-GetCurrentHitPoints(oBest);
               if ((nN>0||GetIsDead(oBest))&&nState!=4)
               { // healing needed
                 if (GetIsPC(oBest)) nN=caiGetAvailableHealPC(oMe,oBest);
                 else if (GetIsDead(oBest)==FALSE) {
                        nN=caiGetAvailableHeal(oMe,nN); }
                 if (nN!=0)
                 { // heal
                   AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
                 } // heal
                 else
                 { // combat
                   DetermineCombatRound(oTarget);
                 } // combat
               } // healing needed
               else if (GetMaxHitPoints(oMe)-GetCurrentHitPoints(oMe)>0)
               { // heal self
                 bMUST=FALSE;
                 if ((IntToFloat(GetCurrentHitPoints(oMe))/IntToFloat(GetMaxHitPoints(oMe)))<0.51) bMUST=TRUE;
                 nN=caiGetAvailableHeal(oMe,nN,bMUST);
                 if (nN!=0)
                 { // heal self
                   AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
                 } // heal self
                 else
                 { // combat
                   DetermineCombatRound(oTarget);
                 } // combat
               } // heal self
               else if (nState!=4)
               { // defense, buff
                 if (nState==1)
                 { // defense
                   nN=caiGetAvailableDefensiveSpell(oMe,oBest);
                   if (nN!=0)
                   { // spell
                     AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
                   } // spell
                   else
                   { // combat
                     DetermineCombatRound(oTarget);
                   } // combat
                 } // defense
                 else
                 { // buff
                   nN=caiGetAvailableBuff(oMe,oBest);
                   if (nN!=0)
                   { // spell
                     AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
                   } // spell
                   else
                   { // combat
                     DetermineCombatRound(oTarget);
                   } // combat
                 } // buff
               } // defense, buff
               else
               { // combat
                 DetermineCombatRound(oTarget);
               } // combat
             } // friend to help found
           } // friends are close
           else
           { // I am alone
             if (nState==1)
             { // defensive spell
               nN=caiGetAvailableDefensiveSpell(oMe,oMe);
               if (nN!=0)
               { // spell
                 AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
               } // spell
               else
               { // combat
                 DetermineCombatRound(oMe);
               } // combat
             } // defensive spell
             else if (nState==2||nState==4)
             { // combat
               DetermineCombatRound(oMe);
             } // combat
             else if (nState==3)
             { // buff spell
               nN=caiGetAvailableBuff(oMe,oMe);
               if (nN!=0)
               { // spell
                 AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
               } // spell
               else
               { // combat
                 DetermineCombatRound(oMe);
               } // combat
             } // buff spell
           } // I am alone
         } // there are friends
         else
         { // I am alone
           if (nState==1)
             { // defensive spell
               nN=caiGetAvailableDefensiveSpell(oMe,oMe);
               if (nN!=0)
               { // spell
                 AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
               } // spell
               else
               { // combat
                 DetermineCombatRound(oMe);
               } // combat
             } // defensive spell
             else if (nState==2||nState==4)
             { // combat
               DetermineCombatRound(oMe);
             } // combat
             else if (nState==3)
             { // buff spell
               nN=caiGetAvailableBuff(oMe,oMe);
               if (nN!=0)
               { // spell
                 AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
               } // spell
               else
               { // combat
                 DetermineCombatRound(oMe);
               } // combat
             } // buff spell
         } // I am alone
       } // not close
     } // there are enemies
   } // target is valid
   // check need to heal self and remove negative spells
   // set up sentinel for resting
   if (GetLocalInt(oMe,"bCAIRESTSENTINEL")!=TRUE)
   { // trigger the sentinel
     SetLocalInt(oMe,"bCAIRESTSENTINEL",TRUE);
     caiRestSentinel(oMe);
   } // trigger the sentinel
}