////////////////////////////////////////////////////////////////////////////////
// ai_header - shared functions used in computer controlled team AI
// By Deva Bryson Winblood.  04/13/2004
////////////////////////////////////////////////////////////////////////////////

int MAX_AI_UNITS = 30;
int AI_GUARD = 2;
int AI_FOLLOW = 5;
int AI_GUARD_LAIR = 11;
int AI_HEAL = 9;
int AI_RAID = 17;
int AI_RAID_ATTACK = 1;
int AI_RAID_GOLD = 3;
int AI_RAID_MANA = 2;
//////////////////////////////
// PROTOTYPES
//////////////////////////////

// returns the starting waypoint where players and leaders spawn
object fnAIGetLairStart(string sID);

// returns the object that the taxes are stored in for this team
object fnAIGetLairChest(string sID);

// returns how many units currently have 30 max for AI controlled team
int fnAICountMembers(string sID);

// returns a unit that matches the sTag type.  If bNotBusy is set to
// TRUE it will only return 1 that is not on a RAID, GUARD, or GUARD LAIR
object fnAIGetMemberByType(string sID,string sTag,int bNotBusy=FALSE);

// returns a unit that is not on RAID, GUARD, or GUARD LAIR
// if bIncludeGuardLair is set to TRUE then it will return them if they
// are doing Guard Lair also.
object fnAIGetNonBusyMember(string sID,int bIncludeGuardLair=FALSE);

// This returns how many of the sTag unit are currently under this AI Team
// Leaders control.
int fnAICountMemberType(string sID,string sTag);

// This AI works with 30 units.  This will return a # to an unaffiliated
// number that can be used to create a new unit.
int fnAIGetEmptyUnitNumber(string sID);

// This will create sRes unit at location and assign it to unit
// nNum 1-30.
object fnAICreateUnit(string sID,string sRes,location lLoc,int nNum);

// this returns a unit that is wounded down to nPercent or less of
// their life.  If bIncludeRaid is set to FALSE it will not return
// any units on raiding missions.
object fnAIGetWoundedUnit(string sID,int nPercent,int bIncludeRaid=FALSE);

// returns the ID of the team that has the most trespass counts against this
// team sID.
string fnAIGetLargestTrespasser(string sID);

// returns the ID of a team that has trespassed randomly chosen.
string fnAIGetRandomTrespasser(string sID);

// upgrade the unit oUnit to a new unit sRes with the same mission,
// same nNum, etc.
void fnAIUpgradeUnit(object oUnit,string sRes);

// returns the team unit type # from the tag  und25 would return 25
int fnAIGetUnitNumberFromTag(string sTag);

// This function should be called once before any of the other AI
// functions as it will populate the statistics and reduce the
// amount of processing that would otherwise be required for some
// of these.
void fnAISetupStatistics(string sID);

//////////////////////////////
// FUNCTIONS
//////////////////////////////
object fnAIGetLairStart(string sID)
{
  object oWP=GetWaypointByTag(sID+"_START");
  return oWP;
} // fnAIGetLairStart()

object fnAIGetLairChest(string sID)
{
  object oOb=GetObjectByTag(sID+"_CHEST");
  return oOb;
} // fnAIGetLairChest()

int fnAICountMembers(string sID)
{
  int nRet=0;
  object oMod=GetModule();
  string sUnit;
  sUnit="nAdvAIUnitT"+sID;
  nRet=GetLocalInt(oMod,sUnit+"UC");
  return nRet;
} // fnAICountMembers()

object fnAIGetMemberByType(string sID,string sTag,int bNotBusy=FALSE)
{
  object oRet=OBJECT_INVALID;
  string sUnit;
  object oMod=GetModule();
  object oUnit;
  int nState;
  int nL=1;
  while(nL<MAX_AI_UNITS+1&&oRet==OBJECT_INVALID)
  { // get member
    sUnit="oAdvAIUnit"+IntToString(nL);
    oUnit=GetLocalObject(oMod,sUnit);
    if (oUnit!=OBJECT_INVALID&&GetTag(oUnit)==sTag)
    { // busy
      if (bNotBusy==FALSE) return oRet;
      else { // see if busy
        nState=GetLocalInt(oUnit,"nMState");
        if (nState!=AI_GUARD&&nState!=AI_FOLLOW&&nState!=AI_HEAL&&nState!=AI_GUARD_LAIR&&nState!=AI_RAID) return oRet;
      } // see if busy
    } // busy
    nL++;
  } // get member
  return oRet;
} // fnAIAIGetMembersByType()

