TheHordeOrcs_PRC8/_module/nss/0i_spells.nss

2158 lines
106 KiB
Plaintext
Raw Normal View History

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