HoS_PRC8/_mod/_module/nss/cai_warmagic.nss
Jaysyn904 04165202c0 Initial upload
Initial upload
2024-11-25 19:36:07 -05:00

530 lines
21 KiB
Plaintext

////////////////////////////////////////////////////////////////////////////////
// cai_warmagic - Another caster AI to try out. This one may be more effective
// with some NPCs.
// By Deva Bryson Winblood. 11/2004
////////////////////////////////////////////////////////////////////////////////
#include "cai_inc_hos"
#include "nw_I0_generic"
#include "prc_inc_racial"
const float CLOSE_QUARTERS = 3.1; // range for close quarters combat
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)
{ // check all nearby targets
nThis=1;
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
oOb=GetNearestCreature(CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,OBJECT_SELF,1,CREATURE_TYPE_IS_ALIVE,TRUE,CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN);
if (GetIsObjectValid(oOb)&&oOb!=oRet)
{ // check to see if someone is really close
if (GetDistanceBetween(oOb,OBJECT_SELF)<2.5) oRet=oOb;
} // check to see if someone is really close
return oRet;
} // tell me who my best target is at the moment
int fnCanCastSpells(object oNPC)
{
if (fnClassIsSpellCaster(1,oNPC)) return TRUE;
else if (fnClassIsSpellCaster(2,oNPC)) return TRUE;
else if (fnClassIsSpellCaster(3,oNPC)) return TRUE;
return FALSE;
}
void fnSetCounts()
{ // PURPOSE: Count enemy types
object oMe=OBJECT_SELF;
object oEnemy;
int nN=1;
int nUND=0;
int nOUT=0;
int nCON=0;
int nARC=0;
int nDIV=0;
int nDRA=0;
int nRGD=0;
int nMEL=0;
int nV;
object oItem;
oEnemy=GetNearestCreature(CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN,oMe,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_IS_ALIVE,TRUE);
while(GetIsObjectValid(oEnemy))
{ // count enemies
oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oEnemy);
nV=caiGetWeaponType(oItem);
if (nV==CAI_WEAPON_MELEE||nV==CAI_WEAPON_CREATURE) nMEL++;
else if (nV==CAI_WEAPON_RANGED) nRGD++;
if (caiGetIsCleric(oEnemy)) nDIV++;
if (caiGetIsArcane(oEnemy)) nARC++;
nV=MyPRCGetRacialType(oEnemy);
if (nV==RACIAL_TYPE_UNDEAD) nUND++;
else if (nV==RACIAL_TYPE_OUTSIDER) nOUT++;
else if (nV==RACIAL_TYPE_CONSTRUCT) nCON++;
else if (nV==RACIAL_TYPE_DRAGON) nDRA++;
nN++;
oEnemy=GetNearestCreature(CREATURE_TYPE_PERCEPTION,PERCEPTION_SEEN,oMe,nN,CREATURE_TYPE_REPUTATION,REPUTATION_TYPE_ENEMY,CREATURE_TYPE_IS_ALIVE,TRUE);
} // count enemies
SetLocalInt(oMe,"nCAICountUND",nUND);
SetLocalInt(oMe,"nCAICountOUT",nOUT);
SetLocalInt(oMe,"nCAICountCON",nCON);
SetLocalInt(oMe,"nCAICountARC",nARC);
SetLocalInt(oMe,"nCAICountDIV",nDIV);
SetLocalInt(oMe,"nCAICountDRA",nDRA);
SetLocalInt(oMe,"nCAICountRGD",nRGD);
SetLocalInt(oMe,"nCAICountMEL",nMEL);
} // fnSetCounts()
void main()
{
object oMe=OBJECT_SELF;
object oTarget=GetAttackTarget();
int nMyRace=GetRacialType(oMe);
object oBest=fnGetBestTarget(nMyRace);
int nCAICountUND=GetLocalInt(oMe,"nCAICountUND");
int nCAICountOUT=GetLocalInt(oMe,"nCAICountOUT");
int nCAICountCON=GetLocalInt(oMe,"nCAICountCON");
int nCAICountARC=GetLocalInt(oMe,"nCAICountARC");
int nCAICountDIV=GetLocalInt(oMe,"nCAICountDIV");
int nCAICountDRA=GetLocalInt(oMe,"nCAICountDRA");
int nCAICountRGD=GetLocalInt(oMe,"nCAICountRGD");
int nCAICountMEL=GetLocalInt(oMe,"nCAICountMEL");
int bOverrideStates=FALSE;
int nState=GetLocalInt(oMe,"nCAIState");
int nN;
object oItem;
int bTargetChanged=FALSE;
if (oTarget!=oBest)
{ // target changed...
if (GetArea(oTarget)==GetArea(oMe)&&GetIsDead(oTarget)==FALSE)
{ // old target is still in same area and living
if (GetObjectSeen(oTarget)==FALSE&&GetDistanceBetween(oTarget,oMe)<20.0)
{ // the target is not visible - suspect invisibility
nN=caiProfessionalAntiStealth();
if (nN!=0)
{ // spell returned
bOverrideStates=TRUE;
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // spell returned
else
{ // can't do anything about it
oTarget=oBest;
bTargetChanged=TRUE;
} // can't do anything about it
} // the target is not visible - suspect invisibility
else
{ // not unseen - simply found a better target
oTarget=oBest;
bTargetChanged=TRUE;
} // not unseen - simply found a better target
} // old target is still in same area and living
} // target changed...
if (!bOverrideStates)
{ // override was not called
if (bTargetChanged)
{ // recount the types of enemies nearby
fnSetCounts();
} // recount the types of enemies nearby
if (GetDistanceBetween(oMe,oTarget)<CLOSE_QUARTERS)
{ // close quarters AI
nState=nState+1;
if(nState>4) nState=1;
if (nState<4)
{ // close quarters
nN=caiProfessionalCloseQuarters(oTarget);
if (nN!=0)
{ // spell to cast returned
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // spell to cast returned
else
{
nState=4;
}
} // close quarters
if (nState==4)
{ // defensive
nN=caiProfessionalDefensive(oTarget);
if (nN!=0)
{ // defensive spell found
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // defensive spell found
else
{ // no defensive
nN=caiProfessionalCloseQuarters(oTarget);
if (nN!=0)
{ // offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // offensive spell
else
{ // combat
AssignCommand(oMe,ActionEquipMostDamagingMelee(oTarget));
if (GetLastDamager()==oTarget)
{ // check to see if back off
if(GetLocalInt(oMe,"nCAILastHP")!=GetCurrentHitPoints(oMe))
{ // run away
AssignCommand(oMe,ActionMoveAwayFromObject(oTarget,TRUE,7.0));
SetLocalInt(oMe,"nCAILastHP",GetCurrentHitPoints(oMe));
} // run away
} // check to see if back off
DetermineCombatRound(oTarget);
} // combat
} // no defensive
} // defensive
} // close quarters AI
else
{ // distance AI
nState=nState+1;
if(nState>5) nState=1;
if (nState==1)
{ // state 1 defensive (summon vs. Melee)
oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oMe);
if (fnCanCastSpells(oTarget)||caiGetWeaponType(oItem)==CAI_WEAPON_RANGED)
{ // defensive
nN=caiProfessionalDefensive(oTarget);
if (nN!=0)
{ // defensive spell found
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // defensive spell found
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // defensive
else
{ // summon
if (GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_SUMMONED,oMe,1))==FALSE)
{ // try to summon something
nN=caiProfessionalSummon(oTarget,1);
if (nN!=0)
{ // summon something
AssignCommand(oMe,ActionCastSpellAtLocation(nN,GetLocation(oMe)));
} // summon something
else
{ // check for familiars or animal companions to summon
if(GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMe,1))==FALSE&&GetHasFeat(FEAT_SUMMON_FAMILIAR))
{ // summon familiar
AssignCommand(oMe,ActionCastSpellAtObject(SPELLABILITY_SUMMON_FAMILIAR,oMe));
AssignCommand(oMe,ActionDoCommand(DecrementRemainingFeatUses(oMe,FEAT_SUMMON_FAMILIAR)));
} // summon familiar
else if(GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oMe,1))==FALSE&&GetHasFeat(FEAT_ANIMAL_COMPANION))
{ // summon animal companion
AssignCommand(oMe,ActionCastSpellAtObject(SPELLABILITY_SUMMON_ANIMAL_COMPANION,oMe));
AssignCommand(oMe,ActionDoCommand(DecrementRemainingFeatUses(oMe,FEAT_ANIMAL_COMPANION)));
} // summon animal companion
else
{ // try to buff existing familiar or animal companion
oItem=GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMe,1);
if (GetIsObjectValid(oItem)==FALSE) oItem=GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oMe,1);
if (GetIsObjectValid(oItem))
{ // valid target
nN=caiProfessionalAssocBuff(oItem);
if (nN!=0)
{ // buff associate
AssignCommand(oMe,ActionCastSpellAtObject(nN,oItem));
} // buff associate
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // valid target
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // try to buff existing familiar or animal companion
} // check for familiars or animal companions to summon
} // try to summon something
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // summon
} // state 1 defensive (summon vs. Melee)
if (nState==2)
{ // state 2 summon (defensive vs. melee)
oItem=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oMe);
if (fnCanCastSpells(oTarget)||caiGetWeaponType(oItem)==CAI_WEAPON_RANGED)
{ // summon
if (GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_SUMMONED,oMe,1))==FALSE)
{ // try to summon something
nN=caiProfessionalSummon(oTarget,1);
if (nN!=0)
{ // summon something
AssignCommand(oMe,ActionCastSpellAtLocation(nN,GetLocation(oMe)));
} // summon something
else
{ // check for familiars or animal companions to summon
if(GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMe,1))==FALSE&&GetHasFeat(FEAT_SUMMON_FAMILIAR))
{ // summon familiar
AssignCommand(oMe,ActionCastSpellAtObject(SPELLABILITY_SUMMON_FAMILIAR,oMe));
AssignCommand(oMe,ActionDoCommand(DecrementRemainingFeatUses(oMe,FEAT_SUMMON_FAMILIAR)));
} // summon familiar
else if(GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oMe,1))==FALSE&&GetHasFeat(FEAT_ANIMAL_COMPANION))
{ // summon animal companion
AssignCommand(oMe,ActionCastSpellAtObject(SPELLABILITY_SUMMON_ANIMAL_COMPANION,oMe));
AssignCommand(oMe,ActionDoCommand(DecrementRemainingFeatUses(oMe,FEAT_ANIMAL_COMPANION)));
} // summon animal companion
else
{ // try to buff existing familiar or animal companion
oItem=GetAssociate(ASSOCIATE_TYPE_FAMILIAR,oMe,1);
if (GetIsObjectValid(oItem)==FALSE) oItem=GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION,oMe,1);
if (GetIsObjectValid(oItem))
{ // valid target
nN=caiProfessionalAssocBuff(oItem);
if (nN!=0)
{ // buff associate
AssignCommand(oMe,ActionCastSpellAtObject(nN,oItem));
} // buff associate
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // valid target
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // try to buff existing familiar or animal companion
} // check for familiars or animal companions to summon
} // try to summon something
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // summon
else
{ // defensive
nN=caiProfessionalDefensive(oTarget);
if (nN!=0)
{ // defensive spell found
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // defensive spell found
else
{ // try offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // cast offensive spell
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // cast offensive spell
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // try offensive
} // defensive
} // state 2 summon (defensive vs. melee)
if (nState==3)
{ // state 3 offensive (counter vs. those with spells on them)
if (fnGetHasSpellDefense(oTarget))
{ // counter
nN=caiProfessionalCounter(oTarget);
if (nN!=0)
{ // counter magic found
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // counter magic found
else
{ // offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // offensive
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // offensive
else
{ // defensive
nN=caiProfessionalDefensive(oTarget);
if (nN!=0)
{ // defensive
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // defensive
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // defensive
} // offensive
} // counter
else
{ // offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // offensive
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // offensive
else
{ // defensive
nN=caiProfessionalDefensive(oTarget);
if (nN!=0)
{ // defensive
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // defensive
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // defensive
} // offensive
} // state 3 offensive (counter vs. caster)
if (nState==4)
{ // state 4 offensive - check for scrolls and grenades to use as well
oItem=caiProfessionalItem(oTarget);
if (GetIsObjectValid(oItem))
{ // an item to use was returned... use it
} // an item to use was returned... use it
else
{ // offensive
nN=caiProfessionalOffensive(oTarget);
if (nN!=0)
{ // offensive
AssignCommand(oMe,ActionCastSpellAtObject(nN,oTarget));
} // offensive
else
{ // defensive
nN=caiProfessionalDefensive(oTarget);
if (nN!=0)
{ // defensive
AssignCommand(oMe,ActionCastSpellAtObject(nN,oMe));
} // defensive
else
{ // melee
AssignCommand(oMe,ActionEquipMostDamagingRanged(oTarget));
DetermineCombatRound(oTarget);
} // melee
} // defensive
} // offensive
} // state 4 offensive - check for scrolls and grenades to use as well
} // distance AI
} // override was not called
if (GetLocalInt(oMe,"bCAIRESTSENTINEL")!=TRUE)
{ // trigger the sentinel
SetLocalInt(oMe,"bCAIRESTSENTINEL",TRUE);
caiRestSentinel(oMe);
} // trigger the sentinel
}