///////////////////////////////////////////////////////////////////////////////////
// REAL TIME STRATEGY ADVENTURE - Kit
// FILE: rtsa_p_garrison
// NAME: Garrison Heartbeat script
// SCRIPTED BY: Deva Bryson Winblood
// DATE: 06/16/2003
// LAST MODIFIED: 03/07/2005
///////////////////////////////////////////////////////////////////////////////////
#include "rtsa_headerp"  // Parser function header
#include "antistuck_h"

void fnMsgAllPlayers(string sMsg)
{ // PURPOSE: send a message to all players
  object oPC=GetFirstPC();
  while(oPC!=OBJECT_INVALID)
  { // each player
    SendMessageToPC(oPC,sMsg);
    oPC=GetNextPC();
  } // each player
} // fnMsgAllPlayers()

string fnBanter(int nLang,int nPhrase,int nSpeaker)
{ // return the proper phrase for language for phrase 1-5, speaker 1 or 2
  string sRet="";
  switch(nPhrase)
  { // phrase switch
    case 1: { // Stay Alert line
      switch(nLang)
      { // Language Switch
        case 1: { // Ruffian
        if (nSpeaker==2) sRet="Aye, they're out in force lately.";
        else sRet="Keep an eye out for the militia.";
        break;
        } // Ruffian
        case 2: { // Noble
        if (nSpeaker==2) sRet="Yes sir, sergeant.";
        else sRet="Stay sharp.  There are ruffians about.";
        break;
        } // Noble
        case 3: { // Cant
        if (nSpeaker==2) sRet="I'm fly.";
        else sRet="If the lout ribald smokes you I'll razor your whistle!";
        break;
        } // Cant
        case 4: { // Mercenary
        if (nSpeaker==2) sRet="The purse better be big for this one.";
        else sRet="Stay alert!  This is what we're paid for.";
        break;
        } // Mercenary
        case 5: { // Monster1
        if (nSpeaker==2) sRet="Just funny shape rock.";
        else sRet="Urk.  I see elf!";
        break;
        } // Monster1
        case 6: { // Monster2
        if (nSpeaker==2) sRet="That's more pleasant than what would probably happen.";
        else sRet="Stand fast.  They will melt the skin from our bodies if anyone gets by.";
        break;
        } // Monster2
        case 7: { // Pious
        if (nSpeaker==2) sRet="Blessings of the Lord of Light be upon us.  Strike true!";
        else sRet="I sense danger nearby.";
        break;
        } // Pious
        case 8: { // Nature
        if (nSpeaker==2) sRet="That was just a shadow cast by the clouds.";
        else sRet="Beware I saw an orc!";
        break;
        } // Nature
        default: break;
      } // Language Switch
    break;
    } // Stay Alert line
    case 2: { // Hunger/Thirst Line
      switch(nLang)
      { // Language Switch
        case 1: { // Ruffian
        if (nSpeaker==2) sRet="A mug sounds good here, too.";
        else sRet="I could really do with a bit o' ale.";
        break;
        } // Ruffian
        case 2: { // Noble
        if (nSpeaker==2) sRet="I am having enough trouble doing my duty here.  Keep quiet about food!";
        else sRet="I can just imagine the lovely taste of food.";
        break;
        } // Noble
        case 3: { // Cant
        if (nSpeaker==2) sRet="You addled?";
        else sRet="Gimme the cackle-farts and grunting peck.";
        break;
        } // Cant
        case 4: { // Mercenary
        if (nSpeaker==2) sRet="Let me guess.  Trail bread and beans again?";
        else sRet="Almost time to hit the mess for some grub.";
        break;
        } // Mercenary
        case 5: { // Monster1
        if (nSpeaker==2) sRet="Gnaw da bones an eat da flesh!";
        else sRet="Hunnnnnngry!";
        break;
        } // Monster1
        case 6: { // Monster2
        if (nSpeaker==2) sRet="Nothing quite quenches the thirst like it.";
        else sRet="I have a thirst for fresh blood.";
        break;
        } // Monster2
        case 7: { // Pious
        if (nSpeaker==2) sRet="Aaah, blessed respite.";
        else sRet="Almost time to feast and take a break.";
        break;
        } // Pious
        case 8: { // Nature
        if (nSpeaker==2) sRet="Nature's bounty is all around us, friend.  Grab some berries off that bush there.";
        else sRet="I could use a bite to eat.";
        break;
        } // Nature
        default: break;
      } // Language Switch
    break;
    } // Hunger/Thirst Line
    case 3: { // Put Down/Dream/Light Chatter Line
      switch(nLang)
      { // Language Switch
        case 1: { // Ruffian
        if (nSpeaker==2) sRet="Don't you be eyeing my weapon.  I don't mean no buggering from ye!";
        else sRet="I be eyeing your weapon there.  You be caring well for it I see.";
        break;
        } // Ruffian
        case 2: { // Noble
        if (nSpeaker==2) sRet="I did too.  I wonder if it was a sign?";
        else sRet="I had a bad dream last night.";
        break;
        } // Noble
        case 3: { // Cant
        if (nSpeaker==2) sRet="Nay!";
        else sRet="You be cutty-eyeing my book?";
        break;
        } // Cant
        case 4: { // Mercenary
        if (nSpeaker==2) sRet="Too bad you lost your cloak playing dice last night.";
        else sRet="Looks like rain coming.";
        break;
        } // Mercenary
        case 5: { // Monster1
        if (nSpeaker==2) sRet="Rrrraagh! I kill you for that.";
        else sRet="*sniff* You smell like rose bush.";
        break;
        } // Monster1
        case 6: { // Monster2
        if (nSpeaker==2) sRet="Haha that was a good one!  Mine was not near as delectable.";
        else sRet="I dreamt last night about deboning a human woman and playing with her flesh.";
        break;
        } // Monster2
        case 7: { // Pious
        if (nSpeaker==2) sRet="Aye, the wonderful voices of the choir just as the sun crested the horizon was a nice touch.";
        else sRet="The morning prayer today was quite nice.";
        break;
        } // Pious
        case 8: { // Nature
        if (nSpeaker==2) sRet="Lets hope it is not so.";
        else sRet="I had a dream that if more than a dream means dark times are coming.";
        break;
        } // Nature
        default: break;
      } // Language Switch
    break;
    } // Put Down/Dream/Light Chatter Line
    case 4: { // Recall Loved One/Place Line
      switch(nLang)
      { // Language Switch
        case 1: { // Ruffian
        if (nSpeaker==2) sRet="And I'm the Duke.  If ya found a stone that size, why are ye still on guard duty for a copper a day?";
        else sRet="...and in the chest was a gem big as my fist!";
        break;
        } // Ruffian
        case 2: { // Noble
        if (nSpeaker==2) sRet="Don't remind me.";
        else sRet="My special one has beautiful eyes.";
        break;
        } // Noble
        case 3: { // Cant
        if (nSpeaker==2) sRet="Aye, if'n spoils ye has, Cuz ye seedy?";
        else sRet="I've just milled some rum swag to bate.";
        break;
        } // Cant
        case 4: { // Mercenary
        if (nSpeaker==2) sRet="Aye, I had someone special when I was a young sprout your age.";
        else sRet="What about you?";
        break;
        } // Mercenary
        case 5: { // Monster1
        if (nSpeaker==2) sRet="Ach! It burns us! Evil light!";
        else sRet="Me want dark cave.";
        break;
        } // Monster1
        case 6: { // Monster2
        if (nSpeaker==2) sRet="Yeah, me too.  The portions of scalp that clung to those spikes always made me think of home.";
        else sRet="I miss the old pit with all its bloody spikes.";
        break;
        } // Monster2
        case 7: { // Pious
        if (nSpeaker==2) sRet="Probably ascended to the higher planes, they were so kindly and good.";
        else sRet="What happened to them?";
        break;
        } // Pious
        case 8: { // Nature
        if (nSpeaker==2) sRet="I agree that one's beauty is second to none.  Except maybe someone I met long ago.";
        else sRet="�skin was pale and shined beneath the moonlight.";
        break;
        } // Nature
        default: break;
      } // Language Switch
    break;
    } // Recall Loved One/Place Line
    case 5: { // Bored Line
      switch(nLang)
      { // Language Switch
        case 1: { // Ruffian
        if (nSpeaker==2) sRet="I hear ye.";
        else sRet="I hope we have some excitement here soon.";
        break;
        } // Ruffian
        case 2: { // Noble
        if (nSpeaker==2) sRet="Surely, but we get paid to stand here.";
        else sRet="These days just seem to go on and on.";
        break;
        } // Noble
        case 3: { // Cant
        if (nSpeaker==2) sRet="Nay, I benar bother nix.";
        else sRet="Couch a hogshead in the strommel.";
        break;
        } // Cant
        case 4: { // Mercenary
        if (nSpeaker==2) sRet="So you'd rather have someone trying to stick a sword through your belly, then?";
        else sRet="Even THIS much money isn't worth all this boredom.";
        break;
        } // Mercenary
        case 5: { // Monster1
        if (nSpeaker==2) sRet="Rock chopper chucking!";
        else sRet="Me want dwarf to throw at wall.";
        break;
        } // Monster1
        case 6: { // Monster2
        if (nSpeaker==2) sRet="Like squashing a baby or two?  Harhar.";
        else sRet="Hell! I can think of much better things than standing here.";
        break;
        } // Monster2
        case 7: { // Pious
        if (nSpeaker==2) sRet="Aye, not a single enemy seen for quite a time now.";
        else sRet="Peace at last.";
        break;
        } // Pious
        case 8: { // Nature
        if (nSpeaker==2) sRet="I agree.  I am sure that tree over there was not there when we got here.";
        else sRet="Trees grow faster than time passes when standing this watch.";
        break;
        } // Nature
        default: break;
      } // Language Switch
    break;
    } // Bored Line
    default: break;
  } // phrase switch
  return sRet;
} // fnBanter()

