260 lines
9.2 KiB
Plaintext
260 lines
9.2 KiB
Plaintext
////////////////////////////////////////////////////////////////////////////////
|
|
// cai_Support - Custom Combat AI - Support Mode
|
|
// By Deva Bryson Winblood. 11/2004
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#include "cai_inc_hos"
|
|
#include "nw_I0_generic"
|
|
|
|
int fnClassIsSpellCaster(int nPos,object oNPC)
|
|
{ // tell me if this class is a spell caster
|
|
int nClass=GetClassByPosition(nPos,oNPC);
|
|
int bRet=FALSE;
|
|
switch(nClass)
|
|
{
|
|
case CLASS_TYPE_ARCANE_ARCHER:
|
|
case CLASS_TYPE_BARD:
|
|
case CLASS_TYPE_CLERIC:
|
|
case CLASS_TYPE_DRUID:
|
|
case CLASS_TYPE_FEY:
|
|
case CLASS_TYPE_DRAGONDISCIPLE:
|
|
case CLASS_TYPE_PALADIN:
|
|
case CLASS_TYPE_PALEMASTER:
|
|
case CLASS_TYPE_RANGER:
|
|
case CLASS_TYPE_SHADOWDANCER:
|
|
case CLASS_TYPE_SHIFTER:
|
|
case CLASS_TYPE_SORCERER:
|
|
case CLASS_TYPE_WIZARD: { bRet=TRUE; break; }
|
|
default: { break; }
|
|
}
|
|
return bRet;
|
|
} // fnClassIsSpellCaster()
|
|
|
|
object fnGetBestTarget(int nRace)
|
|
{ // tell me who my best target is at the moment
|
|
object oRet=OBJECT_INVALID;
|
|
int nN;
|
|
float fD;
|
|
int nVal;
|
|
object oOb;
|
|
int nThis;
|
|
int nC;
|
|
nN=1;
|
|
oOb=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,OBJECT_SELF,nN,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
|
|
fD=GetDistanceBetween(oOb,OBJECT_SELF);
|
|
while(oOb!=OBJECT_INVALID&&fD<30.0)
|
|
{ // check all nearby targets
|
|
nThis=0;
|
|
nN++;
|
|
if (fD<20.0) nThis++;
|
|
if (fD<15.0) nThis++;
|
|
if (fD<10.0) nThis++;
|
|
if (fD<8.0) nThis++;
|
|
if (fD<5.0) nThis++;
|
|
fD=IntToFloat(GetCurrentHitPoints(oOb))/IntToFloat(GetMaxHitPoints(oOb));
|
|
if (fD<0.51) nThis++;
|
|
if (fD<0.31) nThis++;
|
|
if (fD<0.11) nThis++;
|
|
if (nRace==RACIAL_TYPE_UNDEAD)
|
|
{ // see if target is a cleric
|
|
nC=GetClassByPosition(1,oOb);
|
|
if (nC==CLASS_TYPE_CLERIC||nC==CLASS_TYPE_PALADIN) nThis=nThis+3;
|
|
nC=GetClassByPosition(2,oOb);
|
|
if (nC==CLASS_TYPE_CLERIC||nC==CLASS_TYPE_PALADIN) nThis=nThis+3;
|
|
nC=GetClassByPosition(3,oOb);
|
|
if (nC==CLASS_TYPE_CLERIC||nC==CLASS_TYPE_PALADIN) nThis=nThis+3;
|
|
} // see if target is a cleric
|
|
if (fnClassIsSpellCaster(1,oOb)) nThis++;
|
|
if (fnClassIsSpellCaster(2,oOb)) nThis++;
|
|
if (fnClassIsSpellCaster(3,oOb)) nThis++;
|
|
if (nThis>nVal)
|
|
{ // new best target
|
|
oRet=oOb;
|
|
nVal=nThis;
|
|
} // new best target
|
|
oOb=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,OBJECT_SELF,nN,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
|
|
fD=GetDistanceBetween(oOb,OBJECT_SELF);
|
|
} // check all nearby targets
|
|
return oRet;
|
|
} // tell me who my best target is at the moment
|
|
|
|
void main()
|
|
{
|
|
object oMe=OBJECT_SELF;
|
|
object oTarget=GetAttackTarget();
|
|
float fF;
|
|
int nN;
|
|
int bMUST=FALSE;
|
|
int nMyRace=GetRacialType(oMe);
|
|
object oBest=fnGetBestTarget(nMyRace);
|
|
int bTargetChanged=FALSE;
|
|
int nState=GetLocalInt(oMe,"nCAIState");
|
|
if (oBest!=oTarget)
|
|
{
|
|
oTarget=oBest;
|
|
bTargetChanged=TRUE;
|
|
}
|
|
if (GetIsObjectValid(oTarget))
|
|
{ // target is valid
|
|
nState++;
|
|
if (nState>4) nState=1;
|
|
oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,oMe,1,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
|
|
if (GetIsObjectValid(oBest))
|
|
{ // there are enemies
|
|
if (GetDistanceBetween(oMe,oBest)<5.1)
|
|
{ // enemy too close
|
|
DetermineCombatRound(oBest);
|
|
} // enemy too close
|
|
else
|
|
{ // not close
|
|
oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,1,CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
if (GetIsObjectValid(oBest))
|
|
{ // there are friends
|
|
if (GetDistanceBetween(oMe,oBest)<10.1)
|
|
{ // friends are close
|
|
nN=1;
|
|
oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,d6(),CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
while((GetIsObjectValid(oBest)==FALSE||(GetDistanceBetween(oBest,oMe)<10.1&&GetDistanceBetween(oBest,oMe)!=0.0))&&nN<4)
|
|
{ // find friend
|
|
nN++;
|
|
oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,d6(),CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
} // find friend
|
|
if (GetIsObjectValid(oBest)==FALSE) oBest=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_FRIEND,oMe,1,CREATURE_TYPE_IS_ALIVE,TRUE);
|
|
if (GetIsObjectValid(oBest))
|
|
{ // friend to help found
|
|
nN=GetMaxHitPoints(oBest)-GetCurrentHitPoints(oBest);
|
|
if ((nN>0||GetIsDead(oBest))&&nState!=4)
|
|
{ // healing needed
|
|
if (GetIsPC(oBest)) nN=caiGetAvailableHealPC(oMe,oBest);
|
|
else if (GetIsDead(oBest)==FALSE) {
|
|
nN=caiGetAvailableHeal(oMe,nN); }
|
|
if (nN!=0)
|
|
{ // heal
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
|
|
} // heal
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oTarget);
|
|
} // combat
|
|
} // healing needed
|
|
else if (GetMaxHitPoints(oMe)-GetCurrentHitPoints(oMe)>0)
|
|
{ // heal self
|
|
bMUST=FALSE;
|
|
if ((IntToFloat(GetCurrentHitPoints(oMe))/IntToFloat(GetMaxHitPoints(oMe)))<0.51) bMUST=TRUE;
|
|
nN=caiGetAvailableHeal(oMe,nN,bMUST);
|
|
if (nN!=0)
|
|
{ // heal self
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
|
|
} // heal self
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oTarget);
|
|
} // combat
|
|
} // heal self
|
|
else if (nState!=4)
|
|
{ // defense, buff
|
|
if (nState==1)
|
|
{ // defense
|
|
nN=caiGetAvailableDefensiveSpell(oMe,oBest);
|
|
if (nN!=0)
|
|
{ // spell
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
|
|
} // spell
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oTarget);
|
|
} // combat
|
|
} // defense
|
|
else
|
|
{ // buff
|
|
nN=caiGetAvailableBuff(oMe,oBest);
|
|
if (nN!=0)
|
|
{ // spell
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oBest));
|
|
} // spell
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oTarget);
|
|
} // combat
|
|
} // buff
|
|
} // defense, buff
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oTarget);
|
|
} // combat
|
|
} // friend to help found
|
|
} // friends are close
|
|
else
|
|
{ // I am alone
|
|
if (nState==1)
|
|
{ // defensive spell
|
|
nN=caiGetAvailableDefensiveSpell(oMe,oMe);
|
|
if (nN!=0)
|
|
{ // spell
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
|
|
} // spell
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oMe);
|
|
} // combat
|
|
} // defensive spell
|
|
else if (nState==2||nState==4)
|
|
{ // combat
|
|
DetermineCombatRound(oMe);
|
|
} // combat
|
|
else if (nState==3)
|
|
{ // buff spell
|
|
nN=caiGetAvailableBuff(oMe,oMe);
|
|
if (nN!=0)
|
|
{ // spell
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
|
|
} // spell
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oMe);
|
|
} // combat
|
|
} // buff spell
|
|
} // I am alone
|
|
} // there are friends
|
|
else
|
|
{ // I am alone
|
|
if (nState==1)
|
|
{ // defensive spell
|
|
nN=caiGetAvailableDefensiveSpell(oMe,oMe);
|
|
if (nN!=0)
|
|
{ // spell
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
|
|
} // spell
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oMe);
|
|
} // combat
|
|
} // defensive spell
|
|
else if (nState==2||nState==4)
|
|
{ // combat
|
|
DetermineCombatRound(oMe);
|
|
} // combat
|
|
else if (nState==3)
|
|
{ // buff spell
|
|
nN=caiGetAvailableBuff(oMe,oMe);
|
|
if (nN!=0)
|
|
{ // spell
|
|
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
|
|
} // spell
|
|
else
|
|
{ // combat
|
|
DetermineCombatRound(oMe);
|
|
} // combat
|
|
} // buff spell
|
|
} // I am alone
|
|
} // not close
|
|
} // there are enemies
|
|
} // target is valid
|
|
// check need to heal self and remove negative spells
|
|
// set up sentinel for resting
|
|
if (GetLocalInt(oMe,"bCAIRESTSENTINEL")!=TRUE)
|
|
{ // trigger the sentinel
|
|
SetLocalInt(oMe,"bCAIRESTSENTINEL",TRUE);
|
|
caiRestSentinel(oMe);
|
|
} // trigger the sentinel
|
|
}
|