object fnAIGetNonBusyMember(string sID,int bIncludeGuardLair=FALSE)
{
  object oRet=OBJECT_INVALID;
  string sUnit;
  object oMod=GetModule();
  object oUnit;
  int nState;
  int nL=1;
  while(nL<MAX_AI_UNITS+1&&oRet==OBJECT_INVALID)
  { // get member
    sUnit="oAdvAIUnit"+IntToString(nL);
    oUnit=GetLocalObject(oMod,sUnit);
    if (oUnit!=OBJECT_INVALID)
    { // unit
      nState=GetLocalInt(oUnit,"nMState");
      if (nState!=AI_GUARD&&nState!=AI_FOLLOW&&nState!=AI_HEAL&&nState!=AI_RAID)
      { // possible candidate
        if (bIncludeGuardLair==FALSE) return oUnit;
        else if (nState!=AI_GUARD_LAIR) return oUnit;
      } // possible candidate
    } // unit
    nL++;
  } // get member
  return oRet;
} // fnAIGetNonBusyMember()

int fnAICountMemberType(string sID,string sTag)
{
  int nRet=0;
  object oMod=GetModule();
  string sUnit;
  object oUnit;
  nRet=fnAIGetUnitNumberFromTag(GetTag(oUnit));
  sUnit="nAdvAIUnitC"+sID+IntToString(nRet);
  nRet=GetLocalInt(oMod,sUnit);
  return nRet;
} // fnAICountMemberType()

int fnAIGetEmptyUnitNumber(string sID)
{
  int nRet=0;
  int nL=1;
  string sUnit;
  object oMod=GetModule();
  while(nL<MAX_AI_UNITS+1&&nRet==0)
  { // find empty slot
    sUnit="oAdvAIUnit"+IntToString(nL);
    if (GetLocalObject(oMod,sUnit)==OBJECT_INVALID) nRet=nL;
    nL++;
  } // find empty slot
  return nRet;
} // fnAIGetEmptyUnitNumber()

object fnAICreateUnit(string sID,string sRes,location lLoc,int nNum)
{
  object oRet=OBJECT_INVALID;
  object oMod=GetModule();
  string sUnit="oAdvAIUnit"+sID+IntToString(nNum);
  object oCreate=CreateObject(OBJECT_TYPE_CREATURE,sRes,lLoc);
  //object oDM=GetFirstPC();
  //while(oDM!=OBJECT_INVALID&&GetIsDM(oDM)!=TRUE)
  //{
  // oDM=GetNextPC();
  //}
  //if (GetIsDM(oDM)==TRUE) SendMessageToPC(oDM,GetName(oCreate)+" created in area "+GetName(GetArea(oCreate))+" RES="+sRes+" nNum="+IntToString(nNum));
  SetLocalString(oCreate,"sTeamID",sID);
  SetLocalObject(oMod,sUnit,oCreate);
  SetLocalInt(oCreate,"nAIControlNum",nNum);
  oRet=oCreate;
  if (oCreate!=OBJECT_INVALID) PrintString(GetName(oCreate)+" spawned.");
  return oRet;
} // fnAICreateUnit()

object fnAIGetWoundedUnit(string sID,int nPercent,int bIncludeRaid=FALSE)
{
  object oRet=OBJECT_INVALID;
  int nMax;
  int nCur;
  int nP;
  int nL=1;
  int nState;
  string sUnit;
  object oUnit;
  float fP;
  object oMod=GetModule();
  while(nL<MAX_AI_UNITS+1&&oRet==OBJECT_INVALID)
  { // find wounded
    sUnit="oAdvAIUnit"+IntToString(nL);
    oUnit=GetLocalObject(oMod,sUnit);
    if (oUnit!=OBJECT_INVALID)
    { // !OI
      nMax=GetMaxHitPoints(oUnit);
      nCur=GetCurrentHitPoints(oUnit);
      if (nMax!=nCur)
      { // wounded
        if ((bIncludeRaid==FALSE&&GetLocalInt(oUnit,"nMState")!=AI_RAID)||bIncludeRaid==TRUE)
        { // okay to use
          fP=IntToFloat(nCur)/IntToFloat(nMax);
          fP=fP*100;
          nP=FloatToInt(fP);
          if (nP<=nPercent) return oUnit;
        } // okay to use
      } // wounded
    } // !OI
    nL++;
  } // find wounded
  return oRet;
} // fnAIGetWoundedUnit()

string fnAIGetLargestTrespasser(string sID)
{
  string sRet="NA";
  object oMod=GetModule();
  string sTresPrefix="nAITrespass"+sID+"_";
  int nCount=0;
  if (sID!="DWF"&&GetLocalInt(oMod,sTresPrefix+"DWF")>nCount) { nCount=GetLocalInt(oMod,sTresPrefix+"DWF"); sRet="DWF"; }
  if (sID!="UND"&&GetLocalInt(oMod,sTresPrefix+"UND")>nCount) { nCount=GetLocalInt(oMod,sTresPrefix+"UND"); sRet="UND"; }
  if (sID!="UNC"&&GetLocalInt(oMod,sTresPrefix+"UNC")>nCount) { nCount=GetLocalInt(oMod,sTresPrefix+"UNC"); sRet="UNC"; }
  if (sID!="SPID"&&GetLocalInt(oMod,sTresPrefix+"SPID")>nCount) { nCount=GetLocalInt(oMod,sTresPrefix+"SPID"); sRet="SPID"; }
  return sRet;
} // fnAIGetLargestTrespasser()