int fnUnitCount(int nUC)
{ // count units
  int nRet=0;
  object oH=OBJECT_INVALID;
  int nC=0;
  while(nC<nUC)
  { // count
    oH=GetLocalObject(OBJECT_SELF,"oGarrison"+IntToString(nC+1));
    if (oH!=OBJECT_INVALID&&GetIsDead(oH)!=TRUE) nRet++;
    nC++;
  } // count
  return nRet;
} // fnUnitCount()

int fnUnitSpot(int nUC)
{ // find a valid object number
   int nC=0;
   int nRet=0;
   object oH=OBJECT_INVALID;
   while(nC<nUC&&nRet==0)
   { // count
     oH=GetLocalObject(OBJECT_SELF,"oGarrison"+IntToString(nC+1));
     if (oH==OBJECT_INVALID||GetIsDead(oH)==TRUE) nRet=nC+1;
     nC++;
   } // count
   return nRet;
} // fnUnitSpot()

void fnDespawn()
{ // AI units
  if (GetIsInCombat(OBJECT_SELF)!=TRUE)
    DestroyObject(OBJECT_SELF);
  else { DelayCommand(10.0,fnDespawn()); }
} // fnDespawn()

void fnDespawnLeader(object oLeader)
{ // destroy leader and the AI units it has
  object oAI1=GetLocalObject(oLeader,"oAIUnit1");
  object oAI2=GetLocalObject(oLeader,"oAIUnit2");
  object oAI3=GetLocalObject(oLeader,"oAIUnit3");
  if (oAI1!=OBJECT_INVALID) AssignCommand(oAI1,fnDespawn());
  if (oAI2!=OBJECT_INVALID) AssignCommand(oAI2,fnDespawn());
  if (oAI3!=OBJECT_INVALID) AssignCommand(oAI3,fnDespawn());
  DestroyObject(oLeader);
} // fnDespawnLeader()

