////////////////////////////////////////////////////////////////////////////////
// hos_chat -   Replaces the invisible listeners that were needed before
// By Deva B. Winblood.   August 16th, 2008
////////////////////////////////////////////////////////////////////////////////

#include "x3_inc_string"
#include "npcactivitiesh"
#include "color_header"
#include "header_sounds"
#include "speech_j_h"
#include "prc_inc_racial"

/////////////////////////////////
// CONSTANTS
/////////////////////////////////

string sHOLLY_BERRY_WORD = "MISTLETOE";

/////////////////////////////////
// PROTOTYPES
/////////////////////////////////

void fnListHelp(object oPC);
void fnListCmd(object oPC);
void fnListTroops(object oPC);
void fnListSettings(object oPC);
void fnListTroop(object oPC);
void fnSayDM(object oPC,string sSay);
void fnSayLeader(object oPC,string sSay);
void fnSayTeam(object oPC,string sSay);
void fnSaySquad(object oPC,string sSay);
void fnTroopsCommand(object oPC,string sCmd);
void fnSpecificTroop(object oPC, string sCmd);
void fnVoiceChat(object oPC,string sMaster);
void fnAnimate(object oPC,string sMaster);
void fnVampire(object oPC,string sMaster);
void fnDetonateSeed(object oSeed);
void fnFireSeedDetonate(object oPC);
void fnSettings(object oPC,string sCmd);
void fnListLeader(object oPC);
void fnDebugThis(object oPC,string sMsg);
void fnCountContainers(object oPC);


////////////////////////////////////////////////////////////////[ MAIN ]////////
void main()
{
     string sMsg=GetPCChatMessage();
     string sOriginal=sMsg;
     object oMod=GetModule();
     object oPC=GetPCChatSpeaker();
     int nVolume=GetPCChatVolume();
     int bGameInProgress=GetLocalInt(oMod,"nInProgress");
     int nShoutTokens=GetLocalInt(oPC,"nShoutTokens");
     int bIsLeader=FALSE;
     int nShoutGameLevel=GetLocalInt(oMod,"nShoutGameLevel");
     string sLetter=GetStringLeft(sMsg,1);
     string sWord=StringParse(sMsg);
     string sTeamID=GetLocalString(oPC,"sTeamID");
     string sUpperWord=GetStringUpperCase(sWord);
     string sMsgUpper;
     string sOrigUpper=GetStringUpperCase(sOriginal);
     object oOb;
     if (GetLocalObject(oMod,"oTeamLead"+sTeamID)==oPC) bIsLeader=TRUE;
     int bWasCommand=FALSE;
     if (sLetter=="@")
     { // say to leader
         fnSayLeader(oPC,sOriginal);
         bWasCommand=TRUE;
     } // say to leader
     else if (sLetter=="[")
     { // debug
         oOb=GetItemPossessedBy(oPC,"rts_debug_wand");
         if (GetIsObjectValid(oOb))
         { // has debug wand
             SetLocalString(oPC,"sOriginal",sOriginal);
             SetLocalString(oPC,"sMsg",GetStringRight(sMsg,GetStringLength(sMsg)-1));
             ExecuteScript("debug_routine",oPC);
         } // has debug wand
         else
         { // need wand
             SendMessageToPC(oPC,"You cannot use debug commands without a debug wand.  :DEBUGWAND will create a wand.  [WARNING: This wand can be used to cheat.]  Other players will be notified when you use this.");
         } // need wand
     } // debug
     else if (sLetter=="!")
     { // say to squad
         fnSaySquad(oPC,sOriginal);
         bWasCommand=TRUE;
     } // say to squad
     else if (sLetter=="?")
     { // say to team
         fnSayTeam(oPC,sOriginal);
         bWasCommand=TRUE;
     } // say to team
     else if (sLetter=="]")
     { // say to DM and get their attention
         fnSayDM(oPC,sOriginal);
         bWasCommand=TRUE;
     } // say to DM and get their attention
     else if (sLetter=="'")
     { // say MUD style
         sMsg=GetStringRight(sMsg,GetStringLength(sMsg)-1);
         SetPCChatMessage(sMsg);
     } // say MUD style
     else if (sLetter==":")
     { // system command
         sMsg=GetStringRight(sMsg,GetStringLength(sMsg)-1);
         sWord=StringParse(sMsg," ");
         sUpperWord=GetStringUpperCase(sWord);
         if (sUpperWord=="DEBUG")
         { // debug
             fnDebugThis(oPC,sOriginal);
         } // debug
         else if (sUpperWord=="CSKIN")
         { // Change Skin Color
             sMsg=StringRemoveParsed(sMsg,sWord," ");
             SendMessageToPC(oPC,"Set Skin Color:"+sMsg);
             SetColor(oPC,COLOR_CHANNEL_SKIN,StringToInt(sMsg));
         } // Change Skin Color
         else if (sUpperWord=="CHAIR")
         { // Change hair color
             sMsg=StringRemoveParsed(sMsg,sWord," ");
             SendMessageToPC(oPC,"Set Hair Color:"+sMsg);
             SetColor(oPC,COLOR_CHANNEL_HAIR,StringToInt(sMsg));
         } // change hair color
         else if (sUpperWord=="CONTAINERS")
         { // count containers
             fnCountContainers(oPC);
         } // count containers
         else if (sUpperWord=="?"||sUpperWord=="HELP")
         { // list
             SendMessageToPC(oPC,":DEBUG <debug message>");
             SendMessageToPC(oPC,":CSKIN #");
             SendMessageToPC(oPC,":CHAIR #");
             SendMessageToPC(oPC,":CONTAINERS");
             SendMessageToPC(oPC,":COLORS");
         } // list
         else if (sUpperWord=="COLORS")
         { // list colors
             SendMessageToPC(oPC,"Skin:"+IntToString(GetColor(oPC,COLOR_CHANNEL_SKIN)));
             SendMessageToPC(oPC,"Hair:"+IntToString(GetColor(oPC,COLOR_CHANNEL_HAIR)));
             SendMessageToPC(oPC,"Tattoo1:"+IntToString(GetColor(oPC,COLOR_CHANNEL_TATTOO_1)));
             SendMessageToPC(oPC,"Tattoo2:"+IntToString(GetColor(oPC,COLOR_CHANNEL_TATTOO_2)));
         } // list colors
         else if (sUpperWord=="DEBUGWAND")
         { // create debug wand
             oOb=GetItemPossessedBy(oPC,"rts_debug_wand");
             if (!GetIsObjectValid(oOb))
             { // create
                 oOb=CreateItemOnObject("rts_debug_wand",oPC);
                 PrintString("[DEBUG WAND CREATED] by "+GetName(oOb)+" controlled by "+GetPCPlayerName(oOb));
             } // create
             SendMessageToPC(oPC,"Chat [? or [help to get a list of debug commands.");
             sMsgUpper=GetName(oPC)+" created a debug wand... it is possible to use this to cheat.";
             sMsgUpper=StringToRGBString(sMsgUpper,"744");
             oOb=GetFirstPC();
             while(GetIsObjectValid(oOb))
             { // notify players
                 if (oOb!=oPC)
                 { // don't tell the PC that created it
                     SendMessageToPC(oOb,sMsgUpper);
                 } // don't tell the PC that created it
                 oOb=GetNextPC();
             } // notify players
         } // create debug wand
         bWasCommand=TRUE;
     } // system command
     else
     { // other
         sMsg=StringRemoveParsed(sMsg,sWord," ");
         sMsgUpper=GetStringUpperCase(sMsg);
         if (sUpperWord=="LIST")
         { // list command
             if (sMsgUpper=="COMMANDS")
             { // list commands
                 fnListCmd(oPC);
             } // list commands
             else if (sMsgUpper=="HELP")
             { // list help
                 fnListHelp(oPC);
             } // list help
             else if (sMsgUpper=="TROOP")
             { // list troop
                 fnListTroop(oPC);
             } // list troop
             else if (sMsgUpper=="TROOPS")
             { // list troops
                 fnListTroops(oPC);
             } // list troops
             else if (sMsgUpper=="SETTINGS")
             { // list settings
                 fnListSettings(oPC);
             } // list settings
             else if (sMsgUpper=="LEADER")
             { // list leaders
                 fnListLeader(oPC);
             } // list leaders
             else
             { // error
                 SendMessageToPC(oPC,"TRY: list help, list commands, or list leader");
             } // error
             bWasCommand=TRUE;
         } // list command
         else if (sUpperWord=="TROOP,")
         { // single unit command
             fnSpecificTroop(oPC,GetStringLowerCase(sOriginal));
         } // single unit command
         else if (sUpperWord=="TROOPS,")
         { // multiple unit command
             fnTroopsCommand(oPC,GetStringLowerCase(sOriginal));
         } // multiple unit command
         else if (sUpperWord=="ANIM")
         { // Play animation
             fnAnimate(oPC,GetStringLowerCase(sOriginal));
             bWasCommand=TRUE;
         } // Play animation
         else if (sUpperWord=="VOICECHAT")
         { // Play Voicechat
             fnVoiceChat(oPC,GetStringLowerCase(sOriginal));
             bWasCommand=TRUE;
         } // Play Voicechat
         else if (sUpperWord=="VAMPIRE")
         { // vampire abilities
             if (GetLocalInt(oPC,"nIsVampire"))
             { // is vampire
                 fnVampire(oPC,GetStringLowerCase(sOriginal));
             } // is vampire
             bWasCommand=TRUE;
         } // vampire abilities
         else if (sUpperWord=="SETTINGS")
         { // chat and game settings adjustment
             fnSettings(oPC,GetStringLowerCase(sOriginal));
             bWasCommand=TRUE;
         } // chat and game settings adjustment
         else if (sUpperWord==sHOLLY_BERRY_WORD)
         { // fireseed
             fnFireSeedDetonate(oPC);
             bWasCommand=TRUE;
         } // fireseed
         else if (sOrigUpper=="SUMMON MINOR"||sOrigUpper=="SUMMON MEDIUM"||sOrigUpper=="SUMMON MAJOR")
         { // leader summon emergency help
             if (bIsLeader)
             { // is leader
                 oOb=GetWaypointByTag(sTeamID+"_START");
                 if (GetArea(oOb)==GetArea(oPC))
                 { // is in the lair
                     int nN=1;
                     if (sOrigUpper=="SUMMON MEDIUM") nN=2;
                     else if (sOrigUpper=="SUMMON MAJOR") nN=3;
                     SetLocalInt(oPC,"nParm",nN);
                     ExecuteScript("leader_summon",oPC);
                 } // is in the lair
                 else
                 { // not in the lair
                     SendMessageToPC(oPC,"You can only use this ability when in your lair.");
                 } // not in the lair
             } // is leader
             else
             { // not leader
                 SendMessageToPC(oPC,"Only the team leader can use this command when in the lair.");
             } // not leader
             bWasCommand=TRUE;
         } // leader summon emergency help
     } // other
     if (bWasCommand) SetPCChatMessage(""); // display nothing
     else
     { // adjust volume?
         // 0 = only leaders and PCs with shout tokens can shout [ Default ]
         // 1 = Anyone can shout
         // 2 = only people with shout tokens can shout
         if (nVolume==TALKVOLUME_SHOUT&&!GetIsDM(oPC))
         { // was a shout check rules
             if (nShoutGameLevel==2&&nShoutTokens<1)
             { // no shouting without tokens
                 SendMessageToPC(oPC,"Shout Tokens via spell or other method are required to shout.  Your volume has been lowered.");
                 SetPCChatVolume(TALKVOLUME_TALK);
             } // no shouting without tokens
             else if (nShoutGameLevel==0&&!bIsLeader&&nShoutTokens<1)
             { // only leaders and people with tokens can shout
                 SendMessageToPC(oPC,"Only Leaders or Players with unused Shout Tokens are permitted to shout.   Your volume has been lowered.");
                 SetPCChatVolume(TALKVOLUME_TALK);
             } // only leaders and people with tokens can shout
             else
             { // shouting okay
                 nShoutTokens=nShoutTokens-1;
                 if (nShoutTokens<1) nShoutTokens=0;
                 SetLocalInt(oPC,"nShoutTokens",nShoutTokens);
                 if (nShoutGameLevel==2) SendMessageToPC(oPC,"Shout Tokens Remaining:"+IntToString(nShoutTokens));
                 else if (nShoutGameLevel==0&&!bIsLeader) SendMessageToPC(oPC,"Shout Tokens Remaining:"+IntToString(nShoutTokens));
             } // shouting okay
         } // was a shout check rules
     } // adjust volume?
}
////////////////////////////////////////////////////////////////[ MAIN ]////////


