// v2.1.0 - Updated for new version of AI.
// v2.0.0 -
//::////////////////////////////////////////////////////////////////////////////
//:: FileName:  hark_i0_heal
//::////////////////////////////////////////////////////////////////////////////
/*
   This file contains routines used for healing and curing conditions.
*/
//::////////////////////////////////////////////////////////////////////////////
#include "hark_i0_spells"
//::////////////////////////////////////////////////////////////////////////////


// * FUNCTION PROTOTYPES

// Compares the percentage of hitpoint loss between the local saved previously
// and oTarget. If there was no previous, then save oTarget.
void CompareDamage(object oTarget);

// Returns TRUE if the Henchman should attempt to heal oTarget.
int GetAssociateHealAlly(object oTarget);

// Returns TRUE if the Henchman should attempt to heal their Master.
int GetAssociateHealMaster();

// Returns TRUE if the Henchman should attempt to heal themself.
int GetAssociateHealSelf();

// Returns the best HCR Non-Stack Cure potion oTarget has.
object GetBestHCRPotion(object oHench = OBJECT_SELF);

// Returns the best Healing Kit oTarget has.
object GetBestHealingKit(object oHench = OBJECT_SELF);

// Uses nSpellType to search for a scroll of that type. (BW and HCR scrolls)
// nSpellType = SPELL_* (example: SPELL_RESURRECTION)
// Returns TRUE if oHench has a scroll of that type.
int GetHasScroll(int nSpellType, object oHench = OBJECT_SELF);

// Returns the rank of the healing spell passed in. Use the SPELL_* constants.
int GetSpellRank(int nSpellID);

// This will go through and remove any negative effects on Self and all Allies.
int TalentCureConditionAllAllies(int nMelee);

// This will start with oHeal and cycle through the whole faction checking to
// see if any of them needs healing. If so, heal them if the caller can do so.
// Returns TRUE if the caller finds a target they can heal.
int TalentHealAllAllies(object oHeal, int nMelee = 0, int nForce = FALSE, int bHasEffect = FALSE);

// Returns TRUE if the caller can heal oHeal. Do not use on dying or dead targets.
int TalentHealAlly(object oHeal, int nMelee = 0, int nForce = FALSE, int bHasEffect = FALSE);

// Returns TRUE if the caller can heal oHeal. Use on dying targets only.
int TalentHealDyingAlly(object oHeal, int nMelee = 0, int nForce = FALSE);

// This will use the Non-Stack (misc. item) HCR Cure Variety Potions.
void UseHCRPotion(object oHeal, object oPotion);

// This will use the custom HCR Heal Kit.
void UseHCRHealKit(object oHeal, object oHealKit);

// The caller will use a scroll with nSpell on oTarget.
void UseScrollOnObject(int nSpell, object oTarget);


// * FUNCTION DEFINITIONS