int fnGetLeaderLevel()
{ //
  int nRet=1;
  object oMe=OBJECT_SELF;
  object oMod=GetModule();
  object oLeader;
  string sID=GetLocalString(oMe,"sTeamID");
  oLeader=GetLocalObject(oMod,"oTeamLead"+sID);
  if (oLeader==OBJECT_INVALID)
  { // no leadership
    oMod=GetObjectByTag("LEADER_"+sID);
    if (oMod!=OBJECT_INVALID)
    { // leader check
      oLeader=GetLocalObject(oMod,"oLeader");
      if (oLeader!=OBJECT_INVALID&&GetIsDead(oLeader)==FALSE)
      { // leader found
        nRet=GetLevelByPosition(1,oLeader);
        nRet=nRet+GetLevelByPosition(2,oLeader);
        nRet=nRet+GetLevelByPosition(3,oLeader);
      } // leader found
    } // leader check
  } // no leadership
  else
  { // get level of leader
    if (oLeader==OBJECT_INVALID) SendMessageToPC(GetFirstPC(),"RTSA_P_GARRISON ERROR:Leader not found. ["+GetTag(oMe)+"]");
    nRet=GetLevelByPosition(1,oLeader);
    nRet=nRet+GetLevelByPosition(2,oLeader);
    nRet=nRet+GetLevelByPosition(3,oLeader);
  } // get level of leader
  //SendMessageToPC(GetFirstPC(),"Leader Level for ["+GetTag(oMe)+"]="+IntToString(nRet));
  return nRet;
} // fnGetLeaderLevel()

void fnSpawn(string sR1,string sR2,string sR3,int nR2,int nR3,int nUC,int nSF)
{ // spawn units
  object oMe=OBJECT_SELF;
  object oSpawn;
  int nCount=fnUnitCount(nUC);
  int nRatio=GetLocalInt(oMe,"nRatio");
  int nN;
  int nAllowed=((fnGetLeaderLevel()+1)/2)+1;
  if (nRatio==0) nRatio=1;
  if (nAllowed>nUC) nAllowed=nUC;
  nN=nRatio/nR2;
  if (nCount<nAllowed)
  { // spawn is okay
    nCount=fnUnitSpot(nUC);
    if (nCount==0) SendMessageToPC(GetFirstPC(),"RTSA_P_GARRISON ERROR: Could not find a spawn object variable for "+GetTag(oMe)+".");
    if (nRatio==nR3)
    { // Spawn R3
      //SendMessageToPC(GetFirstPC(),"Spawn R3");
      oSpawn=CreateObject(OBJECT_TYPE_CREATURE,sR3,GetLocation(oMe),TRUE);
    } // spawn R3
    else if (nR2*nN==nRatio)
    { // Spawn R2
      //SendMessageToPC(GetFirstPC(),"Spawn R2");
      oSpawn=CreateObject(OBJECT_TYPE_CREATURE,sR2,GetLocation(oMe),TRUE);
    } // Spawn R2
    else
    { // spawn R1
      oSpawn=CreateObject(OBJECT_TYPE_CREATURE,sR1,GetLocation(oMe),TRUE);
    } // spawn R1
    nRatio++;
    if (nRatio>nAllowed) nRatio=1;
    //SendMessageToPC(GetFirstPC(),"SPAWN nRatio:"+IntToString(nRatio)+" nR2:"+IntToString(nR2)+" nR3:"+IntToString(nR3));
    SetLocalInt(oMe,"nRatio",nRatio);
    SetLocalObject(oMe,"oGarrison"+IntToString(nCount),oSpawn);
    SetLocalInt(oSpawn,"bGarrison",TRUE);
  } // spawn is okay
  if (nSF==0) nSF=3;
  DelayCommand(IntToFloat(nSF)*60.0,fnSpawn(sR1,sR2,sR3,nR2,nR3,nUC,nSF));
} // fnSpawn()