string fnAIGetRandomTrespasser(string sID)
{
  string sRet="NA";
  object oMod=GetModule();
  int nCount=0;
  int nR;
  string sTresPrefix="nAITrespass"+sID+"_";
  if (fnAIGetLargestTrespasser(sID)!="NA")
  { // there is a trespasser
    while(sRet=="NA")
    { // get trespasser
      nR=d4();
      if (sID!="DWF"&&GetLocalInt(oMod,sTresPrefix+"DWF")>nCount&&nR==1) return "DWF";
      else if (sID!="UND"&&GetLocalInt(oMod,sTresPrefix+"UND")>nCount&&nR==2) return "UND";
      else if (sID!="UNC"&&GetLocalInt(oMod,sTresPrefix+"UNC")>nCount&&nR==3) return "UNC";
      else if (sID!="SPID"&&GetLocalInt(oMod,sTresPrefix+"SPID")>nCount&&nR==4) return "SPID";
    } // get trespasser
  } // there is a trespasser
  return sRet;
} // fnAIGetRandomTrespasser()

void fnAIUpgradeUnit(object oUnit,string sRes)
{
  string sID=GetLocalString(oUnit,"sTeamID");
  int nNum=GetLocalInt(oUnit,"nAIControlNum");
  object oMod=GetModule();
  string sUnit="oAdvAIUnit"+sID+IntToString(nNum);
  int nMState=GetLocalInt(oUnit,"nMState");
  object oDest=GetLocalObject(oUnit,"oDestWP");
  int nParm=GetLocalInt(oUnit,"nParm");
  int nRun=GetLocalInt(oUnit,"nRun");
  object oNew=CreateObject(OBJECT_TYPE_CREATURE,sRes,GetLocation(oUnit));
  DestroyObject(oUnit);
  SetLocalString(oNew,"sTeamID",sID);
  SetLocalInt(oNew,"nAIControlNum",nNum);
  SetLocalInt(oNew,"nMState",nMState);
  SetLocalObject(oNew,"oDestWP",oDest);
  SetLocalInt(oNew,"nParm",nParm);
  SetLocalInt(oNew,"nRun",nRun);
  SetLocalObject(oMod,sUnit,oNew);
} // fnAIUpgradeUnit()

int fnAIGetUnitNumberFromTag(string sTag)
{
  int nRet=0;
  string sParse=GetStringRight(sTag,2);
  if (StringToInt(GetStringLeft(sParse,1))==0) sParse=GetStringRight(sTag,1);
  nRet=StringToInt(sParse);
  return nRet;
} // fnAIGetUnitNumberFromTag()

void fnAISetupStatistics(string sID)
{
  object oMod=GetModule();
  int nL=0;
  int nS;
  string sUnit;
  object oUnit;
  int nUC=0;
  int nD=0;
  int nG=0;
  int nO=0;
  // clear statistics
  while(nL<30)
  { // clear total
    sUnit="nAdvAIUnitC"+sID+IntToString(nL);
    DeleteLocalInt(oMod,sUnit);
    nL++;
  } // clear total
  sUnit="nAdvAIUnitT"+sID;
  DeleteLocalInt(oMod,sUnit+"D"); // defensive
  DeleteLocalInt(oMod,sUnit+"G"); // guard
  DeleteLocalInt(oMod,sUnit+"O"); // offensive
  // get new statistics
  nL=1;
  while(nL<MAX_AI_UNITS+1)
  { // look at each unit
    sUnit="oAdvAIUnit"+sID+IntToString(nL);
    oUnit=GetLocalObject(oMod,sUnit);
    if (oUnit!=OBJECT_INVALID)
    { // !OI
      nS=GetLocalInt(oUnit,"nMState");
      nUC++;
      if (nS==AI_GUARD_LAIR||nS==AI_GUARD||nS==AI_HEAL) nD++;
      if (nS==AI_GUARD) nG++;
      if (nS==AI_RAID) nO++;
      nS=fnAIGetUnitNumberFromTag(GetTag(oUnit));
      sUnit="nAdvAIUnitC"+sID+IntToString(nS);
      nS=GetLocalInt(oMod,sUnit);
      nS++;
      SetLocalInt(oMod,sUnit,nS);
    } // !OI
    nL++;
  } // look at each unit
  sUnit="nAdvAIUnitT"+sID;
  SetLocalInt(oMod,sUnit+"D",nD);
  SetLocalInt(oMod,sUnit+"G",nG);
  SetLocalInt(oMod,sUnit+"O",nO);
  SetLocalInt(oMod,sUnit+"UC",nUC);
} // fnAISetupStatistics()

//void main(){}