////////////////////////////////////////////////////////////////////////////////
// npcact_h_speak - NPC ACTIVITIES 6.0 Speaking related Functions
//------------------------------------------------------------------------------
// by Deva Bryson Winblood.
//------------------------------------------------------------------------------
// Last Modified by: Deva Bryson Winblood
// Last Modified Date: 06/22/2004
////////////////////////////////////////////////////////////////////////////////
#include "npcactivitiesh"
#include "npcact_h_support" // has support functions used by TT
/////////////////////////////
// PROTOTYPES
/////////////////////////////

// FILE: npcact_h_speak                       FUNCTION: fnNPCACTLyrical()
// This will cause the NPC to sing a song as stored on a waypoint with the
// specified tag.  The duration is specified in heartbeats (6 second intervals).
// If nOpt==1 then it means sCom is the actual song prefixed by the duration.
float fnNPCACTLyrical(string sCom,int nOpt=0);

// FILE: npcact_h_speak                       FUNCTION: fnNPCACTRandomSpeak()
// This function will cause the NPC to speak one of the random phrases stored
// on the specified waypoint. If nOpt=1 it will treat sCom as the random phrase
// name instead of looking for a waypoint.
float fnNPCACTRandomSpeak(string sCom,int nOpt=0);

// FILE: npcact_h_speak                       FUNCTION: fnNPCACTTalkTo()
// PARAMETERS: TT<tag>/<animation>/<optional conversation file>
// This function will cause the NPC to engage a target in conversation.  It
// will obey NO NPC interaction, and NO PC interaction rules.
// If the tag passed is PC then it will speak to a PC and not an NPC.
// If the tag passed is ANY then it will attempt to speak to anyone at all.
// If the tag passed is RACE<race> it will look for someone of that race to speak to
// If the tag passed is CLASS<class> it will look for someone of that class to speak to
// If the tag passed is GENDER<gender> it will look for someone of that gender to speak to
// If the tag passed is ALIGN<alignment> it will look for someone of that alignment
// If the tag passed is WOUNDED<%> it will look for someone wounded percent of their hit points
// If the tag passed is ARMED it will look for someone with weapons out
// If the tag passed is BESPELLED it will look for someone with spell effects
// If the tag passed is VARCHECK<variable>_<value> it will look for someone with the variable equal to value
// RACES: ANIMAL,BEAST, CONSTRUCT,DRAGON, DWARF, ELEMENTAL, ELF, FEY, GIANT,
//        GNOME, HALFELF, HALFORC, HUMAN, GOBLIN, MONSTROUS, ORC, REPTILE,
//        MAGICALBEAST, OOZE, OUTSIDER, SHAPECHANGER, UNDEAD, VERMIN
// CLASSES: ARCHER, ASSASSIN, BARBARIAN, BARD, BLACKGUARD, CLERIC, COMMONER,
//          CHAMPION, DISCIPLE, DRUID, DEFENDER, FIGHTER, HARPER, MONK,
//          PALADIN, PALEMASTER, RANGER, ROGUE, SHADOWDANCER, SHIFTER,
//          SORCERER, WIZARD, WEAPONMASTER.
// GENDERS: MALE, FEMALE, NONE, BOTH, OTHER
// ALIGNMENTS: EVIL,GOOD,LAWFUL,CHAOTIC,NEUTRAL,LG,NG,CG,LN,TN,CN,LE,NE,CE
// WOUNDED %: 1-99
// EXAMPLE: TTWilco2/N would initiate the NPCs standard conversation
//   with an NPC with the tag of Wilco2 using Normal speaking animations.
// EXAMPLE2: TTARMED/F/PutAwayWeapon  would cause the NPC to speak to anyone
//   that has a weapon out and will use the forceful animation and fire off
//   the PutAwayWeapon conversation.
float fnNPCACTTalkTo(string sCom);

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