int fnIsBusy(object oNPC)
{ // is the object busy?
  int nRet=FALSE;
  object oAttacker=GetLastAttacker();
  float fDist=-1.0;
  object oEnemy=GetNearestCreature(CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN,OBJECT_SELF,1,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_IS_ALIVE,TRUE);
  if (oEnemy!=OBJECT_INVALID) return TRUE;
  if (IsInConversation(oNPC)==TRUE) return TRUE;
  if (!GetIsDead(oAttacker))
  { // check on last attacker
   fDist=GetDistanceToObject(oAttacker);
   if (fDist!=-1.0&&fDist<20.0) return TRUE;
  } // check on last attacker
  return nRet;
} // fnIsBusy()

void fnReturnToGuardArea(object oNPC,object oArea)
{ // return to guard area
  if (fnIsBusy(oNPC)==FALSE)
  { // go ahead and return
    AssignCommand(oNPC,ClearAllActions());
    AssignCommand(oNPC,ActionForceMoveToObject(oArea,FALSE,6.0,90.0));
  } // go ahead and return
  else
  { // Busy
    SetLocalInt(oArea,"nAttacked",0);
  } // Busy
} // fnReturnToGuardArea()

void fnFindSpeechPartner(int nUnit,int nUC,int nLang,int nPhrase,object oSpawner,object oStarter)
{
  int nUnitN=nUnit+1;
  object oPartner;
  if (nUnitN>nUC) nUnitN=1;
  oPartner=GetLocalObject(oSpawner,"oGarrison"+IntToString(nUnitN));
  if ((oPartner!=OBJECT_INVALID&&GetIsDead(oPartner)!=TRUE)||oPartner==oStarter)
  { // speak
    DelayCommand(0.5,AssignCommand(oPartner,SpeakString(fnBanter(nLang,nPhrase,2))));
  } // speak
  else
  { // keep searching via delay loop recursion
    DelayCommand(0.2,fnFindSpeechPartner(nUnitN,nUC,nLang,nPhrase,oSpawner,oStarter));
  } // keep searching via delay loop recursion
} // fnFindSpeechPartner()

void fnSpeechAction(object oNPC,int nLang, int nUnit, object oSpawner)
{ // do speaking interplay
  object oPartner=OBJECT_INVALID;
  int nAt=GetLocalInt(oSpawner,"nAttacked");
  int nR;
  int nUC=GetLocalInt(oSpawner,"nUnitCap");
  nAt++;
  if (nAt>7) nAt=7;
  nR=Random(nAt)+1;
  if (nR>5) nR=5;
  SetLocalInt(oSpawner,"nAttacked",nAt);
  SetLocalInt(oSpawner,"nSpoken",TRUE);
  AssignCommand(oNPC,ClearAllActions());
  AssignCommand(oNPC,SpeakString(fnBanter(nLang,nR,1)));
  fnFindSpeechPartner(nUnit,nUC,nLang,nR,oSpawner,oNPC);
  //SendMessageToPC(GetFirstPC(),"Speech: Partner["+IntToString(nC-1)+"] nLang:"+IntToString(nLang)+" Phrase:"+IntToString(nR));
} // fnSpeechAction()

void fnDoSitAction(object oNPC)
{ // sit on chair if available else crosslegged
  object oChair=GetNearestObjectByTag("Chair",oNPC);
  float fDist=GetDistanceBetween(oNPC,oChair);
  if (fDist<10.0&&fDist!=0.0&&GetSittingCreature(oChair)==OBJECT_INVALID)
  { // do sit
    AssignCommand(oNPC,ClearAllActions());
    AssignCommand(oNPC,ActionMoveToObject(oChair));
    AssignCommand(oNPC,ActionSit(oChair));
  } // do sit
  else
  { // crosslegged
    AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS,1.0,30.0));
  } // crosslegged
} // fnDoSitAction()

void fnRandomCommand(object oNPC,int nLang, int nUnit, object oSpawner)
{ // Random Command
  int nR; // random
  object oH; // Object handle
  int nSpoken=GetLocalInt(oSpawner,"nSpoken");
  int bBanterDisabled=GetLocalInt(GetModule(),"bBanterDisabled");
  if (fnIsBusy(oNPC)==FALSE)
  { // go ahead and do random commands
    nR=Random(5)+1;
    //SendMessageToPC(GetFirstPC(),"fnRandomCommand("+GetTag(oNPC)+" #:"+IntToString(nR)+")");
    switch(nR)
    { // random action switch
      case 1: {// do speech
       if(nSpoken==FALSE&&!bBanterDisabled)fnSpeechAction(oNPC,nLang,nUnit,oSpawner);
      break;
      } // do speech
      case 2: { // Sit in chair or Cross legged
        DelayCommand(5.0,fnDoSitAction(oNPC));
      break;
      } // Sit in chair or Cross legged
      case 3: { // Random movement
       oH=GetNearestObject(OBJECT_TYPE_PLACEABLE,oSpawner,d20());
       if (oH==OBJECT_INVALID) oH=GetNearestObject(OBJECT_TYPE_PLACEABLE,oSpawner,1);
       DelayCommand(5.1,AssignCommand(oNPC,ASActionMoveToObject(oH)));
       DelayCommand(13.1,AssignCommand(oNPC,ClearAllActions()));
      break;
      } // Random movement
      case 4: { // Look far animation
        DelayCommand(5.0,AssignCommand(oNPC,ClearAllActions()));
        DelayCommand(5.1,AssignCommand(oNPC,SetFacing(GetFacing(oSpawner)+180.0)));
        DelayCommand(5.2,AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_LOOK_FAR,1.0,10.0)));
      break;
      } // Look far animation
      case 5: { // bored animation
        DelayCommand(5.0,AssignCommand(oNPC,ClearAllActions()));
        DelayCommand(5.1,AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_BORED,1.0,10.0)));
      break;
      } // bored animation
      default: break;
    } // random action switch
  } // go ahead and do Random commands
  else
  { // Busy
    SetLocalInt(oSpawner,"nAttacked",0);
  } // Busy
} // fnRandomCommand()