//::////////////////////////////////////////////////////////////////////////////
void CompareDamage(object oTarget)
{
  object oPrevious = GetLocalObject(OBJECT_SELF, HS_HEAL_TARGET);
  if (!GetIsObjectValid(oPrevious))
    {
     SetLocalObject(OBJECT_SELF, HS_HEAL_TARGET, oTarget);
     return;
    }

  int nDam1 = GetPercentageHPLoss(oPrevious);
  int nDam2 = GetPercentageHPLoss(oTarget);
  if (nDam1 > nDam2)
    { SetLocalObject(OBJECT_SELF, HS_HEAL_TARGET, oTarget); }
}
//::////////////////////////////////////////////////////////////////////////////
int GetAssociateHealAlly(object oTarget)
{
  if (!GetIsObjectValid(oTarget) || GetIsDead(oTarget)) { return FALSE; }

  if (GetAssociateState(NW_ASC_HAVE_MASTER) && GetAssociateState(NW_ASC_HEAL_OTH))
    {
     int nLoss = GetPercentageHPLoss(oTarget);
     if (GetAssociateState(NW_ASC_HEAL_OTH_AT_75) && nLoss <= 75)
       { return TRUE; }
     else if (GetAssociateState(NW_ASC_HEAL_OTH_AT_50) && nLoss <= 50)
       { return TRUE; }
     else if (GetAssociateState(NW_ASC_HEAL_OTH_AT_25) && nLoss <= 25)
       { return TRUE; }
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
int GetAssociateHealMaster()
{
  if (GetAssociateState(NW_ASC_HAVE_MASTER))
    {
     object oMaster = GetRealMaster();
     int nLoss = GetPercentageHPLoss(oMaster);
     if (!GetIsDead(oMaster))
       {
        if (GetAssociateState(NW_ASC_HEAL_AT_75) && nLoss <= 75)
          { return TRUE; }
        else if (GetAssociateState(NW_ASC_HEAL_AT_50) && nLoss <= 50)
          { return TRUE; }
        else if (GetAssociateState(NW_ASC_HEAL_AT_25) && nLoss <= 25)
          { return TRUE; }
       }
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
int GetAssociateHealSelf()
{
  if (GetIsObjectValid(OBJECT_SELF))
    {
     int nLoss = GetPercentageHPLoss(OBJECT_SELF);
     if (GetAssociateState(NW_ASC_HEAL_SLF_AT_75) && nLoss <= 75)
       { return TRUE; }
     else if (GetAssociateState(NW_ASC_HEAL_SLF_AT_50) && nLoss <= 50)
       { return TRUE; }
     else if (GetAssociateState(NW_ASC_HEAL_SLF_AT_25) && nLoss <= 25)
       { return TRUE; }
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
object GetBestHCRPotion(object oHench = OBJECT_SELF)
{
  object oItem;
  if (GetIsObjectValid(oHench))
    {
     if (GetIsObjectValid(GetItemPossessedBy(oHench, "PotionOfCCW")))
       { oItem = GetItemPossessedBy(oHench, "PotionOfCCW"); }
     else if (GetIsObjectValid(GetItemPossessedBy(oHench, "PotionOfCSW")))
       { oItem = GetItemPossessedBy(oHench, "PotionOfCSW"); }
     else if (GetIsObjectValid(GetItemPossessedBy(oHench, "PotionOfCMW")))
       { oItem = GetItemPossessedBy(oHench, "PotionOfCMW"); }
     else if (GetIsObjectValid(GetItemPossessedBy(oHench, "PotionOfCLW")))
       { oItem = GetItemPossessedBy(oHench, "PotionOfCLW"); }
    }
  return oItem;
}
//::////////////////////////////////////////////////////////////////////////////
object GetBestHealingKit(object oHench = OBJECT_SELF)
{
  object oKit = OBJECT_INVALID;
  object oItem = GetFirstItemInInventory(oHench);
  int iRunningValue = 0;
  int iItemValue, iStackSize;
  while (GetIsObjectValid(oItem))
    {
     if (GetBaseItemType(oItem) == BASE_ITEM_HEALERSKIT)
       {
        iItemValue = GetGoldPieceValue(oItem);
        iStackSize = GetNumStackedItems(oItem);
        // Stacked kits be worth what they should be seperatly.
        iItemValue = iItemValue/iStackSize;
        if (iItemValue > iRunningValue)
          {
           iRunningValue = iItemValue;
           oKit = oItem;
          }
       }
     oItem = GetNextItemInInventory(oHench);
    }
  return oKit;
}
//::////////////////////////////////////////////////////////////////////////////
int GetHasScroll(int nSpellType, object oHench = OBJECT_SELF)
{
  if (nSpellType == SPELL_RESURRECTION)
    {
     if (GetIsObjectValid(GetItemPossessedBy(oHench, "7ResurrectionD_C")))
       { return TRUE; }
     if (GetIsObjectValid(GetItemPossessedBy(oHench, "NW_IT_SPDVSCR702")))
       { return TRUE; }
    }
  else if (nSpellType == SPELL_RAISE_DEAD)
    {
     if (GetIsObjectValid(GetItemPossessedBy(oHench, "5RaiseDeadD_C")))
       { return TRUE; }
     if (GetIsObjectValid(GetItemPossessedBy(oHench, "NW_IT_SPDVSCR501")))
       { return TRUE; }
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
int GetSpellRank(int nSpellID)
{
  switch (nSpellID)
    {
     case SPELL_CURE_MINOR_WOUNDS:
     case SPELL_INFLICT_MINOR_WOUNDS:
       return 1;
     case SPELL_CURE_LIGHT_WOUNDS:
     case SPELLABILITY_LESSER_BODY_ADJUSTMENT:
     case SPELL_INFLICT_LIGHT_WOUNDS:
     case SPELL_NEGATIVE_ENERGY_RAY:
       return 2;
     case SPELL_CURE_MODERATE_WOUNDS:
     case SPELL_INFLICT_MODERATE_WOUNDS:
       return 3;
     case SPELL_CURE_SERIOUS_WOUNDS:
     case SPELL_INFLICT_SERIOUS_WOUNDS:
     case SPELL_HEALING_CIRCLE:
     case SPELL_NEGATIVE_ENERGY_BURST:
     case SPELL_CIRCLE_OF_DOOM:
       return 4;
     case SPELL_CURE_CRITICAL_WOUNDS:
     case SPELL_INFLICT_CRITICAL_WOUNDS:
       return 5;
     case SPELL_HEAL:
     case SPELL_MASS_HEAL:
     case SPELL_HARM:
       return 6;
    }
  return 0;
}
//::////////////////////////////////////////////////////////////////////////////
int TalentCureConditionAllAllies(int nMelee)
{
  // TODO: Add dispelling?
  int nSum;
  effect eEffect;
  //object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(OBJECT_SELF));
  object oTarget = GetFirstFactionMember(OBJECT_SELF, FALSE);
  while (GetIsObjectValid(oTarget))
    {
     if (GetIsFriend(oTarget) && GetObjectSeen(oTarget))
       {
        eEffect = GetFirstEffect(oTarget);
        nSum = 0;
        while (GetIsEffectValid(eEffect))
          {
           int nType = GetEffectType(eEffect);
           if (nType == EFFECT_TYPE_DISEASE)
             { nSum = nSum | nDisease; }
           else if (nType == EFFECT_TYPE_FRIGHTENED)
             { nSum = nSum | nFear; }
           else if (nType == EFFECT_TYPE_POISON)
             { nSum = nSum | nPoison; }
           else if (nType == EFFECT_TYPE_CURSE)
             { nSum = nSum | nCurse; }
           else if (nType == EFFECT_TYPE_NEGATIVELEVEL)
             { nSum = nSum | nDrained; }
           else if (nType == EFFECT_TYPE_ABILITY_DECREASE ||
                    nType == EFFECT_TYPE_AC_DECREASE ||
                    nType == EFFECT_TYPE_ATTACK_DECREASE ||
                    nType == EFFECT_TYPE_DAMAGE_DECREASE ||
                    nType == EFFECT_TYPE_DAMAGE_IMMUNITY_DECREASE ||
                    nType == EFFECT_TYPE_SAVING_THROW_DECREASE ||
                    nType == EFFECT_TYPE_SPELL_RESISTANCE_DECREASE ||
                    nType == EFFECT_TYPE_SKILL_DECREASE)
             { nSum = nSum | nAbilityDrain; }
           else if (nType == EFFECT_TYPE_PARALYZE)
             { nSum = nSum | nParalsis; }
           else if (nType == EFFECT_TYPE_SLOW)
             { nSum = nSum | nSlow; }
           else if (nType == EFFECT_TYPE_CHARMED || nType == EFFECT_TYPE_DAZED ||
                    nType == EFFECT_TYPE_CONFUSED || nType == EFFECT_TYPE_STUNNED)
             { nSum = nSum | nClarity; }
           else if (nType == EFFECT_TYPE_BLINDNESS || nType == EFFECT_TYPE_DEAF)
             { nSum = nSum | nBlindDeaf; }
           else if (nType == EFFECT_TYPE_ENTANGLE ||
                    nType == EFFECT_TYPE_MOVEMENT_SPEED_DECREASE)
             { nSum = nSum | nFreedom; }
           else if (nType == EFFECT_TYPE_SLEEP)
             { nSum = nSum | nSleep; }
           else if (nType == EFFECT_TYPE_DARKNESS)
             {
              if (!GetHasSpellEffect(SPELL_DARKVISION, oTarget) &&
                  !GetHasSpellEffect(SPELL_TRUE_SEEING, oTarget) &&
                  !GetCreatureHasItemProperty(ITEM_PROPERTY_TRUE_SEEING, oTarget))
                { nSum = nSum | nDarkness; }
             }
           else if (nType == EFFECT_TYPE_PETRIFY)
             { nSum = nSum | nPetrified; }
           eEffect = GetNextEffect(oTarget);
          }
       }
     if (nSum != 0)
       {
        int bNotIsSelf = oTarget != OBJECT_SELF;
        if (nSum & nPetrified)
          {
           if ((GetHasFixedSpell(SPELL_STONE_TO_FLESH) && !(bFoundPotionOnly && bNotIsSelf)))
             {
              CastFixedSpellOnObject(SPELL_STONE_TO_FLESH, oTarget, nMelee);
              return TRUE;
             }
           else
             {
              nSum = 0;
             }
          }
        if ((nSum & nFear) && (GetHasFixedSpell(SPELL_REMOVE_FEAR) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_FEAR, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum == nDisease) && (GetHasFixedSpell(SPELL_REMOVE_DISEASE) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_DISEASE, oTarget, nMelee);
           return TRUE;
          }
        if (((nSum & ~(nDisease | nPoison)) == 0) && (GetHasFixedSpell(SPELL_NEUTRALIZE_POISON) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_NEUTRALIZE_POISON, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum == nCurse) && (GetHasFixedSpell(SPELL_REMOVE_CURSE) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_CURSE, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum == nBlindDeaf) && (GetHasFixedSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum == nParalsis) && (GetHasFixedSpell(SPELL_REMOVE_PARALYSIS)  && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_PARALYSIS, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum == nAbilityDrain) && GetHasFixedSpell(SPELL_LESSER_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_LESSER_RESTORATION, oTarget, nMelee);
           return TRUE;
          }
        if (((nSum & ~(nParalsis | nFreedom)) == 0) && (GetHasFixedSpell(SPELL_FREEDOM_OF_MOVEMENT) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_FREEDOM_OF_MOVEMENT, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & ~(nClarity | nSleep)) == 0)
          {
           if (GetHasFixedSpell(SPELL_MIND_BLANK) && !(bFoundPotionOnly && bNotIsSelf))
             {
              CastFixedSpellOnObject(SPELL_MIND_BLANK, oTarget, nMelee);
              return TRUE;
             }
           if (GetHasFixedSpell(SPELL_LESSER_MIND_BLANK) && !(bFoundPotionOnly && bNotIsSelf))
             {
              CastFixedSpellOnObject(SPELL_LESSER_MIND_BLANK, oTarget, nMelee);
              return TRUE;
             }
          }
        if (((nSum & ~(nClarity | nSleep | nSlow)) == 0) && (GetHasFixedSpell(SPELL_CLARITY) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_CLARITY, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & (nFear | nSlow | nClarity | nParalsis | nCurse)) && (GetHasFixedSpell(SPELL_GREATER_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_GREATER_RESTORATION, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nDarkness) && (GetHasFixedSpell(SPELL_TRUE_SEEING) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_TRUE_SEEING, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nDarkness) && (GetHasFixedSpell(SPELL_DARKVISION) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           CastFixedSpellOnObject(SPELL_DARKVISION, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & (nBlindDeaf | nParalsis | nDrained)) && (GetHasFixedSpell(SPELL_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf)) && (GetHasFixedSpell(SPELL_GREATER_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf)))
          {
           if (GetHasFixedSpell(SPELL_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf))
             {
              CastFixedSpellOnObject(SPELL_RESTORATION, oTarget, nMelee);
              return TRUE;
             }
           CastFixedSpellOnObject(SPELL_GREATER_RESTORATION, oTarget, nMelee);
           return TRUE;
          }
        // After this point try to get rid of anything doing worst first.
        if ((nSum & (nClarity | nSleep | nSlow)) && GetHasFixedSpell(SPELL_CLARITY) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_CLARITY, oTarget, nMelee);
           return TRUE;
          }
        if (nSum & (nClarity | nSleep))
          {
           if (GetHasFixedSpell(SPELL_MIND_BLANK) && !(bFoundPotionOnly && bNotIsSelf))
             {
              CastFixedSpellOnObject(SPELL_MIND_BLANK, oTarget, nMelee);
              return TRUE;
             }
           if (GetHasFixedSpell(SPELL_LESSER_MIND_BLANK) && !(bFoundPotionOnly && bNotIsSelf))
             {
              CastFixedSpellOnObject(SPELL_LESSER_MIND_BLANK, oTarget, nMelee);
              return TRUE;
             }
          }
        if ((nSum & (nParalsis | nFreedom)) && GetHasFixedSpell(SPELL_FREEDOM_OF_MOVEMENT) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_FREEDOM_OF_MOVEMENT, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nParalsis) && GetHasFixedSpell(SPELL_REMOVE_PARALYSIS) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_PARALYSIS, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nBlindDeaf) && GetHasFixedSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nCurse) && GetHasFixedSpell(SPELL_REMOVE_CURSE) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_CURSE, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nAbilityDrain) && GetHasFixedSpell(SPELL_LESSER_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_LESSER_RESTORATION, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nPoison) && GetHasFixedSpell(SPELL_NEUTRALIZE_POISON) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_NEUTRALIZE_POISON, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nDisease) && GetHasFixedSpell(SPELL_REMOVE_DISEASE) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_REMOVE_DISEASE, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nDisease) && GetHasFixedSpell(SPELL_NEUTRALIZE_POISON) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_NEUTRALIZE_POISON, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & (nPoison | nDisease | nAbilityDrain)) && GetHasFixedSpell(SPELL_GREATER_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_GREATER_RESTORATION, oTarget, nMelee);
           return TRUE;
          }
        if ((nSum & nAbilityDrain) && GetHasFixedSpell(SPELL_RESTORATION) && !(bFoundPotionOnly && bNotIsSelf))
          {
           CastFixedSpellOnObject(SPELL_RESTORATION, oTarget, nMelee);
           return TRUE;
          }
       }
     //oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_HUGE, GetLocation(OBJECT_SELF));
     oTarget = GetNextFactionMember(OBJECT_SELF, FALSE);
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
int TalentHealAllAllies(object oHeal, int nMelee = 0, int nForce = FALSE, int bHasEffect = FALSE)
{
  // If healing is not being forced, I am in melee combat and I was told to NOT
  // heal while in melee, save overhead and stop checking.
  if (!nForce && nMelee && GetLocalInt(OBJECT_SELF, "DoNotHealMelee") == TRUE)
    { return FALSE; }

  // Declare and set up all of the major variables.
  object oHealTarget;
  location lMyLoc = GetLocation(OBJECT_SELF);
  int nCanHealOth = GetAssociateState(NW_ASC_HEAL_OTH);
  int nCanHealHen = GetAssociateState(NW_ASC_HEAL_HEN);
  int nCanHealFam = GetAssociateState(NW_ASC_HEAL_FAM);
  int nCanHealAni = GetAssociateState(NW_ASC_HEAL_ANI);
  int nMyTrump;
  int nCount;

  // Set the random trump. This can break us out of the loop early and add some
  // randomness to the healing process. This will be overrode however by the
  // Heal Other variable set by our master. Example if I am set to heal at 50%
  // or less and I have a trump of 75% then the healing attempt will fail and
  // the loop will continue. Also if I am set to not heal OTHER then I will only
  // try to heal myself and then my master.
  int nRandom = d12(1);
  switch (nRandom)
    {
     case  1: nMyTrump = 25; break;
     case  2: nMyTrump = 30; break;
     case  3: nMyTrump = 35; break;
     case  4: nMyTrump = 40; break;
     case  5: nMyTrump = 45; break;
     case  6: nMyTrump = 50; break;
     case  7: nMyTrump = 55; break;
     case  8: nMyTrump = 60; break;
     case  9: nMyTrump = 65; break;
     case 10: nMyTrump = 70; break;
     case 11: nMyTrump = 75; break;
     case 12: nMyTrump = 80; break;
    }

  if (GetIsObjectValid(oHeal))
    {
     // If healing is forced, priority is to Self first. Master second.
     // If we are not healing others, priority is the same.
     if (nForce || !nCanHealOth)
       {
        if (TalentHealAlly(OBJECT_SELF, nMelee, nForce))
          { return TRUE; }
        if (TalentHealAlly(GetRealMaster(), nMelee, nForce))
          { return TRUE; }
       }

     // Search for wounded allies. Up to 10.
     nCount = 0;
     object oTarget = GetFirstFactionMember(oHeal, FALSE);
     while (GetIsObjectValid(oTarget) && nCanHealOth && nCount < 10)
       {
        // If I have a target and it matches my trump percent.
        oHealTarget = GetLocalObject(OBJECT_SELF, HS_HEAL_TARGET);
        if (GetIsObjectValid(oHealTarget) && !GetIsDead(oHealTarget) &&
            GetDistanceToObject(oHealTarget) <= 25.0 &&
            GetArea(oHealTarget) == GetArea(OBJECT_SELF) &&
            GetPercentageHPLoss(oHealTarget) <= nMyTrump)
          {
           if (TalentHealAlly(oHealTarget, nMelee, nForce))
             { return TRUE; }
          }

        // If I have a target that is not dead or dying and within 25m.
        if (!GetIsDead(oTarget) && GetObjectSeen(oTarget))
          {
           if (GetIsPC(oTarget) && !GetIsDM(oTarget))
             {
              if (GetPercentageHPLoss(oTarget) < 100)
                { CompareDamage(oTarget); }
             }
           else if (oTarget == OBJECT_SELF)
             {
              if (GetPercentageHPLoss(oTarget) < 100)
                { CompareDamage(oTarget); }
             }
           else if (nCanHealHen || nCanHealFam || nCanHealAni)
             {
              if (nCanHealHen && GetHenchman(GetMaster(oTarget)) == oTarget)
                {
                 if (GetPercentageHPLoss(oTarget) < 100)
                   { CompareDamage(oTarget); }
                }
              else if (nCanHealFam && GetAssociate(ASSOCIATE_TYPE_FAMILIAR, GetMaster(oTarget)) == oTarget)
                {
                 if (GetPercentageHPLoss(oTarget) < 100)
                   { CompareDamage(oTarget); }
                }
              else if (nCanHealAni && GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, GetMaster(oTarget)) == oTarget)
                {
                 if (GetPercentageHPLoss(oTarget) < 100)
                   { CompareDamage(oTarget); }
                }
             }
          }
        nCount++;
        oTarget = GetNextFactionMember(oHeal, FALSE);
       }

     // We have a wounded target, try to heal it.
     oHealTarget = GetLocalObject(OBJECT_SELF, HS_HEAL_TARGET);
     if (GetIsObjectValid(oHealTarget) && GetObjectSeen(oHealTarget) &&
         GetPercentageHPLoss(oHealTarget) < 100 && !GetIsDead(oHealTarget))
       {
        if (TalentHealAlly(oHealTarget, nMelee, nForce))
          { return TRUE; }
       }

     // Search for bleeding or dead allies. Up to 10.
     nCount = 0;
     oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 25.0, lMyLoc, TRUE);
     while (GetIsObjectValid(oTarget) && nCanHealOth && (nForce || !nMelee) && nCount < 10)
       {
        if (GetIsPC(oTarget) && !GetIsDM(oTarget) && GetIsDead(oTarget) &&
            GetCurrentHitPoints(oTarget) >= -9 && GetFactionEqual(oTarget))
          {
           if (TalentHealDyingAlly(oTarget, nMelee, nForce))
             { return TRUE; }
          }
        else if (!GetIsPC(oTarget) && !GetIsDM(oTarget) &&
                 !GetIsDead(oTarget) && nCanHealHen &&
                 GetAssociateState(NW_ASC_MODE_DYING, oTarget) &&
                 GetIsObjectValid(GetRealMaster(oTarget)) &&
                 GetFactionEqual(GetRealMaster(oTarget)))
          {
           if (TalentHealDyingAlly(oTarget, nMelee, nForce))
             { return TRUE; }
          }
        else if (GetTag(oTarget) == "DeathCorpse" &&
                 GetIsPC(GetLocalObject(oTarget, "Owner")) &&
                 !GetIsDM(GetLocalObject(oTarget, "Owner")) &&
                 GetFactionEqual(GetLocalObject(oTarget, "Owner")))
          {
           if (GetHasSpell(SPELL_RESURRECTION))
             {
              ActionCastSpellAtObject(SPELL_RESURRECTION, oTarget);
              return TRUE;
             }
           else if (GetHasSpell(SPELL_RAISE_DEAD))
             {
              ActionCastSpellAtObject(SPELL_RAISE_DEAD, oTarget);
              return TRUE;
             }
           else if (GetHasScroll(SPELL_RESURRECTION))
             {
              UseScrollOnObject(SPELL_RESURRECTION, oTarget);
              return TRUE;
             }
           else if (GetHasScroll(SPELL_RAISE_DEAD))
             {
              UseScrollOnObject(SPELL_RAISE_DEAD, oTarget);
              return TRUE;
             }
          }
        else if (GetTag(oTarget) == "DeathCorpse" && nCanHealHen &&
                !GetIsPC(GetLocalObject(oTarget, "Owner")) &&
                !GetIsDM(GetLocalObject(oTarget, "Owner")) &&
                 GetIsObjectValid(GetRealMaster(GetLocalObject(oTarget, "Owner"))) &&
                 GetFactionEqual(GetRealMaster(GetLocalObject(oTarget, "Owner"))))
          {
           if (GetHasSpell(SPELL_RESURRECTION))
             {
              ActionCastSpellAtObject(SPELL_RESURRECTION, oTarget);
              return TRUE;
             }
           else if (GetHasSpell(SPELL_RAISE_DEAD))
             {
              ActionCastSpellAtObject(SPELL_RAISE_DEAD, oTarget);
              return TRUE;
             }
           else if (GetHasScroll(SPELL_RESURRECTION))
             {
              UseScrollOnObject(SPELL_RESURRECTION, oTarget);
              return TRUE;
             }
           else if (GetHasScroll(SPELL_RAISE_DEAD))
             {
              UseScrollOnObject(SPELL_RAISE_DEAD, oTarget);
              return TRUE;
             }
          }
        nCount++;
        oTarget = GetNextObjectInShape(SHAPE_SPHERE, 25.0, lMyLoc, TRUE);
       }
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
int TalentHealAlly(object oHeal, int nMelee = 0, int nForce = FALSE, int bHasEffect = FALSE)
{
  // We should have a valid target that is not at max health or already dead.
  if (!GetIsObjectValid(oHeal) || GetIsDead(oHeal) ||
       GetCurrentHitPoints(oHeal) == GetMaxHitPoints(oHeal))
    { return FALSE; }

  // If healing is not being forced, I am in melee combat and I was told to NOT
  // heal while in melee, stop checking.
  if (!nForce && nMelee && GetLocalInt(OBJECT_SELF, "DoNotHealMelee") == TRUE)
    { return FALSE; }

  // Constructs cant be healed.
  if (GetRacialType(oHeal) == RACIAL_TYPE_CONSTRUCT) { return FALSE; }

  // Make sure I can see oHeal.
  if (!GetObjectSeen(oHeal)) { return FALSE; }

  // Cant heal if I have a negative effect that should prevent it.
  if (MatchCantHealEffects(OBJECT_SELF)) { return FALSE; }

  // Declare and set up all of the initial variables.
  object oHealKit  = GetBestHealingKit();
  object oPotion   = GetBestHCRPotion();
  int bUseItems    = GetCreatureUseItems(GetRacialType(OBJECT_SELF));
  int bIsSelf      = (oHeal == OBJECT_SELF);
  int bHealSelf    = GetAssociateHealSelf();
  int bUseTalent   = FALSE;
  int bHealAlly    = FALSE;
  int bCanHeal     = FALSE;
  int nHealSkill   = GetSkillRank(SKILL_HEAL);
  int nPotionBoost = nMelee ? 2 : 0;
  int nHealSpell   = 0;
  int nHealRank    = 0;
  talent tHealBest;

  // Determine if I am able to heal the target.
  if (oHeal == GetRealMaster())
    { bHealAlly = GetAssociateHealMaster(); }
  else
    { bHealAlly = GetAssociateHealAlly(oHeal); }

  // If healing is forced, it's ok to heal self or it's ok to heal my ally.
  if (nForce || (bHealSelf && bIsSelf) || (bHealAlly && !bIsSelf))
    {
     // Reset flags.
     if (bIsSelf) { SetLocalInt(OBJECT_SELF, "INeedHealing", 1); }
     SetLocalInt(OBJECT_SELF, "nRoundsSkipped", 0);

     // Dont use normal healing on undead.
     if (GetRacialType(oHeal) != RACIAL_TYPE_UNDEAD)
       {
        // Use any feats that can heal first.
        if (GetHasFeat(FEAT_WHOLENESS_OF_BODY) && bIsSelf)
          {
           bCanHeal = TRUE;
           ActionUseFeat(FEAT_WHOLENESS_OF_BODY, OBJECT_SELF);
           return TRUE;
          }

        if (GetHasFeat(FEAT_LAY_ON_HANDS))
          {
           bCanHeal = TRUE;
           ActionUseFeat(FEAT_LAY_ON_HANDS, oHeal);
           return TRUE;
          }

        // Search for the best spell available if I am not silenced.
        if (!GetHasEffect1(EFFECT_TYPE_SILENCE))
          {
           if (GetHasFixedSpell(SPELL_MASS_HEAL))
             { nHealSpell = SPELL_MASS_HEAL; }
           else if (GetHasFixedSpell(SPELL_HEAL))
             { nHealSpell = SPELL_HEAL; }
           else if (GetHasFixedSpell(SPELL_CURE_CRITICAL_WOUNDS))
             { nHealSpell = SPELL_CURE_CRITICAL_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_HEALING_CIRCLE))
             { nHealSpell = SPELL_HEALING_CIRCLE; }
           else if (GetHasFixedSpell(SPELL_CURE_SERIOUS_WOUNDS))
             { nHealSpell = SPELL_CURE_SERIOUS_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_CURE_MODERATE_WOUNDS))
             { nHealSpell = SPELL_CURE_MODERATE_WOUNDS; }
           else if (GetHasFixedSpell(SPELLABILITY_LESSER_BODY_ADJUSTMENT))
             { nHealSpell = SPELLABILITY_LESSER_BODY_ADJUSTMENT; }
           else if (GetHasFixedSpell(SPELL_CURE_LIGHT_WOUNDS))
             { nHealSpell = SPELL_CURE_LIGHT_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_CURE_MINOR_WOUNDS))
             { nHealSpell = SPELL_CURE_MINOR_WOUNDS; }
           nHealRank = GetSpellRank(nHealSpell);
          }

        // Look for a potion that can be used.
        if (bUseItems)
          {
           talent tBestPotion = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_POTION, 20);
           if (GetIsTalentValid(tBestPotion))
             {
              int nTestSpell = GetIdFromTalent(tBestPotion);
              int nTestRank  = GetSpellRank(nTestSpell) + nPotionBoost;
              if (nTestRank > nHealRank)
                {
                 nHealRank  = nTestRank;
                 nHealSpell = nTestSpell;
                 tHealBest  = tBestPotion;
                 bUseTalent = TRUE;
                }
             }
          }
       }
     else
       {
        // Search for the best spell available if I am not silenced.
        if (!GetHasEffect1(EFFECT_TYPE_SILENCE))
          {
           if (GetHasFixedSpell(SPELL_HARM))
             { nHealSpell = SPELL_HARM; }
           else if (GetHasFixedSpell(SPELL_INFLICT_CRITICAL_WOUNDS))
             { nHealSpell = SPELL_INFLICT_CRITICAL_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_INFLICT_SERIOUS_WOUNDS))
             { nHealSpell = SPELL_INFLICT_SERIOUS_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_NEGATIVE_ENERGY_BURST))
             { nHealSpell = SPELL_NEGATIVE_ENERGY_BURST; }
           else if (GetHasFixedSpell(SPELL_CIRCLE_OF_DOOM))
             { nHealSpell = SPELL_CIRCLE_OF_DOOM; }
           else if (GetHasFixedSpell(SPELL_INFLICT_MODERATE_WOUNDS))
             { nHealSpell = SPELL_INFLICT_MODERATE_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_NEGATIVE_ENERGY_RAY) && !bIsSelf)
             { nHealSpell = SPELL_NEGATIVE_ENERGY_RAY; }
           else if (GetHasFixedSpell(SPELL_INFLICT_LIGHT_WOUNDS))
             { nHealSpell = SPELL_INFLICT_LIGHT_WOUNDS; }
           else if (GetHasFixedSpell(SPELL_INFLICT_MINOR_WOUNDS))
             { nHealSpell = SPELL_INFLICT_MINOR_WOUNDS; }
           nHealRank = GetSpellRank(nHealSpell);
          }
       }

     // Use the chosen spell or potion on the heal target.
     if (nHealSpell == SPELL_HEAL ||
         nHealSpell == SPELL_MASS_HEAL || nHealSpell == SPELL_HARM)
       {
        bCanHeal = TRUE;
        if (!nForce && nMelee)
          { HenchTalentHide(bHasEffect, nMelee); }
        if (bUseTalent)
          { ActionUseTalentOnObject(tHealBest, oHeal); }
        else
          { CastFixedSpellOnObject(nHealSpell, oHeal, nMelee); }
        return TRUE;
       }
     else if (nHealRank > 0)
       {
        bCanHeal = TRUE;
        if (!nForce && nMelee)
          { HenchTalentHide(bHasEffect, nMelee); }
        if (bUseTalent)
          { ActionUseTalentOnObject(tHealBest, oHeal); }
        else
          { CastFixedSpellOnObject(nHealSpell, oHeal, nMelee); }
        return TRUE;
       }
     else if (GetIsObjectValid(oHealKit) && nHealSkill > 0 && bUseItems)
       {
        bCanHeal = TRUE;
        ActionUseSkill(SKILL_HEAL, oHeal, 0, oHealKit);
        return TRUE;
       }
     else if (GetIsObjectValid(oPotion) && bUseItems)
       {
        bCanHeal = TRUE;
        UseHCRPotion(oHeal, oPotion);
        return TRUE;
       }

     // Indicate that I was unable to heal myself.
     if (bIsSelf  && !bCanHeal) { SetLocalInt(OBJECT_SELF, "INeedHealing", 2); }
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
int TalentHealDyingAlly(object oHeal, int nMelee = 0, int nForce = FALSE)
{
  // We should have a valid seen target that is dying and a valid race.
  if (!GetIsObjectValid(oHeal) || GetIsDM(oHeal) || !GetObjectSeen(oHeal) ||
       oHeal == OBJECT_SELF || GetCurrentHitPoints(oHeal) > 1 ||
       GetRacialType(oHeal) == RACIAL_TYPE_UNDEAD ||
       GetRacialType(oHeal) == RACIAL_TYPE_CONSTRUCT ||
     (!nForce && nMelee && GetLocalInt(OBJECT_SELF, "DoNotHealMelee")))
    { return FALSE; }

  // Try to use a HCR Non-Stack Cure Potion.
  object oHCRPotion = GetBestHCRPotion();
  if (GetIsObjectValid(oHCRPotion))
    {
     if (GetDistanceToObject(oHeal) > 3.0)
       { ActionMoveToObject(oHeal, TRUE); }
     ActionDoCommand(UseHCRPotion(oHeal, oHCRPotion));
     return TRUE;
    }

  // Try to use a HCR Heal Kit.
  object oHCRHealKit = GetItemPossessedBy(OBJECT_SELF, "hc_healkit");
  int nHealSkill = GetSkillRank(SKILL_HEAL);
  if (GetIsObjectValid(oHCRHealKit) && nHealSkill > 0)
    {
     if (GetDistanceToObject(oHeal) > 3.0)
       { ActionMoveToObject(oHeal, TRUE); }
     ActionDoCommand(UseHCRHealKit(oHeal, oHCRHealKit));
     return TRUE;
    }
  return FALSE;
}
//::////////////////////////////////////////////////////////////////////////////
void UseHCRPotion(object oHeal, object oPotion)
{
  if (GetIsObjectValid(oHeal) && GetIsObjectValid(oPotion))
    {
     SetLocalObject(OBJECT_SELF, "OTHER", oHeal);
     SetLocalObject(OBJECT_SELF, "ITEM", oPotion);
     SetLocalString(OBJECT_SELF, "TAG", GetTag(oPotion));
     ExecuteScript(sPotionScript, OBJECT_SELF);
    }
}
//::////////////////////////////////////////////////////////////////////////////
void UseHCRHealKit(object oHeal, object oHealKit)
{
  if (GetIsObjectValid(oHeal) && GetIsObjectValid(oHealKit))
    {
     SetLocalObject(OBJECT_SELF, "OTHER", oHeal);
     SetLocalObject(OBJECT_SELF, "ITEM", oHealKit);
     ExecuteScript(sUseKitScript, OBJECT_SELF);
    }
}
//::////////////////////////////////////////////////////////////////////////////
void UseScrollOnObject(int nSpell, object oTarget)
{
  // Determine what scroll we are using.
  object oItem;
  if (nSpell == SPELL_RESURRECTION)
    {
     if (GetIsObjectValid(GetItemPossessedBy(OBJECT_SELF, "NW_IT_SPDVSCR702")))
       { oItem = GetItemPossessedBy(OBJECT_SELF, "NW_IT_SPDVSCR702"); }
     else
       { oItem = GetItemPossessedBy(OBJECT_SELF, "7ResurrectionD_C"); }
    }
  else if (nSpell == SPELL_RAISE_DEAD)
    {
     if (GetIsObjectValid(GetItemPossessedBy(OBJECT_SELF, "NW_IT_SPDVSCR501")))
       { oItem = GetItemPossessedBy(OBJECT_SELF, "NW_IT_SPDVSCR501"); }
     else
       { oItem = GetItemPossessedBy(OBJECT_SELF, "5RaiseDeadD_C"); }
    }

  // Verify we have a valid scroll before continuing.
  if (!GetIsObjectValid(oItem)) { return; }

  object oOwner = GetLocalObject(oTarget, "Owner");
  int bFeedBack = GetLocalInt(GetObjectByTag(HS_VARS_OBJ), HS_VAR_REZ_FDB);
  if (bFeedBack && GetTag(oTarget) == "DeathCorpse" &&
      GetIsObjectValid(oOwner) && GetIsPC(oOwner) && !GetIsDM(oOwner))
    { SendMessageToPC(oOwner, GetName(OBJECT_SELF) + REZ_MSG30); }

  ClearActions(CLEAR_I0_HEAL_UseScrollOnObject);
  DestroyObject(oItem);
  SetAssociateState(NW_ASC_IS_BUSY, TRUE);
  ActionPlayAnimation(ANIMATION_FIREFORGET_READ);
  DelayCommand(2.0, ActionCastSpellAtObject(nSpell, oTarget, METAMAGIC_ANY, TRUE));
  DelayCommand(6.0, SetAssociateState(NW_ASC_IS_BUSY, FALSE));
}
//::////////////////////////////////////////////////////////////////////////////
//void main(){}