///////////////////////////////////
// FUNCTIONS
///////////////////////////////////


void fnCountContainers(object oPC)
{ // PURPOSE: containers
    int nN=0;
    int nC;
    object oWP=GetObjectByTag("AREA_DEBUG",nN);
    object oC;
    SendMessageToPC(oPC,"Begin Containers capable of being cleaned report===");
    while(GetIsObjectValid(oWP))
    { // look at containers
        nC=1;
        if (GetLocalInt(GetArea(oWP),"nCleanMode")!=5)
        { // no clean
            oC=GetNearestObject(OBJECT_TYPE_PLACEABLE,oWP,nC);
            while(GetIsObjectValid(oC))
            { // check placeables
                if (!GetPlotFlag(oC)&&!GetLocalInt(oC,"bNoClean")&&GetHasInventory(oC))
                { // report
                    SendMessageToPC(oPC,"Container '"+GetName(oC)+"' in area '"+GetName(GetArea(oWP))+"'.");
                } // report
                nC++;
                oC=GetNearestObject(OBJECT_TYPE_PLACEABLE,oWP,nC);
            } // check placeables
        } // no clean
        nN++;
        oWP=GetObjectByTag("AREA_DEBUG",nN);
    } // look at containers
    SendMessageToPC(oPC,"End of Container Report===");
} // fnCountContainers


void fnSettings(object oPC,string sCmd)
{ // PURPOSE: To adjust settings for these commands
  string sMaster=sCmd;
  string sParse=StringParse(sMaster," ");
  int nN;
  sMaster=StringRemoveParsed(sMaster,sParse," "); // strip settings
  sParse=StringParse(sMaster," ");
  sMaster=StringRemoveParsed(sMaster,sParse," ");
  nN=StringToInt(sMaster);
  if (sParse=="range")
  { // range
    if (nN<1||nN>30) nN=30;
    SetLocalInt(oPC,"nVoiceRange",nN);
    SendMessageToPC(oPC,"Voice range has been set to "+IntToString(nN)+" meters.");
  } // range
  else if (sParse=="number")
  { // number
    if (nN<1||nN>40) nN=20;
    SetLocalInt(oPC,"nVoiceNumber",nN);
    SendMessageToPC(oPC,"Number of units to respond to voice commands has been set to "+IntToString(nN)+".");
  } // number
  else
  { // unknown
    SendMessageToPC(oPC,"That is an unknown settings command '"+sParse+"' try 'list settings' for more help. [You typed '"+sCmd+"']");
  } // unknown
} // fnSettings()

void fnIssueCommand(object oPC,object oCreature,int nCMD,int nOpt=0)
{ // PURPOSE: Issue the command to the unit
  string sS;
  switch(nCMD)
  { // switch
    case 1: { // Follow
      SetLocalInt(oCreature,"nMState",5);
      SetLocalInt(oCreature,"nSState",0);
      SetLocalObject(oCreature,"oDestWP",oPC);
      SetLocalInt(oCreature,"nFollowDist",nOpt);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Following"));
      break;
    } // Follow
    case 2: { // Recon
      SetLocalInt(oCreature,"nMState",14);
      SetLocalInt(oCreature,"nSState",0);
      SetLocalObject(oCreature,"oDestWP",oPC);
      SetLocalInt(oCreature,"nFollowDist",nOpt);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Reconnoitering"));
      break;
    } // Recon
    case 3: { // Guard Lair
      SetLocalInt(oCreature,"nMState",11);
      SetLocalInt(oCreature,"nSState",0);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Guarding Lair"));
      break;
    } // Guard Lair
    case 4: { // Stop
      SetLocalInt(oCreature,"nMState",7);
      SetLocalInt(oCreature,"nSState",0);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Stopped"));
      break;
    } // Stop
    case 5: { // Guard Mana
      SetLocalInt(oCreature,"nMState",2);
      SetLocalInt(oCreature,"nSState",0);
      SetLocalInt(oCreature,"nParm",4);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Mana Guard"));
      break;
    } // Guard Mana
    case 6: { // Guard Throne
      SetLocalInt(oCreature,"nMState",2);
      SetLocalInt(oCreature,"nSState",0);
      SetLocalInt(oCreature,"nParm",5);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Throne Guard"));
      break;
    } // Guard Throne
    case 7: { // Guard Here
      SetLocalInt(oCreature,"nMState",2);
      SetLocalInt(oCreature,"nSState",0);
      SetLocalInt(oCreature,"nParm",0);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Guarding"));
      break;
    } // Guard Here
    case 8: { // Raid
      SetLocalInt(oCreature,"nMState",17);
      SetLocalInt(oCreature,"nSState",0);
      if (nOpt==1) SetLocalString(oCreature,"sTeamToRaid","DWF");
      else if (nOpt==2) SetLocalString(oCreature,"sTeamToRaid","UNC");
      else if (nOpt==3) SetLocalString(oCreature,"sTeamToRaid","UND");
      else if (nOpt==4) SetLocalString(oCreature,"sTeamToRaid","SPID");
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Raiding"));
      SetLocalInt(oCreature,"nParm",1);
      break;
    } // Raid
    case 9: { // control point capture
      SetLocalInt(oCreature,"nMState",18);
      SetLocalInt(oCreature,"nSState",0);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Capturing Control Points"));
      break;
    } // control point capture
    case 10: { // Roam
      SetLocalInt(oCreature,"nMState",8);
      SetLocalInt(oCreature,"nSState",0);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Roaming"));
      break;
    } // Roam
    case 11: { // power reservoir
      SetLocalInt(oCreature,"nMState",23);
      SetLocalInt(oCreature,"nSState",0);
      if (GetAbilityScore(oCreature,ABILITY_INTELLIGENCE)>9) AssignCommand(oCreature,SpeakString("Power Reservoir Recovery Duty"));
      break;
    } // power reservoir
    case 12: { // tactics
      if (nOpt==9)
      { // disable
        DeleteLocalInt(oCreature,"bCOMBAT_AI_ON");
        DeleteLocalString(oCreature,"sCOMBAT_AI_TYPE");
      } // disable
      else
      { // enable
        sS="melee";
        if (nOpt==2) sS="ranged";
        else if (nOpt==3) sS="stealth";
        else if (nOpt==4) sS="flee";
        else if (nOpt==5) sS="hold";
        else if (nOpt==6) sS="support";
        else if (nOpt==7) sS="summon";
        else if (nOpt==8) sS="caster";
        SetLocalInt(oCreature,"bCOMBAT_AI_ON",TRUE);
        SetLocalString(oCreature,"sCOMBAT_AI_TYPE",sS);
      } // enable
      break;
    } // tactics
    default: break;
  } // switch
} // fnIssueCommand()