void fnSubGarrison(object oUnit,int nUnit,int nLang,object oMe)
{ // actions for NPCS
  float fDist=GetDistanceBetween(oUnit,oMe);
  //SendMessageToPC(GetFirstPC(),"fnSubGarrison("+GetTag(oUnit)+")");
  if (fDist>15.0||(fDist==0.0&&GetArea(oUnit)!=GetArea(oMe)))
  { // unit is too far away
    DelayCommand(3.0,fnReturnToGuardArea(oUnit,oMe));
  } // unit is too far away
  else
  { // assign a random command
    DelayCommand(3.0,fnRandomCommand(oUnit,nLang,nUnit,oMe));
  } // assign a random command
} // fnSubGarrison()

void fnGarrisonLoop(object oMe,int nUnit,int nUC, int nLang)
{ // delayed loop ANTI-TMI
  int nDelay=d4();
  object oUnit=GetLocalObject(oMe,"oGarrison"+IntToString(nUnit));
  //SendMessageToPC(GetFirstPC(),"fnGarrisonLoop("+GetTag(oMe)+",UNIT:"+IntToString(nUnit)+")");
  if(oUnit!=OBJECT_INVALID&&GetIsDead(oUnit)!=TRUE) DelayCommand(IntToFloat(nDelay),fnSubGarrison(oUnit,nUnit,nLang,oMe));
  if (nUnit<nUC)
    DelayCommand(0.3,fnGarrisonLoop(oMe,nUnit+1,nUC,nLang));
} // fnGarrisonLoop()

void fnGarrison()
{ // run the garrison
  object oMe=OBJECT_SELF;
  object oMod=GetModule();
  string sID=GetLocalString(oMe,"sTeamID");
  int nTeamNum=GetLocalInt(oMe,"nTeamNum");
  string sRes1=GetLocalString(oMe,"sRes1");
  string sRes2=GetLocalString(oMe,"sRes2");
  string sRes3=GetLocalString(oMe,"sRes3");
  int nRatio2=GetLocalInt(oMe,"nRatio2");
  int nRatio3=GetLocalInt(oMe,"nRatio3");
  int nSpawnFreq=GetLocalInt(oMe,"nSpawnFreq");
  int nUnitCap=GetLocalInt(oMe,"nUnitCap");
  int nLang=GetLocalInt(oMe,"nLang");
  int nActFreq=GetLocalInt(oMe,"nActFreq");
  int nAttacked=GetLocalInt(oMe,"nAttacked");
  object oEnemy=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oMe,1,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC);
  if (oEnemy==OBJECT_INVALID||GetDistanceBetween(oMe,oEnemy)>39.9)
  { // okay to do things
  //SendMessageToPC(GetFirstPC(),"fnGarrison("+GetTag(oMe)+")");
  SetLocalInt(oMe,"nSpoken",FALSE);
  if (GetLocalInt(oMe,"nSpawning")!=TRUE)
  { // initiate spawning
    fnSpawn(sRes1,sRes2,sRes3,nRatio2,nRatio3,nUnitCap,nSpawnFreq);
    SetLocalInt(oMe,"nSpawning",TRUE);
  } // initiate spawning
  if (GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,oMe,1)!=OBJECT_INVALID)
  { // PC is near
    fnGarrisonLoop(oMe,1,nUnitCap,nLang);
  } // PC is near
  if (nActFreq<10) nActFreq=10;
  } // okay to do things
  DelayCommand(IntToFloat(nActFreq),fnGarrison());
} // fnGarrison()

int fnLangChoose(string sIn)
{ // which language chosen
  int nRet=0;
  if (sIn=="RUFFIAN") nRet=1;
  else if (sIn=="NOBLE") nRet=2;
  else if (sIn=="CANT") nRet=3;
  else if (sIn=="MERCENARY") nRet=4;
  else if (sIn=="MONSTER1") nRet=5;
  else if (sIn=="MONSTER2") nRet=6;
  else if (sIn=="PIOUS") nRet=7;
  else if (sIn=="NATURE") nRet=8;
  return nRet;
} // fnLangChoose()

