You probably have to write your own healing AI. Here’s a starter pack.
Put this as NPC’s OnHeartbeat. It will cause it to heal itself, all nearby faction members and PCs. You can experiment with talents to implement buffs, cures, etc.
The NPC needs to know healing spells to make this work.
#include "x0_i0_talent"
void main()
{
object oTarget;
location lLocation;
// Proceed only when idle or walking
switch(GetCurrentAction())
{
case ACTION_INVALID:
case ACTION_FOLLOW:
case ACTION_RANDOMWALK:
case ACTION_MOVETOPOINT:
{
break;
}
default:
{
return;
}
}
// Cycle through all PCs and faction members seen in 20m sphere and
// heal everyone who has less than 50% HP.
lLocation = GetLocation(OBJECT_SELF);
oTarget = GetFirstObjectInShape(SHAPE_SPHERE, 20.0, lLocation, TRUE);
while(GetIsObjectValid(oTarget))
{
if((GetIsPC(oTarget) || GetFactionEqual(oTarget)) &&
(GetCurrentHitPoints(oTarget) * 2 < GetMaxHitPoints(oTarget)))
{
if(TalentHeal(TRUE, oTarget))
{
SpeakString("Healing " + GetName(oTarget) + ".");
}
else
{
SpeakString("I can't heal " + GetName(oTarget) + ".");
}
return;
}
oTarget = GetNextObjectInShape(SHAPE_SPHERE, 20.0, lLocation, TRUE);
}
}
Note that writing AI is tough as you need to cover a lot of situations, some of which may not be obvious. For example, AI should prioritize itself (survival), then PC master (when being a hench), then faction memebers. This is not present in this example.
I went back to this problem when making an example script for another project. Since the work is already done, I’m leaving this here as an educational resource.
The issue I have is that BW’s TalentHeal always goes for the best healing spell, which makes AI use i.e. Heal regardless of target (overkill, wasteful, you call it). TRUE parameter just makes it heal any damage, not just <50%. It can however find the target itself - no need for GetFirstObjectInShape (but it doesn’t honor LOS).
So, I made a smarter healer script that gradually tries to find the lowest spell level that should match the target. GetTalent below does this magic.
/*
CREATURE HEARTBEAT SCRIPT
Caller checks all seen, friendly and alive creatures (not undead)
and heals closest one with less than 50% of maximum HP.
*/
#include "x0_i0_talent"
const int TALENT_HEAL = TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH;
// Returns most economical talent in iCategory to be used by oSelf on oTarget.
// Most economical means one that is closest to oTarget's CR. If iBest is TRUE
// then this function returns talent at highest CR. If no talent is available
// in this category, an invalid talent will be returned.
talent GetTalent(int iCategory, object oTarget, int iBest=FALSE, object oSelf=OBJECT_SELF)
{
talent tTalent;
int iCR;
iCR = iBest ? TALENT_ANY : FloatToInt(GetChallengeRating(oTarget));
do
{
tTalent = GetCreatureTalentBest(iCategory, iCR++, oSelf);
}
while(!GetIsTalentValid(tTalent) && iCR <= TALENT_ANY);
return tTalent;
}
void main()
{
object oTarget;
talent tHeal;
int iTarget;
// Proceed only when idle or walking.
switch(GetCurrentAction())
{
case ACTION_INVALID:
case ACTION_FOLLOW:
case ACTION_RANDOMWALK:
case ACTION_MOVETOPOINT:
{
break;
}
default:
{
return;
}
}
// Start healing target scan with self.
oTarget = OBJECT_SELF;
while(GetIsObjectValid(oTarget))
{
// Check if target has less than 50% of its maximum HP and
// is not an undead (too late for healing in this case).
if(GetCurrentHitPoints(oTarget) * 2 < GetMaxHitPoints(oTarget)
&& GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD)
{
// Obtain most economical healing talent for target.
tHeal = GetTalent(TALENT_HEAL, oTarget);
// Check if target can be healed (talent is valid).
if(GetIsTalentValid(tHeal))
{
ClearAllActions(TRUE);
ActionSpeakString("Healing " + GetName(oTarget) + ".");
ActionUseTalentOnObject(tHeal, oTarget);
}
// Return (if target cannot be healed then nobody can be).
return;
}
// Get next target (nearest seen allied creature).
oTarget = GetNearestSeenFriend(OBJECT_SELF, iTarget++);
}
}