void fnTroopsCommand(object oPC,string sCmd)
{ // PURPOSE: Commands issued to all units within range
  int nRange=GetLocalInt(oPC,"nVoiceRange");
  int nNumber=GetLocalInt(oPC,"nVoiceNumber");
  float fRange;
  int nC=0;
  float fF;
  int nN;
  object oOb;
  string sID=GetLocalString(oPC,"sTeamID");
  string sVocalized;
  string sDo;
  string sParse;
  string sHeader;
  int bNoSubLeader;
  object oLeader=GetLocalObject(GetModule(),"oTeamLead"+sID);
  fRange=IntToFloat(nRange);
  if (fRange<1.0||fRange>30.0) fRange=30.0;
  if (nNumber<1||nNumber>40) nNumber=20;
  if (sID=="DWF") sHeader="Friends, ";
  else if (sID=="SPID") sHeader="Minions, ";
  else if (sID=="UNC") sHeader="Grunts, ";
  else if (sID=="UND") sHeader="Slaves, ";
  sDo=GetStringRight(sCmd,GetStringLength(sCmd)-7);
  //SendMessageToPC(oPC,sDo);
  while(GetStringLeft(sDo,1)==" ") { sDo=GetStringRight(sDo,GetStringLength(sDo)-1); }
  //SendMessageToPC(oPC,sDo);
  sParse=fnParse(sDo," ");
  //SendMessageToPC(oPC,"PARSE:"+sParse);
  //SendMessageToPC(oPC,sDo);
  sDo=fnRemoveParsed(sDo,sParse," ");
  //SendMessageToPC(oPC,"FINAL sDo:"+sDo);
  //SendMessageToPC(oPC,"nNumber:"+IntToString(nNumber)+"  fRange"+FloatToString(fRange));
  nN=1;
  oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC);
  fF=GetDistanceBetween(oPC,oOb);
  while(oOb!=OBJECT_INVALID&&nC<nNumber&&fF<=fRange)
  { // look for units
    //SendMessageToPC(oPC,"["+IntToString(nN)+"]"+GetName(oOb)+"   ID:"+GetLocalString(oOb,"sTeamID"));
    bNoSubLeader=GetLocalInt(oOb,"nNoSubLeader");
    if (GetLocalString(oOb,"sTeamID")==sID&&(oPC==oLeader||bNoSubLeader==FALSE))
    { // same team
      if (sParse=="follow")
      { // follow
        if (sDo=="3")
        { // 3 meters
          fnIssueCommand(oPC,oOb,1,0);
          nC++;
          if (sID=="DWF") sVocalized=" stick to me heels!";
          else if (sID=="UNC") sVocalized=" keep on me arse!";
          else if (sID=="UND") sVocalized=" follow me closely!";
          else if (sID=="SPID") sVocalized=" come with me, and stay close!";
        } // 3 meters
        else if (sDo=="5")
        { // 5 meters
          fnIssueCommand(oPC,oOb,1,1);
          nC++;
          if (sID=="DWF") sVocalized=" stick to me heels!";
          else if (sID=="UNC") sVocalized=" keep on me arse!";
          else if (sID=="UND") sVocalized=" follow me closely!";
          else if (sID=="SPID") sVocalized=" come with me, and stay close!";
        } // 5 meters
        else if (sDo=="7")
        { // 7 meters
          fnIssueCommand(oPC,oOb,1,2);
          nC++;
          if (sID=="DWF") sVocalized=" stay with me!";
          else if (sID=="UNC") sVocalized=" come wid' me!";
          else if (sID=="UND") sVocalized=" follow me!";
          else if (sID=="SPID") sVocalized=" come with me!";
        } // 7 meters
      } // follow
      else if (sParse=="recon")
      { // recon
        if (sDo=="3")
        { // 3 meters
          fnIssueCommand(oPC,oOb,2,0);
          nC++;
          if (sID=="DWF") sVocalized=" walk in front a bit!";
          else if (sID=="UNC") sVocalized=" go ahead a me!";
          else if (sID=="UND") sVocalized=" walk ahead of your master!";
          else if (sID=="SPID") sVocalized=" scout ahead a short distance!";
        } // 3 meters
        else if (sDo=="5")
        { // 5 meters
          fnIssueCommand(oPC,oOb,2,1);
          nC++;
          if (sID=="DWF") sVocalized=" walk in front a bit!";
          else if (sID=="UNC") sVocalized=" go ahead a me!";
          else if (sID=="UND") sVocalized=" walk ahead of your master!";
          else if (sID=="SPID") sVocalized=" scout ahead a short distance!";
        } // 5 meters
        else if (sDo=="7")
        { // 7 meters
          fnIssueCommand(oPC,oOb,2,2);
          nC++;
          if (sID=="DWF") sVocalized=" walk in front a ways!";
          else if (sID=="UNC") sVocalized=" go far ahead a me!";
          else if (sID=="UND") sVocalized=" walk far ahead of your master!";
          else if (sID=="SPID") sVocalized=" scout ahead a longer distance!";
        } // 7 meters
      } // recon
      else if (sParse=="guard")
      { // guard
        if (sDo=="lair")
        { // guard lair
          fnIssueCommand(oPC,oOb,3);
          nC++;
          if (sID=="DWF") sVocalized=" guard our hold!";
          else if (sID=="UNC") sVocalized=" keep the rats out of the cave!";
          else if (sID=="UND") sVocalized=" guard against trespassers!";
          else if (sID=="SPID") sVocalized=" ward our lair from those that come!";
        } // guard lair
        else if (sDo=="mana")
        { // guard mana
          fnIssueCommand(oPC,oOb,5);
          nC++;
          if (sID=="DWF") sVocalized=" protect our magic vault!";
          else if (sID=="UNC") sVocalized=" protect our power juice!";
          else if (sID=="UND") sVocalized=" defend the mana reserves!";
          else if (sID=="SPID") sVocalized=" protect our holy power source!";
        } // guard mana
        else if (sDo=="throne")
        { // guard throne
          fnIssueCommand(oPC,oOb,6);
          nC++;
          if (sID=="DWF") sVocalized=" guard the throne room!";
          else if (sID=="UNC") sVocalized=" watch out around the bosses chair!";
          else if (sID=="UND") sVocalized=" guard the throne of your master!";
          else if (sID=="SPID") sVocalized=" guard the high chair!";
        } // guard throne
        else if (sDo=="here")
        { // guard here
          fnIssueCommand(oPC,oOb,7);
          nC++;
          if (sID=="DWF") sVocalized=" guard where you stand!";
          else if (sID=="UNC") sVocalized=" stay here and looks!";
          else if (sID=="UND") sVocalized=" guard this location!";
          else if (sID=="SPID") sVocalized=" where you stand should be protected!";
        } // guard here
      } // guard
      else if (sParse=="stop")
      { // stop
        fnIssueCommand(oPC,oOb,4);
        nC++;
        if (sID=="DWF") sVocalized=" halt!";
          else if (sID=="UNC") sVocalized=" move not!";
          else if (sID=="UND") sVocalized=" stand where you are!";
          else if (sID=="SPID") sVocalized=" cease!";
      } // stop
      else if (sParse=="raid")
      { // raid
        if (sDo=="dwarves"&&sID!="DWF")
        { // dwarves
          fnIssueCommand(oPC,oOb,8,1);
          nC++;
          if (sID=="DWF") sVocalized=" guard our hold!";
          else if (sID=="UNC") sVocalized=" attack the shorties!";
          else if (sID=="UND") sVocalized=" attack the foul beards!";
          else if (sID=="SPID") sVocalized=" attack the stone brothers!";
        } // dwarves
        else if (sDo=="unclean"&&sID!="UNC")
        { // unclean
          fnIssueCommand(oPC,oOb,8,2);
          nC++;
          if (sID=="DWF") sVocalized=" attack the dirty scum!";
          else if (sID=="UNC") sVocalized=" attack the shorties!";
          else if (sID=="UND") sVocalized=" attack the mentally challenged!";
          else if (sID=="SPID") sVocalized=" attack the cave dwellers!";
        } // unclean
        else if (sDo=="undead"&&sID!="UND")
        { // undead
          fnIssueCommand(oPC,oOb,8,3);
          nC++;
          if (sID=="DWF") sVocalized=" attack those who forgot to die!";
          else if (sID=="UNC") sVocalized=" attack the death walkers!";
          else if (sID=="UND") sVocalized=" attack the foul beards!";
          else if (sID=="SPID") sVocalized=" attack the necromancers spawn!";
        } // undead
        else if (sDo=="spiders"&&sID!="SPID")
        { // spider cultists
          fnIssueCommand(oPC,oOb,8,4);
          nC++;
          if (sID=="DWF") sVocalized=" attack the brainwashed cultists!";
          else if (sID=="UNC") sVocalized=" attack the creepy crawlies!";
          else if (sID=="UND") sVocalized=" attack the drow and their vermin!";
          else if (sID=="SPID") sVocalized=" attack the necromancers spawn!";
        } // spider cultists
        else
        { // cannot do
          sVocalized="I cannot raid my own team and I must name the teams as 'dwarves, unclean, undead, or spiders'!";
        } // cannot do
      } // raid
      else if (sParse=="capture")
      { // control point
        fnIssueCommand(oPC,oOb,9);
        nC++;
        if (sID=="DWF") sVocalized=" go forth and secure the places of power!";
          else if (sID=="UNC") sVocalized=" get me some places that feel good!";
          else if (sID=="UND") sVocalized=" capture the four locations of power!";
          else if (sID=="SPID") sVocalized=" capture the control points!";
      } // control point
      else if (sParse=="roam")
      { // roam
        fnIssueCommand(oPC,oOb,10);
        nC++;
        if (sID=="DWF") sVocalized=" wander about a bit!";
          else if (sID=="UNC") sVocalized=" get lost!";
          else if (sID=="UND") sVocalized=" wander and cause mayhem!";
          else if (sID=="SPID") sVocalized=" roam where you may!";
      } // roam
      else if (sParse=="power")
      { // power reservoir
        fnIssueCommand(oPC,oOb,11);
        nC++;
        if (sID=="DWF") sVocalized=" if someone has the power doodad, get it back!";
          else if (sID=="UNC") sVocalized=" gets the power thingy if someone finds it!";
          else if (sID=="UND") sVocalized=" recover the power reservoir if others find it!";
          else if (sID=="SPID") sVocalized=" bring back the power reservoir if others have it in their possession!";
      } // power reservoir
      else if (sParse=="run")
      { // run
        SetLocalInt(oOb,"nGNBRun",TRUE);
        SetLocalInt(oOb,"nRun",TRUE);
        nC++;
        if (sID=="DWF") sVocalized=" move fast!";
          else if (sID=="UNC") sVocalized=" move!";
          else if (sID=="UND") sVocalized=" move at a running speed!";
          else if (sID=="SPID") sVocalized=" run!";
      } // run
      else if (sParse=="walk")
      { // walk
        DeleteLocalInt(oOb,"nGNBRun");
        DeleteLocalInt(oOb,"nRun");
        nC++;
        if (sID=="DWF") sVocalized=" move slow!";
          else if (sID=="UNC") sVocalized=" slow!";
          else if (sID=="UND") sVocalized=" move at a walking speed!";
          else if (sID=="SPID") sVocalized=" walk!";
      } // walk
      else if (sParse=="tactics")
      { // set tactics
        sVocalized=" use "+sDo+" tactics in combat.";
        if (sDo=="melee") fnIssueCommand(oPC,oOb,12,1);
        else if (sDo=="ranged") fnIssueCommand(oPC,oOb,12,2);
        else if (sDo=="stealth") fnIssueCommand(oPC,oOb,12,3);
        else if (sDo=="flee") fnIssueCommand(oPC,oOb,12,4);
        else if (sDo=="hold") fnIssueCommand(oPC,oOb,12,5);
        else if (sDo=="support") fnIssueCommand(oPC,oOb,12,6);
        else if (sDo=="summon") fnIssueCommand(oPC,oOb,12,7);
        else if (sDo=="caster") fnIssueCommand(oPC,oOb,12,8);
        else if (sDo=="bioware") fnIssueCommand(oPC,oOb,12,9);
        if (sDo=="support"||sDo=="summon"||sDo=="caster") sVocalized=sVocalized+"[Spell caster tactic]";
        nC++;
      } // set tactics
    } // same team
    nN++;
    oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC);
    fF=GetDistanceBetween(oPC,oOb);
  } // look for units
  if (GetStringLength(sVocalized)>2) SetPCChatMessage(sHeader+sVocalized);
  else { SetPCChatMessage();  SendMessageToPC(oPC,"Nothing to do."); }
} // fnTroopsCommand()