void fnInitializeGarrison()
{ // start the RTSA_GARRISON process
  object oMe=OBJECT_SELF;
  string sTag=GetTag(oMe);
  string sName=GetName(oMe);
  string sParse;
  int nN1;
  string sID=GetLocalString(oMe,"sTeamID");
  object oMod=GetModule();
  //SendMessageToPC(GetFirstPC(),"Initialize Garrison");
  SetLocalString(oMe,"sTeamID",sID);
  nN1=GetLocalInt(oMod,"n"+sID+"Num");
  SetLocalInt(oMe,"nTeamNum",nN1);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalString(oMe,"sRes1",sParse);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalString(oMe,"sRes2",sParse);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalString(oMe,"sRes3",sParse);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalInt(oMe,"nRatio2",StringToInt(sParse));
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalInt(oMe,"nRatio3",StringToInt(sParse));
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalInt(oMe,"nSpawnFreq",StringToInt(sParse));
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalInt(oMe,"nUnitCap",StringToInt(sParse));
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalInt(oMe,"nLang",fnLangChoose(sParse));
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalInt(oMe,"nActFreq",StringToInt(sParse));
  DelayCommand(1.0,fnGarrison());
} // fnInitializeGarrison()


void fnMoveThroughOuterPlanes(object oNPC)
{ // Try to escape the outer planes
  object oEscape=GetLocalObject(oNPC,"oEscapeDest");
  float fD=GetLocalFloat(oNPC,"fEscapeDest");
  if (oNPC!=OBJECT_INVALID)
  { // NPC exists
  if (GetArea(oEscape)!=GetArea(oNPC)||GetDistanceBetween(oNPC,oEscape)<1.5)
  { // not in same area or very close
    oEscape=GetNearestObjectByTag("DEAD_LEADER_WALK",oNPC,1);
    if (oEscape!=OBJECT_INVALID)
    { // move
      SetLocalObject(oNPC,"oEscapeDest",oEscape);
      SetLocalFloat(oNPC,"fEscapeDest",0.0);
    } // move
    else
    { // stuck
      SendMessageToPC(GetFirstPC(),"The leader '"+GetName(oNPC)+"' is stuck in area '"+GetName(GetArea(oNPC))+"!'");
    } // stuck
  } // not in same area or very close
  else
  { // try to reach destination
    if (fD==0.0)
    { // first move phase
      AssignCommand(oNPC,ASActionMoveToObject(oEscape,TRUE,1.0));
      fD=GetDistanceBetween(oNPC,oEscape);
      SetLocalFloat(oNPC,"fEscapeDest",fD);
    } // first move phase
    else if (fD!=GetDistanceBetween(oNPC,oEscape))
    { // has moved
      fD=GetDistanceBetween(oNPC,oEscape);
      SetLocalFloat(oNPC,"fEscapeDest",fD);
      AssignCommand(oNPC,ASActionMoveToObject(oEscape,TRUE,1.0));
    } // has moved
    else
    { // cannot reach destination try another
      oEscape=GetNearestObjectByTag("DEAD_LEADER_WALK",oNPC,1+d2());
      if (oEscape==OBJECT_INVALID) oEscape=GetNearestObjectByTag("DEAD_LEADER_WALK",oNPC,1+d2());
      if (oEscape==OBJECT_INVALID) oEscape=GetNearestObjectByTag("DEAD_LEADER_WALK",oNPC,1);
      if (oEscape==OBJECT_INVALID) SendMessageToPC(GetFirstPC(),"The leader '"+GetName(oNPC)+"' is stuck in area '"+GetName(GetArea(oNPC))+"!'");
      else
      { // escape exists
        SetLocalObject(oNPC,"oEscapeDest",oEscape);
        SetLocalFloat(oNPC,"fEscapeDest",0.0);
      } // escape exists
    } // cannot reach destination try another
  } // try to reach destination
  } // NPC exists
} // fnMoveThroughOuterPlanes()