void NPCACTSing(string sSong)
{ // PURPOSE: a recursive singing support function
  // LAST MODIFIED BY: Deva Bryson Winblood
  string sPhrase=fnParse(sSong,"/");
  string sRemaining=fnRemoveParsed(sSong,sPhrase,"/");
  if (GetLocalInt(OBJECT_SELF,"bNPCACTSinging")==TRUE&&GetStringLength(sPhrase)>0)
  { // still singing
    AssignCommand(OBJECT_SELF,SpeakString(sPhrase));
    DelayCommand(4.0,NPCACTSing(sRemaining));
  } // still singing
} // NPCACTSing()

float fnNPCACTLyrical(string sCom,int nOpt=0)
{ // PURPOSE: To sing a song stored on a waypoint.
  // LAST MODIFIED BY: Deva Bryson Winblood
  float fDur=0.0;
  string sParm;
  string sSong;
  string sDur;
  object oWP;
  fnDebug("["+GetTag(OBJECT_SELF)+"] SING");
  if (nOpt==0)
  { // nOpt==0
    if (GetStringLeft(sCom,5)=="LYRIC") sParm=GetStringRight(sCom,GetStringLength(sCom)-5);
    else { sParm=GetStringRight(sCom,GetStringLength(sCom)-2); }
    sSong=fnParse(sParm,"/");
    sDur=fnRemoveParsed(sParm,sSong,"/");
  } // nOpt==0
  else
  {
    sDur=fnParse(sCom,"/");
    sSong=fnRemoveParsed(sCom,sDur,"/");
  }
  fnDebug("["+GetTag(OBJECT_SELF)+"] SONG:"+sSong+" Duration:"+sDur,TRUE);
  fDur=IntToFloat(StringToInt(sDur)*6);
  if (fDur<6.0) fDur=45.0;
  oWP=GetWaypointByTag(sSong);
  if (fDur>0.0&&(GetIsObjectValid(oWP)||nOpt!=0))
  { // duration is greater than 0 and waypoint is valid
    SetLocalInt(OBJECT_SELF,"bNPCACTSinging",TRUE);
    if (nOpt==0) NPCACTSing(GetName(oWP));
    else { NPCACTSing(sSong); }
    DelayCommand(fDur,DeleteLocalInt(OBJECT_SELF,"bNPCACTSinging"));
  } // duration is greater than 0 and waypoint is valid
  return fDur;
} // fnNPCACTLyrical()


float fnNPCACTRandomSpeak(string sCom,int nOpt=0)
{ // PURPOSE: This will cause the NPC to say a random phrase that is stored
  // on a waypoint.
  // LAST MODIFIED BY: Deva Bryson Winblood
  string sWordTag;
  object oWP;
  string sBuffer;
  string sParse;
  int nR;
  int nC;
  int nCount;
  float fDur=0.1;
  fnDebug("["+GetTag(OBJECT_SELF)+"] fnNPCACTRandomSpeak("+sCom+","+IntToString(nOpt)+") ",TRUE);
  if (nOpt==0)
  { // nOpt==0
    if (GetStringLeft(sCom,3)=="RWL") sWordTag=GetStringRight(sCom,GetStringLength(sCom)-3);
    else { sWordTag=GetStringRight(sCom,GetStringLength(sCom)-2); }
    oWP=GetNearestObjectByTag(sWordTag);
    if (!GetIsObjectValid(oWP)) oWP=GetWaypointByTag(sWordTag);
  } // nOpt==0
  if (GetIsObjectValid(oWP)||nOpt==1)
  { // found waypoint
    fDur=1.0;
    if (nOpt==0) sWordTag=GetName(oWP);
    else { sWordTag=sCom; }
    sBuffer=sWordTag;
    nCount=0;
    sParse=fnParse(sBuffer,"/");
    while(GetStringLength(sParse)>0)
    { // count phrases
      nCount++;
      sBuffer=fnRemoveParsed(sBuffer,sParse,"/");
      sParse=fnParse(sBuffer,"/");
    } // count phrases
    if (nCount>0)
    { // pick a random saying
      nC=1;
      nR=Random(nCount)+1;
      //fnDebug("         nCount="+IntToString(nCount)+" nR="+IntToString(nR)+" sWordTag='"+sWordTag+"'");
      sParse=fnParse(sWordTag,"/");
      while(nC<nR)
      { // pick phrase
        nC++;
        sWordTag=fnRemoveParsed(sWordTag,sParse,"/");
        sParse=fnParse(sWordTag,"/");
      } // pick phrase
      fnDebug("     SpeakString("+sParse+")",TRUE);
      AssignCommand(OBJECT_SELF,SpeakString(sParse));
    } // pick a random saying
    else { AssignCommand(OBJECT_SELF,SpeakString("The place that stores the things to say actually has nothing for me to say.")); }
  } // found waypoint
  else { AssignCommand(OBJECT_SELF,SpeakString("The place I get my things to say from is missing.")); }
  return fDur;
} // fnNPCACTRandomSpeak()

