//////////////////////////////////////////////////////////////////////////////// // 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)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 }