void fnLeaderAI(object oNPC,object oSpawner)
{ // do leader AI
  object oTarget;
  int nR;
  float fDist;
  string sID=GetLocalString(oNPC,"sTeamID");
  oTarget=GetWaypointByTag(sID+"_START");
  if (GetArea(oTarget)==GetArea(oNPC))
  { // in lair
    DeleteLocalObject(oNPC,"oEscapeDest");
    DeleteLocalFloat(oNPC,"fEscapeDest");
    if(fnIsBusy(oNPC)==FALSE&&GetCurrentAction(oNPC)!=ACTION_MOVETOPOINT)
    { // okay to do stuff
      nR=d8();
      switch(nR)
      { // action switch
        case 5:
        case 6:
        case 7:
        case 8:
        case 1: { // sit in throne
          AssignCommand(oNPC,ClearAllActions());
          oTarget=GetNearestObjectByTag(sID,oNPC,1);
          if (oTarget==OBJECT_INVALID)
          oTarget=GetNearestObjectByTag("Throne_Evil",oNPC,1);
          if (oTarget==OBJECT_INVALID)
          oTarget=GetNearestObjectByTag("Throne_Good",oNPC,1);
          if (oTarget==OBJECT_INVALID)
          oTarget=GetNearestObjectByTag("Throne",oNPC,1);
          if (oTarget==OBJECT_INVALID)
          AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS,1.0,11.0));
          else
          { // sit down in chair
            AssignCommand(oNPC,ActionMoveToObject(oTarget));
            AssignCommand(oNPC,ActionSit(oTarget));
          } // sit down in chair
          break;
        } // sit in throne
        case 2: { // return to spawn area
         AssignCommand(oNPC,ClearAllActions());
         AssignCommand(oNPC,ActionForceMoveToObject(oSpawner,TRUE,1.0,90.0));
        break;
        } // return to spawn area
        case 3: { // random walk
          oTarget=GetNearestObject(OBJECT_TYPE_WAYPOINT,oNPC,d20());
          if (oTarget==OBJECT_INVALID) oTarget=GetNearestObject(OBJECT_TYPE_WAYPOINT,oNPC,1);
          AssignCommand(oNPC,ASActionMoveToObject(oTarget));
        break;
        } // random walk
        case 4: { // random animation
         nR=d6();
         if (nR==1)
         AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_SCRATCH_HEAD,1.0,4.0));
         else if (nR==2)
         AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_FIREFORGET_PAUSE_BORED,1.0,3.0));
         else if (nR==3)
         AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_TALK_LAUGHING,1.0,6.0));
         else if (nR==4)
         AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_LOOPING_TALK_FORCEFUL,1.0,6.0));
         else if (nR==5)
         AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_FIREFORGET_DRINK,1.0,3.0));
         else if (nR==6)
         AssignCommand(oNPC,ActionPlayAnimation(ANIMATION_FIREFORGET_VICTORY1,1.0,3.0));
        break;
        } // random animation
        default : break;
      } // action switch
    } // okay to do stuff
  } // in lair
  else if (fnIsBusy(oNPC)==FALSE&&oNPC!=OBJECT_INVALID)
  { // not in lair - get back
    fnMoveThroughOuterPlanes(oNPC);
  } // not in lair - get back
  if (oNPC!=OBJECT_INVALID) DelayCommand(2.0,AssignCommand(oNPC,ExecuteScript("ai_opponents",oNPC)));
  DelayCommand(12.0,fnLeaderAI(oNPC,oSpawner));
} // fnLeaderAI()

void fnLevelUp(object oNPC,object oSpawner)
{ // level up the leader
  string sLUT=GetLocalString(oSpawner,"sLevelUpType");
  object oMod=GetModule();
  int nLT=GetLocalInt(oSpawner,"nLevelTime");
  int nExp=GetLocalInt(oSpawner,"nLevelExp");
  int nLvl;
  string sRes=GetResRef(oNPC);
  string sID=GetLocalString(oNPC,"sTeamID");
  int nRV;
  object oSpawn;
  int nOpponentSpeed=GetLocalInt(oMod,"nOpponentSpeed"); // for future support of speeding or slowing the AI
  float fAIDelay=100.0;
  int bVerbose=GetLocalInt(oMod,"bVerboseAIMessaging"); // is verbose messaging on
  string sTeamName="Undead";
  if (sID=="DWF") sTeamName="Dwarven";
  else if (sID=="UNC") sTeamName="Unclean";
  else if (sID=="SPID") sTeamName="Spider Cultists";
  if (nOpponentSpeed<1) nOpponentSpeed=3;
  fAIDelay=fAIDelay*IntToFloat(nOpponentSpeed);
  nLT=GetLocalInt(GetModule(),"nAILevelSpeed");
  if (nLT==0) nLT=GetLocalInt(oSpawner,"nLevelTime");
  if (nExp==TRUE)
  {
    nLvl=GetLevelByPosition(1,oNPC);
    nLvl=nLvl+GetLevelByPosition(2,oNPC);
    nLvl=nLvl+GetLevelByPosition(3,oNPC);
    nLT=nLT+(2*(nLvl+1));
  }
  if (bVerbose)
  { // verbose message
    fnMsgAllPlayers("The leader of "+sTeamName+" has just leveled to "+IntToString(nLvl+1)+". They will level again in "+IntToString(nLT)+" minutes.");
  } // verbose message
  if (sLUT=="A")
  { // level up using 1.30 or SoU
    DelayCommand(IntToFloat(nLT)*60.0,fnLevelUp(oNPC,oSpawner));
  } // level up using 1.30 or SoU
  else
  { // resref increment method
    nRV=StringToInt(GetStringRight(sRes,2));
    sRes=GetStringLeft(sRes,GetStringLength(sRes)-2);
    if (nRV!=0)
    { // levelup
      nRV++;
      if (nRV<10) sRes=sRes+"0";
      sRes=sRes+IntToString(nRV);
      oSpawn=CreateObject(OBJECT_TYPE_CREATURE,sRes,GetLocation(oNPC),FALSE);
      SetLocalString(oSpawn,"sTeamID",sID);
      if (GetResRef(oSpawn)!=sRes)
      { // bad spawn stick with current leader
        DelayCommand(1.0,DestroyObject(oSpawn));
      } // bad spawn stick with current leader
      else
      { // new leader
        SetLocalObject(oSpawner,"oLeader",oSpawn);
        //DelayCommand(fAIDelay,ExecuteScript("ai_opponents",oSpawn));
        fnDespawnLeader(oNPC);
        if(nRV<20) DelayCommand(IntToFloat(nLT)*60.0,fnLevelUp(oSpawn,oSpawner));
        DelayCommand(12.0,fnLeaderAI(oSpawn,oSpawner));
      } // new leader
    } // levelup
  } // resref increment method
} // fnLevelUp()