void fnSpecificTroop(object oPC, string sCmd)
{ // PURPOSE: Commands issued to specific unit type within range
  int nRange=GetLocalInt(oPC,"nVoiceRange");
  int nNumber=GetLocalInt(oPC,"nVoiceNumber");
  float fRange;
  int nC=0;
  float fF;
  int nN;
  object oOb;
  string sID=GetLocalString(oPC,"sTeamID");
  string sVocalized;
  string sDo;
  string sParse;
  string sHeader;
  string sName;
  int bNoSubLeader;
  object oLeader=GetLocalObject(GetModule(),"oTeamLead"+sID);
  fRange=IntToFloat(nRange);
  if (fRange<1.0||fRange>30.0) fRange=30.0;
  if (nNumber<1||nNumber>40) nNumber=20;
  if (sID=="DWF") sHeader="Friends, ";
  else if (sID=="SPID") sHeader="Minions, ";
  else if (sID=="UNC") sHeader="Grunts, ";
  else if (sID=="UND") sHeader="Slaves, ";
  sDo=GetStringRight(sCmd,GetStringLength(sCmd)-6);
  //SendMessageToPC(oPC,sDo);
  while(GetStringLeft(sDo,1)==" ") { sDo=GetStringRight(sDo,GetStringLength(sDo)-1); }
  //SendMessageToPC(oPC,sDo);
  sParse=fnParse(sDo,":");
  //SendMessageToPC(oPC,"PARSE:"+sParse);
  //SendMessageToPC(oPC,sDo);
  sDo=fnRemoveParsed(sDo,sParse,":");
  sName=sParse;
  while(GetStringLeft(sDo,1)==" ") { sDo=GetStringRight(sDo,GetStringLength(sDo)-1); }
  sParse=fnParse(sDo," ");
  sDo=fnRemoveParsed(sDo,sParse," ");
  //SendMessageToPC(oPC,"FINAL sDo:"+sDo);
  //SendMessageToPC(oPC,"nNumber:"+IntToString(nNumber)+"  fRange"+FloatToString(fRange));
  nN=1;
  oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC);
  fF=GetDistanceBetween(oPC,oOb);
  while(oOb!=OBJECT_INVALID&&nC<nNumber&&fF<=fRange)
  { // look for units
    //SendMessageToPC(oPC,"["+IntToString(nN)+"]"+GetName(oOb)+"   ID:"+GetLocalString(oOb,"sTeamID"));
    bNoSubLeader=GetLocalInt(oOb,"nNoSubLeader");
    if (GetLocalString(oOb,"sTeamID")==sID&&(oPC==oLeader||bNoSubLeader==FALSE)&&GetStringLowerCase(GetName(oOb))==sName)
    { // same team
      if (sParse=="follow")
      { // follow
        if (sDo=="3")
        { // 3 meters
          fnIssueCommand(oPC,oOb,1,0);
          nC++;
          if (sID=="DWF") sVocalized=" stick to me heels!";
          else if (sID=="UNC") sVocalized=" keep on me arse!";
          else if (sID=="UND") sVocalized=" follow me closely!";
          else if (sID=="SPID") sVocalized=" come with me, and stay close!";
        } // 3 meters
        else if (sDo=="5")
        { // 5 meters
          fnIssueCommand(oPC,oOb,1,1);
          nC++;
          if (sID=="DWF") sVocalized=" stick to me heels!";
          else if (sID=="UNC") sVocalized=" keep on me arse!";
          else if (sID=="UND") sVocalized=" follow me closely!";
          else if (sID=="SPID") sVocalized=" come with me, and stay close!";
        } // 5 meters
        else if (sDo=="7")
        { // 7 meters
          fnIssueCommand(oPC,oOb,1,2);
          nC++;
          if (sID=="DWF") sVocalized=" stay with me!";
          else if (sID=="UNC") sVocalized=" come wid' me!";
          else if (sID=="UND") sVocalized=" follow me!";
          else if (sID=="SPID") sVocalized=" come with me!";
        } // 7 meters
      } // follow
      else if (sParse=="recon")
      { // recon
        if (sDo=="3")
        { // 3 meters
          fnIssueCommand(oPC,oOb,2,0);
          nC++;
          if (sID=="DWF") sVocalized=" walk in front a bit!";
          else if (sID=="UNC") sVocalized=" go ahead a me!";
          else if (sID=="UND") sVocalized=" walk ahead of your master!";
          else if (sID=="SPID") sVocalized=" scout ahead a short distance!";
        } // 3 meters
        else if (sDo=="5")
        { // 5 meters
          fnIssueCommand(oPC,oOb,2,1);
          nC++;
          if (sID=="DWF") sVocalized=" walk in front a bit!";
          else if (sID=="UNC") sVocalized=" go ahead a me!";
          else if (sID=="UND") sVocalized=" walk ahead of your master!";
          else if (sID=="SPID") sVocalized=" scout ahead a short distance!";
        } // 5 meters
        else if (sDo=="7")
        { // 7 meters
          fnIssueCommand(oPC,oOb,2,2);
          nC++;
          if (sID=="DWF") sVocalized=" walk in front a ways!";
          else if (sID=="UNC") sVocalized=" go far ahead a me!";
          else if (sID=="UND") sVocalized=" walk far ahead of your master!";
          else if (sID=="SPID") sVocalized=" scout ahead a longer distance!";
        } // 7 meters
      } // recon
      else if (sParse=="guard")
      { // guard
        if (sDo=="lair")
        { // guard lair
          fnIssueCommand(oPC,oOb,3);
          nC++;
          if (sID=="DWF") sVocalized=" guard our hold!";
          else if (sID=="UNC") sVocalized=" keep the rats out of the cave!";
          else if (sID=="UND") sVocalized=" guard against trespassers!";
          else if (sID=="SPID") sVocalized=" ward our lair from those that come!";
        } // guard lair
        else if (sDo=="mana")
        { // guard mana
          fnIssueCommand(oPC,oOb,5);
          nC++;
          if (sID=="DWF") sVocalized=" protect our magic vault!";
          else if (sID=="UNC") sVocalized=" protect our power juice!";
          else if (sID=="UND") sVocalized=" defend the mana reserves!";
          else if (sID=="SPID") sVocalized=" protect our holy power source!";
        } // guard mana
        else if (sDo=="throne")
        { // guard throne
          fnIssueCommand(oPC,oOb,6);
          nC++;
          if (sID=="DWF") sVocalized=" guard the throne room!";
          else if (sID=="UNC") sVocalized=" watch out around the bosses chair!";
          else if (sID=="UND") sVocalized=" guard the throne of your master!";
          else if (sID=="SPID") sVocalized=" guard the high chair!";
        } // guard throne
        else if (sDo=="here")
        { // guard here
          fnIssueCommand(oPC,oOb,7);
          nC++;
          if (sID=="DWF") sVocalized=" guard where you stand!";
          else if (sID=="UNC") sVocalized=" stay here and looks!";
          else if (sID=="UND") sVocalized=" guard this location!";
          else if (sID=="SPID") sVocalized=" where you stand should be protected!";
        } // guard here
      } // guard
      else if (sParse=="stop")
      { // stop
        fnIssueCommand(oPC,oOb,4);
        nC++;
        if (sID=="DWF") sVocalized=" halt!";
          else if (sID=="UNC") sVocalized=" move not!";
          else if (sID=="UND") sVocalized=" stand where you are!";
          else if (sID=="SPID") sVocalized=" cease!";
      } // stop
      else if (sParse=="raid")
      { // raid
        if (sDo=="dwarves"&&sID!="DWF")
        { // dwarves
          fnIssueCommand(oPC,oOb,8,1);
          nC++;
          if (sID=="DWF") sVocalized=" guard our hold!";
          else if (sID=="UNC") sVocalized=" attack the shorties!";
          else if (sID=="UND") sVocalized=" attack the foul beards!";
          else if (sID=="SPID") sVocalized=" attack the stone brothers!";
        } // dwarves
        else if (sDo=="unclean"&&sID!="UNC")
        { // unclean
          fnIssueCommand(oPC,oOb,8,2);
          nC++;
          if (sID=="DWF") sVocalized=" attack the dirty scum!";
          else if (sID=="UNC") sVocalized=" attack the shorties!";
          else if (sID=="UND") sVocalized=" attack the mentally challenged!";
          else if (sID=="SPID") sVocalized=" attack the cave dwellers!";
        } // unclean
        else if (sDo=="undead"&&sID!="UND")
        { // undead
          fnIssueCommand(oPC,oOb,8,3);
          nC++;
          if (sID=="DWF") sVocalized=" attack those who forgot to die!";
          else if (sID=="UNC") sVocalized=" attack the death walkers!";
          else if (sID=="UND") sVocalized=" attack the foul beards!";
          else if (sID=="SPID") sVocalized=" attack the necromancers spawn!";
        } // undead
        else if (sDo=="spiders"&&sID!="SPID")
        { // spider cultists
          fnIssueCommand(oPC,oOb,8,4);
          nC++;
          if (sID=="DWF") sVocalized=" attack the brainwashed cultists!";
          else if (sID=="UNC") sVocalized=" attack the creepy crawlies!";
          else if (sID=="UND") sVocalized=" attack the drow and their vermin!";
          else if (sID=="SPID") sVocalized=" attack the necromancers spawn!";
        } // spider cultists
        else
        { // cannot do
          sVocalized="I cannot raid my own team and I must name the teams as 'dwarves, unclean, undead, or spiders'!";
        } // cannot do
      } // raid
      else if (sParse=="capture")
      { // control point
        fnIssueCommand(oPC,oOb,9);
        nC++;
        if (sID=="DWF") sVocalized=" go forth and secure the places of power!";
          else if (sID=="UNC") sVocalized=" get me some places that feel good!";
          else if (sID=="UND") sVocalized=" capture the four locations of power!";
          else if (sID=="SPID") sVocalized=" capture the control points!";
      } // control point
      else if (sParse=="roam")
      { // roam
        fnIssueCommand(oPC,oOb,10);
        nC++;
        if (sID=="DWF") sVocalized=" wander about a bit!";
          else if (sID=="UNC") sVocalized=" get lost!";
          else if (sID=="UND") sVocalized=" wander and cause mayhem!";
          else if (sID=="SPID") sVocalized=" roam where you may!";
      } // roam
      else if (sParse=="power")
      { // power reservoir
        fnIssueCommand(oPC,oOb,11);
        nC++;
        if (sID=="DWF") sVocalized=" if someone has the power doodad, get it back!";
          else if (sID=="UNC") sVocalized=" gets the power thingy if someone finds it!";
          else if (sID=="UND") sVocalized=" recover the power reservoir if others find it!";
          else if (sID=="SPID") sVocalized=" bring back the power reservoir if others have it in their possession!";
      } // power reservoir
      else if (sParse=="run")
      { // run
        SetLocalInt(oOb,"nGNBRun",TRUE);
        SetLocalInt(oOb,"nRun",TRUE);
        nC++;
        if (sID=="DWF") sVocalized=" move fast!";
          else if (sID=="UNC") sVocalized=" move!";
          else if (sID=="UND") sVocalized=" move at a running speed!";
          else if (sID=="SPID") sVocalized=" run!";
      } // run
      else if (sParse=="walk")
      { // walk
        DeleteLocalInt(oOb,"nGNBRun");
        DeleteLocalInt(oOb,"nRun");
        nC++;
        if (sID=="DWF") sVocalized=" move slow!";
          else if (sID=="UNC") sVocalized=" slow!";
          else if (sID=="UND") sVocalized=" move at a walking speed!";
          else if (sID=="SPID") sVocalized=" walk!";
      } // walk
      else if (sParse=="tactics")
      { // set tactics
        sVocalized=" use "+sDo+" tactics in combat.";
        if (sDo=="melee") fnIssueCommand(oPC,oOb,12,1);
        else if (sDo=="ranged") fnIssueCommand(oPC,oOb,12,2);
        else if (sDo=="stealth") fnIssueCommand(oPC,oOb,12,3);
        else if (sDo=="flee") fnIssueCommand(oPC,oOb,12,4);
        else if (sDo=="hold") fnIssueCommand(oPC,oOb,12,5);
        else if (sDo=="support") fnIssueCommand(oPC,oOb,12,6);
        else if (sDo=="summon") fnIssueCommand(oPC,oOb,12,7);
        else if (sDo=="caster") fnIssueCommand(oPC,oOb,12,8);
        else if (sDo=="bioware") fnIssueCommand(oPC,oOb,12,9);
        if (sDo=="support"||sDo=="summon"||sDo=="caster") sVocalized=sVocalized+"[Spell caster tactic]";
        nC++;
      } // set tactics
    } // same team
    nN++;
    oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC);
    fF=GetDistanceBetween(oPC,oOb);
  } // look for units
  if (GetStringLength(sVocalized)>2) SetPCChatMessage(sHeader+sVocalized);
  else { SetPCChatMessage();   SendMessageToPC(oPC,"Nothing to do.");}
} // fnSpecificTroop()

