/*//////////////////////////////////////////////////////////////////////////////////////////////////// Script Name: 0i_spells Programmer: Philos ////////////////////////////////////////////////////////////////////////////////////////////////////// Include scripts for base spells. Category: Enhancement E Protection P Indiscriminant I Discriminant D Range R Touch T Summon S Healing H Cure C Buff Duration: 1 - All 2 - Short 3 - Long Buff Target: 0 - Caster only 1-6 Str, Dex, Con, Int, Wis, Cha: Highest Ability Score 7 - Lowest AC 8 - Lowest AC without AC Bonus 9 - Highest Atk 10 - Most Wounded 11 - Lowest Fortitude 12 - Lowest Reflex 13 - Lowest Will 14 - Lowest total saves 15 - Buffs an Item Buff Groups: -1 - Elemental Resistances. -2 - Summons -3 - AC (Non armor) -4 - AC (for Armor/Shield) -5 - Chance to Miss (Invisibility) -6 - Regeneration -7 - Globes of Invulnerablitity -8 - Damage Reduction -9 - Mantles -10 - Alignment vs Chaos -11 - Alignment vs Evil -12 - Alignment vs Good -13 - Alignment vs Law -14 - Atk Bonus (for Weapon) -15 - Light effects -16 - Haste effects -17 - Polymorph effects *///////////////////////////////////////////////////////////////////////////////////////////////////// #include "0i_messages" #include "0i_states_cond" #include "0i_items" #include "X0_I0_POSITION" struct stSpell { object oPC; object oCaster; object oTarget; int nBuffType; int nTarget; int nPosition; int nClass; int nLevel; int nMaxSlots; int nSlot; }; // Returns TRUE if oCreature can cast nSpell from nLevel. int ai_GetCanCastSpell(object oCreature, int nSpell, int nClass, int nLevel, int nMetaMagic = 0, int nDomain = 0); // Returns TRUE if oCreature is immune to petrification. int ai_IsImmuneToPetrification(object oCaster, object oCreature); // Returns TRUE if oCreature has an effect from a mind affecting spell. int ai_DoIHaveAMindAffectingSpellOnMe(object oCreature); // Returns TRUE if nSpell is a cure spell. int ai_IsCureSpell(int nSpell); // Returns TRUE if nSpell is an inflict spell. int ai_IsInflictSpell(int nSpell); // Returns TRUE if nSpell is an area of effect spell. int ai_IsAreaOfEffectSpell(int nSpell); // Returns 1(TRUE) if oAssociate is a spellcaster. // Rturns 2(TRUE) if oAssociate is a memorizing spellcaster. int ai_GetIsSpellCaster(object oAssociate); // Returns TRUE if oCreature is immune to nSpells effects. int ai_CreatureImmuneToEffect(object oCaster, object oCreature, int nSpell); // Returns the ranged of nSpell from the spells.2da(Column "Range"). // S = 8.0f, M = 20.0f, L = 40.0f, T = 5.0f, else = 0.1f; float ai_GetSpellRange(int nSpell); // Returns TRUE if oTarget has a spell that we would want to dispel. // Checks for harmful effects as well as buffing effects. int ai_CreatureHasDispelableEffect(object oCaster, object oCreature); // Remove nEffectType of Type specified on oCreature; // nEffectType uses the constants EFFECT_TYPE_* void ai_RemoveASpecificEffect(object oCreature, int nEffectType); // Returns TRUE if oCreature has nEffectType. // nEffectType uses the constants EFFECT_TYPE_* int ai_GetHasEffectType(object oCreature, int nEffectType); // Checks oCreature for special abilities have a long duration. void ai_CheckCreatureSpecialAbilities(object oCreature); // Checks oCreature for the silence effect and if the spell only has a somatic component. int ai_IsSilenced(object oCreature, int nSpell); // Returns TRUE if ArcaneSpellFailure is too high to chance casting the spell. int ai_ArcaneSpellFailureTooHigh(object oCreature, int nClass, int nLevel, int nSlot); // Returns TRUE if oCaster casts nSpell on oTarget. // This will only cast the spell if oTarget DOES NOT already have the spell // effect, and the caster has the spell ready. int ai_TryToCastSpell(object oCaster, int nSpell, object oTarget); // In "Buff_Target" column the value of 0 in the "ai_spells.2da" references the Caster. // In "Buff_Target" column this is value 1-6(STR, DEX, CON, INT, WIS, CHA) in the "ai_spells.2da". object ai_BuffHighestAbilityScoreTarget(object oCaster, int nSpell, int nAbilityScore, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 7 in the "ai_spells.2da". object ai_BuffLowestACTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 8 in the "ai_spells.2da". object ai_BuffLowestACWithOutACBonus(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 9 in the "ai_spells.2da". object ai_BuffHighestAttackTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 10 in the "ai_spells.2da". object ai_BuffMostWoundedTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 11 in the "ai_spells.2da". object ai_BuffLowestFortitudeSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 12 in the "ai_spells.2da". object ai_BuffLowestReflexSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 13 in the "ai_spells.2da". object ai_BuffLowestWillSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 14 in the "ai_spells.2da". object ai_BuffLowestSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // In "Buff_Target" column this is value 15 in the "ai_spells.2da". object ai_BuffItemTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_"); // Returns a target for nSpell cast by oCaster based on ai_spells.2da file. object ai_GetBuffTarget(object oCaster, int nSpell); // Casts a memorized spell from oCaster of nClass, nSpellLevel, nSpellSlot on oTarget. void ai_CastMemorizedSpell(object oCaster, int nClass, int nSpellLevel, int nSpellSlot, object oTarget, int bInstant, object oPC = OBJECT_INVALID); // Casts a known spell from oCaster of nClass, nSpell on oTarget. void ai_CastKnownSpell(object oCaster, int nClass, int nSpell, object oTarget, int bInstant, object oPC = OBJECT_INVALID); // Returns true if the spell is cast. // Checks if they have the spell and will cast it if possible. int ai_CheckAndCastSpell(object oCaster, int nSpell, int nSpellGroup, float fDelay, object oTarget, object oPC = OBJECT_INVALID); // Setup monsters for oCaster to buff in ai_CastSpells. void ai_SetupMonsterBuffTargets(object oCaster); // Setup the targets for an NPC to buff one of the PC's members or the whole group. void ai_SetupAllyTargets(object oCaster, object oPC); // Setup the targets for an NPC to heal one of the PC's members. void ai_SetupAllyHealingTargets(object oCaster, object oPC); // Clears the casters buff targets. void ai_ClearBuffTargets(object oCaster, string sVariable); // Cycles through a casters spells casting all buffs via actions. void ai_ActionCastMemorizedBuff(struct stSpell stSpell); // Cycles through a casters spells casting all buffs via actions. void ai_ActionCastKnownBuff(struct stSpell stSpell); // Checks oCaster for buffing spells and casts them based on nTarget; // These are cast as actions and will happen at the speed based on // AI_HENCHMAN_BUFF_DELAY, but are still actions. // nTarget is 0-9 where 0 is all targets, 1 is oPC, 2 is the caster // 3 Familiar, 4 is Animal Companion, 5 is Summons, 6 is Dominated, and 7+ is henchman. // Targets must be defined in variable AI_ALLY_TARGET_* where * is 1 to #. // nBuffType is the duration 1 - all, 2 - short, 3 - long. void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC); // Returns TRUE if oCaster cast spontaneous cure spell on oTarget. // This uses an action and must use AssignCommand or OBJECT_SELF is the caster! int ai_CastSpontaneousCure(object oCreature, object oTarget, object oPC); // Returns TRUE if oCaster casts a memorized cure spell on oTarget. // This uses an action and must use AssignCommand or OBJECT_SELF is the caster! int ai_CastMemorizedHealing(object oCreature, object oTarget, object oPC, int nClass); // Returns TRUE if oCaster casts a known cure spell on oTarget. // This uses an action and must use AssignCommand or OBJECT_SELF is the caster! int ai_CastKnownHealing(object oCreature, object oTarget, object oPC, int nClass); // Returns TRUE if oCreature has an effect that will break their concentration. int ai_ConcentrationCondition(object oCreature); // Check to see if a spell's concentration has been broken, works for summons as well. void ai_SpellConcentrationCheck(object oCaster); // Returns TRUE if oCreature can safely cast nSpell defensively or has a good // chance of casting while in melee. int ai_CastInMelee(object oCreature, int nSpell, int nInMelee); // Returns a float range for the caster to search for a target of an offensive spell. float ai_GetOffensiveSpellSearchRange(object oCreature, int nSpell); // Returns TRUE if nSpell is a cure spell and will not over heal for nDamage. int ai_ShouldWeCastThisCureSpell(int nSpell, int nDamage); // Casts the spell on the current target for oAssociate. void ai_CastWidgetSpell(object oPC, object oAssociate, object oTarget, location lLocation); // Uses the feat on the current target for oAssociate. void ai_UseWidgetFeat(object oPC, object oAssociate, object oTarget, location lLocation); // Uses the item on the current target for oAssociate. void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lLocation); int ai_GetCanCastSpell(object oCreature, int nSpell, int nClass, int nLevel, int nMetaMagic = 0, int nDomain = 0) { int nIndex, nSpellCount, nClassPosition, nSlot, nMaxSlots, nPosition = 1; while(nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { nClassPosition = GetClassByPosition(nPosition, oCreature); if(nClassPosition == CLASS_TYPE_INVALID) return FALSE; if(nClass = nClassPosition) { if(Get2DAString("classes", "SpellCaster", nClass) == "1") { nSlot = 0; if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") { nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, nClass, nLevel); while(nSlot < nMaxSlots) { if(GetMemorizedSpellId(oCreature, nClass, nLevel, nSlot) == nSpell && GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot)) return TRUE; nSlot++; } } else return GetSpellUsesLeft(oCreature, nClass, nSpell, nMetaMagic, nDomain); } } nPosition++; } return FALSE; } int ai_IsImmuneToPetrification(object oCaster, object oCreature) { int nAppearance = GetAppearanceType(oCreature); switch(nAppearance) { case APPEARANCE_TYPE_BASILISK: case APPEARANCE_TYPE_COCKATRICE: case APPEARANCE_TYPE_MEDUSA: case APPEARANCE_TYPE_ALLIP: case APPEARANCE_TYPE_ELEMENTAL_AIR: case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER: case APPEARANCE_TYPE_ELEMENTAL_EARTH: case APPEARANCE_TYPE_ELEMENTAL_EARTH_ELDER: case APPEARANCE_TYPE_ELEMENTAL_FIRE: case APPEARANCE_TYPE_ELEMENTAL_FIRE_ELDER: case APPEARANCE_TYPE_ELEMENTAL_WATER: case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER: case APPEARANCE_TYPE_GOLEM_STONE: case APPEARANCE_TYPE_GOLEM_IRON: case APPEARANCE_TYPE_GOLEM_CLAY: case APPEARANCE_TYPE_GOLEM_BONE: case APPEARANCE_TYPE_GORGON: case APPEARANCE_TYPE_HEURODIS_LICH: case APPEARANCE_TYPE_LANTERN_ARCHON: case APPEARANCE_TYPE_SHADOW: case APPEARANCE_TYPE_SHADOW_FIEND: case APPEARANCE_TYPE_SHIELD_GUARDIAN: case APPEARANCE_TYPE_SKELETAL_DEVOURER: case APPEARANCE_TYPE_SKELETON_CHIEFTAIN: case APPEARANCE_TYPE_SKELETON_COMMON: case APPEARANCE_TYPE_SKELETON_MAGE: case APPEARANCE_TYPE_SKELETON_PRIEST: case APPEARANCE_TYPE_SKELETON_WARRIOR: case APPEARANCE_TYPE_SKELETON_WARRIOR_1: case APPEARANCE_TYPE_SPECTRE: case APPEARANCE_TYPE_WILL_O_WISP: case APPEARANCE_TYPE_WRAITH: case APPEARANCE_TYPE_BAT_HORROR: case 405: // Dracolich: case 415: // Alhoon case 418: // shadow dragon case 420: // mithral golem case 421: // admantium golem case 430: // Demi Lich case 469: // animated chest case 474: // golems case 475: // golems return TRUE; } // Petrification immunity can also be granted as an item property. if(ResistSpell(oCaster, oCreature) == 2 ) return TRUE; // Prevent people from petrifying DM, resulting in GUI even when effect is not successful. if(!GetPlotFlag(oCreature) && GetIsDM(oCreature)) return TRUE; return FALSE; } int ai_DoIHaveAMindAffectingSpellOnMe(object oCreature) { if(GetHasSpellEffect(SPELL_SLEEP, oCreature) || GetHasSpellEffect(SPELL_DAZE, oCreature) || GetHasSpellEffect(SPELL_HOLD_ANIMAL, oCreature) || GetHasSpellEffect(SPELL_HOLD_MONSTER, oCreature) || GetHasSpellEffect(SPELL_HOLD_PERSON, oCreature) || GetHasSpellEffect(SPELL_CHARM_MONSTER, oCreature) || GetHasSpellEffect(SPELL_CHARM_PERSON, oCreature) || GetHasSpellEffect(SPELL_CHARM_PERSON_OR_ANIMAL, oCreature) || GetHasSpellEffect(SPELL_MASS_CHARM, oCreature) || GetHasSpellEffect(SPELL_DOMINATE_ANIMAL, oCreature) || GetHasSpellEffect(SPELL_DOMINATE_MONSTER, oCreature) || GetHasSpellEffect(SPELL_DOMINATE_PERSON, oCreature) || GetHasSpellEffect(SPELL_CONFUSION, oCreature) || GetHasSpellEffect(SPELL_MIND_FOG, oCreature) || GetHasSpellEffect(SPELL_CLOUD_OF_BEWILDERMENT, oCreature) || GetHasSpellEffect(SPELLABILITY_BOLT_DOMINATE,oCreature) || GetHasSpellEffect(SPELLABILITY_BOLT_CHARM,oCreature) || GetHasSpellEffect(SPELLABILITY_BOLT_CONFUSE,oCreature) || GetHasSpellEffect(SPELLABILITY_BOLT_DAZE,oCreature)) return TRUE; return FALSE; } int ai_IsCureSpell(int nSpell) { switch(nSpell) { case SPELL_CURE_CRITICAL_WOUNDS: case SPELL_CURE_LIGHT_WOUNDS: case SPELL_CURE_MINOR_WOUNDS: case SPELL_CURE_MODERATE_WOUNDS: case SPELL_CURE_SERIOUS_WOUNDS: case SPELL_HEAL: return TRUE; break; } return FALSE; } int ai_IsInflictSpell(int nSpell) { switch(nSpell) { case SPELL_INFLICT_CRITICAL_WOUNDS: case SPELL_INFLICT_LIGHT_WOUNDS: case SPELL_INFLICT_MINOR_WOUNDS: case SPELL_INFLICT_MODERATE_WOUNDS: case SPELL_INFLICT_SERIOUS_WOUNDS: case SPELL_HARM: return TRUE; break; } return FALSE; } int ai_IsAreaOfEffectSpell(int nSpell) { switch(nSpell) { case SPELL_ACID_FOG : case SPELL_MIND_FOG : case SPELL_STORM_OF_VENGEANCE: case SPELL_WEB : case SPELL_GREASE : case SPELL_CREEPING_DOOM : // case SPELL_DARKNESS : case SPELL_SILENCE : case SPELL_BLADE_BARRIER : case SPELL_CLOUDKILL : case SPELL_STINKING_CLOUD : case SPELL_WALL_OF_FIRE : case SPELL_INCENDIARY_CLOUD : case SPELL_ENTANGLE : case SPELL_EVARDS_BLACK_TENTACLES: case SPELL_CLOUD_OF_BEWILDERMENT : case SPELL_STONEHOLD : case SPELL_VINE_MINE : case SPELL_SPIKE_GROWTH : case SPELL_DIRGE : case 530 : // vine mine case 531 : // vine mine case 532 : // vine mine case 961 : // Prismatic Sphere return TRUE; } return FALSE; } int ai_GetIsSpellCaster(object oAssociate) { int nIndex, nSpellCaster, nClass; for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) { nClass = GetClassByPosition(nIndex, oAssociate); if(nClass == CLASS_TYPE_INVALID) return nSpellCaster; if(Get2DAString("classes", "SpellCaster", nClass) == "1") { if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") return 2; else nSpellCaster = 1; } } return nSpellCaster; } int ai_GetIsSpellBookRestrictedCaster(object oAssociate) { int nIndex, nSpellCaster, nClass; for(nIndex = 1; nIndex <= AI_MAX_CLASSES_PER_CHARACTER; nIndex++) { nClass = GetClassByPosition(nIndex, oAssociate); if(nClass == CLASS_TYPE_INVALID) return FALSE; if(Get2DAString("classes", "SpellbookRestricted", nClass) == "1") return TRUE; } return FALSE; } int ai_CreatureImmuneToEffect(object oCaster, object oCreature, int nSpell) { string sIType = Get2DAString("ai_spells", "ImmunityType", nSpell); if(sIType != "") { if(AI_DEBUG) ai_Debug("0i_spells", "290", "Checking spell immunity type(" + sIType + ")."); if(sIType == "Death" && GetIsImmune(oCreature, IMMUNITY_TYPE_DEATH)) return TRUE; else if(sIType == "Level_Drain" && GetIsImmune(oCreature, IMMUNITY_TYPE_NEGATIVE_LEVEL)) return TRUE; else if(sIType == "Ability_Drain" && GetIsImmune(oCreature, IMMUNITY_TYPE_ABILITY_DECREASE)) return TRUE; else if(sIType == "Poison" && GetIsImmune(oCreature, IMMUNITY_TYPE_POISON)) return TRUE; else if(sIType == "Disease" && GetIsImmune(oCreature, IMMUNITY_TYPE_DISEASE)) return TRUE; else if(sIType == "Curse" && GetIsImmune(oCreature, IMMUNITY_TYPE_CURSED)) return TRUE; else if(sIType == "Mind_Affecting" && GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS)) return TRUE; else if(sIType == "Petrification" && ai_IsImmuneToPetrification(oCaster, oCreature)) return TRUE; else if(sIType == "Fear" && (GetIsImmune(oCreature, IMMUNITY_TYPE_FEAR) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Sleep" && (GetIsImmune(oCreature, IMMUNITY_TYPE_SLEEP) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Paralysis" && (GetIsImmune(oCreature, IMMUNITY_TYPE_PARALYSIS) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Domination" && (GetIsImmune(oCreature, IMMUNITY_TYPE_DOMINATE) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Confusion" && (GetIsImmune(oCreature, IMMUNITY_TYPE_CONFUSED) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Blindness" && (GetIsImmune(oCreature, IMMUNITY_TYPE_BLINDNESS) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Dazed" && (GetIsImmune(oCreature, IMMUNITY_TYPE_DAZED) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; else if(sIType == "Charm" && (GetIsImmune(oCreature, IMMUNITY_TYPE_CHARM) || GetIsImmune(oCreature, IMMUNITY_TYPE_MIND_SPELLS))) return TRUE; // Check for damage immunities. // Negative damage does not work on undead! else if(sIType == "Negative" && GetRacialType(oCreature) == RACIAL_TYPE_UNDEAD) { if(AI_DEBUG) ai_Debug("0i_spell", "325", "Undead are immune to Negative energy!"); return TRUE; } // Elemental damage resistances should be checked. if(sIType == "Acid" || sIType == "Cold" || sIType == "Fire" || sIType == "Electricty" || sIType == "Sonic") { if(ai_GetHasEffectType(oCreature, EFFECT_TYPE_DAMAGE_RESISTANCE)) { if(AI_DEBUG) ai_Debug("0i_spell", "334", GetName(oCreature) + " has damage resistance to my " + sIType + " spell!"); return TRUE; } // Check for resistances and immunities. Treat resistance as immune. int nIPResist = GetLocalInt(oCreature, sIPResistVarname); if(AI_DEBUG) ai_Debug("0i_spell", "372", "nIPResist:" + IntToString(nIPResist)); int nIPImmune = GetLocalInt(oCreature, sIPImmuneVarname) | nIPResist; if(AI_DEBUG) ai_Debug("0i_spell", "374", "nIPImmune:" + IntToString(nIPImmune)); if(nIPImmune > 0) { if(AI_DEBUG) ai_Debug("0i_spell", "391", GetName(oCreature) + " is immune/resistant to my " + sIType + " spell through an item!"); if(sIType == "Acid" && (nIPImmune & DAMAGE_TYPE_ACID)) return TRUE; else if(sIType == "Cold" && (nIPImmune & DAMAGE_TYPE_COLD)) return TRUE; else if(sIType == "Fire" && (nIPImmune & DAMAGE_TYPE_FIRE)) return TRUE; else if(sIType == "Electricity" && (nIPImmune & DAMAGE_TYPE_ELECTRICAL)) return TRUE; else if(sIType == "Sonic" && (nIPImmune & DAMAGE_TYPE_SONIC)) return TRUE; } } } int nLevel = StringToInt(Get2DAString("spells", "Innate", nSpell)); // Globe spells should be checked... if((GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oCreature) || GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, oCreature)) && nLevel < 4 && d100() < 75) return TRUE; if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, oCreature) && nLevel < 5 && d100() < 75) return TRUE; // Check creatures items for immunity. int nIndex; json jSpellImmunity = GetLocalJson(oCreature, AI_TALENT_IMMUNITY); json jSpell = JsonArrayGet(jSpellImmunity, nIndex); while(JsonGetType(jSpell) != JSON_TYPE_NULL) { if(nSpell == JsonGetInt(jSpell)) { if(AI_DEBUG) ai_Debug("0i_spells", "407", GetName(oCreature) + " is immune to the spell via an Item!"); return TRUE; } jSpell = JsonArrayGet(jSpellImmunity, ++nIndex); } if(AI_DEBUG) ai_Debug("0i_spell", "347", GetName(oCreature) + " is not immune to the spell."); return FALSE; } float ai_GetSpellRange(int nSpell) { string sRange = Get2DAString("spells", "Range", nSpell); if(sRange == "S") return AI_SHORT_DISTANCE; else if(sRange == "M") return AI_MEDIUM_DISTANCE; else if(sRange == "L") return AI_LONG_DISTANCE; else if(sRange == "T") return AI_RANGE_MELEE; return 0.1; } int ai_CreatureHasDispelableEffect(object oCaster, object oCreature) { int nSpellID, nLastSpellID, bSpell, nDispelChance; // Cycle through the targets effects. effect eEffect = GetFirstEffect(oCreature); if(AI_DEBUG) ai_Debug("0i_spells", "485", "nSpell: " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", GetEffectSpellId(eEffect)))) + " oCreature: " + GetName(oCreature)); while(GetIsEffectValid(eEffect)) { nSpellID = GetEffectSpellId(eEffect); // -1 is not a spell. if(AI_DEBUG) ai_Debug("0i_spells", "491", "nSpell: (" + IntToString(nSpellID) + ") " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpellID)))); if(nSpellID > -1 && nLastSpellID != nSpellID) { // We check if the spell is Hostile(-1) or Helpful(+1). if(Get2DAString("ai_spells", "HostileSetting", nSpellID) == "1") nDispelChance--; else nDispelChance++; if(AI_DEBUG) ai_Debug("0i_spells", "497", "HostileSetting: " + Get2DAString("ai_spells", "HostileSetting", nSpellID) + " nDispelChance: " + IntToString(nDispelChance)); } nLastSpellID = nSpellID; eEffect = GetNextEffect(oCreature); } // if the target has more Helpful spells than harmful spells effecting them // then use dispel! if(AI_DEBUG) ai_Debug("0i_spells", "505", "nDispelChance: " + IntToString(nDispelChance)); return (nDispelChance > 0); } void ai_RemoveASpecificEffect(object oCreature, int nEffectType) { effect eEffect = GetFirstEffect(oCreature); //Search for the effect. while(GetIsEffectValid(eEffect)) { if(GetEffectType(eEffect) == nEffectType) { //Remove effect. RemoveEffect(oCreature, eEffect); eEffect = GetFirstEffect(oCreature); } else eEffect = GetNextEffect(oCreature); } } int ai_GetHasEffectType(object oCreature, int nEffectType) { effect eEffect = GetFirstEffect(oCreature); while(GetIsEffectValid(eEffect)) { if(GetEffectType(eEffect, TRUE) == nEffectType) return TRUE; eEffect = GetNextEffect(oCreature); } return FALSE; } void ai_CheckCreatureSpecialAbilities(object oCreature) { int nMaxSpecialAbilities = GetSpellAbilityCount(oCreature); if(nMaxSpecialAbilities) { int nIndex, bCanCast; // Struct is id, ready, level. int nSpell; while(nIndex < nMaxSpecialAbilities) { nSpell = GetSpellAbilitySpell(oCreature, nIndex); if(GetSpellAbilityReady(oCreature, nSpell)) { bCanCast = FALSE; if(GetSpellAbilityCasterLevel(oCreature, nIndex) > 4) { // 1 Min/Lvl spell that is too low of level so it must be cast at 5th lvl or greater. if(nSpell == SPELL_FLAME_WEAPON) bCanCast = TRUE; else if(nSpell == SPELL_BLESS) bCanCast = TRUE; else if(nSpell == SPELL_AID) bCanCast = TRUE; else if(nSpell == SPELL_DEATH_WARD) bCanCast = TRUE; } if(nSpell == SPELL_ENERGY_BUFFER) bCanCast = TRUE; else if(nSpell == SPELL_PROTECTION_FROM_ELEMENTS) bCanCast = TRUE; else if(nSpell == SPELL_RESIST_ELEMENTS) bCanCast = TRUE; else if(nSpell == SPELL_ENDURE_ELEMENTS) bCanCast = TRUE; else if(nSpell == SPELL_MAGE_ARMOR) bCanCast = TRUE; else if(nSpell == SPELL_MAGIC_VESTMENT) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_MAGIC_WEAPON) bCanCast = TRUE; else if(nSpell == SPELL_MAGIC_WEAPON) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_IX) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_VIII) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_VII) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_VI) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_V) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_IV) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_III) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_II) bCanCast = TRUE; else if(nSpell == SPELL_SUMMON_CREATURE_I) bCanCast = TRUE; else if(nSpell == SPELL_BARKSKIN) bCanCast = TRUE; else if(nSpell == SPELL_SHIELD) bCanCast = TRUE; else if(nSpell == SPELL_ENTROPIC_SHIELD) bCanCast = TRUE; else if(nSpell == SPELL_SHIELD_OF_FAITH) bCanCast = TRUE; else if(nSpell == SPELL_REMOVE_FEAR) bCanCast = TRUE; else if(nSpell == SPELL_IRONGUTS) bCanCast = TRUE; else if(nSpell == SPELL_PREMONITION) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_STONESKIN) bCanCast = TRUE; else if(nSpell == SPELL_GHOSTLY_VISAGE) bCanCast = TRUE; else if(nSpell == SPELL_IMPROVED_INVISIBILITY) bCanCast = TRUE; else if(nSpell == SPELL_INVISIBILITY_SPHERE) bCanCast = TRUE; else if(nSpell == SPELL_INVISIBILITY) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_BULLS_STRENGTH) bCanCast = TRUE; else if(nSpell == SPELL_BULLS_STRENGTH) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_CATS_GRACE) bCanCast = TRUE; else if(nSpell == SPELL_CATS_GRACE) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_EAGLE_SPLENDOR) bCanCast = TRUE; else if(nSpell == SPELL_EAGLE_SPLEDOR) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_ENDURANCE) bCanCast = TRUE; else if(nSpell == SPELL_ENDURANCE) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_FOXS_CUNNING) bCanCast = TRUE; else if(nSpell == SPELL_FOXS_CUNNING) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_OWLS_WISDOM) bCanCast = TRUE; else if(nSpell == SPELL_OWLS_WISDOM) bCanCast = TRUE; else if(nSpell == SPELL_KEEN_EDGE) bCanCast = TRUE; else if(nSpell == SPELL_ANIMATE_DEAD) bCanCast = TRUE; else if(nSpell == SPELL_INVISIBILITY_PURGE) bCanCast = TRUE; else if(nSpell == SPELL_CLAIRAUDIENCE_AND_CLAIRVOYANCE) bCanCast = TRUE; else if(nSpell == SPELL_DARKFIRE) bCanCast = TRUE; else if(nSpell == SPELL_NEGATIVE_ENERGY_PROTECTION) bCanCast = TRUE; else if(nSpell == SPELL_MAGIC_CIRCLE_AGAINST_GOOD) bCanCast = TRUE; else if(nSpell == SPELL_FREEDOM_OF_MOVEMENT) bCanCast = TRUE; else if(nSpell == SPELL_NEUTRALIZE_POISON) bCanCast = TRUE; else if(nSpell == SPELL_MIND_BLANK) bCanCast = TRUE; else if(nSpell == SPELL_LESSER_MIND_BLANK) bCanCast = TRUE; else if(nSpell == SPELL_SPELL_RESISTANCE) bCanCast = TRUE; else if(nSpell == SPELL_PROTECTION_FROM_GOOD) bCanCast = TRUE; else if(nSpell == SPELL_CREATE_UNDEAD) bCanCast = TRUE; else if(nSpell == SPELL_PLANAR_ALLY) bCanCast = TRUE; else if(nSpell == SPELL_LESSER_PLANAR_BINDING) bCanCast = TRUE; else if(nSpell == SPELL_ETHEREALNESS) bCanCast = TRUE; else if(nSpell == SPELL_PROTECTION_FROM_SPELLS) bCanCast = TRUE; else if(nSpell == SPELL_SHADOW_SHIELD) bCanCast = TRUE; else if(nSpell == SPELL_CREATE_GREATER_UNDEAD) bCanCast = TRUE; else if(nSpell == SPELL_GREATER_PLANAR_BINDING) bCanCast = TRUE; if(bCanCast && GetSpellAbilityReady(oCreature, nIndex)) { ActionCastSpellAtObject(nSpell, oCreature, 255, 0, 0, 0, TRUE); } } nIndex++; } } } int ai_IsSilenced(object oCreature, int nSpell) { if(Get2DAString("spells", "VS", nSpell) == "s") return FALSE; if(ai_GetHasEffectType(oCreature, EFFECT_TYPE_SILENCE)) return TRUE; return FALSE; } int ai_ArcaneSpellFailureTooHigh(object oCreature, int nClass, int nLevel, int nSlot) { if(AI_DEBUG) ai_Debug("0i_spells", "561", "Arcane Spells: " + Get2DAString("classes", "ASF", nClass) + " Arcane Spell Failure: " + IntToString(GetArcaneSpellFailure(oCreature)) + " AI_ASF_WILL_USE: " + IntToString(AI_ASF_WILL_USE)); if(Get2DAString("classes", "ASF", nClass) == "1" && GetArcaneSpellFailure(oCreature) > AI_ASF_WILL_USE) { if(GetMemorizedSpellMetaMagic(oCreature, nClass, nLevel, nSlot) == METAMAGIC_STILL) return FALSE; return TRUE; } return FALSE; } int ai_TryToCastSpell(object oCaster, int nSpell, object oTarget) { if(GetHasSpell(nSpell, oCaster) && !GetHasSpellEffect(nSpell, oTarget)) { ActionCastSpellAtObject(nSpell, oTarget); return TRUE; } return FALSE; } int ai_SpellGroupNotCast(object oCreature, string sBuffGroup) { return !GetLocalInt(oCreature, sBuffGroup); } void ai_ClearSpellsCastGroups(object oCreature) { int nCounter; for(nCounter = -1; nCounter <= AI_BUFF_GROUPS; nCounter--) { DeleteLocalInt(oCreature, "AI_USED_SPELL_GROUP_" + IntToString(nCounter)); } } int ai_CanUseSpell(object oCaster, object oTarget, int nSpell, int nTargetType) { // Should we ignore associates? if(ai_GetAIMode(oCaster, AI_MODE_IGNORE_ASSOCIATES) && GetAssociateType(oTarget) > 1) return FALSE; // For ability scores we return a bonus to the ability to be checked against // the target with the highest ability getting the spell first. if(nTargetType == 1) // Ability score buff for strength. { // We don't want to buff the strength for someone using weapon finesse! if(GetHasFeat(FEAT_WEAPON_FINESSE, oTarget)) return -5; return TRUE; } if(nTargetType == 7) // Lowest AC. { // Stone bones only effects the undead. if(nSpell == SPELL_STONE_BONES) { if(GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD) return FALSE; } return TRUE; } if(nTargetType == 8) // Lowest AC without AC Bonus. { if(nSpell == SPELL_MAGIC_VESTMENT) { object oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); if(oArmor == OBJECT_INVALID) return FALSE; } return TRUE; } if(nTargetType == 9) // Highest Attack. { return TRUE; } if(nTargetType == 10) // Most wounded, Lowest Hp. { return TRUE; } if(nTargetType == 11) // Lowest Fortitude save. { return TRUE; } if(nTargetType == 12) // Lowest Reflex save. { return TRUE; } if(nTargetType == 13) // Lowest Will save. { return TRUE; } if(nTargetType == 14) // Lowest Save. { return TRUE; } if(nSpell == SPELL_MAGIC_FANG) { object oCompanion = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster); if(oTarget != oCompanion) return FALSE; } return TRUE; } // Used to check if the targets weapon can be buffed by the spells effects. int ai_CanItemBeBuffed(int nSpell, object oTarget) { object oWeapon, oArmor; if(nSpell == SPELL_MAGIC_WEAPON || nSpell == SPELL_GREATER_MAGIC_WEAPON || nSpell == SPELL_BLADE_THIRST) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ENHANCEMENT_BONUS)) return FALSE; } else if(nSpell == SPELL_MAGIC_VESTMENT) { oArmor = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); if(oArmor == OBJECT_INVALID) return FALSE; if(ai_GetHasItemProperty(oArmor, ITEM_PROPERTY_AC_BONUS)) return FALSE; } else if(nSpell == SPELL_DARKFIRE) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, 127)) return FALSE; } else if(nSpell == SPELL_FLAME_WEAPON) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, 124)) return FALSE; } else if(nSpell == SPELL_KEEN_EDGE) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsSlashingWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_KEEN)) return FALSE; } else if(nSpell == SPELL_DEAFENING_CLANG) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, 137)) return FALSE; } else if(nSpell == SPELL_BLESS_WEAPON) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_DAMAGE_BONUS_VS_RACIAL_GROUP, IP_CONST_RACIALTYPE_UNDEAD)) return FALSE; } else if(nSpell == SPELL_HOLY_SWORD) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(!ai_GetIsMeleeWeapon(oWeapon)) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_HOLY_AVENGER)) return FALSE; } else if(nSpell == SPELL_BLACKSTAFF) { oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget); if(GetBaseItemType(oWeapon) != BASE_ITEM_QUARTERSTAFF) return FALSE; if(ai_GetHasItemProperty(oWeapon, ITEM_PROPERTY_ON_HIT_PROPERTIES, IP_CONST_ONHIT_DISPELMAGIC)) return FALSE; } return TRUE; } // In "Buff_Target" column the value of 0 in the "ai_spells.2da" references the Caster. // In "Buff_Target" column this is value 1-6(STR, DEX, CON, INT, WIS, CHA) in the "ai_spells.2da". object ai_BuffHighestAbilityScoreTarget(object oCaster, int nSpell, int nAbilityScore, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup)) return oMaster; } int nCntr = 1, nAB, nHighAB, nTarget, nUseSpell; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange) { nUseSpell = ai_CanUseSpell(oCaster, oTarget, nSpell, nAbilityScore + 1); if(nUseSpell == 0) {} else { nAB = GetAbilityScore(oTarget, nAbilityScore) + nUseSpell; if(nAB > nHighAB) {nHighAB = nAB; nTarget = nCntr; } } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 7 in the "ai_spells.2da". object ai_BuffLowestACTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { object oMaster = GetMaster(); if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 7)) return oMaster; } int nCntr = 1, nAC, nLowAC = 100, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nAC = GetAC(oTarget); if(nAC < nLowAC && ai_CanUseSpell(oCaster, oTarget, nSpell, 7)) {nLowAC = nAC; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); return oTarget; } // In "Buff_Target" column this is value 8 in the "ai_spells.2da". object ai_BuffLowestACWithOutACBonus(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 8)) return oMaster; } int nCntr = 1, nAC, nLowAC = 50, nTarget; object oItem, oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nAC = GetAC(oTarget); oItem = GetItemInSlot(INVENTORY_SLOT_CHEST, oTarget); if(nAC < nLowAC && ai_CanUseSpell(oCaster, oTarget, nSpell, 8) && !GetItemHasItemProperty(oItem, ITEM_PROPERTY_AC_BONUS)) { nLowAC = nAC; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 9 in the "ai_spells.2da". object ai_BuffHighestAttackTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 9)) return oMaster; } int nCntr = 1, nAtk, nHighAtk, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nAtk = GetBaseAttackBonus(oTarget); if(nAtk > nHighAtk && ai_CanUseSpell(oCaster, oTarget, nSpell, 9)) {nHighAtk = nAtk; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); return oTarget; } // In "Buff_Target" column this is value 10 in the "ai_spells.2da". object ai_BuffMostWoundedTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 9)) return oMaster; } int nCntr = 1, nDmg, nMostDmg, nHp, nLowHp = 10000, nTarget, nHpTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup) && ai_CanUseSpell(oCaster, oTarget, nSpell, 10)) { nHp = GetCurrentHitPoints(oTarget); nDmg = GetMaxHitPoints(oTarget) - nHp; if(nDmg > nMostDmg) { nMostDmg = nDmg; nTarget = nCntr; } if(nHp < nLowHp) { nLowHp = nHp; nHpTarget = nCntr; } } // If no one is damage then put regeneration on the lowest hp target. if(nMostDmg == 0) nTarget = nHpTarget; oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 11 in the "ai_spells.2da". object ai_BuffLowestFortitudeSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 11)) return oMaster; } int nCntr = 1, nSave, nLowSave = 100, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nSave = GetFortitudeSavingThrow(oTarget); if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 11)) {nLowSave = nSave; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 12 in the "ai_spells.2da". object ai_BuffLowestReflexSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 12)) return oMaster; } int nCntr = 1, nSave, nLowSave = 100, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nSave = GetReflexSavingThrow(oTarget); if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 12)) {nLowSave = nSave; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 13 in the "ai_spells.2da". object ai_BuffLowestWillSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 13)) return oMaster; } int nCntr = 1, nSave, nLowSave = 100, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nSave = GetWillSavingThrow(oTarget); if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 13)) {nLowSave = nSave; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 14 in the "ai_spells.2da". object ai_BuffLowestSaveTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(!GetHasSpellEffect(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup) && ai_CanUseSpell(oCaster, oMaster, nSpell, 14)) return oMaster; } int nCntr = 1, nSave, nLowSave = 200, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && !GetHasSpellEffect(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nSave = GetFortitudeSavingThrow(oTarget) + GetReflexSavingThrow(oTarget) + GetWillSavingThrow(oTarget); if(nSave < nLowSave && ai_CanUseSpell(oCaster, oTarget, nSpell, 14)) {nLowSave = nSave; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; else return GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); } // In "Buff_Target" column this is value 15 in the "ai_spells.2da". object ai_BuffItemTarget(object oCaster, int nSpell, string sBuffGroup, float fRange, string sTargetType = "AI_ALLY_TARGET_") { if(ai_GetMagicMode(oCaster, AI_MAGIC_BUFF_MASTER)) { object oMaster = GetMaster(); if(ai_CanItemBeBuffed(nSpell, oMaster) && ai_SpellGroupNotCast(oMaster, sBuffGroup)) return oMaster; } int nCntr = 1, nAtk, nHighAtk = -9999, nTarget; object oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nCntr)); while (nCntr < 10) { if(oTarget != OBJECT_INVALID && ai_CanItemBeBuffed(nSpell, oTarget) && GetDistanceBetween(oCaster, oTarget) <= fRange && ai_SpellGroupNotCast(oTarget, sBuffGroup)) { nAtk = GetBaseAttackBonus(oTarget); if(nAtk > nHighAtk) { nHighAtk = nAtk; nTarget = nCntr; } } oTarget = GetLocalObject(oCaster, sTargetType + IntToString(++nCntr)); } if(nTarget == 0) return OBJECT_INVALID; oTarget = GetLocalObject(oCaster, sTargetType + IntToString(nTarget)); return oTarget; } object ai_GetBuffTarget(object oCaster, int nSpell) { object oTarget = OBJECT_INVALID; string sGroup = Get2DAString("ai_spells", "Buff_Group", nSpell); if(sGroup == "") sGroup = IntToString(nSpell); string sBuffGroup = "AI_USED_SPELL_GROUP_" + sGroup; string sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); if(AI_DEBUG) ai_Debug("0i_spells", "769", "BuffTarget: " + sBuffTarget); if(sBuffTarget == "0") { if(ai_SpellGroupNotCast(oCaster, sBuffGroup) && !GetHasSpellEffect(nSpell, oCaster) && ai_CanUseSpell(oCaster, oTarget, nSpell, 0)) { oTarget = oCaster; } } else if(sBuffTarget == "1") oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_STRENGTH, "", AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "2") oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_DEXTERITY, "", AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "3") oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_CONSTITUTION, "", AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "4") oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_INTELLIGENCE, "", AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "5") oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_WISDOM, "", AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "6") oTarget = ai_BuffHighestAbilityScoreTarget(oCaster, nSpell, ABILITY_CHARISMA, "", AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "7") oTarget = ai_BuffLowestACTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "8") oTarget = ai_BuffLowestACWithOutACBonus(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "9") oTarget = ai_BuffHighestAttackTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "10") oTarget = ai_BuffMostWoundedTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "11") oTarget = ai_BuffLowestFortitudeSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "12") oTarget = ai_BuffLowestReflexSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "13") oTarget = ai_BuffLowestWillSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "14") oTarget = ai_BuffLowestSaveTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); else if(sBuffTarget == "15") oTarget = ai_BuffItemTarget(oCaster, nSpell, sBuffGroup, AI_RANGE_BATTLEFIELD); if(oTarget != OBJECT_INVALID) { SetLocalInt(oTarget, sBuffGroup, TRUE); DelayCommand(6.0, DeleteLocalInt(oTarget, sBuffGroup)); } if(AI_DEBUG) ai_Debug("0i_spells", "939", GetName(oCaster) + " is targeting " + GetName(oTarget) + " with " + GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))) + " spell" + " sBuffGroup: " + sBuffGroup + "."); return oTarget; } void ai_CastMemorizedSpell(object oCaster, int nClass, int nSpellLevel, int nSpellSlot, object oTarget, int bInstant, object oPC = OBJECT_INVALID) { int nDomain; int nSpell = GetMemorizedSpellId(oCaster, nClass, nSpellLevel, nSpellSlot); if(GetMemorizedSpellIsDomainSpell(oCaster, nClass, nSpellLevel, nSpellSlot) == 1) nDomain = nSpellLevel; else nDomain = 0; int nMetaMagic = GetMemorizedSpellMetaMagic(oCaster, nClass, nSpellLevel, nSpellSlot); if(AI_DEBUG) ai_Debug("0i_spells", "951", "nSpell: " + IntToString(nSpell) + " oTarget: " + GetName(oTarget) + " nMetaMagic: " + IntToString(nMetaMagic) + " nDomain: " + IntToString(nDomain) + " bInstant: " + IntToString(bInstant) + " nClass: " + IntToString(nClass)); ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain, 0, bInstant); // Right now I cannot get nClass to work here... //DelayCommand(fDelay, ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain, 0, TRUE, nClass)); if(oPC != OBJECT_INVALID) { string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); ai_SendMessages(GetName(oCaster) + " has cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_GREEN, oPC); } } void ai_CastKnownSpell(object oCaster, int nClass, int nSpell, object oTarget, int bInstant, object oPC = OBJECT_INVALID) { if(AI_DEBUG) ai_Debug("0i_Spells", "965", GetName(oCaster) + " is casting " + IntToString(nSpell)); ActionCastSpellAtObject(nSpell, oTarget, 255, FALSE, 0, 0, bInstant); // Right now I cannot get nClass to work here... //ActionCastSpellAtObject(nSpell, oTarget, 255, FALSE, 0, 0, TRUE, nClass); if(oPC != OBJECT_INVALID) { string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); ai_SendMessages(GetName(oCaster) + " has cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_GREEN, oPC); } } int ai_CheckAndCastSpell(object oCaster, int nSpell, int nSpellGroup, float fDelay, object oTarget, object oPC = OBJECT_INVALID) { int nClassCnt = 1, nClass, nMaxSlot, nSpellLevel, nSpellSlot, nMemorizedSpell, nDomain, nMetaMagic; string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); while(nClassCnt <= AI_MAX_CLASSES_PER_CHARACTER && nClass != CLASS_TYPE_INVALID) { nClass = GetClassByPosition(nClassCnt); // Search all memorized spells for the spell. if(Get2DAString("classes", "MemorizesSpells", nClass) == "1") { // Check each level starting with the highest to lowest. nSpellLevel = 0; while(nSpellLevel < 10) { // Check each slot within each level. nMaxSlot = GetMemorizedSpellCountByLevel(oCaster, nClass, nSpellLevel); nSpellSlot = 0; while(nSpellSlot < nMaxSlot) { if(GetMemorizedSpellReady(oCaster, nClass, nSpellLevel, nSpellSlot)) { nMemorizedSpell = GetMemorizedSpellId(oCaster, nClass, nSpellLevel, nSpellSlot); if(nMemorizedSpell == nSpell) { ai_CastMemorizedSpell(oCaster, nClass, nSpellLevel, nSpellSlot, oTarget, FALSE, oPC); return TRUE; } } nSpellSlot++; } nSpellLevel++; } } // Check non-memorized known lists for the spell. else if(GetSpellUsesLeft(oCaster, nClass, nSpell)) { ai_CastKnownSpell(oCaster, nClass, nSpell, oTarget, FALSE, oPC); return TRUE; } nClassCnt++; } return FALSE; } void ai_SetupMonsterBuffTargets(object oCaster) { if(AI_DEBUG) ai_Debug("0i_spells", "1020", GetName(oCaster) + " is setting buff targets."); SetLocalObject (oCaster, "AI_ALLY_TARGET_1" , oCaster); SetLocalObject (oCaster, "AI_ALLY_TARGET_2", oCaster); int nCntr = 1; object oCreature = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, oCaster, nCntr); if(AI_DEBUG) ai_Debug("0i_spells", "864", GetName(oCreature) + " nCntr: " + IntToString(nCntr) + " Distance: " + FloatToString(GetDistanceBetween(oCaster, oCreature), 0, 2)); while(oCreature != OBJECT_INVALID && nCntr < 8 && GetDistanceBetween(oCaster, oCreature) < AI_RANGE_CLOSE) { if(AI_DEBUG) ai_Debug("0i_spells", "1133", "Setting " + GetName(oCreature) + " as AI_ALLY_TARGET_" + IntToString(nCntr + 2)); SetLocalObject (oCaster, "AI_ALLY_TARGET_" + IntToString(nCntr + 2), oCreature); oCreature = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, oCaster, ++nCntr); if(AI_DEBUG) ai_Debug("0i_spells", "1136", GetName(oCreature) + " nCntr: " + IntToString(nCntr) + " Distance: " + FloatToString(GetDistanceBetween(oCaster, oCreature), 0, 2)); } } void ai_SetupAllyTargets(object oCaster, object oPC) { // Setup our targets. int nTarget; if(oCaster != oPC) SetLocalObject (oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oPC); SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCaster); object oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oCreature); int nCntr = 1; int nMaxHenchman = GetMaxHenchmen() + nTarget; object oHenchman = GetHenchman(oPC, nCntr); while(oHenchman != OBJECT_INVALID && nCntr <= nMaxHenchman) { if(oHenchman == OBJECT_INVALID) break; if(oHenchman != oCaster) SetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(++nTarget), oHenchman); oHenchman = GetHenchman(oPC, ++nCntr); } nCntr = 1; while(nCntr <= nMaxHenchman) { if(AI_DEBUG) ai_Debug("0i_spells", "1166", "AI_ALLY_TARGET_" + IntToString(nCntr) + ": " + GetName(GetLocalObject(oCaster, "AI_ALLY_TARGET_" + IntToString(nCntr)))); nCntr++; } } void ai_SetupAllyHealingTargets(object oCaster, object oPC) { int nMaxHenchman = 1; if(oPC == OBJECT_INVALID) oPC = oCaster; if(ai_GetAIMode(oCaster, AI_MODE_PARTY_HEALING_OFF)) { if(!ai_GetAIMode(oCaster, AI_MODE_SELF_HEALING_OFF)) SetLocalObject(oCaster, "AI_ALLY_HEAL_1", oCaster); } else { int nTarget; if(oCaster != oPC) { SetLocalObject (oCaster, "AI_ALLY_HEAL_1", oPC); nTarget++; } if(!ai_GetAIMode(oCaster, AI_MODE_SELF_HEALING_OFF)) { SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCaster); } object oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_SUMMONED, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oPC); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); oCreature = GetAssociate(ASSOCIATE_TYPE_DOMINATED, oCaster); if(oCreature != OBJECT_INVALID) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oCreature); int nCntr = 1; nMaxHenchman = GetMaxHenchmen() + nTarget; object oHenchman = GetHenchman(oPC, nCntr); while(oHenchman != OBJECT_INVALID && nTarget <= nMaxHenchman) { if(oHenchman == OBJECT_INVALID) break; if(oHenchman != oCaster) SetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(++nTarget), oHenchman); oHenchman = GetHenchman(oPC, ++nCntr); } } int nCntr = 1; while(nCntr <= nMaxHenchman) { if(AI_DEBUG) ai_Debug("0i_spells", "1211", "AI_ALLY_HEAL_" + IntToString(nCntr) + ": " + GetName(GetLocalObject(oCaster, "AI_ALLY_HEAL_" + IntToString(nCntr++)))); } } void ai_ClearBuffTargets(object oCaster, string sVariable) { if(AI_DEBUG) ai_Debug("0i_spells", "1216", GetName(oCaster) + " is clearing " + sVariable + " targets."); int nIndex; int nMaxTargets = GetMaxHenchmen() + 6; for(nIndex = 1; nIndex < nMaxTargets; nIndex++) { DeleteLocalObject (oCaster, sVariable + IntToString(nIndex)); } } void ai_CheckForPerDayProperties(object oCreature, object oItem, int nBuffType, int bEquiped = FALSE) { if(AI_DEBUG) ai_Debug("0i_spells", "1150", "Checking Item properties on " + GetName(oItem)); // We have established that we can use the item if it is equiped. if(!bEquiped && !ai_CheckIfCanUseItem(oCreature, oItem)) return; int nPerDay, nCharges, nUses, nSpellBuffDuration; int nIprpSubType, nSpell, nLevel, nIPType, nIndex; object oTarget; itemproperty ipProp = GetFirstItemProperty(oItem); // Lets skip this if there are no properties. if(!GetIsItemPropertyValid(ipProp)) return; // Check for cast spell property and add them to the talent list. while(GetIsItemPropertyValid(ipProp)) { if(AI_DEBUG) ai_Debug("0i_spells", "1163", "ItempropertyType(15): " + IntToString(GetItemPropertyType(ipProp))); nIPType = GetItemPropertyType(ipProp); if(nIPType == ITEM_PROPERTY_CAST_SPELL) { // Get how they use the item (charges or uses per day). nUses = GetItemPropertyCostTableValue(ipProp); // We only check uses per day. if(AI_DEBUG) ai_Debug("0i_spells", "1172", "Item uses: " + IntToString(nPerDay)); if(nUses > 7 && nUses < 13) { nPerDay = GetItemPropertyUsesPerDayRemaining(oItem, ipProp); if(AI_DEBUG) ai_Debug("0i_spells", "1176", "Item uses per day: " + IntToString(nPerDay)); if(nPerDay > 0) { // SubType is the ip spell index for iprp_spells.2da nIprpSubType = GetItemPropertySubType(ipProp); nSpell = StringToInt(Get2DAString("iprp_spells", "SpellIndex", nIprpSubType)); nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); if(AI_DEBUG) ai_Debug("0i_spells", "1183", "nSpell: " + IntToString(nSpell) + " nBuffType: " + IntToString(nBuffType) + " nSpellBuffDuration: " + IntToString(nSpellBuffDuration)); if(nBuffType == nSpellBuffDuration || nBuffType == 1) { oTarget = ai_GetBuffTarget(oCreature, nSpell); if(oTarget != OBJECT_INVALID) { if(AI_DEBUG) ai_Debug("0i_spells", "1190", GetName(oCreature) + " is using" + GetName(oItem) + " to cast " + IntToString(nSpell) + " on " + GetName(oTarget)); ActionUseItemOnObject(oItem, ipProp, oTarget); } } } } } ipProp = GetNextItemProperty(oItem); } } void ai_CheckForPerDayItems(object oCreature, object oPC, int nBuffType) { if(AI_DEBUG) ai_Debug("0i_spells", "1198", GetName(oCreature) + ": Checking items for per day buffs."); if(!ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC_ITEMS)) { int bEquiped; string sSlots; // Cycle through all the creatures inventory items. object oItem = GetFirstItemInInventory(oCreature); while(oItem != OBJECT_INVALID) { if(GetIdentified(oItem)) { // Does the item need to be equiped to use its powers? sSlots = Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)); if(AI_DEBUG) ai_Debug("0i_talents", "1211", GetName(oItem) + " requires " + Get2DAString("baseitems", "EquipableSlots", GetBaseItemType(oItem)) + " slots."); if(sSlots == "0x00000") ai_CheckForPerDayProperties(oCreature, oItem, nBuffType); } oItem = GetNextItemInInventory(oCreature); } int nSlot; // Cycle through all the creatures equiped items. oItem = GetItemInSlot(nSlot, oCreature); while(nSlot < 11) { if(oItem != OBJECT_INVALID) ai_CheckForPerDayProperties(oCreature, oItem, nBuffType, TRUE); oItem = GetItemInSlot(++nSlot, oCreature); } oItem = GetItemInSlot(INVENTORY_SLOT_CARMOUR, oCreature); if(oItem != OBJECT_SELF) ai_CheckForPerDayProperties(oCreature, oItem, nBuffType, TRUE); } // Clean up our variables. Must be done here since these are actions! int nCntr; object oTarget; while(nCntr < 11) { oTarget = GetLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nCntr)); if(oTarget != OBJECT_INVALID) { ai_ClearSpellsCastGroups(oTarget); DeleteLocalObject(oCreature, "AI_ALLY_TARGET_" + IntToString(nCntr)); } nCntr++; } } void ai_CheckForBuffSpells(struct stSpell stSpell) { ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC); stSpell.nPosition = 1; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); if(AI_DEBUG) ai_Debug("0i_spells", "1208", "nClass: " + IntToString(stSpell.nClass)); if(stSpell.nClass == CLASS_TYPE_INVALID) break; if(AI_DEBUG) ai_Debug("0i_spells", "1210", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; if(AI_DEBUG) ai_Debug("0i_spells", "1214", "Memorizes Spells: " + Get2DAString("classes", "MemorizesSpells", stSpell.nClass)); if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); return; } else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); return; } } stSpell.nPosition++; } ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); } void ai_ActionCastMemorizedSummons(struct stSpell stSpell) { if(AI_DEBUG) ai_Debug("0i_spells", "1122", "Start of ActionCastMemorizedSummons!"); int nSpell; string sBuffGroup, sBuffTarget; object oTarget; while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { //ai_Debug("0i_spells", "1128", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { //ai_Debug("0i_spells", "1131", "nLevel: " + IntToString(stSpell.nLevel)); while(stSpell.nLevel > -1) { //ai_Debug("0i_spells", "1134", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + // " nSlots: " + IntToString(stSpell.nSlot)); while(stSpell.nSlot < stSpell.nMaxSlots) { //ai_Debug("0i_spells", "1238", "Ready: " + IntToString(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot))); if(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot)) { nSpell = GetMemorizedSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); //ai_Debug("0i_spells", "1142", "nSpell: " + IntToString(nSpell)); if(Get2DAString("ai_spells", "Category", nSpell) == "S") { SetLocalInt(stSpell.oCaster, "AI_USED_SPELL_GROUP_-2", TRUE); ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, stSpell.oCaster, TRUE, stSpell.oPC); stSpell.nPosition = 1; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; DelayCommand(2.0, ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC)); DelayCommand(2.0 + 0.5, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell))); return; } } stSpell.nSlot++; } stSpell.nLevel--; //ai_Debug("0i_spells", "1153", "nLevel: " + IntToString(stSpell.nLevel)); if(stSpell.nLevel > -1) { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; } } } stSpell.nPosition++; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); //ai_Debug("0i_spells", "1164", "nClass: " + IntToString(stSpell.nClass)); if(stSpell.nClass == CLASS_TYPE_INVALID) break; if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nSlot = 0; if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); } else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); return; } } } ai_CheckForBuffSpells(stSpell); } void ai_ActionCastKnownSummons(struct stSpell stSpell) { //ai_Debug("0i_spells", "1184", "Start of ActionCastKnownSummons!"); int nSpell; string sBuffGroup, sBuffTarget; object oTarget; while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { //ai_Debug("0i_spells", "1190", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { //ai_Debug("0i_spells", "1193", "nLevel: " + IntToString(stSpell.nLevel)); while(stSpell.nLevel > -1) { if(stSpell.nMaxSlots) { //ai_Debug("0i_spells", "1198", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + // " nSlots: " + IntToString(stSpell.nSlot)); while(stSpell.nSlot < stSpell.nMaxSlots) { nSpell = GetKnownSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); //ai_Debug("0i_spells", "1203", "Ready: " + IntToString(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell))); if(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell)) { if(Get2DAString("ai_spells", "Category", nSpell) == "S") { SetLocalInt(stSpell.oCaster, "AI_USED_SPELL_GROUP_S", TRUE); //ai_Debug("0i_spells", "1209", "nSpell: " + IntToString(nSpell)); ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, stSpell.oCaster, TRUE, stSpell.oPC); stSpell.nPosition = 1; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; ai_SetupAllyTargets(stSpell.oCaster, stSpell.oPC); DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell))); return; } } stSpell.nSlot++; } } stSpell.nLevel--; //ai_Debug("0i_spells", "1218", "nLevel: " + IntToString(stSpell.nLevel)); if(stSpell.nLevel > -1) { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; } } } stSpell.nPosition++; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); if(stSpell.nClass == CLASS_TYPE_INVALID) break; //ai_Debug("0i_spells", "1229", "nClass: " + IntToString(stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nSlot = 0; if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); return; } else stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); } } ai_CheckForBuffSpells(stSpell); } void ai_ActionCastMemorizedBuff(struct stSpell stSpell) { int nSpell; string sBuffGroup, sBuffTarget; object oTarget; while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { ai_Debug("0i_spells", "1252", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { ai_Debug("0i_spells", "1255", "nLevel: " + IntToString(stSpell.nLevel)); while(stSpell.nLevel > -1) { ai_Debug("0i_spells", "1258", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + " nSlots: " + IntToString(stSpell.nSlot)); while(stSpell.nSlot < stSpell.nMaxSlots) { ai_Debug("0i_spells", "1262", "Ready: " + IntToString(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot))); if(GetMemorizedSpellReady(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot)) { nSpell = GetMemorizedSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); ai_Debug("0i_spells", "1267", "nBuffType: " + IntToString(stSpell.nBuffType) + " nSpellBuffDuration: " + IntToString(nSpellBuffDuration) + " sBuffGroup: " + Get2DAString("ai_spells", "Buff_Group", nSpell)); if(stSpell.nBuffType == nSpellBuffDuration || stSpell.nBuffType == 1) { if(stSpell.nTarget > 0) { sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); oTarget = GetLocalObject(stSpell.oCaster, "AI_ALLY_TARGET_" + IntToString(stSpell.nTarget)); if(sBuffTarget != "0" || (sBuffTarget == "0" && stSpell.oCaster == oTarget)) { sBuffGroup = "AI_USED_SPELL_GROUP_" + Get2DAString("ai_spells", "Buff_Group", nSpell); if(!ai_SpellGroupNotCast(oTarget, sBuffGroup)) oTarget == OBJECT_INVALID; } else oTarget == OBJECT_INVALID; } else oTarget = ai_GetBuffTarget(stSpell.oCaster, nSpell); ai_Debug("0i_spells", "1284", "nSpell: " + IntToString(nSpell) + " oTarget: " + GetName(oTarget)); if(oTarget != OBJECT_INVALID) { ai_CastMemorizedSpell(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot, oTarget, TRUE, stSpell.oPC); stSpell.nSlot++; DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell))); return; } } } stSpell.nSlot++; } stSpell.nLevel--; ai_Debug("0i_spells", "1298", "nLevel: " + IntToString(stSpell.nLevel)); if(stSpell.nLevel > -1) { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; } } } stSpell.nPosition++; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); if(stSpell.nClass == CLASS_TYPE_INVALID) break; ai_Debug("0i_spells", "1309", "nClass: " + IntToString(stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nSlot = 0; if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); } else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell)); return; } } } ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); } void ai_ActionCastKnownBuff(struct stSpell stSpell) { int nSpell; string sBuffGroup, sBuffTarget; object oTarget; while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { //ai_Debug("0i_spells", "1347", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { //ai_Debug("0i_spells", "1350", "nLevel: " + IntToString(stSpell.nLevel)); while(stSpell.nLevel > -1) { if(stSpell.nMaxSlots) { //ai_Debug("0i_spells", "1356", "nMaxSlots: " + IntToString(stSpell.nMaxSlots) + // " nSlots: " + IntToString(stSpell.nSlot)); while(stSpell.nSlot < stSpell.nMaxSlots) { nSpell = GetKnownSpellId(stSpell.oCaster, stSpell.nClass, stSpell.nLevel, stSpell.nSlot); int nSpellBuffDuration = StringToInt(Get2DAString("ai_spells", "Buff_Duration", nSpell)); //ai_Debug("0i_spells", "1361", "nBuffType: " + IntToString(stSpell.nBuffType) + // " nSpellBuffDuration: " + IntToString(nSpellBuffDuration) + // " sBuffGroup: " + Get2DAString("ai_spells", "Buff_Group", nSpell)); if(stSpell.nBuffType == nSpellBuffDuration || stSpell.nBuffType == 1) { //ai_Debug("0i_spells", "1367", "Ready: " + IntToString(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell))); if(GetSpellUsesLeft(stSpell.oCaster, stSpell.nClass, nSpell)) { if(stSpell.nTarget > 0) { sBuffTarget = Get2DAString("ai_spells", "Buff_Target", nSpell); oTarget = GetLocalObject(stSpell.oCaster, "AI_ALLY_TARGET_" + IntToString(stSpell.nTarget)); if(sBuffTarget != "0" || (sBuffTarget == "0" && stSpell.oCaster == oTarget)) { sBuffGroup = "AI_USED_SPELL_GROUP_" + Get2DAString("ai_spells", "Buff_Group", nSpell); if(!ai_SpellGroupNotCast(oTarget, sBuffGroup)) oTarget == OBJECT_INVALID; } else oTarget == OBJECT_INVALID; } else oTarget = ai_GetBuffTarget(stSpell.oCaster, nSpell); //ai_Debug("0i_spells", "1382", "nSpell: " + IntToString(nSpell) + // " oTarget: " + GetName(oTarget)); if(oTarget != OBJECT_INVALID) { ai_CastKnownSpell(stSpell.oCaster, stSpell.nClass, nSpell, oTarget, TRUE, stSpell.oPC); stSpell.nSlot++; DelayCommand(AI_HENCHMAN_BUFF_DELAY, AssignCommand(stSpell.oCaster, ai_ActionCastKnownBuff(stSpell))); return; } } } stSpell.nSlot++; } } stSpell.nLevel--; //ai_Debug("0i_spells", "1396", "nLevel: " + IntToString(stSpell.nLevel)); if(stSpell.nLevel > -1) { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); stSpell.nSlot = 0; } } } stSpell.nPosition++; stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); if(stSpell.nClass == CLASS_TYPE_INVALID) break; //ai_Debug("0i_spells", "921", "nClass: " + IntToString(stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; stSpell.nSlot = 0; if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedBuff(stSpell)); return; } else stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); } } ai_CheckForPerDayItems(stSpell.oCaster, stSpell.oPC, stSpell.nBuffType); } void ai_CastBuffs(object oCaster, int nBuffType, int nTarget, object oPC) { // buff types: 1 - All, 2 - Short duration, 3 - Long duration // Buff groups are used to prevent a henchmen to cast spells that have the same effect, // for example: resist elements and protection from elements are similiar so the henchmen // would cast only the most powerful among these if he has them both. if(AI_DEBUG) ai_Debug("0i_spells", "1670", GetName(oCaster) + " is casting buffs: " + IntToString(nBuffType) + " nTarget: " + IntToString(nTarget) + "!"); struct stSpell stSpell; stSpell.oPC = oPC; stSpell.oCaster = oCaster; stSpell.nBuffType = nBuffType; stSpell.nTarget = nTarget; stSpell.nPosition = 1; // Look for summons spells on All, Long durations and the whole party. if((nBuffType == 1 || nBuffType == 3) && nTarget == 0) { while(stSpell.nPosition <= AI_MAX_CLASSES_PER_CHARACTER) { stSpell.nClass = GetClassByPosition(stSpell.nPosition, stSpell.oCaster); if(AI_DEBUG) ai_Debug("0i_spells", "1684", "nClass: " + IntToString(stSpell.nClass)); if(stSpell.nClass == CLASS_TYPE_INVALID) break; if(AI_DEBUG) ai_Debug("0i_spells", "1686", "SpellCaster: " + Get2DAString("classes", "SpellCaster", stSpell.nClass)); if(Get2DAString("classes", "SpellCaster", stSpell.nClass) == "1") { stSpell.nLevel = (GetLevelByPosition(stSpell.nPosition, stSpell.oCaster) + 1) / 2; if(AI_DEBUG) ai_Debug("0i_spells", "1692", "MemorizesSpells: " + Get2DAString("classes", "MemorizesSpells", stSpell.nClass)); if(Get2DAString("classes", "MemorizesSpells", stSpell.nClass) == "1") { stSpell.nMaxSlots = GetMemorizedSpellCountByLevel(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastMemorizedSummons(stSpell)); return; } else { stSpell.nMaxSlots = GetKnownSpellCount(stSpell.oCaster, stSpell.nClass, stSpell.nLevel); AssignCommand(stSpell.oCaster, ai_ActionCastKnownSummons(stSpell)); return; } } stSpell.nPosition++; } // Exit here; if we summoned a monster then it linked off of that spell // cast to continue the action queue for all buff spell cast actions. } ai_CheckForBuffSpells(stSpell); } int ai_CastSpontaneousCure(object oCreature, object oTarget, object oPC) { if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_MAGIC)) return FALSE; if(ai_GetMagicMode(oCreature, AI_MAGIC_NO_SPONTANEOUS_CURE)) return FALSE; if(AI_DEBUG) ai_Debug("0i_spells", "1643", GetName(oCreature) + " is looking to cast a spontaneous cure spell."); if(!GetLevelByClass(CLASS_TYPE_CLERIC, oCreature)) return FALSE; int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); int nSpell, nSlot, nMaxSlots, nLevel = 4; int nSpellSave, nSlotSave, nLevelSave = 5; string sSpellName; while(nLevel > -1) { // We check CLASS_TYPE_CLERIC as thats the only class with spontaneous cure spells. nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, CLASS_TYPE_CLERIC, nLevel); nSlot = 0; if(AI_DEBUG) ai_Debug("0i_spells", "1653", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); while(nSlot < nMaxSlots) { if(AI_DEBUG) ai_Debug("0i_spells", "1656", "nSlot: " + IntToString(nSlot) + " Spell Ready: " + IntToString(GetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevel, nSlot))); if(GetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevel, nSlot)) { if(nLevel == 4) nSpell = SPELL_CURE_CRITICAL_WOUNDS; else if(nLevel == 3) nSpell = SPELL_CURE_SERIOUS_WOUNDS; else if(nLevel == 2) nSpell = SPELL_CURE_MODERATE_WOUNDS; else if(nLevel == 1) nSpell = SPELL_CURE_LIGHT_WOUNDS; else nSpell = 0; if(AI_DEBUG) ai_Debug("0i_spells", "1665", "nSpell: " + IntToString(nSpell)); if(nSpell) { if(ai_ShouldWeCastThisCureSpell(nSpell, nDamage)) { SetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevel, nSlot, FALSE); sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); ai_SendMessages(GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_MAGENTA, oPC); if(AI_DEBUG) ai_Debug("0i_spells", "1673", GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + "."); ActionCastSpellAtObject(nSpell, oTarget, 255, TRUE); return TRUE; } // Save the lowest level cure spell as we might need to cast it. else if(nLevel < nLevelSave) { nSpellSave = nSpell; nLevelSave = nLevel; nSlotSave = nSlot; } } } nSlot++; } nLevel--; } // Did we find a cure spell? If we did then use it. if(nSpellSave) { if(AI_DEBUG) ai_Debug("0i_spells", "1693", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); SetMemorizedSpellReady(oCreature, CLASS_TYPE_CLERIC, nLevelSave, nSlotSave, FALSE); sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpellSave))); ai_SendMessages(GetName(oCreature) + " has spontaneously cast " + sSpellName + " on " + GetName(oTarget) + ".", AI_COLOR_MAGENTA, oPC); ActionCastSpellAtObject(nSpellSave, oTarget, 255, TRUE); return TRUE; } return FALSE; } int ai_CastMemorizedHealing(object oCreature, object oTarget, object oPC, int nClass) { if(AI_DEBUG) ai_Debug("0i_spells", "1702", GetName(oCreature) + " is looking to cast a memorized cure spell."); int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); int nSpell, nSlot, nMaxSlots, nLevel = 9; int nClassSave, nSlotSave, nLevelSave = 10; while(nLevel > -1) { nMaxSlots = GetMemorizedSpellCountByLevel(oCreature, nClass, nLevel); nSlot = 0; if(AI_DEBUG) ai_Debug("0i_spells", "1710", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); while(nSlot < nMaxSlots) { if(AI_DEBUG) ai_Debug("0i_spells", "1713", "nSlot: " + IntToString(nSlot) + " Spell Ready: " + IntToString(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot))); if(GetMemorizedSpellReady(oCreature, nClass, nLevel, nSlot)) { nSpell = GetMemorizedSpellId(oCreature, nClass, nLevel, nSlot); if(ai_ShouldWeCastThisCureSpell(nSpell, nDamage)) { string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); if(AI_DEBUG) ai_Debug("0i_spells", "1721", GetName(oCreature) + " has cast " + sSpellName + " on " + GetName(oTarget) + "."); ai_CastMemorizedSpell(oCreature, nClass, nLevel, nSlot, oTarget, FALSE, oPC); return TRUE; } // Save the lowest level cure spell as we might need to cast it. else if(nLevel < nLevelSave && (nSpell > 26 && nSpell < 32)) { nClassSave = nClass; nLevelSave = nLevel; nSlotSave = nSlot; } } nSlot++; } nLevel--; } // Did we find a cure spell? If we did then use it. if(nLevelSave < 10) { if(AI_DEBUG) ai_Debug("0i_spells", "1740", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); ai_CastMemorizedSpell(oCreature, nClassSave, nLevelSave, nSlotSave, oTarget, FALSE, oPC); return TRUE; } return FALSE; } int ai_CastKnownHealing(object oCreature, object oTarget, object oPC, int nClass) { if(AI_DEBUG) ai_Debug("0i_spells", "1748", GetName(oCreature) + " is looking to cast a known cure spell."); int nDamage = GetMaxHitPoints(oTarget) - GetCurrentHitPoints(oTarget); int nSpell, nSlot, nMaxSlots, nLevel = 9; int nClassSave, nSpellSave, nLevelSave = 10; while(nLevel > -1) { nMaxSlots = GetKnownSpellCount(oCreature, nClass, nLevel); nSlot = 0; if(AI_DEBUG) ai_Debug("0i_spells", "1756", "nLevel: " + IntToString(nLevel) + " nMaxSlots: " + IntToString(nMaxSlots)); while(nSlot < nMaxSlots) { nSpell = GetKnownSpellId(oCreature, nClass, nLevel, nSlot); if(AI_DEBUG) ai_Debug("0i_spells", "1760", "nSlot: " + IntToString(nSlot) + " Spell Ready: " + IntToString(GetSpellUsesLeft(oCreature, nClass, nSpell))); if(GetSpellUsesLeft(oCreature, nClass, nSpell)) { if(ai_ShouldWeCastThisCureSpell(nSpell, nDamage)) { string sSpellName = GetStringByStrRef(StringToInt(Get2DAString("spells", "Name", nSpell))); if(AI_DEBUG) ai_Debug("0i_spells", "1767", GetName(oCreature) + " has cast " + sSpellName + " on " + GetName(oTarget) + "."); ai_CastKnownSpell(oCreature, nClass, nSpell, oTarget, FALSE, oPC); return TRUE; } // Save the lowest level cure spell as we might need to cast it. else if(nLevel < nLevelSave && (nSpell > 26 && nSpell < 32)) { nClassSave = nClass; nLevelSave = nLevel; nSpellSave = nSpell; } } nSlot++; } nLevel--; } return FALSE; // Did we find a cure spell? If we did then use it. if(nLevelSave < 10) { if(AI_DEBUG) ai_Debug("0i_spells", "1781", GetName(oCreature) + " has cast the lowest level cure spell on " + GetName(oTarget) + "."); ai_CastKnownSpell(oCreature, nClassSave, nSpellSave, oTarget, FALSE, oPC); return TRUE; } } int ai_ConcentrationCondition(object oCreature) { int nType; effect eEffect = GetFirstEffect(oCreature); while(GetIsEffectValid(eEffect)) { nType = GetEffectType(eEffect); if(nType == EFFECT_TYPE_STUNNED || nType == EFFECT_TYPE_PARALYZE || nType == EFFECT_TYPE_SLEEP || nType == EFFECT_TYPE_FRIGHTENED || nType == EFFECT_TYPE_PETRIFY || nType == EFFECT_TYPE_CONFUSED || nType == EFFECT_TYPE_DOMINATED || nType == EFFECT_TYPE_POLYMORPH) { return TRUE; } eEffect = GetNextEffect(oCreature); } return FALSE; } void ai_SpellConcentrationCheck(object oCaster = OBJECT_SELF) { object oMaster = GetMaster(); if(GetLocalInt(oCaster,"X2_L_CREATURE_NEEDS_CONCENTRATION")) { if(GetIsObjectValid(oMaster)) { int nAction = GetCurrentAction(oMaster); // master doing anything that requires attention and breaks concentration if(nAction == ACTION_DISABLETRAP || nAction == ACTION_TAUNT || nAction == ACTION_PICKPOCKET || nAction ==ACTION_ATTACKOBJECT || nAction == ACTION_COUNTERSPELL || nAction == ACTION_FLAGTRAP || nAction == ACTION_CASTSPELL || nAction == ACTION_ITEMCASTSPELL) { SignalEvent(oCaster,EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); } else if(ai_ConcentrationCondition(oMaster)) { SignalEvent(oCaster,EventUserDefined(X2_EVENT_CONCENTRATION_BROKEN)); } } } } int ai_CastInMelee(object oCreature, int nSpell, int nInMelee) { // If this is a spell and we are in melee. if(nInMelee > 0 && !GetHasFeat(FEAT_EPIC_IMPROVED_COMBAT_CASTING, oCreature)) { // Using DC 19 so we will use with up to a 50% failure. int nSpellLevel = StringToInt(Get2DAString("spells", "Innate", nSpell)); int nDC = AI_DEFENSIVE_CASTING_DC + nSpellLevel; int nRoll = Random(AI_DEFENSIVE_CASTING_DIE) + 1; int nConcentration = GetSkillRank(SKILL_CONCENTRATION, oCreature); if(GetHasFeat(FEAT_COMBAT_CASTING, oCreature)) nConcentration += 4; if(AI_DEBUG) ai_Debug("0i_spells", "1081", "Use Defensive Casting? nDC: " + IntToString(nDC) + " FEAT_COMBAT_CASTING: " + IntToString(GetHasFeat(FEAT_COMBAT_CASTING, oCreature)) + " nConcentration: " + IntToString(nConcentration) + " + nRoll: " + IntToString(nRoll)); if(nConcentration + nRoll > nDC) { if(AI_DEBUG) ai_Debug("0i_spells", "1086", GetName(oCreature) + " is casting defensively!"); SetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST, TRUE); } // Defensive casting is a bad idea so maybe casting anyspell is a bad idea. else { object oMelee = GetLocalObject(oCreature, AI_ENEMY_NEAREST); if(GetIsObjectValid(oMelee)) { nRoll = Random(AI_CASTING_IN_MELEE_ROLL) + 1; nDC = AI_CASTING_IN_MELEE_DC + nSpellLevel + nInMelee * ai_GetCreatureAttackBonus(oMelee); if(AI_DEBUG) ai_Debug("0i_spells", "1097", "Cast anyway: nConcentration: " + IntToString(nConcentration) + " nRoll: " + IntToString(nRoll) + " nDC: " + IntToString(nDC) + " oMelee: " + GetName(oMelee)); if(nConcentration + nRoll > nDC) return TRUE; if(AI_DEBUG) ai_Debug("0i_spells", "1101", GetName(oCreature) + " is not casting in melee against " + GetName(oMelee)); return FALSE; } } } // We don't need to cast defensively so lets make sure it's off. else if(GetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST)) { SetActionMode(oCreature, ACTION_MODE_DEFENSIVE_CAST, FALSE); } return TRUE; } float ai_GetOffensiveSpellSearchRange(object oCreature, int nSpell) { // Search the spell range + the distance to the closest enemy - 7.5 meters). // This will keep the caster from running up on an enemy to cast. // But allow them to move up some if needed. float fRange = ai_GetSpellRange(nSpell); object oNearestEnemy = GetLocalObject(oCreature, AI_ENEMY_NEAREST); float fEnemyDistance = GetDistanceBetween(oCreature, oNearestEnemy); // Spell range is less than the nearest enemy. Restrict based on nearest enemy. // Spell range is less than the nearestenemy. Check enemy action then adjust. if(fRange < fEnemyDistance) { // We check this because if the enemy is moving or has not started acting // then we don't want to move up on them as they might move towards us! int nAction = GetCurrentAction(oNearestEnemy); if(AI_DEBUG) ai_Debug("0i_spells", "1130", GetName(oNearestEnemy) + " current action: " + IntToString(nAction)); if(nAction != ACTION_MOVETOPOINT || nAction != ACTION_ITEMCASTSPELL || nAction != ACTION_INVALID || nAction != ACTION_USEOBJECT || nAction != ACTION_RANDOMWALK) fRange = fEnemyDistance + (fRange - 7.5); } if(fRange > AI_RANGE_BATTLEFIELD) return AI_RANGE_BATTLEFIELD; else if(fRange < 0.1f) return 0.1f; return fRange; } int ai_ShouldWeCastThisCureSpell(int nSpell, int nDamage) { if(AI_DEBUG) ai_Debug("0i_spells", "1127", "nSpell: " + IntToString(nSpell) + " nDamage: " + IntToString(nDamage)); if(nSpell == SPELL_HEAL && nDamage > 50) return TRUE; else if(nSpell == SPELL_CURE_CRITICAL_WOUNDS && nDamage > 31) return TRUE; else if(nSpell == SPELL_CURE_SERIOUS_WOUNDS && nDamage > 23) return TRUE; else if(nSpell == SPELL_CURE_MODERATE_WOUNDS && nDamage > 15) return TRUE; else if(nSpell == SPELL_CURE_LIGHT_WOUNDS && nDamage > 6) return TRUE; else if(nSpell == SPELL_CURE_MINOR_WOUNDS) return TRUE; return FALSE; } void ai_CastWidgetSpell(object oPC, object oAssociate, object oTarget, location lLocation) { int nIndex = GetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); DeleteLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); string sAssociateType = ai_GetAssociateType(oPC, oAssociate); json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); json jSpells = JsonArrayGet(jAIData, 10); json jWidget = JsonArrayGet(jSpells, 2); json jSpell = JsonArrayGet(jWidget, nIndex); int nSpell = JsonGetInt(JsonArrayGet(jSpell, 0)); int nClass = JsonGetInt(JsonArrayGet(jSpell, 1)); int nMetaMagic = JsonGetInt(JsonArrayGet(jSpell, 3)); int nDomain = JsonGetInt(JsonArrayGet(jSpell, 4)); //SendMessageToPC(oPC, "nSpell: " + IntToString(nSpell) + // " oTarget: " + GetName(oTarget) + // " nMetaMagic: " + IntToString(nMetaMagic) + // " nDomain: " + IntToString(nDomain)); if(GetCurrentAction(oAssociate) != ACTION_CASTSPELL) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); if(!GetIsObjectValid(oTarget)) { AssignCommand(oAssociate, ActionCastSpellAtLocation(nSpell, lLocation, nMetaMagic, FALSE, 0, FALSE, -1, FALSE, nDomain)); } else AssignCommand(oAssociate, ActionCastSpellAtObject(nSpell, oTarget, nMetaMagic, FALSE, nDomain)); } void ai_UseWidgetFeat(object oPC, object oAssociate, object oTarget, location lLocation) { int nIndex = GetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); DeleteLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); string sAssociateType = ai_GetAssociateType(oPC, oAssociate); json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); json jSpells = JsonArrayGet(jAIData, 10); json jWidget = JsonArrayGet(jSpells, 2); json jFeat = JsonArrayGet(jWidget, nIndex); int nFeat = JsonGetInt(JsonArrayGet(jFeat, 5)); if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); //SendMessageToPC(oPC, "0i_spells, 2104, nFeat: " + IntToString(nFeat) + " oTarget: " + GetName(oTarget)); if(!GetIsObjectValid(oTarget)) { AssignCommand(oAssociate, ActionUseFeat(nFeat, OBJECT_INVALID, 0, lLocation)); } else AssignCommand(oAssociate, ActionUseFeat(nFeat, oTarget)); } void ai_UseWidgetItem(object oPC, object oAssociate, object oTarget, location lLocation) { int nIndex = GetLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); DeleteLocalInt(oAssociate, "AI_WIDGET_SPELL_INDEX"); string sAssociateType = ai_GetAssociateType(oPC, oAssociate); json jAIData = ai_GetAssociateDbJson(oPC, sAssociateType, "aidata"); json jSpells = JsonArrayGet(jAIData, 10); json jWidget = JsonArrayGet(jSpells, 2); json jItem = JsonArrayGet(jWidget, nIndex); int nSpell = JsonGetInt(JsonArrayGet(jItem, 0)); int nIprpSubType = JsonGetInt(JsonArrayGet(jItem, 4)); object oItem = GetObjectByUUID(JsonGetString(JsonArrayGet(jItem, 5))); itemproperty ipProperty; if(ai_GetIsInCombat(oAssociate)) AssignCommand(oAssociate, ai_ClearCreatureActions(TRUE)); if(nSpell == SPELL_HEALINGKIT) { ipProperty = GetFirstItemProperty(oItem); if(GetItemPropertyType(ipProperty) == ITEM_PROPERTY_HEALERS_KIT) { if(ai_GetIsCharacter(oPC)) ai_SendMessages(GetName(oAssociate) + " uses " + GetName(oItem) + " on " + GetName(oTarget) + ".", AI_COLOR_YELLOW, oPC); AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oTarget)); return; } } ipProperty = GetFirstItemProperty(oItem); while(GetIsItemPropertyValid(ipProperty)) { if(nIprpSubType == GetItemPropertySubType(ipProperty)) break; ipProperty = GetNextItemProperty(oItem); } if(!GetIsObjectValid(oTarget)) { AssignCommand(oAssociate, ActionUseItemAtLocation(oItem, ipProperty, lLocation)); } else AssignCommand(oAssociate, ActionUseItemOnObject(oItem, ipProperty, oTarget)); }