void fnGarrisonL()
{ // Process Leader Garrison
  object oMod=GetModule();
  object oMe=OBJECT_SELF;
  string sID=GetLocalString(oMe,"sTeamID");
  object oPC=GetLocalObject(oMod,"oTeamLead"+sID);
  object oNPC=GetLocalObject(oMe,"oLeader");
  string sRes=GetLocalString(oMe,"sRes");
  int nLevel=GetLocalInt(oMe,"nLevelTime");
  int nStartLevel=GetLocalInt(oMod,"nGSStartingLevel");
  int nOpponentSpeed=GetLocalInt(oMod,"nOpponentSpeed"); // for future support of speeding or slowing the AI
  float fAIDelay=100.0;
  object oEnemy=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oMe,1,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC);
  if(oEnemy==OBJECT_INVALID||GetDistanceBetween(oMe,oEnemy)>39.9)
  { // !ENEMY CLOSE
  if (nOpponentSpeed<1) nOpponentSpeed=3;
  fAIDelay=fAIDelay*IntToFloat(nOpponentSpeed);
  if (GetLocalInt(oMe,"nLevelExp")==TRUE) nLevel=nLevel+2;
  if (oPC==OBJECT_INVALID&&(oNPC==OBJECT_INVALID||GetIsDead(oNPC)==TRUE))
  { // this team needs a leader
    if (nStartLevel>1)
    { // level other than 1
      sRes=GetStringLeft(sRes,GetStringLength(sRes)-2);
      if (nStartLevel<10) sRes=sRes+"0"+IntToString(nStartLevel);
      else { sRes=sRes+IntToString(nStartLevel); }
    } // level other than 1
    oNPC=CreateObject(OBJECT_TYPE_CREATURE,sRes,GetLocation(oMe),TRUE);
    SetLocalObject(oMe,"oLeader",oNPC);
    SetLocalString(oNPC,"sTeamID",sID);
    if (oNPC==OBJECT_INVALID||GetResRef(oNPC)!=sRes) SendMessageToPC(GetFirstPC(),"GARRISON LEADER ERROR: Leader resref incorrect for ["+GetTag(oMe)+"] ResRef:'"+sRes+"'");
    else
    {
     if (nStartLevel!=20) DelayCommand(IntToFloat(nLevel)*60.0,fnLevelUp(oNPC,oMe));
     DelayCommand(12.0,fnLeaderAI(oNPC,oMe));
    }
  } // this team needs a leader
  else if (oPC!=OBJECT_INVALID&&oNPC!=OBJECT_INVALID)
  { // destroy leader
    if (GetIsDead(oNPC)!=TRUE)
    { // valid
      AssignCommand(oNPC,ClearAllActions());
      AssignCommand(oNPC,SpeakString("You may now control this team.  Farewell."));
      DelayCommand(4.0,fnDespawnLeader(oNPC));
    } // valid
    DeleteLocalObject(oMe,"oLeader");
  } // destroy leader
  } // !enemy close
  DelayCommand(120.0,fnGarrisonL()); // rerun every 2 minutes make sure leader
} // fnGarrisonL()

void fnInitializeLeader()
{ // start the RTSA_GARRISONL process
  object oMe=OBJECT_SELF;
  string sTag=GetTag(oMe);
  string sName=GetName(oMe);
  string sParse;
  int nN1;
  int nR;
  string sID=GetStringRight(sTag,GetStringLength(sTag)-7);
  object oMod=GetModule();
  SetLocalString(oMe,"sTeamID",sID);
  nN1=GetLocalInt(oMod,"n"+sID+"Num");
  SetLocalInt(oMe,"nTeamNum",nN1);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalString(oMe,"sRes",sParse);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  //SetLocalInt(oMe,"nLevelTime",StringToInt(sParse));
  nR=GetLocalInt(GetModule(),"nAILevelSpeed");
  if (nR==0) nR=15;
  SetLocalInt(oMe,"nLevelTime",nR);
  nN1=FALSE;
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  if (sParse=="T"||sParse=="TRUE"||sParse=="t") nN1=TRUE;
  SetLocalInt(oMe,"nLevelExp",nN1);
  sParse=fnParse(sName);
  sName=fnRemoveParsed(sName,sParse);
  SetLocalString(oMe,"sLevelUpType",sParse);
  DelayCommand(120.0,fnGarrisonL()); // give 2 minutes for team to be commanded
} // fnInitializeLeader()

/////////////////////////////////////////////// MAIN /////////////////////////////
void main()
{
   if (GetFirstPC()!=OBJECT_INVALID&&GetLocalInt(GetModule(),"nInProgress")==TRUE)
   { // PCs are present
   if (GetLocalInt(OBJECT_SELF,"nGarrisoning")!=TRUE)
   { // initiate Garrison Process
     SetLocalInt(OBJECT_SELF,"nGarrisoning",TRUE);
     if(GetTag(OBJECT_SELF)=="RTSA_GARRISON")
       fnInitializeGarrison();
     else if (GetLocalInt(GetModule(),"nAISetting")!=-1)
       fnInitializeLeader();
     if (GetLocalInt(GetModule(),"nMonitorOn")!=TRUE) ExecuteScript("team_monitor",OBJECT_SELF);
   } // initiate Garrison Process
   } // PCs are present
}