void fnSay(object oPC,string sSay)
{ // PURPOSE: say something outloud if you are using the DM channel
  string sWords=GetStringRight(sSay,GetStringLength(sSay)-1);
  AssignCommand(oPC,SpeakString(sWords));
} // fnSay()

void fnSayDM(object oPC,string sSay)
{ // PURPOSE: Send message to DMs
  object oDM;
  string sWords=GetStringRight(sSay,GetStringLength(sSay)-1);
  sWords=ColorRGBString(sWords,4,4,6);
  sWords=ColorRGBString("["+GetName(oPC)+"]: ",4,4,4)+sWords;
  oDM=GetFirstPC();
  while(oDM!=OBJECT_INVALID)
  { // DM
    if (GetIsDM(oDM))
    {
      SendMessageToPC(oDM,sWords);
      AssignCommand(oDM,fnSoundAlert("as_an_catmeow1"));
      ChatJ_AddToJournal(oDM,sWords);
    }
    oDM=GetNextPC();
  } // DM
} // fnSayDM()

void fnSayLeader(object oPC,string sSay)
{ // PURPOSE: send message to team leader
  object oMod=GetModule();
  string sID=GetLocalString(oPC,"sTeamID");
  object oLeader=GetLocalObject(oMod,"oTeamLead"+sID);
  string sWords=GetStringRight(sSay,GetStringLength(sSay)-1);
  sWords=ColorRGBString(sWords,6,4,4);
  sWords=ColorRGBString("["+GetName(oPC)+"]: ",4,4,4)+sWords;
  if (oLeader!=OBJECT_INVALID)
  {
    SendMessageToPC(oLeader,sWords);
    AssignCommand(oLeader,fnSoundAlert("as_an_gull1"));
    ChatJ_AddToJournal(oLeader,sWords);
  }
} // fnSayLeader()