int NPCACTIsBusy(object oTarget)
{ // PURPOSE: Returns true if target is in conversation or combat
  // LAST MODIFIED BY: Deva Bryson Winblood
  int nRet=FALSE;
  if (GetIsObjectValid(oTarget))
  { // valid object
    if (IsInConversation(oTarget)==TRUE) nRet=TRUE;
    if (GetIsInCombat(oTarget)==TRUE) nRet=TRUE;
    if (GetIsDead(oTarget)==TRUE) nRet=TRUE;
  } // valid object
  return nRet;
} // NPCACTIsBusy()

object NPCACTFindNonBusyPC()
{ // PURPOSE: Return a nearby PC that is not busy
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  object oPC;
  int nC=1;
  oPC=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(oPC!=OBJECT_INVALID&&oRet==OBJECT_INVALID)
  { // find a PC
    if (!NPCACTIsBusy(oPC)) oRet=oPC;
    nC++;
    oPC=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_IS_PC,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find a PC
  return oRet;
} // NPCACTFindNonBusyPC()

object NPCACTFindNonBusyNPC()
{ // PURPOSE: Return a non-busy NPC
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  object oPC;
  int nC=1;
  oPC=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(oPC!=OBJECT_INVALID&&oRet==OBJECT_INVALID)
  { // find a PC
    if (!NPCACTIsBusy(oPC)) oRet=oPC;
    nC++;
    oPC=GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR,PLAYER_CHAR_NOT_PC,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find a PC
  return oRet;
} // NPCACTFindNonBusyNPC()

object NPCACTFindRace(string sRace,int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this RACE
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  int nRace;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      nRace=GetRacialType(oCr);
      if (sRace=="ANIMAL"&&nRace==RACIAL_TYPE_ANIMAL) oRet=oCr;
      else if (sRace=="BEAST"&&nRace==RACIAL_TYPE_BEAST) oRet=oCr;
      else if (sRace=="CONSTRUCT"&&nRace==RACIAL_TYPE_CONSTRUCT) oRet=oCr;
      else if (sRace=="DRAGON"&&nRace==RACIAL_TYPE_DRAGON) oRet=oCr;
      else if (sRace=="DWARF"&&nRace==RACIAL_TYPE_DWARF) oRet=oCr;
      else if (sRace=="ELEMENTAL"&&nRace==RACIAL_TYPE_ELEMENTAL) oRet=oCr;
      else if (sRace=="ELF"&&nRace==RACIAL_TYPE_ELF) oRet=oCr;
      else if (sRace=="FEY"&&nRace==RACIAL_TYPE_FEY) oRet=oCr;
      else if (sRace=="GIANT"&&nRace==RACIAL_TYPE_GIANT) oRet=oCr;
      else if (sRace=="GNOME"&&nRace==RACIAL_TYPE_GNOME) oRet=oCr;
      else if (sRace=="HALFELF"&&nRace==RACIAL_TYPE_HALFELF) oRet=oCr;
      else if (sRace=="HALFLING"&&nRace==RACIAL_TYPE_HALFLING) oRet=oCr;
      else if (sRace=="HALFORC"&&nRace==RACIAL_TYPE_HALFORC) oRet=oCr;
      else if (sRace=="HUMAN"&&nRace==RACIAL_TYPE_HUMAN) oRet=oCr;
      else if (sRace=="GOBLIN"&&nRace==RACIAL_TYPE_HUMANOID_GOBLINOID) oRet=oCr;
      else if (sRace=="MONSTROUS"&&nRace==RACIAL_TYPE_HUMANOID_MONSTROUS) oRet=oCr;
      else if (sRace=="ORC"&&nRace==RACIAL_TYPE_HUMANOID_ORC) oRet=oCr;
      else if (sRace=="REPTILE"&&nRace==RACIAL_TYPE_HUMANOID_REPTILIAN) oRet=oCr;
      else if (sRace=="MAGICALBEAST"&&nRace==RACIAL_TYPE_MAGICAL_BEAST) oRet=oCr;
      else if (sRace=="OOZE"&&nRace==RACIAL_TYPE_OOZE) oRet=oCr;
      else if (sRace=="OUTSIDER"&&nRace==RACIAL_TYPE_OUTSIDER) oRet=oCr;
      else if (sRace=="SHAPECHANGER"&&nRace==RACIAL_TYPE_SHAPECHANGER) oRet=oCr;
      else if (sRace=="UNDEAD"&&nRace==RACIAL_TYPE_UNDEAD) oRet=oCr;
      else if (sRace=="VERMIN"&&nRace==RACIAL_TYPE_VERMIN) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindRace()

object NPCACTFindClass(string sClass,int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this Class
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      if (sClass=="ARCHER"&&GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER,oCr)>0) oRet=oCr;
      else if (sClass=="ASSASSIN"&&GetLevelByClass(CLASS_TYPE_ASSASSIN,oCr)>0) oRet=oCr;
      else if (sClass=="BARBARIAN"&&GetLevelByClass(CLASS_TYPE_BARBARIAN,oCr)>0) oRet=oCr;
      else if (sClass=="BARD"&&GetLevelByClass(CLASS_TYPE_BARD,oCr)>0) oRet=oCr;
      else if (sClass=="BLACKGUARD"&&GetLevelByClass(CLASS_TYPE_BLACKGUARD,oCr)>0) oRet=oCr;
      else if (sClass=="CLERIC"&&GetLevelByClass(CLASS_TYPE_CLERIC,oCr)>0) oRet=oCr;
      else if (sClass=="COMMONER"&&GetLevelByClass(CLASS_TYPE_COMMONER,oCr)>0) oRet=oCr;
      else if (sClass=="CHAMPION"&&GetLevelByClass(CLASS_TYPE_DIVINECHAMPION,oCr)>0) oRet=oCr;
      else if (sClass=="DISCIPLE"&&GetLevelByClass(CLASS_TYPE_DRAGONDISCIPLE,oCr)>0) oRet=oCr;
      else if (sClass=="DRUID"&&GetLevelByClass(CLASS_TYPE_DRUID,oCr)>0) oRet=oCr;
      else if (sClass=="DEFENDER"&&GetLevelByClass(CLASS_TYPE_DWARVENDEFENDER,oCr)>0) oRet=oCr;
      else if (sClass=="FIGHTER"&&GetLevelByClass(CLASS_TYPE_FIGHTER,oCr)>0) oRet=oCr;
      else if (sClass=="HARPER"&&GetLevelByClass(CLASS_TYPE_HARPER,oCr)>0) oRet=oCr;
      else if (sClass=="MONK"&&GetLevelByClass(CLASS_TYPE_MONK,oCr)>0) oRet=oCr;
      else if (sClass=="PALADIN"&&GetLevelByClass(CLASS_TYPE_PALADIN,oCr)>0) oRet=oCr;
      else if (sClass=="PALEMASTER"&&GetLevelByClass(CLASS_TYPE_PALEMASTER,oCr)>0) oRet=oCr;
      else if (sClass=="RANGER"&&GetLevelByClass(CLASS_TYPE_RANGER,oCr)>0) oRet=oCr;
      else if (sClass=="ROGUE"&&GetLevelByClass(CLASS_TYPE_ROGUE,oCr)>0) oRet=oCr;
      else if (sClass=="SHADOWDANCER"&&GetLevelByClass(CLASS_TYPE_SHADOWDANCER,oCr)>0) oRet=oCr;
      else if (sClass=="SHIFTER"&&GetLevelByClass(CLASS_TYPE_SHIFTER,oCr)>0) oRet=oCr;
      else if (sClass=="SORCERER"&&GetLevelByClass(CLASS_TYPE_SORCERER,oCr)>0) oRet=oCr;
      else if (sClass=="WEAPONMASTER"&&GetLevelByClass(CLASS_TYPE_WEAPON_MASTER,oCr)>0) oRet=oCr;
      else if (sClass=="WIZARD"&&GetLevelByClass(CLASS_TYPE_WIZARD,oCr)>0) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindClass()

object NPCACTFindGender(string sGender,int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this Gender
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  int nGender;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      nGender=GetGender(oCr);
      if (sGender=="FEMALE"&&nGender==GENDER_FEMALE) oRet=oCr;
      else if (sGender=="MALE"&&nGender==GENDER_MALE) oRet=oCr;
      else if (sGender=="BOTH"&&nGender==GENDER_BOTH) oRet=oCr;
      else if (sGender=="NONE"&&nGender==GENDER_NONE) oRet=oCr;
      else if (sGender=="OTHER"&&nGender==GENDER_OTHER) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindGender()

object NPCACTFindAlignment(string sAlignment,int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this Alignment
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  int nAGE;
  int nALC;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      nAGE=GetAlignmentGoodEvil(oCr);
      nALC=GetAlignmentLawChaos(oCr);
      if (sAlignment=="EVIL"&&nAGE==ALIGNMENT_EVIL) oRet=oCr;
      else if (sAlignment=="GOOD"&&nAGE==ALIGNMENT_GOOD) oRet=oCr;
      else if (sAlignment=="NEUTRAL"&&(nAGE==ALIGNMENT_NEUTRAL||nALC==ALIGNMENT_NEUTRAL)) oRet=oCr;
      else if (sAlignment=="LAWFUL"&&nALC==ALIGNMENT_LAWFUL) oRet=oCr;
      else if (sAlignment=="CHAOTIC"&&nALC==ALIGNMENT_CHAOTIC) oRet=oCr;
      else if (sAlignment=="LG"&&nAGE==ALIGNMENT_GOOD&&nALC==ALIGNMENT_LAWFUL) oRet=oCr;
      else if (sAlignment=="NG"&&nAGE==ALIGNMENT_GOOD&&nALC==ALIGNMENT_NEUTRAL) oRet=oCr;
      else if (sAlignment=="CG"&&nAGE==ALIGNMENT_GOOD&&nALC==ALIGNMENT_CHAOTIC) oRet=oCr;
      else if (sAlignment=="LN"&&nAGE==ALIGNMENT_NEUTRAL&&nALC==ALIGNMENT_LAWFUL) oRet=oCr;
      else if (sAlignment=="TN"&&nAGE==ALIGNMENT_NEUTRAL&&nALC==ALIGNMENT_NEUTRAL) oRet=oCr;
      else if (sAlignment=="CN"&&nAGE==ALIGNMENT_NEUTRAL&&nALC==ALIGNMENT_CHAOTIC) oRet=oCr;
      else if (sAlignment=="LE"&&nAGE==ALIGNMENT_EVIL&&nALC==ALIGNMENT_LAWFUL) oRet=oCr;
      else if (sAlignment=="NE"&&nAGE==ALIGNMENT_EVIL&&nALC==ALIGNMENT_NEUTRAL) oRet=oCr;
      else if (sAlignment=="CE"&&nAGE==ALIGNMENT_EVIL&&nALC==ALIGNMENT_CHAOTIC) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindAlignment()

object NPCACTFindWounded(string sWounded,int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this Wounded
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  int nP=StringToInt(sWounded);
  int nMAXHP;
  int nCURHP;
  int nPERHP;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      nMAXHP=GetMaxHitPoints(oCr);
      nCURHP=GetCurrentHitPoints(oCr);
      nPERHP=(nCURHP*100)/nMAXHP;
      if (nPERHP<=nP) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindWounded()

object NPCACTFindArmed(int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this Armed
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  object oItem;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      oItem=GetItemInSlot(INVENTORY_SLOT_LEFTHAND,oCr);
      if (fnIsAWeapon(oItem)) oRet=oCr;
      oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oCr);
      if (fnIsAWeapon(oItem)) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindArmed()

object NPCACTFindBespelled(int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this Bespelled
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
      if (fnIsBespelled(oCr)) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindBespelled()

object NPCACTFindVarCheck(string sVar,int bAllowPCs,int bAllowNPCs)
{ // PURPOSE: Find a non-busy target of this has variable set to value
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  int nC=1;
  object oCr;
  int bAllow;
  string sVarName=fnParse(sVar,"_");
  string sValue=fnRemoveParsed(sVar,sVarName,"_");
  oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(GetIsObjectValid(oCr)&&oRet==OBJECT_INVALID)
  { // find armed target
    bAllow=TRUE;
    if (GetIsPC(oCr)&&!bAllowPCs) bAllow=FALSE;
    else if (!GetIsPC(oCr)&&!bAllowNPCs) bAllow=FALSE;
    if (bAllow&&!NPCACTIsBusy(oCr))
    { // not prohibited
     if (GetLocalInt(oCr,sVarName)==StringToInt(sValue)) oRet=oCr;
    } // not prohibited
    nC++;
    oCr=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find armed target
  return oRet;
} // NPCACTFindVarCheck()

object NPCACTFindNonBusyCreature()
{ // PURPOSE: To find a PC or NPC that is not busy
  // LAST MODIFIED BY: Deva Bryson Winblood
  object oRet=OBJECT_INVALID;
  object oPC;
  int nC=1;
  oPC=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  while(oPC!=OBJECT_INVALID&&oRet==OBJECT_INVALID)
  { // find a PC
    if (!NPCACTIsBusy(oPC)) oRet=oPC;
    nC++;
    oPC=GetNearestCreature(CREATURE_TYPE_IS_ALIVE,TRUE,OBJECT_SELF,nC,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
  } // find a PC
  return oRet;
} // NPCACTFindNonBusyCreature()

float fnNPCACTTalkTo(string sCom)
{ // PURPOSE: To initiate conversation
  // LAST MODIFIED BY: Deva Bryson Winblood
  float fDur=0.0;
  object oTarget=OBJECT_INVALID;
  string sParameters=GetStringRight(sCom,GetStringLength(sCom)-2);
  string sTag=fnParse(sParameters,"/");
  string sAnimation;
  string sOptionalConversation;
  sParameters=fnRemoveParsed(sParameters,sTag,"/");
  sAnimation=fnParse(sParameters,"/");
  sOptionalConversation=fnRemoveParsed(sParameters,sAnimation,"/");
  int bValidTargetType=TRUE;
  int bPCsAllowed=TRUE;
  int bNPCsAllowed=TRUE;
  if (GetLocalInt(OBJECT_SELF,"nNN")==TRUE) bNPCsAllowed=FALSE;
  if (GetLocalInt(OBJECT_SELF,"bNPCACTNOPC")==TRUE) bPCsAllowed=FALSE;
  if (sTag=="PC"&&bPCsAllowed==FALSE) bValidTargetType=FALSE;
  else if (sTag=="PC") oTarget=NPCACTFindNonBusyPC();
  if (sTag=="ANY")
  { // find any
    if (bPCsAllowed&&bNPCsAllowed) oTarget=NPCACTFindNonBusyCreature();
    else if (bPCsAllowed) oTarget=NPCACTFindNonBusyPC();
    if (!GetIsObjectValid(oTarget)&&bNPCsAllowed) oTarget=NPCACTFindNonBusyNPC();
  } // find any
  else if (GetStringLeft(sTag,4)=="RACE")
  { // specific race
    oTarget=NPCACTFindRace(GetStringRight(sTag,GetStringLength(sTag)-4),bPCsAllowed,bNPCsAllowed);
  } // specific race
  else if (GetStringLeft(sTag,5)=="CLASS")
  { // specific class
    oTarget=NPCACTFindClass(GetStringRight(sTag,GetStringLength(sTag)-5),bPCsAllowed,bNPCsAllowed);
  } // specific class
  else if (GetStringLeft(sTag,6)=="GENDER")
  { // specific gender
    oTarget=NPCACTFindGender(GetStringRight(sTag,GetStringLength(sTag)-6),bPCsAllowed,bNPCsAllowed);
  } // specific gender
  else if (GetStringLeft(sTag,5)=="ALIGN")
  { // specific alignment
    oTarget=NPCACTFindAlignment(GetStringRight(sTag,GetStringLength(sTag)-5),bPCsAllowed,bNPCsAllowed);
  } // specific alignment
  else if (GetStringLeft(sTag,7)=="WOUNDED")
  { // wounded % 1-99
    oTarget=NPCACTFindWounded(GetStringRight(sTag,GetStringLength(sTag)-7),bPCsAllowed,bNPCsAllowed);
  } // wounded % 1-99
  else if (sTag=="ARMED")
  { // armed
    oTarget=NPCACTFindArmed(bPCsAllowed,bNPCsAllowed);
  } // armed
  else if (sTag=="BESPELLED")
  { // bespelled
    oTarget=NPCACTFindBespelled(bPCsAllowed,bNPCsAllowed);
  } // bespelled
  else if (GetStringLeft(sTag,8)=="VARCHECK")
  { // variable check
    oTarget=NPCACTFindVarCheck(GetStringRight(sTag,GetStringLength(sTag)-8),bPCsAllowed,bNPCsAllowed);
  } // variable check
  else if (sTag!="PC")
  { // specific tag
    oTarget=GetNearestObjectByTag(sTag);
    if (GetIsObjectValid(oTarget))
    { // there is a target nearby with that tag
      if (GetObjectSeen(oTarget)==TRUE)
      { // I can see the target
        if (!NPCACTIsBusy(oTarget))
        { // The target is not Busy
        } // The target is not Busy
        else { oTarget=OBJECT_INVALID; }
      } // I can see the target
      else { oTarget=OBJECT_INVALID; }
    } // there is a target nearby with that tag
  } // specific tag
  if(GetIsObjectValid(oTarget))
  { // Valid target found
    ActionMoveToObject(oTarget);
    if (sAnimation=="F") ActionPlayAnimation(ANIMATION_LOOPING_TALK_FORCEFUL,1.0,5.0);
    else if (sAnimation=="P") ActionPlayAnimation(ANIMATION_LOOPING_TALK_PLEADING,1.0,5.0);
    else if (sAnimation=="N") ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL,1.0,5.0);
    else if (sAnimation=="L") ActionPlayAnimation(ANIMATION_LOOPING_TALK_LAUGHING,1.0,5.0);
    if (GetStringLength(sOptionalConversation)<2) ActionStartConversation(oTarget);
    else { ActionStartConversation(oTarget,sOptionalConversation); }
    fDur=30.0;
  } // Valid target found
  return fDur;
} // fnNPCACTTalkTo()


//void main(){}