void fnSayTeam(object oPC,string sSay)
{ // PURPOSE: send message to team members
  object oMod=GetModule();
  string sID=GetLocalString(oPC,"sTeamID");
  string sWords=GetStringRight(sSay,GetStringLength(sSay)-1);
  object oOb;
  sWords=ColorRGBString(sWords,6,6,6);
  sWords=ColorRGBString("["+GetName(oPC)+"]: ",4,4,4)+sWords;
  oOb=GetFirstPC();
  while(oOb!=OBJECT_INVALID)
  { // look for team members
    if (GetLocalString(oOb,"sTeamID")==sID)
    {
      SendMessageToPC(oOb,sWords);
      ChatJ_AddToJournal(oOb,sWords);
    }
    oOb=GetNextPC();
  } // look for team members
} // fnSayTeam()

void fnSaySquad(object oPC,string sSay)
{ // PURPOSE: Send a message to squad members
  object oMod=GetModule();
  string sID=GetLocalString(oPC,"sTeamID");
  string sWords=GetStringRight(sSay,GetStringLength(sSay)-1);
  object oOb;
  int nSQN=GetLocalInt(oPC,"nSquadNum");
  sWords=ColorRGBString(sWords,2,6,2);
  sWords=ColorRGBString("["+GetName(oPC)+"]: ",4,4,4)+sWords;
  oOb=GetFirstPC();
  while(oOb!=OBJECT_INVALID)
  { // look for team members
    if (GetLocalString(oOb,"sTeamID")==sID&&GetLocalInt(oOb,"nSquadNum")==nSQN)
    {
     AssignCommand(oOb,fnSoundAlert("as_an_woodpeckr1"));
     SendMessageToPC(oOb,sWords);
     ChatJ_AddToJournal(oOb,sWords);
    }
    oOb=GetNextPC();
  } // look for team members
} // fnSaySquad()

void fnListHelp(object oPC)
{ // PURPOSE: display help
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"|  Spoken Commands Help  |");
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"list help = this help");
  SendMessageToPC(oPC,"'<what you want to say> = will output text even if spoken into the DM channel.");
  SendMessageToPC(oPC,"]<what you want to say> = will send message to DM.");
  SendMessageToPC(oPC,"@<what you want to say> = will send message to the team leader.");
  SendMessageToPC(oPC,"!<what you want to say> = will send message to the squad you are assigned to.");
  SendMessageToPC(oPC,"?<what you want to say> = will send message to all your team members.");
  SendMessageToPC(oPC,":<type> <notes you wish to add> = will write a debug message to log file.");
  SendMessageToPC(oPC,"    Types are: placeable, transition, creature, and other.");
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"|  Roleplaying Commands  |");
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"VOICECHAT #             = will play that voice chat # for your character.");
  SendMessageToPC(oPC,"ANIM #/#                = will cause PC to play animation # for # seconds.");
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"|    Vampire Commands    |");
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"VAMPIRE command         = If the PC is a vampire they can access some of");
  SendMessageToPC(oPC,"        COMMAND         The vampire commands using spoken commands.");
  SendMessageToPC(oPC,"        level           = How much blood and vampire XP do I have?");
  SendMessageToPC(oPC,"        wolf            = costs 40 blood to shape change into a wolf");
  SendMessageToPC(oPC,"        dire            = costs 60 blood to shape change into a dire wolf");
  SendMessageToPC(oPC,"        bury            = bury yourself in the ground to protect from sunlight");
  SendMessageToPC(oPC,"        children        = costs 20 blood and summons children of the night");
  SendMessageToPC(oPC,"        heal5           = costs 1 blood and heals the PC 5 points");
  SendMessageToPC(oPC,"        heal25          = costs 5 blood and heals the PC 25 points");
  SendMessageToPC(oPC,"        coffin          = places your coffin here.  Can only be used once!");
  SendMessageToPC(oPC,"        stun            = attempts to stun nearest neutral or enemy target");
  SendMessageToPC(oPC,"        feed            = attempts to feed upon nearest neutral or enemy target");
  SendMessageToPC(oPC,"=============================");
  SendMessageToPC(oPC,"list commands = list of troop commands");
  SendMessageToPC(oPC,"list leader = list of team leader specific commands");
  SendMessageToPC(oPC,"=============================");
} // fnListHelp()

void fnListCmd(object oPC)
{ // PURPOSE: To list troop related commands
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"|  Spoken Commands Help  |");
  SendMessageToPC(oPC,"+------------------------+");
  SendMessageToPC(oPC,"list help = list other types of commands");
  SendMessageToPC(oPC,"--------------------------");
  SendMessageToPC(oPC,"Troops, <command> = issue command to troops within range");
  SendMessageToPC(oPC,"Troop, <troop name>: <command> = issue command to specific troop in range");
  SendMessageToPC(oPC,"Settings <command> = set the settings for spoken commands");
  SendMessageToPC(oPC,"More info on the above can be had by typing. list troops, list troop, or list settings.");
  SendMessageToPC(oPC,"=============================");
} // fnListCmd()

void fnListTroops(object oPC)
{ // PURPOSE: Troops commands
  SendMessageToPC(oPC,"[==== Troops, <commmand> ====]");
  SendMessageToPC(oPC,"COMMANDS:");
  SendMessageToPC(oPC,"  Follow <distance>  = follow me distance 3 or 7");
  SendMessageToPC(oPC,"  Recon  <distance>  = recon in front of me 3 or 7");
  SendMessageToPC(oPC,"  Guard Lair         = Go into guard lair mode");
  SendMessageToPC(oPC,"  Stop               = abort any existing commands");
  SendMessageToPC(oPC,"  Guard Mana         = Guard Mana Vault");
  SendMessageToPC(oPC,"  Guard Throne       = Guard Throne Room");
  SendMessageToPC(oPC,"  Guard Here         = Guard where you stand");
  SendMessageToPC(oPC,"  Raid <team>        = Go on an attack raid against team");
  SendMessageToPC(oPC,"  Capture Control    = Capture control points");
  SendMessageToPC(oPC,"  Roam               = Roam mode");
  SendMessageToPC(oPC,"  Power Reservoir    = puts them on power reservoir recovery");
  SendMessageToPC(oPC,"  Run                = tell the unit to run");
  SendMessageToPC(oPC,"  Walk               = tell the unit to walk");
  SendMessageToPC(oPC,"  Tactics <tactic>   = will set the troops to the specific combat");
  SendMessageToPC(oPC,"                       AI tactic.  TACTICS ARE: Melee, Ranged, Stealth,");
  SendMessageToPC(oPC,"                       Flee, Hold, Support, Summon, Caster, and Bioware.");
} // fnListTroops()

void fnListSettings(object oPC)
{ // PURPOSE: To list settings
  SendMessageToPC(oPC,"[==== Settings <command> ====]");
  SendMessageToPC(oPC,"COMMANDS:");
  SendMessageToPC(oPC,"  range <1-30> = how many meters people should listen to you");
  SendMessageToPC(oPC,"  number <1-40>= how many units maximum should respond to you");
  SendMessageToPC(oPC,"[==== Current Settings ====]");
  SendMessageToPC(oPC,"Range:  "+IntToString(GetLocalInt(oPC,"nVoiceRange")));
  SendMessageToPC(oPC,"Number: "+IntToString(GetLocalInt(oPC,"nVoiceNumber")));
} // fnListSettings()

void fnListTroop(object oPC)
{ // PURPOSE: To list individual troop commands
  SendMessageToPC(oPC,"[==== Troop, <name>: <command> ====]");
  SendMessageToPC(oPC,"COMMANDS:");
  SendMessageToPC(oPC,"  Follow <distance>  = follow me distance 3 or 7");
  SendMessageToPC(oPC,"  Recon  <distance>  = recon in front of me 3 or 7");
  SendMessageToPC(oPC,"  Guard Lair         = Go into guard lair mode");
  SendMessageToPC(oPC,"  Stop               = abort any existing commands");
  SendMessageToPC(oPC,"  Guard Mana         = Guard Mana Vault");
  SendMessageToPC(oPC,"  Guard Throne       = Guard Throne Room");
  SendMessageToPC(oPC,"  Guard Here         = Guard where you stand");
  SendMessageToPC(oPC,"  Raid <team>        = Go on an attack raid against team");
  SendMessageToPC(oPC,"  Capture Control    = Capture control points");
  SendMessageToPC(oPC,"  Roam               = Roam mode");
  SendMessageToPC(oPC,"  Power Reservoir    = puts them on power reservoir recovery");
  SendMessageToPC(oPC,"  Run                = tell the unit to run");
  SendMessageToPC(oPC,"  Walk               = tell the unit to walk");
  SendMessageToPC(oPC,"  Tactics <tactic>   = will set the troops to the specific combat");
  SendMessageToPC(oPC,"                       AI tactic.  TACTICS ARE: Melee, Ranged, Stealth,");
  SendMessageToPC(oPC,"                       Flee, Hold, Support, Summon, Caster, and Bioware.");
} // fnListTroop()


void fnListLeader(object oPC)
{ // PURPOSE: To list leader specific commands
    SendMessageToPC(oPC,"[==== List of Commands only useable by Leader ====]");
    SendMessageToPC(oPC,"MESSAGE <TEAM> <Message to send>  = Dispatch a messenger with the message to specified team");
    SendMessageToPC(oPC,"        Teams: dwarves, unclean, undead, and spiders");
    SendMessageToPC(oPC,"CLEAN           = Will clean clutter in lair instantly [costs 50 mana]");
    SendMessageToPC(oPC,"SUMMON <Degree> = summons units to the leader in the lair in an emergency.  Can only be use in lair.");
    SendMessageToPC(oPC,"      Degree:  Minor [costs 100 mana], Medium [costs 200 mana], Major [costs 300 mana]");
    SendMessageToPC(oPC,"      This will summon 2 Units that follow the leader, and 8 Units on Guard Lair.   All of these");
    SendMessageToPC(oPC,"      Units will despawn in 4 minutes.  These are emergency units.  They are short lived but powerful.");
} // fnListLeader()


void fnVoiceChat(object oPC,string sMaster)
{ // PURPOSE: play voice chat #
  string sChat=GetStringRight(sMaster,GetStringLength(sMaster)-10);
  int nVC=StringToInt(sChat);
  PlayVoiceChat(nVC,oPC);
} // fnVoiceChat()

void fnAnimate(object oPC,string sMaster)
{ // PURPOSE: Give PCs access to all animations for role playing purposes
  string sFull=sMaster;
  string sParse;
  int nAnim;
  int nDur;
  sFull=GetStringRight(sFull,GetStringLength(sFull)-5);
  sParse=fnParse(sFull,"/");
  sFull=fnRemoveParsed(sFull,sParse,"/");
  nAnim=StringToInt(sParse);
  nDur=StringToInt(sFull);
  AssignCommand(oPC,ActionPlayAnimation(nAnim,1.0,IntToFloat(nDur)));
} // fnAnimate()

void fnVampire(object oPC,string sMaster)
{ // PURPOSE: to handle spoken vampire commands
  string sFull=sMaster;
  int nN;
  object oOb;
  object oNeutral;
  object oEnemy;
  int nRace;
  object oC;
  int nBlood=GetLocalInt(oPC,"nBloodPool");
  string sS;
  int nXP=GetLocalInt(oPC,"nVampireXP");
  sFull=GetStringRight(sFull,GetStringLength(sFull)-8);
  if (sFull=="level")
  { // provide level information
    SendMessageToPC(oPC," VAMPIRE XP:"+IntToString(nXP)+"  BLOOD:"+IntToString(nBlood));
  } // provide level information
  else if (sFull=="wolf")
  { // wolf shape
    if (nBlood>39)
    { // enough blood
      ExecuteScript("vamp_act_wolf",oPC);
    } // enough blood
    else
    { // not enough blood
      SendMessageToPC(oPC,"You do not have enough blood to use that power.  You must feed!");
    } // not enough blood
  } // wolf shape
  else if (sFull=="dire")
  { // dire wolf shape
    if (nBlood>59)
    { // enough blood
      ExecuteScript("vamp_act_dwolf",oPC);
    } // enough blood
    else
    { // not enough blood
      SendMessageToPC(oPC,"You do not have enough blood to use that power.  You must feed!");
    } // not enough blood
  } // dire wolf shape
  else if (sFull=="bury")
  { // bury in ground
    ExecuteScript("vamp_act_bury",oPC);
  } // bury in ground
  else if (sFull=="children")
  { // children of the night
    if (nBlood>19)
    { // enough blood
      ExecuteScript("vamp_act_summon",oPC);
    } // enough blood
    else
    { // not enough blood
      SendMessageToPC(oPC,"You do not have enough blood to use that power.  You must feed!");
    } // not enough blood
  } // children of the night
  else if (sFull=="heal5")
  {  // heal 5 points
    if (nBlood>0)
    { // enough blood
      ExecuteScript("vamp_act_heal5",oPC);
    } // enough blood
    else
    { // not enough blood
      SendMessageToPC(oPC,"You do not have enough blood to use that power.  You must feed!");
    } // not enough blood
  } // heal 5 points
  else if (sFull=="heal25")
  {  // heal 25 points
    if (nBlood>4)
    { // enough blood
      ExecuteScript("vamp_act_heal25",oPC);
    } // enough blood
    else
    { // not enough blood
      SendMessageToPC(oPC,"You do not have enough blood to use that power.  You must feed!");
    } // not enough blood
  } // heal 25 points
  else if (sFull=="coffin")
  { // place coffin
    if (GetLocalInt(oPC,"nCoffinPlaced")==FALSE)
    { // place coffin
      ExecuteScript("vamp_place_coffi",oPC);
    } // place coffin
    else
    { // already placed
      SendMessageToPC(oPC,"You already placed the coffin and are only permitted to do so once.");
    } // already placed
  } // place coffin
  else if (sFull=="stun")
  { // stun nearest neutral or enemy
    nN=1;
    oOb=OBJECT_INVALID;
    oNeutral=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_NEUTRAL,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    while(oNeutral!=OBJECT_INVALID&&oOb==OBJECT_INVALID)
    { // check all nearby neutrals
      if (GetCreatureSize(oNeutral)<=GetCreatureSize(oPC)) oOb=oNeutral;
      nN++;
      oNeutral=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_NEUTRAL,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    } // check all nearby neutrals
    nN++;
    oEnemy=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    while(oEnemy!=OBJECT_INVALID)
    { // check all nearby neutrals
      oC=OBJECT_INVALID;
      if (GetCreatureSize(oEnemy)<=GetCreatureSize(oPC)) oC=oEnemy;
      if (oC!=OBJECT_INVALID)
      { // possible target
        if (oOb==OBJECT_INVALID||GetDistanceBetween(oOb,oPC)>GetDistanceBetween(oC,oPC)) oOb=oC;
      } // possible target
      nN++;
      oEnemy=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    } // check all nearby neutrals
    if (oOb!=OBJECT_INVALID)
    { // target found
      SetLocalObject(oPC,"oTarget",oOb);
      ExecuteScript("vamp_act_stun",oPC);
    } // target found
    else
    { // no good targets
      SendMessageToPC(oPC,"No suitable neutral or enemy targets are nearby.");
    } // no good targets
  } // stun nearest neutral or enemy
  else if (sFull=="feed")
  { // feed upon nearby enemy or neutral
    nN=1;
    oOb=OBJECT_INVALID;
    oNeutral=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_NEUTRAL,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    while(oNeutral!=OBJECT_INVALID&&oOb==OBJECT_INVALID)
    { // check all nearby neutrals
      nRace=MyPRCGetRacialType(oNeutral);
      if (GetIsPC(oNeutral)&&GetLocalInt(oNeutral,"nIsVampire")==FALSE) oOb=oNeutral;
      else if (nRace==RACIAL_TYPE_DWARF||nRace==RACIAL_TYPE_GNOME||nRace==RACIAL_TYPE_HALFLING) oOb=oNeutral;
      else if (nRace==RACIAL_TYPE_GIANT||nRace==RACIAL_TYPE_HALFELF||nRace==RACIAL_TYPE_HALFORC) oOb=oNeutral;
      else if (nRace==RACIAL_TYPE_HUMAN||nRace==RACIAL_TYPE_HUMANOID_GOBLINOID) oOb=oNeutral;
      else if (nRace==RACIAL_TYPE_HUMANOID_ORC||nRace==RACIAL_TYPE_FEY||nRace==RACIAL_TYPE_ELF) oOb=oNeutral;
      nN++;
      oNeutral=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_NEUTRAL,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    } // check all nearby neutrals
    nN++;
    oEnemy=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    while(oEnemy!=OBJECT_INVALID)
    { // check all nearby neutrals
      nRace=MyPRCGetRacialType(oEnemy);
      oC=OBJECT_INVALID;
      if (GetIsPC(oNeutral)&&GetLocalInt(oNeutral,"nIsVampire")==FALSE) oC=oEnemy;
      else if (nRace==RACIAL_TYPE_DWARF||nRace==RACIAL_TYPE_GNOME||nRace==RACIAL_TYPE_HALFLING) oC=oEnemy;
      else if (nRace==RACIAL_TYPE_GIANT||nRace==RACIAL_TYPE_HALFELF||nRace==RACIAL_TYPE_HALFORC) oC=oEnemy;
      else if (nRace==RACIAL_TYPE_HUMAN||nRace==RACIAL_TYPE_HUMANOID_GOBLINOID) oC=oEnemy;
      else if (nRace==RACIAL_TYPE_HUMANOID_ORC||nRace==RACIAL_TYPE_FEY||nRace==RACIAL_TYPE_ELF) oC=oEnemy;
      if (oC!=OBJECT_INVALID)
      { // possible target
        if (oOb==OBJECT_INVALID||GetDistanceBetween(oOb,oPC)>GetDistanceBetween(oC,oPC)) oOb=oC;
      } // possible target
      nN++;
      oEnemy=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
    } // check all nearby neutrals
    if (oOb!=OBJECT_INVALID)
    { // target found
      SetLocalObject(oPC,"oTarget",oOb);
      ExecuteScript("vamp_act_drink",oPC);
    } // target found
    else
    { // no good targets
      SendMessageToPC(oPC,"No suitable neutral or enemy targets are nearby.");
    } // no good targets
  } // feed upon nearby enemy or neutral
} // fnVampire()

void fnDetonateSeed(object oSeed)
{ // PURPOSE: This holly berry bomb must now explode
  object oPC=GetLocalObject(oSeed,"oOwner");
  int nLevel=GetLocalInt(oSeed,"nLevel");
  location lLoc=GetLocation(oSeed);
  int nN;
  int nDmg;
  effect eE;
  object oOb;
  int nEL=nLevel;
  int nRoll;
  int nDC;
  if (nEL>20) nEL=20;
  DestroyObject(oSeed);
  nDC=10+nLevel;
  eE=EffectVisualEffect(VFX_FNF_FIREBALL);
  ApplyEffectAtLocation(DURATION_TYPE_INSTANT,eE,lLoc);
  nN=1;
  oOb=GetNearestCreatureToLocation(CREATURE_TYPE_IS_ALIVE,TRUE,lLoc,nN);
  while(oOb!=OBJECT_INVALID&&GetDistanceBetweenLocations(lLoc,GetLocation(oOb))<=5.0)
  { // in range
    nRoll=d20()+GetReflexSavingThrow(oOb);
    if (nRoll>=nDC)
    { // saved
      nDmg=d8(nEL);
      nDmg=nDmg/2;
      eE=EffectDamage(nDmg,DAMAGE_TYPE_FIRE);
      AssignCommand(oPC,ApplyEffectToObject(DURATION_TYPE_INSTANT,eE,oOb));
      nDmg=nEL/2;
      eE=EffectDamage(nDmg,DAMAGE_TYPE_BLUDGEONING);
      AssignCommand(oPC,ApplyEffectToObject(DURATION_TYPE_INSTANT,eE,oOb));
    } // saved
    else
    { // no-save
      nDmg=d8(nEL);
      eE=EffectDamage(nDmg,DAMAGE_TYPE_FIRE);
      AssignCommand(oPC,ApplyEffectToObject(DURATION_TYPE_INSTANT,eE,oOb));
      eE=EffectDamage(nEL,DAMAGE_TYPE_BLUDGEONING);
      AssignCommand(oPC,ApplyEffectToObject(DURATION_TYPE_INSTANT,eE,oOb));
    } // no-save
    nN++;
    oOb=GetNearestCreatureToLocation(CREATURE_TYPE_IS_ALIVE,TRUE,lLoc,nN);
  } // in range
} // fnDetonateSeed()

void fnFireSeedDetonate(object oPC)
{ // PURPOSE: Detonate any placed holly berry bombs
  int nN;
  object oHB;
  nN=1;
  oHB=GetNearestObjectByTag("wazoo_pl_hbb",oPC,nN);
  while(oHB!=OBJECT_INVALID&&GetDistanceBetween(oPC,oHB)<=90.0)
  { // holly berries in range
    if (GetLocalObject(oHB,"oOwner")==oPC) DelayCommand(0.2,fnDetonateSeed(oHB));
    nN++;
    oHB=GetNearestObjectByTag("wazoo_pl_hbb",oPC,nN);
  } // holly berries in range
} // fnFireSeedDetonate()


void fnHandleCommands(object oMe,object oPC,string sCmd)
{ // PURPOSE: Do the parsing and handle the commands
  string sMaster=GetStringLowerCase(sCmd);
  string sParse;
  if (sMaster=="list help") fnListHelp(oPC);
  else if (sMaster=="list") SendMessageToPC(oPC,"TRY: list help, or list commands");
  else if (sMaster=="list commands") fnListCmd(oPC);
  else if (GetStringLeft(sMaster,1)=="'") fnSay(oPC,sCmd);
  else if (GetStringLeft(sMaster,7)=="troops,") fnTroopsCommand(oPC,sMaster);
  else if (GetStringLeft(sMaster,6)=="troop,") fnSpecificTroop(oPC,sMaster);
  else if (GetStringLeft(sMaster,8)=="settings") fnSettings(oPC,sMaster);
  else if (sMaster=="list troops") fnListTroops(oPC);
  else if (sMaster=="list troop") fnListTroop(oPC);
  else if (sMaster=="list settings") fnListSettings(oPC);
  else if (GetStringLeft(sMaster,1)=="]") fnSayDM(oPC,sCmd);
  else if (GetStringLeft(sMaster,1)=="@") fnSayLeader(oPC,sCmd);
  else if (GetStringLeft(sMaster,1)=="?") fnSayTeam(oPC,sCmd);
  else if (GetStringLeft(sMaster,1)=="!") fnSaySquad(oPC,sCmd);
  else if (GetStringLeft(sMaster,10)=="voicechat ") fnVoiceChat(oPC,sMaster);
  else if (GetStringLeft(sMaster,5)=="anim ") fnAnimate(oPC,sMaster);
  else if (GetStringLeft(sMaster,8)=="vampire "&&GetLocalInt(oPC,"nIsVampire")==TRUE) fnVampire(oPC,sMaster);
  else if (sMaster==sHOLLY_BERRY_WORD) fnFireSeedDetonate(oPC);
} // fnHandleCommands()


void fnDebugThis(object oPC,string sMsg)
{ // PURPOSE: Save a debug Message to the Log
    PrintString("[]-[]-[]-BEGIN-[]-[]");
    PrintString("  "+sMsg);
    PrintString("--------------------");
    string sS;
    object oOb;
    int nN;
    float fF;
    vector vVec;
    sS="  GAME TIME: "+IntToString(GetTimeHour())+" Hour, "+IntToString(GetCalendarDay())+" Day, ";
    sS=sS+IntToString(GetCalendarMonth())+" Month, "+IntToString(GetCalendarYear())+"  Version:"+GetLocalString(GetModule(),"sVersion");
    PrintString(sS);
    sS="  LOCATION: "+GetName(GetArea(oPC))+" Tag("+GetTag(GetArea(oPC))+")  Coordinates: ";
    vVec=GetPosition(oPC);
    sS=sS+FloatToString(vVec.x)+" X, "+FloatToString(vVec.y)+" Y, "+FloatToString(vVec.z)+" Z   FACING:";
    sS=sS+FloatToString(GetFacing(oPC));
    PrintString(sS);
    sS="  TeamID:"+GetLocalString(oPC,"sTeamID")+"    PC Name:"+GetName(oPC)+"   Level:";
    sS=sS+IntToString(GetLevelByPosition(1,oPC)+GetLevelByPosition(2,oPC)+GetLevelByPosition(3,oPC));
    PrintString(sS);
    PrintString("--------------------");
    sS="  ASSOCIATES:";
    oOb=GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Familiar("+GetName(oOb)+" Tag:"+GetTag(oOb)+" RR:"+GetResRef(oOb)+", ";
    oOb=GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Animal Companion("+GetName(oOb)+" Tag:"+GetTag(oOb)+" RR:"+GetResRef(oOb)+", ";
    nN=1;
    oOb=GetAssociate(ASSOCIATE_TYPE_DOMINATED,oPC,nN);
    while(GetIsObjectValid(oOb))
    { // Dominated
        if (GetIsObjectValid(oOb)) sS=sS+" Dominated("+GetName(oOb)+" Tag:"+GetTag(oOb)+" RR:"+GetResRef(oOb)+", ";
        nN++;
        oOb=GetAssociate(ASSOCIATE_TYPE_DOMINATED,oPC,nN);
    } // Dominated
    nN=1;
    oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nN);
    while(GetIsObjectValid(oOb))
    { // Henchmen
        if (GetIsObjectValid(oOb)) sS=sS+" Henchman("+GetName(oOb)+" Tag:"+GetTag(oOb)+" RR:"+GetResRef(oOb)+", ";
        nN++;
        oOb=GetAssociate(ASSOCIATE_TYPE_HENCHMAN,oPC,nN);
    } // Henchmen
    nN=1;
    oOb=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oPC,nN);
    while(GetIsObjectValid(oOb))
    { // Summoned
        if (GetIsObjectValid(oOb)) sS=sS+" Summoned("+GetName(oOb)+" Tag:"+GetTag(oOb)+" RR:"+GetResRef(oOb)+", ";
        nN++;
        oOb=GetAssociate(ASSOCIATE_TYPE_SUMMONED,oPC,nN);
    } // Summoned
    sS=sS+" END.";
    PrintString(sS);
    sS="  NEAREST NON-ASSOCIATE:";
    nN=1;
    oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN);
    while(GetIsObjectValid(oOb))
    { // find nearest non associate
        if (GetAssociateType(oOb)==ASSOCIATE_TYPE_NONE)
        { // found
            sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
            oOb=OBJECT_INVALID;
        } // found
        else
        { // continue
            nN++;
            oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,nN);
        } // continue
    } // find nearest non associate
    PrintString(sS);
    sS="  NEAREST ENEMY:";
    oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,1,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    sS="  NEAREST NEUTRAL:";
    oOb=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,oPC,1,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_NEUTRAL);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    sS="  NEAREST WAYPOINT:";
    oOb=GetNearestObject(OBJECT_TYPE_WAYPOINT,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    sS="  NEAREST PLACEABLE:";
    oOb=GetNearestObject(OBJECT_TYPE_PLACEABLE,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    sS="  NEAREST DOOR:";
    oOb=GetNearestObject(OBJECT_TYPE_DOOR,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    sS="  NEAREST ITEM:";
    oOb=GetNearestObject(OBJECT_TYPE_ITEM,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    sS="  NEAREST TRIGGER:";
    oOb=GetNearestObject(OBJECT_TYPE_TRIGGER,oPC,1);
    if (GetIsObjectValid(oOb)) sS=sS+" Name:"+GetName(oOb)+"  Tag:"+GetTag(oOb)+"  RR:"+GetResRef(oOb)+" Distance:"+FloatToString(GetDistanceBetween(oPC,oOb));
    PrintString(sS);
    PrintString("[]-[]-[]-END---[]-[]");
    SendMessageToPC(oPC,"A Bug message has been written to the Log file.  Please read the log file and copy and paste the information in the bug report into any email, or mantis bug report.");
} // fnDebug()