14949 lines
710 KiB
Plaintext
14949 lines
710 KiB
Plaintext
/************************ [Combat Include File] ********************************
|
||
Filename: J_INC_GENERIC_AI
|
||
************************* [Combat Include File] ********************************
|
||
This is included in j_ai_detercombat, and executed via the main call
|
||
AI_DetermineCombatRound, to determine a hostile action to do against the enemy.
|
||
|
||
It works through a "List" of things, until it finds an appropriate action
|
||
to undertake.
|
||
|
||
It basically runs through:
|
||
|
||
- Should I move out of an AOE spell?
|
||
- Special abilities like Auras
|
||
- Fleeing
|
||
- Spells
|
||
- Feats (For combat, like Bulls Strength)
|
||
- Melee attack (Ranged and Melee)
|
||
************************* [History] ********************************************
|
||
1.3 - All functions prefixed to AI_
|
||
- Lot more documentation
|
||
- All parts changed, mainly:
|
||
- Better targeting
|
||
- Different Effect Setting
|
||
- More randomised spellcasting
|
||
************************* [Workings] *******************************************
|
||
This is included in "j_ai_detercombat". It is the main combat include file
|
||
and determines what to do by checking a lot of conditions.
|
||
|
||
It has 2 User-defined Events:
|
||
EVENT_COMBAT_ACTION_EVENT = 1012;
|
||
EVENT_COMBAT_ACTION_PRE_EVENT = 1032;
|
||
|
||
Useful to check special things, and can be used to override default AI
|
||
actions, or to change them once they have happened. See the user defined
|
||
file for workings of the Pre events.
|
||
************************* [Arguments] ******************************************
|
||
Arguments: Global* Objects/Integers/Toggles, GetSpawnInCondition,
|
||
GetCommandable, AI_ActionCast* AI_ActionUse*
|
||
(Other AI_Action* things) ClearAllActions, GetAC,
|
||
GetCurrentHitPoints, GetMaxHitPoints,
|
||
GetHitDice, GetCurrentAction (and lots more...).
|
||
************************* [Combat Include File] *******************************/
|
||
|
||
/************************ [Intelligence Documentation] *************************
|
||
Intelligence
|
||
|
||
in<69>tel<65>li<6C>gence ( P ) Pronunciation Key (n-tl-jns)
|
||
n.
|
||
|
||
1. a, The capacity to acquire and apply knowledge.
|
||
b, The faculty of thought and reason.
|
||
c, Superior powers of mind.
|
||
2. An intelligent, incorporeal being, especially an angel.
|
||
3. Information; news.
|
||
|
||
4. a, Secret information, especially about an actual or potential enemy.
|
||
b, An agency, staff, or office employed in gathering such information.
|
||
c, Espionage agents, organizations, and activities considered as a
|
||
group: <20>Intelligence is nothing if not an institutionalized black
|
||
market in perishable commodities<65> (John le Carr<72>).
|
||
|
||
We'll take 1. a, as our definition of intelligence in my AI files.
|
||
|
||
It basically has an underlaying effect on how creatures work - it will
|
||
affect some of the AI's choices and decisions.
|
||
|
||
It will not affect everything. It will never affect anything you can
|
||
specifically set in the spawn file, except in rare circumstances. It does
|
||
have no effect on: Targeting, Counterspelling, Shouting, Seeing/Percieving.
|
||
|
||
-
|
||
|
||
|
||
|
||
|
||
|
||
************************* [Intelligence Documentation] ************************/
|
||
|
||
// Include: All constants. Also contains Get/Set/Delete Spawn In Conditions.
|
||
#include "j_inc_constants"
|
||
// Sets effects. This doesn't have to be seperate, but I like it seperate.
|
||
#include "j_inc_seteffects"
|
||
|
||
/******************************************************************************/
|
||
// Combat include, spell effect/immune constants
|
||
/******************************************************************************/
|
||
// Spell targets, note, have the least immunities, but these ARE set here...
|
||
// The spell target genrally is the best average lowest save, or a specific low
|
||
// save if we want to specify (and low SR).
|
||
// List (use Global to not conflict with the nwscript.nss!)
|
||
// - GlobalTargetImmunitiesHex
|
||
const int GlobalImmunityFear = 0x00000001;
|
||
const int GlobalImmunityNecromancy = 0x00000002;
|
||
const int GlobalImmunityMind = 0x00000004;
|
||
const int GlobalImmunityNegativeLevel = 0x00000008;
|
||
const int GlobalImmunityEntangle = 0x00000008;
|
||
const int GlobalImmunitySleep = 0x00000010;
|
||
const int GlobalImmunityPoison = 0x00000020;
|
||
const int GlobalImmunityDisease = 0x00000040;
|
||
const int GlobalImmunityDomination = 0x00000080;
|
||
const int GlobalImmunityStun = 0x00000100;
|
||
const int GlobalImmunityCurse = 0x00000200;
|
||
const int GlobalImmunityConfusion = 0x00000400;
|
||
const int GlobalImmunityBlindDeaf = 0x00000800;
|
||
const int GlobalImmunityDeath = 0x00000800;
|
||
const int GlobalImmunityNegativeEnergy = 0x00001000;
|
||
const int GlobalImmunityMantalProtection = 0x00002000; // Its own immunity, checked sometimes.
|
||
const int GlobalImmunityPetrify = 0x00004000;
|
||
const int GlobalImmunityFlying = 0x00008000; // Earthquake ETC.
|
||
const int GlobalImmunitySlow = 0x00010000;
|
||
|
||
/*************************** Globals set-up ********************************
|
||
We set up all spells and categories, as well as all targets we may use (its
|
||
much easier than imputting countless entries into function headers). Any
|
||
sort of globals that are required are also added here - anything that would
|
||
pop up in more than one place, or would change from one or more event.
|
||
**************************** Globals set-up *******************************/
|
||
|
||
// OUR GLOBAL CONSTANTS
|
||
int GlobalIntelligence, GlobalOurHitDice, GlobalOurSize, GlobalOurAC,
|
||
GlobalOurBaseAttackBonus, // Used and set for melee. USes GetBaseAttackBonus, as weapons are not set.
|
||
GlobalOurRace, GlobalOurGoodEvil,
|
||
GlobalSilenceSoItems, // This is set to TRUE if we are affected with silence
|
||
// - meaning no proper spells! ALWAYS off if we have auto-silence.
|
||
GlobalInTimeStop,// This is used a LOT to see if we are in time stop
|
||
GlobalOurChosenClass, GlobalOurChosenClassLevel, // Our chosen class and level of it.
|
||
GlobalSpellAbilityModifier, GlobalSpellBaseSaveDC,// Spell globals - IE save DCs etc.
|
||
GlobalSaveStupidity, // This is 10 - Intelligence. Makes Save DC checks different
|
||
// for lower casters - they may cast even if the enemy always saves!
|
||
GlobalSpellPenetrationRoll, // SR penetration roll.
|
||
GlobalOurCurrentHP, GlobalOurMaxHP, GlobalOurPercentHP,// HP globals.
|
||
GlobalOurAppearance,// Used normally to stop dragon breaths against same dragons!
|
||
SRA, // "Spell range attacking", mage behaviour, used for constant checking.
|
||
GlobalWeAreSorcerorBard,// Just is GetLevelByClass(CLASS_TYPE_SORCERER/BARD),
|
||
// but a global. With summoning, it won't summon a similar one if the old is at low HP
|
||
GlobalCanSummonSimilarLevel,// Checked for when set up. If a level over 0, we may
|
||
// Summon another spell monster at a similar level.
|
||
// The level is set when we cast a spell of it.
|
||
GlobalBestSpontaeousHealingSpell,// This is set to the best spell we could
|
||
// Spontaeously cast. Set up as talents below.
|
||
GlobalTimeStopArraySize,
|
||
GlobalLastSpellValid, // Random spells, if this is set to > 0, it casts it after random chances of others
|
||
GlobalRandomCastModifier,// Random casting, the extra % added - 2xIntelligence
|
||
GlobalWeAreBuffer; // Are we a spell buffer? AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS
|
||
|
||
|
||
// OUR GLOBAL TARGETS
|
||
|
||
// Melee is actually ANY target we are attacking (depends on ranges and things).
|
||
// If OBJECT_INVALID (default) then we don't do the action!
|
||
object GlobalMeleeTarget, // Set from either a ranged or melee target :-)
|
||
GlobalRangedTarget, // Ranged target
|
||
GlobalSpellTarget, // Spell target
|
||
GlobalDispelTarget, // Used for breaching too
|
||
GlobalNearestEnemySeen, GlobalNearestEnemyHeard,
|
||
GlobalNearestSeenAlly, GlobalNearestAlly,
|
||
GlobalNearestLeader, GlobalThisArea,
|
||
GlobalBuffAlly, GlobalHealingKit,
|
||
/* GlobalPreviousTarget,*/
|
||
// - Global Spell targets
|
||
// - One target, but a float for range to.
|
||
// Right == Ranged slot, non-shield slot.
|
||
// Left == Torch slot, secondary slot, shield slot.
|
||
GlobalLeftHandWeapon, GlobalRightHandWeapon;
|
||
|
||
// ENEMY/ALLY INTEGERS, VLAIDS ETC.
|
||
|
||
// Counting etc...global integers
|
||
// Must be TRUE else we think there is no enemies to attack.
|
||
int GlobalAnyValidTargetObject,
|
||
// Counts of 0 or more - specifically in our line of sight.
|
||
GlobalEnemiesIn4Meters, GlobalTotalSeenHeardEnemies,
|
||
// Ranged/Melee Attackers. Ranged attackers = Attackers with bows.
|
||
GlobalMeleeAttackers, GlobalRangedAttackers,
|
||
// Total allies + People. We don't count ourselves.
|
||
GlobalTotalPeople, GlobalTotalAllies,
|
||
// Average's - BAB, HDs.
|
||
GlobalAverageEnemyBAB, GlobalAverageEnemyHD,
|
||
GlobalAverageFriendlyHD,
|
||
// Melee target things...
|
||
GlobalMeleeTargetAC, GlobalMeleeTargetBAB,
|
||
// Any VALID objects. GetIsObjectValid.
|
||
GlobalValidLeader, GlobalValidAlly, GlobalValidSeenAlly,
|
||
GlobalValidNearestSeenEnemy, GlobalValidNearestHeardEnemy,
|
||
// Dispel counter. Is 1-5.
|
||
GlobalDispelTargetHighestDispel, GlobalDispelTargetHighestBreach,
|
||
// Spell target globals. Used for one target, throughout the spells...
|
||
// - Saves
|
||
GlobalSpellTargetWill, GlobalSpellTargetFort, GlobalSpellTargetReflex,
|
||
// - Target HD, HP and race
|
||
GlobalSpellTargetHitDice, GlobalSpellTargetCurrentHitPoints, GlobalSpellTargetRace,
|
||
// - TRUE if seen can see spell target
|
||
GlobalSeenSpell,
|
||
// Special Spell things - friendly, hostile for spells (!GetIsReactionType,
|
||
// GetIsReactionType etc.).
|
||
GlobalFriendlyFireHostile, GlobalFriendlyFireFriendly,
|
||
// Spell target immunity hex - this is the only one not set to local integer
|
||
// for global effects.
|
||
GlobalTargetImmunitiesHex,
|
||
// If GlobalSpellTarget is immune to our spells VIA. Spell reistance or
|
||
// spell immunity, then hostile SPELL (not abilities) are not used.
|
||
// - This is a NUMBER - all <= X will NOT work against the enemy
|
||
GlobalNormalSpellsNoEffectLevel;
|
||
|
||
// RANGES, Ie FLOATS :-)
|
||
float GlobalRangeToMeleeTarget, GlobalSpellTargetRange, GlobalRangeToNearestEnemy,
|
||
GlobalRangeToAlly, GlobalOurReach, GlobalRangeToFuthestEnemy, GlobalBuffRangeAddon;
|
||
|
||
// One location - GlobalSummonLocation - for summon location :-)
|
||
location GlobalSummonLocation;
|
||
|
||
/* This is the set values of spells that the talent actually is.
|
||
How it works (for the above). We set if we have items, or potions.
|
||
Then, we check the respective talent category. If the spell stored matches, it is true.
|
||
then, we go into a special silence check - we get the talent again. It *should* be the
|
||
same, and if so, use it!
|
||
|
||
Shows what spells are valid, which categories, to skip some GetHasSpell's
|
||
Format:
|
||
(Other)ther (Special - Darkness, light and silence)(Aura - auras/spells like it). (Rage = Rage)
|
||
(Allies) Obtain Allies - summon creature.
|
||
(Con)ditional,(Enh)ancement, (Pro)tection
|
||
(Harm)armful.
|
||
For (B)- (Self) Self. (SinTar)Single Target, (Are)Area effect,
|
||
For (H)- (Breath) Breath, (AreaDis) Discriminate, (AreaInd) Indiscriminate
|
||
*/
|
||
int
|
||
/*1*/ SpellHostAreaDis, ItemHostAreaDis,
|
||
/*2*/ SpellHostRanged, ItemHostRanged,
|
||
/*3*/ SpellHostTouch, ItemHostTouch,
|
||
|
||
/*6*/ SpellConAre, ItemConAre,
|
||
/*7*/ SpellConSinTar, ItemConSinTar,
|
||
/*8*/ SpellEnhAre, ItemEnhAre,
|
||
/*9*/ SpellEnhSinTar, ItemEnhSinTar,
|
||
/*10*/ SpellEnhSelf, ItemEnhSelf,
|
||
/*11*/ SpellHostAreaInd, ItemHostAreaInd,
|
||
/*12*/ SpellProSelf, ItemProSelf,
|
||
/*13*/ SpellProSinTar, ItemProSinTar,
|
||
/*14*/ SpellProAre, ItemProAre,
|
||
/*15*/ SpellAllies, ItemAllies,
|
||
/*16*/ SpellAura,
|
||
|
||
/*18*/ PotionCon,
|
||
/*19*/ SpellHostBreath,
|
||
/*20*/ PotionPro,
|
||
/*21*/ PotionEnh,
|
||
|
||
/*23*/ SpellOtherSpell,
|
||
// Special: validness
|
||
SpellAnySpellValid,
|
||
// Feats
|
||
ValidFeats,
|
||
// Do we have items (wands) avalible? Or potions? To check spells, we
|
||
// make sure that
|
||
GobalOtherItemsValid, GobalPotionsValid;
|
||
|
||
talent tPotionCon, tPotionPro, tPotionEnh; // These are general talents, Always set
|
||
// because we can use these parrallel to spells.
|
||
|
||
/*************************** Functions to order ****************************
|
||
This should be empty each patch. If you start adding your own things, adding
|
||
them here makes changes easy to know about, if you like. I do, anyway :-P
|
||
**************************** Functions to order ***************************/
|
||
|
||
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@ SETUP OF THINGS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
// This is a group of local variables that are set to 0 each time here, and
|
||
// used each round. This is better than local ints to use.
|
||
|
||
//*1*/ SpellHostAreaDis, ItemHostAreaDis,
|
||
//*2*/ SpellHostRanged, ItemHostRanged,
|
||
//*3*/ SpellHostTouch, ItemHostTouch,
|
||
//
|
||
//*6*/ SpellConAre, ItemConAre,
|
||
//*7*/ SpellConSinTar, ItemConSinTar,
|
||
//*8*/ SpellEnhAre, ItemEnhAre,
|
||
//*9*/ SpellEnhSinTar, ItemEnhSinTar,
|
||
//*10*/ SpellEnhSelf, ItemEnhSelf,
|
||
//*11*/ SpellHostAreaInd, ItemHostAreaInd,
|
||
//*12*/ SpellProSelf, ItemProSelf,
|
||
//*13*/ SpellProSinTar, ItemProSinTar,
|
||
//*14*/ SpellProAre, ItemProAre,
|
||
//*15*/ SpellAllies, ItemAllies,
|
||
//*16*/ SpellAura,
|
||
//
|
||
//*18*/ PotionCon,
|
||
//*19*/ SpellHostBreath,
|
||
//*20*/ PotionPro,
|
||
//*21*/ PotionEnh,
|
||
//
|
||
//*23*/ SpellOtherSpell
|
||
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@ CORE AI FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
|
||
// Main call. It goes though many lines of code to decide what to do in combat
|
||
// To lengthy to explain here. Basically, input an object, it should attack
|
||
// it, or a better target. Define OnSpawn many behaviours to influence choice.
|
||
void AI_DetermineCombatRound(object oIntruder = OBJECT_INVALID);
|
||
|
||
// These all have the pre-name AttemptX - as they can return TRUE or FALSE
|
||
//***************************** AttemptSpell ***********************************
|
||
|
||
// This attempts to cast a spell, running down the lists.
|
||
// The only variable is iLowest and iBAB level's, targets are globally set.
|
||
// - iLowestSpellLevel - If it is set to more than 1, then all spells of that
|
||
// level and lower are just ignored. Used for dragons. Default 0.
|
||
// - iBABCheckHighestLevel - We check our BAB to see if attacking them would probably
|
||
// be a better thing to do. Default 3, it adds 5DC for each level.
|
||
// - iLastCheckedRange - Ranged attacking. Starts at 1, and goes to 4 max.
|
||
// - Range: Long 40, Medium: 20, Short: 10, Touch: 2.25 (Game meters). Personal = 0
|
||
// NOTE 1: If GlobalItemsOnly is set, we only check for item talents!
|
||
// NOTE 2: It uses the SAME target, but for ranged attacking, we use a float range to check range :-)
|
||
int AI_AttemptAllSpells(int iLowestSpellLevel = 0, int iBABCheckHighestLevel = 3, int iLastCheckedRange = 1, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE);
|
||
// Returns TRUE if we counterspell a counter spell target, only does it
|
||
// if we have Dispels, and if set to want to be in a group, we are in one :-)
|
||
int AI_AttemptCounterSpell();
|
||
//***************************** AttemptSpell ***********************************
|
||
|
||
//***************************** AttemptAttack **********************************
|
||
|
||
// Used to attack. Called from AI_AttemptMeleeAttackWrapper
|
||
// We do this after we buff up with potions, and so on.
|
||
// 1 - Checks our global ranged/melee targets, range too, equips weapons.
|
||
// 2 - Checks for a feat to use (IE ranged, called shot ETC, else knockdown ETC).
|
||
// 3 - Else normal attack the target, if no feat.
|
||
int AI_EquipAndAttack();
|
||
// Used to attack. Called in determine conbat round.
|
||
// Wrappers the function AI_EquipAndAttack(), and therefore debug codes.
|
||
int AI_AttemptMeleeAttackWrapper();
|
||
// This will:
|
||
// - Get the nearest dying PC and kill them off.
|
||
// - Use the lowest HP person, and kill them off.
|
||
// - Also will check for anyone with the sleep effect, to try and Coup de Grace.
|
||
// Requires high Intelligence, And so on, of course.
|
||
int AI_AttemptGoForTheKill();
|
||
// We will, normally, use a breath weapon, and if we want to, wing buffet
|
||
// It does cast high level spells, using AttemptSpellAllSpells, with
|
||
// level 9 ones more often than level 5-7, and 8 are somewhat used :-)
|
||
int AI_AttemptDragonCombat();
|
||
// Either the chosen class is a dragon, or the appearance type is a dragon type,
|
||
// we return TRUE.
|
||
int AI_GetIsDragon();
|
||
|
||
// Beholders have special rays for eyes. This is used if the setting is set
|
||
// on spawn for beholders, or if the appearance is a beholder.
|
||
int AI_AttemptBeholderCombat();
|
||
// Beholder teleport attempt. Flees from combat.
|
||
int AI_ActionBeholderTeleport();
|
||
// Taken from x2_ai_mflayer.
|
||
// Bioware's implimenation of the Mind Suck.
|
||
void MindFlayerSuck(object oTarget);
|
||
// Illithid Use special attacks.
|
||
// This is set on spawn, or by the user on spawn.
|
||
int AI_AttemptMindflayerCombat();
|
||
//***************************** AttemptAttack **********************************
|
||
|
||
//****************************** AttemptFeat ***********************************
|
||
|
||
// GlobalUseTurning is set if we do have things we can turn (And not already!).
|
||
// It uses it here if so.
|
||
int AI_AttemptFeatTurning();
|
||
// This will use bard song, or damage with curse song!
|
||
int AI_AttemptFeatBardSong();
|
||
// Runs through summoning a familiar - uses the feat if it has it.
|
||
// INCLUDES companion as well! Will attack at range with a bow if possible.
|
||
int AI_AttemptFeatSummonFamiliar();
|
||
// This uses the best combat feat or spell it can, such as barbarian rage, divine power,
|
||
// bulls strength and so on. Spells may depend on melee enemy, or HD, or both, or range of enemy.
|
||
// Target is used for race's ect.
|
||
int AI_AttemptFeatCombatHostile();
|
||
//****************************** AttemptFeat ***********************************
|
||
|
||
//**************************** AttemptSpecial **********************************
|
||
|
||
// This will fire any aura's they have, quickened cast, and added to the top of
|
||
// thier action queue.
|
||
void AI_ActionAbilityAura();
|
||
// This will check if we do not have iSpellID's effects, have got iSpellID, and
|
||
// cheat-instant cast the spell if we do. This means that we can use it unlimited
|
||
// times (incase it gets dispelled!)
|
||
void AI_AttemptCastAuraSpell(int iSpellID);
|
||
// Runs though several basic checks. True, if any are performed
|
||
// 1 - Darkness. If so, dispel (inc. Ultravision) or move out (INT>=4)
|
||
// 2 - AOE spells. Move away from enemy if got effects (more so if no one near) (INT>=3)
|
||
// 3 - If invisible, need to move away from any combations. (INT>=6)
|
||
// - Returns TRUE if we did anything that would mean we don't want to do
|
||
// another action
|
||
int AI_AttemptSpecialChecks();
|
||
// This is a good check against the enemies (highest AC one) Damage against concentration
|
||
// Also, based on allies, enemies, and things, may move back (random chance, bigger with
|
||
// more bad things, like no stoneskins, no invisibility etc.)
|
||
// We will do this more at mid-intelligence, and better checks at most intelligence.
|
||
// * Not used on spells which require no requirement (EG: pulses ETC)
|
||
int AI_AttemptConcentrationCheck(object oTarget);
|
||
// This may make the archer retreat - if they are set to, and have a ranged weapon
|
||
// and don't have point blank shot, and has a nearby ally. (INT>=1 if set to does it more if higher).
|
||
int AI_AttemptArcherRetreat();
|
||
// Will polymorph Self if not already so. Will return TRUE if it casts best on self.
|
||
int AI_AttemptPolyMorph();
|
||
// This will cheat-cast iSpell at oTarget. Note that we will know if we have it
|
||
// by checking what appearance we have.
|
||
void AI_ActionCastShifterSpell(int iSpell, object oTarget = OBJECT_SELF);
|
||
//**************************** AttemptSpecial **********************************
|
||
|
||
//**************************** AttemptSkills ***********************************
|
||
|
||
// This will use empathy, taunt, and if set, pickpocketing. Most are random, and
|
||
// checks are made.Heal is done in healing functions.Done against best melee target,
|
||
// or closest seen/heard.
|
||
int AI_AttemptHostileSkills();
|
||
// Uses iSkill on GlobalMeleeTarget
|
||
// - Fired from AI_AttemptHostileSkills.
|
||
void AI_ActionUseSkillOnMeleeTarget(int iSkill);
|
||
//**************************** AttemptSkills ***********************************
|
||
|
||
//**************************** AttemptMorale ***********************************
|
||
|
||
// Fleeing - set OnSpawn for setup. Uses a WILL save!
|
||
// - Bonuses in groups
|
||
// - May go get help
|
||
// - Won't run on a CR/HD threshold.
|
||
// - Leaders help! And all is intelligence based.
|
||
// Includes commoners fleeing
|
||
int AI_AttemptMoraleFlee();
|
||
// Forces the AI to flee from the nearest enemy, or away from our position at least
|
||
void AI_ActionFleeScared();
|
||
//**************************** AttemptMorale ***********************************
|
||
|
||
//******************************** Other ***************************************
|
||
// Sets a value, 1-5, for what we can Dispel. Also sets a 1-5 value for breach spells.
|
||
// The values are NOT, I repeat NOT what the spell level are. Generally, they
|
||
// class spell-stopping spells as a higher prioritory to Dispel!
|
||
// * 5 - Dispeled before hostile spells are cast at target
|
||
// * 4 - Dispeled just before level 7 or so spells.
|
||
// * 3 - Dispeled just before level 4 or so spells
|
||
// * 2 - Dispeled just before level 2 or so spells.
|
||
// * 1 - Lowest prioritory - Dispeled at the end.
|
||
// There are NO cantrips included (level 0 spells).
|
||
// - Targets GlobalDispelTarget
|
||
void AI_SetDispelableEnchantments();
|
||
// Returns a dismissal target - a target with a master, and they
|
||
// are a Familiar, Animal companion or summon.
|
||
// - Nearest one in 10 M. Seen ones only.
|
||
object AI_GetDismissalTarget();
|
||
// This will run through most avalible protections spells.
|
||
// - TRUE if we cast any.
|
||
// Used when invisible to protect before we break the invisibility.
|
||
// - We may cast many on allies too
|
||
int AI_ActionCastWhenInvisible();
|
||
// This will loop seen allies in a cirtain distance, and get the first one without
|
||
// the spells effects of iSpell1 to iSpell4 (and do not have the spells).
|
||
// - Quite expensive loop. Only used if we have the spell (iSpellToUse1+)
|
||
// in the first place (no items!) and not the timer which stops it for a few rounds (on iSpellToUse1)
|
||
// - TRUE if it casts its any of iSpellToUseX's.
|
||
// * It has only a % chance to cast if GlobalWeAreBuffer is not TRUE.
|
||
int AI_ActionCastAllyBuffSpell(float fMaxRange, int iPercent, int iSpellToUse1, int iSpellToUse2 = -1, int iSpellToUse3 = -1, int iSpellToUse4 = -1, int iSpellOther1 = -1, int iSpellOther2 = -1);
|
||
// This will shout, maybe, some commands to allies. Or just command them!
|
||
void AI_ActionLeaderActions();
|
||
// Dispels or moves out of the oAOE.
|
||
// - If we have >= iMax AND <= iDamage HP...
|
||
// AND under iPercent total HP...
|
||
// - We Dispel, or move out of the AOE. Move if friendly.
|
||
// - Use local object set to iSpell, which should be the nearest of the spell.
|
||
int AI_ActionDispelAOE(int iSpell, int iDamageOnly, float fRange, int iDamage, int iMax, int iPercent);
|
||
// Casts the breach range of spells on GlobalDispelTarget. TRUE if any are cast.
|
||
int AI_ActionCastBreach();
|
||
// Casts the dispel range of spells on GlobalDispelTarget. TRUE if any are cast.
|
||
int AI_ActionCastDispel();
|
||
// Wrappers Premonition, Greater Stoneskin and Stoneskin.
|
||
// Includes Shades Stoneskin too. SPELL_SHADES_STONESKIN
|
||
// - iLowest - 8 = Prem, 6 = Greater, 4 = Stoneskin
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)
|
||
int AI_SpellWrapperPhisicalProtections(int iLowest = 1);
|
||
// Wrappers Energy Buffer, Protection From Elements, Resist Elements, Endure elements
|
||
// - iLowest - Goes 5, 3, 2, 1.
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections)
|
||
int AI_SpellWrapperElementalProtections(int iLowest = 1);
|
||
// Wrappers Haste and Mass Haste.
|
||
// - iLowest - 6 = Mass, 3 = Haste
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)
|
||
int AI_SpellWrapperHasteEnchantments(int iLowest = 1);
|
||
// Wrappers Shadow Shield, Ethereal Visage and Ghostly Visage.
|
||
// Includes Greater Shadow Conjuration Ghostly Visage (SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE
|
||
// - iLowest - 7 = Shadow, 6 = Ethereal 2 = Ghostly
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections)
|
||
int AI_SpellWrapperVisageProtections(int iLowest = 1);
|
||
// Wrappers All Mantals (Greater, Normal, Lesser) (Spell Mantals)
|
||
// - iLowest - 9 = Greater, 7 = Normal. 5 = Lesser
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections)
|
||
int AI_SpellWrapperMantalProtections(int iLowest = 1);
|
||
// Wrappers All Globes (Greater, Shadow Conjuration, Minor)
|
||
// - iLowest - 6 = Greater, 4 = Shadow/Minor
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasGlobeProtections)
|
||
int AI_SpellWrapperGlobeProtections(int iLowest = 1);
|
||
// Wrappers All Shields - Elemental Shield, Wounding Whispers
|
||
// - iLowest - 4 = Elemental, 3 = Wounding, Acid Sheath, 2 = Death Armor.
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalShieldSpell)
|
||
int AI_SpellWrapperShieldProtections(int iLowest = 1);
|
||
// Wrappers All Mind resistance spells - Mind blank, Lesser and Clarity. bioware likes 3's...
|
||
// - iLowest - 8 = Mind Blank, 5 = Lesser, 2 = Clarity
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections)
|
||
int AI_SpellWrapperMindResistanceProtections(int iLowest = 1);
|
||
// Wrappers All Consealment spells - Improved Invisiblity. Displacement.
|
||
// - iLowest - 4 = Improved Invisiblit, 3 = Displacement
|
||
// - Checks !AI_GetAIHaveEffect(GlobalEffectInvisible, oTarget) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oTarget)
|
||
int AI_SpellWrapperConsealmentEnhancements(object oTarget, int iLowest = 1);
|
||
// Shades darkness, assassin feat, normal spell.
|
||
int AI_SpellWrapperDarknessSpells(object oTarget);
|
||
// Invisibility spells + feats
|
||
int AI_SpellWrapperNormalInvisibilitySpells();
|
||
//******************************** Other ***************************************
|
||
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@ HEALING FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
|
||
//*************************** AttemptHealing ***********************************
|
||
// If our current HP is under the percent given, it will check things to heal itself, and use them.
|
||
// Used for animals ETC as well. They just don't use potions.
|
||
// This will also check levels, for appropriate healing. Set iMyHD to higher to use lower level spells.
|
||
int AI_AttemptHealingSelf();
|
||
// Raises dead, and casts healing spells to heal allies.
|
||
// Leaders are checked first.
|
||
int AI_AttemptHealingAlly();
|
||
// This will heal oTarget using the best spell it can, even ones it can
|
||
// spontaeously cast.
|
||
// - Just does best spell.
|
||
// - May use spontaneous ones as well. :-)
|
||
// - Called from AI_AttemptHealing_Self and AI_AttemptHealing_Ally
|
||
int AI_ActionHealObject(object oTarget);
|
||
// Heals oTarget using undead negative energy type spells
|
||
// More basic then the normal healing routines.
|
||
// TRUE if it casts anything.
|
||
int AI_ActionHealUndeadObject(object oTarget);
|
||
// Uses spells to cure conditions. Needs to be checked fully
|
||
// - Uses allies own integers to check thier effects
|
||
// - Loops spells (Best => worst) and if we havn't cast it in an amount of time
|
||
// we check effects (Us, leader, allies seen) until we find someone (nearest)
|
||
// who we can cast it on.
|
||
int AI_AttemptCureCondition();
|
||
// Get the nearest seen ally creature with the effect.
|
||
// - Checks us first
|
||
// - Then checks leader
|
||
// - Then loops seen allies within 20M.
|
||
// See: AI_AttemptCureCondition
|
||
object AI_GetNearestAllyWithEffect(int iEffectHex);
|
||
// Returns the nearest ally with iMin effects, and X more based on HD.
|
||
// - Checks us first. True if (HD/6) Effects and (iMin - 1) effects
|
||
// - Checks leader next. True if (HD/5) Effects and (iMin - 2) effects
|
||
// - Checks allies after. True if (HD/4) Effects and (iMin) effects
|
||
object AI_GetNearestAllyWithRestoreEffects(int iMin);
|
||
|
||
// This will return the best spontaeous healing spell, so:
|
||
// - It uses just normal GetHasSpell for the clerical healing spells.
|
||
// - It gets set up at the start to the global "GlobalBestSpontaeousHealingSpell"
|
||
int AI_GetBestSpontaeousHealingSpell();
|
||
|
||
//*************************** AttemptHealing ***********************************
|
||
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@ TARGETING & ACTUAL ACTION FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
// Actions are ActionX
|
||
|
||
//*************************** ActionsSpells ************************************
|
||
|
||
// Special case - it checks the talent again, in silence (if not already so) and
|
||
// uses the item, if it is an equal talent.
|
||
// - Will call a Shile Equip
|
||
// Returns TRUE if we did find the right talent, and it was cast.
|
||
int AI_ActionCastItemEqualTo(object oTarget, int iSpellID, int iLocation);
|
||
// This attempts to check the talent TALENT_CATEGORY_HARMFUL_RANGED, 2, which
|
||
// holds grenades. Easy checks.
|
||
// - TRUE if they fire a grenade.
|
||
int AI_AttemptGrenadeThrowing(object oTarget);
|
||
// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical):
|
||
// 1. If they have the spell normally
|
||
// 2. If they have an item with the spell.
|
||
// 3. If they have a potion of the right type.
|
||
// - We always attack with a bow at ranged, but may attack normally after the spell.
|
||
// - If nTalent is 0, we do not cast it unless iRequirement is also 0.
|
||
// - If iRequirement is 0, it is considered innate.
|
||
// - Imput iItemTalentValue and iPotionTalentValue to check item talents. -1 == No item.
|
||
int AI_ActionCastSpell(int iSpellID, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1);
|
||
// This is used for INFLICT/CURE spells, as GetHasSpell also can return 1+ for
|
||
// any extra castings - like if we had 2 light wounds and 2 blesses, it'd return
|
||
// 4.
|
||
// This, when cast, removes one of the spell being cast, after cheat casting it.
|
||
// - DecrementRemainingSpells will work this way quite well, but no choice in
|
||
// what to remove, it is faster then 1.3 beta which modified the spell scripts.
|
||
int AI_ActionCastSpontaeousSpell(int iSpellID, int nTalent, object oTarget);
|
||
// This will cast the shadow conjuration/protection version of the spell,
|
||
// via. Cheat Casting, and Decrement Spell Uses.
|
||
// - We always attack with a bow at ranged, but may attack normally after the spell.
|
||
// - If nTalent is 0, we do not cast it unless iRequirement is also 0.
|
||
// - If iRequirement is 0, it is considered innate.
|
||
// - Imput iItemTalentValue to check item talents.
|
||
int AI_ActionCastSubSpell(int iSubSpell, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1);
|
||
// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical):
|
||
// 0. If d100() is <= iRandom.
|
||
// 1. If they have the spell normally
|
||
// 2. If they have an item with the spell.
|
||
// 3. If they have a potion of the right type.
|
||
// - If we are at range from nearest enemy, we attack with a ranged weapon, else do nothing more.
|
||
// - If nTalent is -1, we do not check items.
|
||
// - Sets GlobalLastSpellValid to iSpellID if we can cast it, but don't randomly.
|
||
int AI_ActionCastSpellRandom(int iSpellID, int nTalent, int iRandom, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1);
|
||
// This will cast the spell/feat iThing, depending if it is a spell or a feat,
|
||
// at the summon location chosen before. Works similar to normal spells but
|
||
// at a special location.
|
||
// * If iRequirement is -1, it is a feat. If 0, it is innate (as spells)
|
||
int AI_ActionCastSummonSpell(int iThingID, int iRequirement = 0, int iSummonLevel = 0);
|
||
// This willcast iFirst -> iFirst + iAmount polymorph spell. It will check
|
||
// if we have iMaster (Either by feat or spell, depending on iFeat).
|
||
// TRUE if we polymorph.
|
||
// * Only decrements if iRemove is TRUE
|
||
int AI_ActionPolymorph(int iMaster, int iFirstSpell, int iAmount, int iFeat = FALSE, int iRemove = TRUE);
|
||
|
||
// Used with GlobalLastSpellValid. If GlobalLastSpellValid is 0, sets locals for
|
||
// use later, and sets GlobalLastSpellValid to the spell in question.
|
||
void AI_SetBackupCastingSpellValues(int iSpellID, int nTalent, object oTarget, int iLocation, int iRequirement, int iItemTalentValue, int iPotionTalentValue);
|
||
// This will used a stored GlobalLastSpellValid to see if it should cast that
|
||
// spell (or attempt to!) as a backup. Uses stored targets from when it did know
|
||
// it was valid.
|
||
int AI_ActionCastBackupRandomSpell();
|
||
|
||
// If they have the feat, they will use it on the target, and return TRUE
|
||
// * iFeat - Feat ID to use
|
||
// * oObject - object to use it on (Can't target locations in the AI - not worth it)
|
||
int AI_ActionUseFeatOnObject(int iFeat, object oObject = OBJECT_SELF);
|
||
// If they have nFeat, they cheat-cast nSpell at oTarget.
|
||
// - This is a workaround, as some epic spells, for some reason, won't work with
|
||
// a standard ActionUseFeatOnObject()! Dammit. Beta 3 addition.
|
||
int AI_ActionUseEpicSpell(int nFeat, int nSpell, object oTarget = OBJECT_SELF);
|
||
// Wrappers action Use Talent with debug string
|
||
void AI_ActionUseTalentDebug(talent tChosenTalent, object oTarget);
|
||
// Wrapper to check all dragon breaths, against oTarget
|
||
int AI_ActionDragonBreath(object oTarget, int iWingCounter);
|
||
// Uses tBreath if they are not immune
|
||
// - TRUE if used.
|
||
int AI_ActionUseBreath(object oTarget, talent tBreath, int iSpellID);
|
||
|
||
// Special: Apply Item Start
|
||
void AI_SpecialActionApplyItem();
|
||
// Special: Remove the effect (immobilize)
|
||
void AI_SpecialActionRemoveItem();
|
||
// Only TRUE if we have a flee object.
|
||
// - Uses sArray to debug and speak
|
||
int AI_ActionFlee(string sArray);
|
||
//*************************** ActionsSpells ************************************
|
||
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@ ALL INFO FUNCTIONS @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
//@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||
|
||
// Gets a item talent value, applies EffectCutsceneImmobilize then removes it.
|
||
// - iTalent, 1-21.
|
||
void AI_SetItemTalentValue(int iTalent);
|
||
// This checks if nTalent is one that will be checked for by items.
|
||
int AI_GetSpellCategoryHasItem(int nTalent);
|
||
|
||
// Equips the best shield we have.
|
||
// - Used before we cast a spell so casters can gain maximum AC.
|
||
void AI_EquipBestShield();
|
||
// Turns on/off all melee things (not stealth or search!) but not iMode.
|
||
void AI_SetMeleeMode(int iMode = -1);
|
||
|
||
// GETTING ENEMY INFO
|
||
|
||
// We set up targets to Global* variables, GlobalSpellTarget, GlobalRangeTarget,
|
||
// and GlobalMeleeTarget. Counts enemies, and so on.
|
||
// - Uses oIntruder (to attack or move near) if anything.
|
||
// - We return TRUE if it ActionAttack's, or moves to an enemy - basically
|
||
// that we cannot do an action, but shouldn't search. False if normal.
|
||
int AI_SetUpAllObjects(object oImputBackup);
|
||
// This sets up US, the user! :-)
|
||
// - Determines class to use, dragon or not.
|
||
// - And some other things that don't need to check allies/enemies for.
|
||
// - Intelligence and so on, global effects of us and so on ;-)
|
||
void AI_SetUpUs();
|
||
// Sets up all our effects, ones to heal as well.
|
||
void AI_SetUpUsEffects();
|
||
// Using the array ARRAY_ENEMY_RANGE, we return a % of seen/heard people who
|
||
// DO have any of the spells which see through the invisiblity spells.
|
||
// * iLimit - when we get to this number of people who have invisiblity, we stop and return 100%
|
||
// If ANY of the people are attacking us and have the effect, we return +30% for each.
|
||
int AI_GetSpellWhoCanSeeInvis(int iLimit);
|
||
|
||
// Returns the object to the specifications:
|
||
// * fRange - Within fRange (fTouchRange 2.25, fShortRange 8.0, fMediumRange 20.0, fLongRange = 40.0)
|
||
// * fSpread - Radius Size - RADIUS_SIZE_* constants (1.67 to 10.0M)
|
||
// * nLevel - Used for saving throws/globe checks. Level of the spell added to save checks
|
||
// * iSaveType = FALSE - SAVING_THROW_FORT/REFLEX/WILL. Not type, but the main save applied with it.
|
||
// * nShape = SHAPE_SPHERE - SHAPE_* constants.
|
||
// * nFriendlyFire = FALSE - Can this hurt allies? Best thing is to put
|
||
// GlobalFriendlyFireHostile - GetIsReactionTypeHostile(oTarget) == TRUE
|
||
// GlobalFriendlyFireFriendly - GetIsReactionTypeFriendly(oTarget) == FALSE
|
||
// FALSE - Cannot hurt allies (GetIsEnemy/!GetIsFriend used)
|
||
// * iDeathImmune = FALSE - Does it use a death save? (!GetIsImmune)
|
||
// * iNecromanticSpell = FALSE - Is it a necromancy spell? Undead are also checked in this.
|
||
object AI_GetBestAreaSpellTarget(float fRange, float fSpread, int nLevel, int iSaveType = FALSE, int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE, int iDeathImmune = FALSE, int iNecromanticSpell = FALSE);
|
||
|
||
// Returns the object to the specifications:
|
||
// Within nRange (float)
|
||
// The most targets around the creature in nRange, in nSpread.
|
||
// Can be the caster, of course
|
||
//object AI_GetBestFriendyAreaSpellTarget(float fRange, float fSpread, int nShape = SHAPE_SPHERE);
|
||
|
||
// Returns 1-4 for tiny-huge weapons. Used for disarm etc.
|
||
int AI_GetWeaponSize(object oItem);
|
||
// This returns TRUE if the target will always resist the spell given the parameters.
|
||
// - Uses GlobalOurChosenClassLevel for our level check.
|
||
int AI_SpellResistanceImmune(object oTarget);
|
||
// If the target will always save against iSaveType, and will take no damage, returns TRUE
|
||
// * Target is GlobalSpellTarget. Save is GlobalSpellTargetWill ETC.
|
||
// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE
|
||
// * iSpellLevel - Level of the spell being cast.
|
||
int AI_SaveImmuneSpellTarget(int iSaveType, int iSpellLevel);
|
||
// If the target will always save against iSaveType, and will take no damage, returns TRUE
|
||
// * oTarget - who saving against spell.
|
||
// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE
|
||
// * The save used is GetReflexSavingThrow* ETC.
|
||
// * iSpellLevel - Level of the spell being cast.
|
||
int AI_SaveImmuneAOE(object oTarget, int iSaveType, int iSpellLevel);
|
||
// Gets the percent, of X vs Y, iNumber/iTotal * 100 = %.
|
||
int AI_GetPercentOf(int iNumber, int iTotal);
|
||
// This returns a number, 1-4. This number is the levels
|
||
// of spell they will be totally immune to.
|
||
int AI_GetSpellLevelEffect(object oTarget);
|
||
// Imput oTarget and iLevel and it will check if they are automatically
|
||
// immune to the spell being cast.
|
||
int AI_GetSpellLevelEffectAOE(object oTarget, int iLevel = 9);
|
||
// Returns TRUE if any of the checks the oGroupTarget is immune to.
|
||
int AI_AOEDeathNecroCheck(object oGroupTarget, int iNecromanticSpell, int iDeathImmune);
|
||
// This will do 1 of two things, with the spell ID
|
||
// 1. If iHealAmount is FALSE, it will return what number (rank) in order, which is also used for level checking
|
||
// 2. If TRUE, it will return the average damage it will heal.
|
||
// iSelf - Used for the odd spells "cure other"'s
|
||
int AI_ReturnHealingInfo(int iSpellID, int iSelf = FALSE, int iHealAmount = FALSE);
|
||
// TRUE if the spell is one recorded as being cast before in time stop.
|
||
// - Checks Global "Are we in time stop?" and returns FALSE if not in time stop
|
||
int AI_CompareTimeStopStored(int nSpell, int nSpell2 = 0, int nSpell3 = 0, int nSpell4 = 0);
|
||
// Sets the spell to be stored in our time stop array.
|
||
void AI_SetTimeStopStored(int nSpell);
|
||
// Deletes all time stopped stored numbers.
|
||
void AI_DeleteTimeStopStored();
|
||
// Sets the Global Hex for the enemy spell target immunties.
|
||
// 7+ Intelligence also uses GetIsImmune.
|
||
// The target is GlobalSpellTarget.
|
||
// - Uses effects loop and GetIsImmune to comprehend the most.
|
||
void AI_SortSpellImmunities();
|
||
// This will, in most occasion, ClearAllActions.
|
||
// If it does NOT, it returns FALSE, if we are doing something more important,
|
||
// and we perform that action again (or carry on doing it).
|
||
// - This also sets shadowdancer hiding if no one has trueseeing nearby
|
||
int AI_StopWhatWeAreDoing();
|
||
// Turn of hiding if turn of timer is on, and turn of searching if it is
|
||
// active.
|
||
// This should be called before any action using a feat, spell or similar, if
|
||
// we need to move.
|
||
void AI_ActionTurnOffHiding();
|
||
|
||
// Simple return TRUE if it matches hex GlobalTargetImmunitiesHex
|
||
int AI_GetSpellTargetImmunity(int iImmunityHex);
|
||
// Sets iImmunityHex to GlobalTargetImmunitiesHex.
|
||
void AI_SetSpellTargetImmunity(int iImmunityHex);
|
||
|
||
// This returns an object, not seen not heard ally, who we
|
||
// might flee to. Uses a loop, and runs only when we are going to flee for sure.
|
||
object AI_GetNearbyFleeObject();
|
||
// This returns the best primary or secondary weapon from sArray.
|
||
// It actually returns the number value, IE the place it is at. Easy to get all info from there.
|
||
// - Use iType...
|
||
// - 0 = Highest Value :-)
|
||
// - 1 = Highest Damage
|
||
// - 2 = Biggest
|
||
// - 3 = Smallest
|
||
int AI_GetWeaponArray(string sArray, object oCannotBe = OBJECT_INVALID, int iType = 0);
|
||
// Just sorts out sOriginalArrayName to sNewArrayName based on range only.
|
||
// - Also sets maxint to MAXINT_ + sNewArrayName
|
||
// - Closest to futhest
|
||
void AI_TargetingArrayDistanceStore(string sOriginalArrayName, string sNewArrayName);
|
||
// Just sorts out sOriginalArrayName to sNewArrayName based on iType.
|
||
// iType 1 = AC, iType 2 = Total Saves, iType 3 = Phisical Protections,
|
||
// iType 4 = BAB, iType 5 = Hit Dice, iType 6 = Percent HP, iType 7 = Current HP,
|
||
// iType 8 = Maximum HP. 9 = Attacking us or not. 10 = Is a PC.
|
||
void AI_TargetingArrayIntegerStore(int iType, string sOriginalArrayName);
|
||
// This sets ARRAY_TEMP_ARRAY of integer values to sNewArrayName.
|
||
// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER.
|
||
// - We work until iMinimum is filled, or we get to iMinimum and we get to
|
||
// a target with value > iImputMinimum. (20 - 25 > X?)
|
||
// Returns the amount of targets set in sNewArrayName.
|
||
int AI_TargetingArrayLimitTargets(string sNewArrayName, int iTypeOfTarget, int iImputMinLimit, int iMinLoop, int iMaxLoop);
|
||
// This sets ARRAY_TEMP_ARRAY of float values to sNewArrayName.
|
||
// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER.
|
||
// - We work until iMinimum is filled, or we get to iMinimum and we get to
|
||
// a target with value > iImputMinimum. (20.0 - 25.0 > X?)
|
||
// Returns the amount of targets set in sNewArrayName.
|
||
int AI_TargetingArrayLimitTargetsFloat(string sNewArrayName, int iTypeOfTarget, float fImputMinLimit, int iMinLoop, int iMaxLoop);
|
||
// Deletes all FLoats, Integers and Objects set to sArray for valid
|
||
// objects got by GetLocalObject to sArray.
|
||
void AI_TargetingArrayDelete(string sArray);
|
||
// Makes sure oTarget isn't:
|
||
// - Dead
|
||
// - Petrified
|
||
// - AI Ignore ON
|
||
// - DM
|
||
// Must be: Seen or heard
|
||
// Returns: TRUE if any of these are true.
|
||
int AI_GetTargetSanityCheck(object oTarget);
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_SetSpellTargetImmunity, AI_GetSpellTargetImmunity
|
||
//::///////////////////////////////////////////////
|
||
Immunity settings for spells.
|
||
//:://///////////////////////////////////////////*/
|
||
|
||
// Simple return TRUE if it matches hex.
|
||
// - Uses GlobalSpellTarget for target object
|
||
int AI_GetSpellTargetImmunity(int iImmunityHex)
|
||
{
|
||
return (GlobalTargetImmunitiesHex & iImmunityHex);
|
||
}
|
||
// Sets iImmunityHex to GlobalTargetImmunitiesHex.
|
||
void AI_SetSpellTargetImmunity(int iImmunityHex)
|
||
{
|
||
GlobalTargetImmunitiesHex = GlobalTargetImmunitiesHex | iImmunityHex;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_SetUpUsEffects
|
||
//::///////////////////////////////////////////////
|
||
Sets up the effects. Used before the uncommandable, so we
|
||
know if we are or not! (EG stun)
|
||
//:://///////////////////////////////////////////*/
|
||
void AI_SetUpUsEffects()
|
||
{
|
||
// SetLocal to stop other AI casters ETC from setting effect
|
||
SetLocalInt(OBJECT_SELF, AI_JASPERRES_EFFECT_SET, TRUE);
|
||
// Set locals and whatever on self.
|
||
AI_SetEffectsOnTarget();
|
||
// Set global time stop
|
||
GlobalInTimeStop = AI_GetAIHaveEffect(GlobalEffectTimestop);
|
||
}
|
||
// Gets the percent, of X vs Y, iNumber/iTotal * 100 = %.
|
||
int AI_GetPercentOf(int iNumber, int iTotal)
|
||
{
|
||
return FloatToInt((IntToFloat(iNumber)/IntToFloat(iTotal)) * i100);
|
||
}
|
||
// Special: Apply EffectCutsceneImmobilize
|
||
void AI_SpecialActionApplyItem()
|
||
{
|
||
ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneImmobilize(), OBJECT_SELF);
|
||
}
|
||
// Special: Remove EffectCutsceneImmobilize
|
||
void AI_SpecialActionRemoveItem()
|
||
{
|
||
effect eCheck = GetFirstEffect(OBJECT_SELF);
|
||
while(GetIsEffectValid(eCheck))
|
||
{
|
||
if(GetEffectType(eCheck) == EFFECT_TYPE_CUTSCENEIMMOBILIZE &&
|
||
GetEffectSpellId(eCheck) == iM1) RemoveEffect(OBJECT_SELF, eCheck);
|
||
eCheck = GetNextEffect(OBJECT_SELF);
|
||
}
|
||
}
|
||
// Gets a item talent value, applies EffectCutsceneImmobilize then removes it.
|
||
// - iTalent, 1-21.
|
||
void AI_SetItemTalentValue(int iTalent)
|
||
{
|
||
// Apply EffectCutsceneImmobilize
|
||
AI_SpecialActionApplyItem();
|
||
|
||
// Simply get the best.
|
||
talent tCheck = GetCreatureTalentBest(iTalent, i20);
|
||
int iValue = GetIdFromTalent(tCheck);
|
||
|
||
// Set to constant
|
||
SetAIConstant(ITEM_TALENT_VALUE + IntToString(iTalent), iValue);
|
||
|
||
// Remove EffectCutsceneImmobilize
|
||
AI_SpecialActionRemoveItem();
|
||
}
|
||
// This checks if nTalent is one that will be checked for by items.
|
||
int AI_GetSpellCategoryHasItem(int nTalent)
|
||
{
|
||
// 1, 2, 3, - , 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
|
||
if(nTalent == i4 || nTalent == i5 || nTalent >= i16 || nTalent < i1)
|
||
{
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_SetTimeStopStored
|
||
//::///////////////////////////////////////////////
|
||
Sets the spell to be stored in our time stop array.
|
||
//:://///////////////////////////////////////////*/
|
||
void AI_SetTimeStopStored(int nSpell)
|
||
{
|
||
// Time stop check
|
||
if(!GlobalInTimeStop) return;
|
||
if(GlobalTimeStopArraySize < i0)
|
||
{
|
||
GlobalTimeStopArraySize = i1;// Size is now 1
|
||
SetAIConstant(TIME_STOP_LAST_ + IntToString(i1), nSpell);
|
||
SetAIInteger(TIME_STOP_LAST_ARRAY_SIZE, GlobalTimeStopArraySize);
|
||
}
|
||
else if(GlobalTimeStopArraySize == i0)
|
||
{
|
||
GlobalTimeStopArraySize = GetAIInteger(TIME_STOP_LAST_ARRAY_SIZE);
|
||
GlobalTimeStopArraySize++;
|
||
SetAIConstant(TIME_STOP_LAST_ + IntToString(GlobalTimeStopArraySize), nSpell);
|
||
SetAIInteger(TIME_STOP_LAST_ARRAY_SIZE, GlobalTimeStopArraySize);
|
||
}
|
||
else // Is over 0
|
||
{
|
||
GlobalTimeStopArraySize++;
|
||
SetAIConstant(TIME_STOP_LAST_ + IntToString(GlobalTimeStopArraySize), nSpell);
|
||
SetAIInteger(TIME_STOP_LAST_ARRAY_SIZE, GlobalTimeStopArraySize);
|
||
}
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: DeleteTimeStopStored
|
||
//::///////////////////////////////////////////////
|
||
Deletes all time stopped stored numbers.
|
||
//:://///////////////////////////////////////////*/
|
||
void AI_DeleteTimeStopStored()
|
||
{
|
||
GlobalTimeStopArraySize = GetAIInteger(TIME_STOP_LAST_ARRAY_SIZE);
|
||
if(GlobalTimeStopArraySize)
|
||
{
|
||
int iCnt;
|
||
for(iCnt = i1; iCnt <= GlobalTimeStopArraySize; iCnt++)
|
||
{
|
||
DeleteAIConstant(TIME_STOP_LAST_ + IntToString(iCnt));
|
||
}
|
||
}
|
||
DeleteAIInteger(TIME_STOP_LAST_ARRAY_SIZE);
|
||
GlobalTimeStopArraySize = iM1;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: GetWeaponSize
|
||
//::///////////////////////////////////////////////
|
||
Returns 1-4 for tiny-huge weapons. Used for disarm etc.
|
||
//::////////////////////////////////////////////*/
|
||
int AI_GetWeaponSize(object oItem)
|
||
{
|
||
// Ignore invalid weapons
|
||
if(!GetIsObjectValid(oItem)) return FALSE;
|
||
|
||
// Returns shields as 0
|
||
switch(GetBaseItemType(oItem))
|
||
{
|
||
// Tiny
|
||
// 22, 42, 59.
|
||
case BASE_ITEM_DAGGER:
|
||
case BASE_ITEM_KUKRI:
|
||
case BASE_ITEM_SHURIKEN:
|
||
return i1;// WEAPON_SIZE_TINY
|
||
break;
|
||
// Small
|
||
// 0, 7, 9, 14, 31, 37, 38, 40, 60, 61, 63
|
||
case BASE_ITEM_SHORTSWORD:
|
||
case BASE_ITEM_LIGHTCROSSBOW:
|
||
case BASE_ITEM_LIGHTMACE:
|
||
// case BASE_ITEM_SMALLSHIELD:
|
||
case BASE_ITEM_DART:
|
||
case BASE_ITEM_LIGHTHAMMER:
|
||
case BASE_ITEM_HANDAXE:
|
||
case BASE_ITEM_KAMA:
|
||
case BASE_ITEM_SICKLE:
|
||
case BASE_ITEM_SLING:
|
||
case BASE_ITEM_THROWINGAXE:
|
||
case BASE_ITEM_WHIP: // Hordes
|
||
return i2;// WEAPON_SIZE_SMALL
|
||
break;
|
||
// Medium
|
||
// 1, 2, 3, 4, 5, 6, 11, 28, 41, 47, 51, 53, 56
|
||
// 1-6 =
|
||
// BASE_ITEM_LONGSWORD, BASE_ITEM_BATTLEAXE, BASE_ITEM_BASTARDSWORD
|
||
// BASE_ITEM_LIGHTFLAIL, BASE_ITEM_WARHAMMER, BASE_ITEM_HEAVYCROSSBOW
|
||
case BASE_ITEM_LONGSWORD:
|
||
case BASE_ITEM_BATTLEAXE:
|
||
case BASE_ITEM_BASTARDSWORD:
|
||
case BASE_ITEM_LIGHTFLAIL:
|
||
case BASE_ITEM_WARHAMMER:
|
||
case BASE_ITEM_HEAVYCROSSBOW:
|
||
case BASE_ITEM_SHORTBOW:
|
||
case BASE_ITEM_CLUB:
|
||
case BASE_ITEM_KATANA:
|
||
case BASE_ITEM_MORNINGSTAR:
|
||
case BASE_ITEM_RAPIER:
|
||
case BASE_ITEM_SCIMITAR:
|
||
// case BASE_ITEM_LARGESHIELD:
|
||
case BASE_ITEM_DWARVENWARAXE: // Hordes
|
||
return i3;// WEAPON_SIZE_MEDIUM;
|
||
break;
|
||
// Large weapons
|
||
// 8, 10, 12, 13, 18, 32, 33, 35, 45, 50, 55, 57, 58
|
||
case BASE_ITEM_LONGBOW:
|
||
case BASE_ITEM_HALBERD:
|
||
case BASE_ITEM_TWOBLADEDSWORD:
|
||
case BASE_ITEM_GREATSWORD:
|
||
case BASE_ITEM_GREATAXE:
|
||
case BASE_ITEM_DIREMACE:
|
||
case BASE_ITEM_DOUBLEAXE:
|
||
case BASE_ITEM_HEAVYFLAIL:
|
||
case BASE_ITEM_MAGICSTAFF:
|
||
case BASE_ITEM_QUARTERSTAFF:
|
||
case BASE_ITEM_SCYTHE:
|
||
// case BASE_ITEM_TOWERSHIELD:
|
||
case BASE_ITEM_SHORTSPEAR:
|
||
return i4;// WEAPON_SIZE_LARGE;
|
||
break;
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name AI_GetWeaponArray
|
||
//::///////////////////////////////////////////////
|
||
This returns the best primary or secondary weapon from sArray.
|
||
It actually returns the number value, IE the place it is at. Easy to get all info from there.
|
||
- Use iType...
|
||
- 0 = Highest Value :-)
|
||
- 1 = Highest Damage
|
||
- 2 = Biggest
|
||
- 3 = Smallest
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_GetWeaponArray(string sArray, object oCannotBe = OBJECT_INVALID, int iType = 0)
|
||
{
|
||
int iMax = GetLocalInt(OBJECT_SELF, MAXINT_ + sArray);
|
||
int i, iReturn, iLastValue, iCurrentValue;
|
||
string sCurrentName;
|
||
object oCurrentWeapon;
|
||
if(iMax)
|
||
{
|
||
for(i = i1; i <= iMax; i++) // uses: "break;" to break early.
|
||
{
|
||
sCurrentName = sArray + IntToString(i);
|
||
oCurrentWeapon = GetLocalObject(OBJECT_SELF, sCurrentName);// Object
|
||
if(GetIsObjectValid(oCurrentWeapon) &&
|
||
oCurrentWeapon != oCannotBe)
|
||
{
|
||
// Highest value
|
||
if(iType == i0)
|
||
{
|
||
iReturn = i;// It is highest to lowest value anyway
|
||
break;
|
||
}
|
||
// 1 = Highest Damage
|
||
else if(iType == i1)
|
||
{
|
||
iCurrentValue = GetLocalInt(OBJECT_SELF, sCurrentName + WEAP_DAMAGE);
|
||
// > because if only equal, one was higher value
|
||
if(iCurrentValue > iLastValue)
|
||
{
|
||
iLastValue = iCurrentValue;
|
||
iReturn = i;
|
||
}
|
||
}
|
||
// 2 = Biggest
|
||
// 3 = Smallest
|
||
else
|
||
{
|
||
iCurrentValue = GetLocalInt(OBJECT_SELF, sCurrentName + WEAP_SIZE);
|
||
if(iType == i2)
|
||
{
|
||
if(iCurrentValue > iLastValue)// > because if only equal, one was higher value
|
||
{
|
||
iLastValue = iCurrentValue;
|
||
iReturn = i;
|
||
}
|
||
}
|
||
else // if(iType == i3)
|
||
{
|
||
if(iCurrentValue < iLastValue)// > because if only equal, one was higher value
|
||
{
|
||
iLastValue = iCurrentValue;
|
||
iReturn = i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return iReturn;
|
||
}
|
||
// Equips the best shield we have.
|
||
// - Used before we cast a spell so casters can gain maximum AC.
|
||
void AI_EquipBestShield()
|
||
{
|
||
object oShield = GetAIObject(AI_WEAPON_SHIELD);
|
||
if(GetIsObjectValid(oShield) &&
|
||
GetItemPossessor(oShield) == OBJECT_SELF &&
|
||
GlobalLeftHandWeapon != oShield)
|
||
{
|
||
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
|
||
// Turns on/off all melee things (not stealth or search!) but not iMode.
|
||
void AI_SetMeleeMode(int iMode = -1)
|
||
{
|
||
if(iMode != iM1)
|
||
{
|
||
if(!GetActionMode(OBJECT_SELF, iMode))
|
||
{
|
||
SetActionMode(OBJECT_SELF, iMode, TRUE);
|
||
}
|
||
}
|
||
// Turn off all the rest
|
||
int iCnt;
|
||
// 0 = stealth, 1 = stealth (ignore these 2), 3 = parry. 11 = DF.
|
||
for(iCnt = ACTION_MODE_PARRY; iCnt <= ACTION_MODE_DIRTY_FIGHTING; iCnt++)
|
||
{
|
||
if(iCnt != iMode)
|
||
{
|
||
if(GetActionMode(OBJECT_SELF, iCnt))
|
||
{
|
||
SetActionMode(OBJECT_SELF, iCnt, FALSE);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Turn of hiding if turn of timer is on, and turn of searching if it is
|
||
// active.
|
||
// This should be called before any action using a feat, spell or similar, if
|
||
// we need to move.
|
||
void AI_ActionTurnOffHiding()
|
||
{
|
||
// Turn of searching and hiding here, if we want to!
|
||
if(!GetHasFeat(FEAT_KEEN_SENSE) && GetDetectMode(OBJECT_SELF) == DETECT_MODE_ACTIVE)
|
||
{
|
||
SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, FALSE);
|
||
}
|
||
// Turn of hiding if we have been seen lately.
|
||
if(GetLocalTimer(AI_TIMER_TURN_OFF_HIDE) &&
|
||
GetStealthMode(OBJECT_SELF) == STEALTH_MODE_ACTIVATED)
|
||
{
|
||
SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE);
|
||
}
|
||
}
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name AI_EquipAndAttack
|
||
//::///////////////////////////////////////////////
|
||
This was a bioware script. It has been changed a lot.
|
||
Best target if normal int (equal or more than 2).
|
||
|
||
Will play a random attack taunt, sometimes.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_EquipAndAttack()
|
||
{
|
||
// Taunt the enemy!
|
||
if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_PLAYING_VOICE_CHAT, AI_OTHER_MASTER)
|
||
&& d100() < i7)
|
||
{
|
||
int iVoice = VOICE_CHAT_ATTACK;// Default
|
||
switch(Random(i6)) // Random will do 0-5, so more chance of ATTACK
|
||
{
|
||
case i0: iVoice = VOICE_CHAT_LAUGH; break;
|
||
case i1: iVoice = VOICE_CHAT_BATTLECRY1; break;
|
||
case i2: iVoice = VOICE_CHAT_BATTLECRY2; break;
|
||
case i3: iVoice = VOICE_CHAT_BATTLECRY3; break;
|
||
default: iVoice = VOICE_CHAT_ATTACK; break;// Default
|
||
}
|
||
// Random delay for 0.0 to 1.0 seconds.
|
||
float fDelay = IntToFloat(Random(10) + 1) / 10.0;
|
||
DelayCommand(fDelay, PlayVoiceChat(iVoice));
|
||
}
|
||
|
||
// - Flying
|
||
if(GlobalRangeToMeleeTarget > f8 &&
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_FLYING, AI_COMBAT_MASTER))
|
||
{
|
||
SetAIObject(AI_FLYING_TARGET, GlobalMeleeTarget);
|
||
ExecuteScript(FILE_FLY_ATTACK, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
|
||
// Set up the range to use weapons at, before moving into HTH
|
||
// Default is 5.0 (+ Some for creature size) with no changes...
|
||
float fRange = f5;
|
||
// Might have some pre-set range OnSpawn
|
||
int iRangeFromSetup = GetAIInteger(AI_RANGED_WEAPON_RANGE);
|
||
if(iRangeFromSetup >= i1)
|
||
{
|
||
fRange = IntToFloat(iRangeFromSetup);
|
||
}
|
||
// If our intelligence is enough, we make it 3.0.
|
||
else if(GlobalIntelligence >= i5)
|
||
{
|
||
fRange = f3;
|
||
}
|
||
// We add a base of X for monster sizes
|
||
fRange += (IntToFloat(GlobalOurSize)/f4);
|
||
|
||
// iRangedAttack = TRUE, then equip ranged weapons
|
||
int iRangedAttack = FALSE;
|
||
|
||
// Now check for it...
|
||
// If range to melee target is OVER fRange...
|
||
// - Or setting to always use bow
|
||
if(GlobalRangeToMeleeTarget > fRange ||
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER))
|
||
{
|
||
iRangedAttack = TRUE;
|
||
}
|
||
|
||
// Special check for AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND.
|
||
// Either a % chance, or that they have no enemy in X distance, and we are
|
||
// in Y distance.
|
||
// We always run in at 8 or less M too.
|
||
if(iRangedAttack == TRUE && GetSpawnInCondition(AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND, AI_COMBAT_MASTER))
|
||
{
|
||
// If they are under 8M away, always run in - 80% chance
|
||
if(GlobalRangeToMeleeTarget < f8 && d10() <= i8)
|
||
{
|
||
iRangedAttack = FALSE;
|
||
}
|
||
else
|
||
{
|
||
// Get distance from melee target to nearest ally to it.
|
||
float fAllyToMelee = GetDistanceBetween(GlobalMeleeTarget, GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, GlobalMeleeTarget, i1, CREATURE_TYPE_IS_ALIVE, TRUE));
|
||
|
||
// Check thier range to ours
|
||
// - Basically, if GlobalRangeToMeleeTarget - fAllyToMelee, is
|
||
// a difference of Random(4) + 4;, we move in.
|
||
// + 60% chance!
|
||
if((GlobalRangeToMeleeTarget - fAllyToMelee) <= (IntToFloat(Random(i4) + i4)) &&
|
||
d10() <= i6)
|
||
{
|
||
iRangedAttack = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Declare everything
|
||
object oEquipped, oMainWeapon, oMainPossessor, oPrimary, oSecondary, oTwohanded;
|
||
int iPickUpDisarmed, iRanged, iShield, iValidAmmo, iStrength, iPrimaryNum,
|
||
iSecondaryNum, iValidPrimary, iValidSecondary, iValidTwoHanded, iPrimaryDamage,
|
||
iEquippedShield, iShieldInSlotAlready, iEquippedMostDamaging,
|
||
// Done in melee attacking, if we want more AC - IE expertise.
|
||
iNeedMoreAC;
|
||
float fRangeToWeapon;
|
||
|
||
iShieldInSlotAlready = GetBaseItemType(GlobalLeftHandWeapon);
|
||
|
||
// Here...we determine wether to try for melee or ranged attacks.
|
||
// This is TRUE if we did ActionEquipMostDamaging...
|
||
iEquippedMostDamaging = FALSE;
|
||
|
||
object oShield = GetAIObject(AI_WEAPON_SHIELD);
|
||
int iValidShield = GetIsObjectValid(oShield);
|
||
// SPECIAL: We will just use default calls FIRST and stop IF we have this set
|
||
// same checks to choose between them though :-P
|
||
if(AI_GetAIHaveEffect(GlobalEffectPolymorph) ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_LAG_EQUIP_MOST_DAMAGING, AI_OTHER_MASTER))
|
||
{
|
||
// 1: "[DCR:Melee] Most Damaging Weapon. Target: " + GetName(GlobalMeleeTarget)
|
||
DebugActionSpeakByInt(1, GlobalMeleeTarget);
|
||
// iRangedAttack = 1 then ranged.
|
||
if(iRangedAttack)
|
||
{
|
||
ActionEquipMostDamagingRanged(GlobalMeleeTarget);
|
||
}
|
||
// Special near-range always attack with a bow option.
|
||
else if(GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER))
|
||
{
|
||
ActionEquipMostDamagingRanged(GlobalMeleeTarget);
|
||
}
|
||
// Spcial - if we are set to always move back, 1/10 times we don't equip HTH.
|
||
// BUT we will attack in HTH if the last target was this target.
|
||
else if(GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER)
|
||
&& d10() != i1)
|
||
{
|
||
ActionEquipMostDamagingRanged(GlobalMeleeTarget);
|
||
}
|
||
// Else we should always be in HTH range.
|
||
else // if(!iRangedAttack)
|
||
{
|
||
ActionEquipMostDamagingMelee(GlobalMeleeTarget, TRUE);
|
||
}
|
||
// Always do...
|
||
iEquippedMostDamaging = TRUE;
|
||
}
|
||
// Else normal weapons, IE we check stored ones :-P
|
||
else if(!AI_GetAIHaveEffect(GlobalEffectPolymorph))
|
||
{
|
||
// Declaring
|
||
// Toggle: Do we pick up any disarmed weapons?
|
||
iPickUpDisarmed = GetSpawnInCondition(AI_FLAG_COMBAT_PICK_UP_DISARMED_WEAPONS, AI_COMBAT_MASTER);
|
||
// We may, if at the right range (and got a ranged weapon) equip it.
|
||
oMainWeapon = GetAIObject(AI_WEAPON_RANGED);
|
||
oMainPossessor = GetItemPossessor(oMainWeapon);
|
||
// iRanged = TRUE if we equip a ranged weapon after checks.
|
||
// iShield = TRUE if we should equip a shield
|
||
// Is the ranged weapon valid? And iRangedAttack == TRUE
|
||
// We need valid ammo, else we eqip nothing!
|
||
iValidAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT);
|
||
if(iValidAmmo == INVENTORY_SLOT_RIGHTHAND)
|
||
{
|
||
iValidAmmo = TRUE;
|
||
}
|
||
else
|
||
{
|
||
iValidAmmo = GetIsObjectValid(GetItemInSlot(iValidAmmo));
|
||
}
|
||
// Ranged attack...valid?
|
||
if(iRangedAttack && iValidAmmo && GetIsObjectValid(oMainWeapon))
|
||
{
|
||
// Is the possessor valid? (could be us or someone else)
|
||
if(GetIsObjectValid(oMainPossessor))
|
||
{
|
||
// Check if it is us or not...
|
||
if(oMainPossessor == OBJECT_SELF)
|
||
{
|
||
if(GlobalRightHandWeapon != oMainWeapon)
|
||
{
|
||
// Ranged weapons then equipped in the righthand :-D
|
||
ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
oEquipped = oMainWeapon;
|
||
iRanged = TRUE;// only ranged feats.
|
||
}
|
||
// Else, we delete it and check for secondary one :-)
|
||
else if(GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED_2)))
|
||
{
|
||
// If valid, we re-set weapons! Because we cannot pick up the other.
|
||
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
|
||
// As it happens immediantly (yes, more lag, gee) we check for the original :-)
|
||
oMainWeapon = GetAIObject(AI_WEAPON_RANGED);
|
||
// If it is now valid, equip it!
|
||
if(GetIsObjectValid(oMainWeapon))
|
||
{
|
||
ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND);
|
||
oEquipped = oMainWeapon;
|
||
iRanged = TRUE;// only ranged feats.
|
||
}
|
||
}
|
||
else // Else, delete the ranged thing, someone else has it.
|
||
{
|
||
DeleteAIObject(AI_WEAPON_RANGED);
|
||
}
|
||
}
|
||
// Else, it is on the ground. If near, we pick it up.
|
||
else if(iPickUpDisarmed)
|
||
{
|
||
fRangeToWeapon = GetDistanceToObject(oMainWeapon);
|
||
if((fRangeToWeapon < f5 && fRangeToWeapon > f0)||
|
||
!GlobalMeleeAttackers)
|
||
{
|
||
// We should attempt to pick it up...
|
||
ActionPickUpItem(oMainWeapon);
|
||
ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND);
|
||
oEquipped = oMainWeapon;
|
||
iRanged = TRUE;// only ranged feats.
|
||
}
|
||
else // Else, we can't or won't get it.
|
||
{
|
||
DeleteAIObject(AI_WEAPON_RANGED);
|
||
if(GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED_2)))
|
||
{
|
||
// If valid, we re-set weapons! Because we cannot pick up the other.
|
||
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
|
||
// As it happens immediantly (yes, more lag, gee) we check for the original :-)
|
||
oMainWeapon = GetAIObject(AI_WEAPON_RANGED);
|
||
// If it is now valid, equip it!
|
||
if(GetIsObjectValid(oMainWeapon))
|
||
{
|
||
ActionEquipItem(oMainWeapon, INVENTORY_SLOT_RIGHTHAND);
|
||
oEquipped = oMainWeapon;
|
||
iRanged = TRUE;// only ranged feats.
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// This is set to the items AC value, to take away simulating taking it off when
|
||
// we check AC against other things... :-)
|
||
if(iShieldInSlotAlready == BASE_ITEM_TOWERSHIELD ||
|
||
iShieldInSlotAlready == BASE_ITEM_SMALLSHIELD ||
|
||
iShieldInSlotAlready == BASE_ITEM_LARGESHIELD)
|
||
{
|
||
iShieldInSlotAlready = GetItemACValue(GlobalLeftHandWeapon);
|
||
}
|
||
// NOTE: shields cannot be disarmed! If we don't have it, though, we act as if it isnt valid
|
||
if(!iValidShield || GetItemPossessor(oShield) != OBJECT_SELF)
|
||
{
|
||
// If the second is valid, we re-set weapons.
|
||
if(GetIsObjectValid(GetAIObject(AI_WEAPON_SHIELD_2)))
|
||
{
|
||
// If valid, we re-set weapons! Because we cannot pick up the other.
|
||
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
|
||
}
|
||
// iValidShield is 1 if valid, and now on us.
|
||
oShield = GetAIObject(AI_WEAPON_SHIELD);
|
||
iValidShield = GetIsObjectValid(oShield);
|
||
}
|
||
// iEquippedShield is TRUE if we equip a shield.
|
||
// If something has been equipped, we arm a shield...here
|
||
if(iRanged)
|
||
{
|
||
// Need a vlaid shield AND able to equip one.
|
||
if(iValidShield && GetAIInteger(AI_WEAPON_RANGED_SHIELD))
|
||
{
|
||
if(oShield != GetItemInSlot(INVENTORY_SLOT_LEFTHAND))
|
||
{
|
||
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
iEquippedShield = TRUE;
|
||
}
|
||
}
|
||
else // Else, equip the best combo of HTH weapons!
|
||
{
|
||
// We run through. Of course, first, is it best for
|
||
// What do we have?
|
||
// Get things...
|
||
// First: Primary. If that is valid, we check secondary. These are arrays.
|
||
iPrimaryNum = AI_GetWeaponArray(AI_WEAPON_PRIMARY);
|
||
if(iPrimaryNum)
|
||
{
|
||
oPrimary = GetLocalObject(OBJECT_SELF, AI_WEAPON_PRIMARY + IntToString(iPrimaryNum));
|
||
iValidPrimary = GetIsObjectValid(oPrimary);
|
||
if(iValidPrimary)
|
||
{
|
||
// We want the smallest...
|
||
iSecondaryNum = AI_GetWeaponArray(AI_WEAPON_SECONDARY, oPrimary, i3);
|
||
if(iSecondaryNum)
|
||
{
|
||
oSecondary = GetLocalObject(OBJECT_SELF, AI_WEAPON_SECONDARY + IntToString(iSecondaryNum));
|
||
iValidSecondary = GetIsObjectValid(oSecondary);
|
||
}
|
||
}
|
||
}
|
||
// 2 handed
|
||
oTwohanded = GetAIObject(AI_WEAPON_TWO_HANDED);
|
||
iValidTwoHanded = GetIsObjectValid(oTwohanded);
|
||
// Shield is done above.
|
||
// Now, are any valid?
|
||
if(!iValidPrimary && !iValidTwoHanded)
|
||
{
|
||
ActionEquipMostDamagingMelee(GlobalMeleeTarget, TRUE);
|
||
iEquippedMostDamaging = TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Now, the circumstances...what is best? Lots of damage and lower AC?
|
||
// Maybe more AC? Maybe a second weapon? (as if any are valid, its obvious better to use one).
|
||
// Globals:
|
||
// GlobalOurAC, GlobalOurHitDice, GlobalOurBaseAttackBonus, GlobalMeleeAttackers
|
||
// GlobalAverageEnemyHD, GlobalAverageEnemyBAB
|
||
iStrength = GetAbilityModifier(ABILITY_STRENGTH);
|
||
// Check one: Is a lot more AC better? Or keep shield equipped, even?
|
||
// 1. Is our AC below 2x our HD? (IE under 20 at level 10)
|
||
if((GlobalOurAC < GlobalOurHitDice * i2) ||
|
||
// 2. Our AC with no shield is under average melee BAB + 6 (a badish roll)
|
||
(GlobalOurAC - iShieldInSlotAlready < GlobalAverageEnemyBAB + i6 &&
|
||
GlobalAverageEnemyHD >= GlobalOurHitDice - i5) ||
|
||
// 3. Melee attackers are great, over 1/4th of our HD + 2, and enemy HD is comparable toughness
|
||
(GlobalMeleeAttackers > ((GlobalOurHitDice / i4) + i2) &&
|
||
GlobalAverageEnemyHD >= GlobalOurHitDice - i3))
|
||
{
|
||
// We need more AC!
|
||
iNeedMoreAC = TRUE;
|
||
// Valid primary, for the shield...
|
||
if(iValidPrimary)
|
||
{
|
||
oEquipped = oPrimary;// May change though...
|
||
// If we have a shield (which is the point) we equip any not in slots.
|
||
if(iValidShield)
|
||
{
|
||
iEquippedShield = TRUE;
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
if(GlobalLeftHandWeapon != oShield)
|
||
{
|
||
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
else if(iValidTwoHanded)
|
||
{
|
||
oEquipped = oTwohanded;
|
||
if(GlobalRightHandWeapon != oTwohanded)
|
||
{
|
||
ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
else if(iValidSecondary)
|
||
{
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
if(GlobalLeftHandWeapon != oSecondary)
|
||
{
|
||
ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
}
|
||
else if(iValidTwoHanded)
|
||
{
|
||
oEquipped = oTwohanded;
|
||
if(GlobalRightHandWeapon != oTwohanded)
|
||
{
|
||
ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
}
|
||
// Check two: If we have a good hitting chance, we might equip a 2handed first.
|
||
// 1. We have a decent strength mod, which adds damage to many 2 handed.
|
||
// This is AND things...
|
||
else if((iStrength >= (GlobalOurHitDice / i4 + i2)) &&
|
||
// 2. Greater strength then dexterity
|
||
(iStrength > GetAbilityModifier(ABILITY_DEXTERITY) + Random(i3)))
|
||
{
|
||
if(iValidTwoHanded)
|
||
{
|
||
oEquipped = oTwohanded;
|
||
if(GlobalRightHandWeapon != oTwohanded)
|
||
{
|
||
ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
else if(iValidPrimary)
|
||
{
|
||
oEquipped = oPrimary;
|
||
// Secondary first, rather then shield.
|
||
if(iValidSecondary)
|
||
{
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
if(GlobalLeftHandWeapon != oSecondary)
|
||
{
|
||
ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
else if(iValidShield)
|
||
{
|
||
iEquippedShield = TRUE;
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
if(GlobalLeftHandWeapon != oShield)
|
||
{
|
||
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
else if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
}
|
||
// Check three: None. We equip 2 weapons, then a shield, then a 2 handed, that order.
|
||
// In essense, for 2 weapons, it is less damage more times, as it were (if we hit!)
|
||
else
|
||
{
|
||
if(iValidPrimary)
|
||
{
|
||
oEquipped = oPrimary;
|
||
// Secondary first, rather then shield.
|
||
if(iValidSecondary)
|
||
{
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
if(GlobalLeftHandWeapon != oSecondary)
|
||
{
|
||
ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
else if(iValidShield)
|
||
{
|
||
iEquippedShield = TRUE;
|
||
if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
if(GlobalLeftHandWeapon != oShield)
|
||
{
|
||
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
else if(GlobalRightHandWeapon != oPrimary)
|
||
{
|
||
ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
else if(iValidTwoHanded)
|
||
{
|
||
oEquipped = oTwohanded;
|
||
if(GlobalRightHandWeapon != oTwohanded)
|
||
{
|
||
ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
}
|
||
}
|
||
}// End no valid
|
||
}
|
||
}// End "lag buster" check.
|
||
|
||
// We check for weapon effective here, if we didn't equip most damaging
|
||
// - We only do this if we are attacking the target for more then 1 round.
|
||
if(!iEquippedMostDamaging && !iRangedAttack &&
|
||
GetAIInteger(AI_MELEE_TURNS_ATTACKING) >= i2)
|
||
{
|
||
// If neither weapon can damage the target...and no most damaging...
|
||
if(!GetWeaponRanged(oEquipped) &&
|
||
GetIsObjectValid(oEquipped) &&
|
||
!GetIsWeaponEffective(GlobalMeleeTarget))
|
||
{
|
||
// 2: "[DCR:Melee] Most Damaging as Not Effective"
|
||
DebugActionSpeakByInt(2);
|
||
// We equip a shield (if not already)
|
||
if(!iEquippedShield && iValidShield)
|
||
{
|
||
if(oShield != GetItemInSlot(INVENTORY_SLOT_LEFTHAND))
|
||
{
|
||
ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND);
|
||
}
|
||
}
|
||
// And equip most damaging (melee)
|
||
ActionEquipMostDamagingMelee(GlobalMeleeTarget);
|
||
}
|
||
}
|
||
// Now, we should have equipped something :-D
|
||
// GlobalLeftHandWeapon, GlobalRightHandWeapon
|
||
// GlobalOurSize, GlobalOurBaseAttackBonus, GlobalMeleeTargetAC, GlobalMeleeTargetBAB
|
||
|
||
// We randomly hit, determined by our intelligence.
|
||
// If we have higher intelligence, we take our rolls to be higher, so we use feats more.
|
||
int iOurHit;
|
||
// add a base value...so 18 for Int. 10. 0 for Int. 1.
|
||
iOurHit = GlobalOurBaseAttackBonus + ((GlobalIntelligence * i2) - i2);
|
||
// Randomise...should never get results over 20 now. (0-20 for INT 1, 0-2 for INT 10)
|
||
iOurHit += Random(i20 - ((GlobalIntelligence - i1) * i2));
|
||
|
||
// 1.3 - Add Dexterity to ranged feat checking, strength to melee
|
||
|
||
// Note:
|
||
// - 8+ Intelligence also checks DISCIPLINE.
|
||
|
||
/* Ability: Strength.
|
||
Requires Training: No.
|
||
Classes: All.
|
||
|
||
A successful check allows the character to resist any combat feat
|
||
(Disarm, Called Shot, or Knockdown).n).
|
||
|
||
Check: The DC is equal to the attacker's attack roll.
|
||
Use: Automatic. */
|
||
|
||
// We therefore make ValidFeat mean FALSE if the target has too much
|
||
// disipline (by a margin)
|
||
if(GlobalIntelligence >= i8)
|
||
{
|
||
// If thier rank - 20 is over our BAB + XX, then no go, as it is very
|
||
// likely they'll pass the check.
|
||
if(GetSkillRank(SKILL_DISCIPLINE) - i20 >= iOurHit)
|
||
{
|
||
// No feats
|
||
ValidFeats = FALSE;
|
||
}
|
||
}
|
||
|
||
// Note: If we can only hit on a 20, and have 7+ Intelligence, we make it
|
||
// use ANY feat (as they will have the same chance of hitting - a critical -
|
||
// as a normal attack)
|
||
if(GlobalOurBaseAttackBonus + i20 <= GlobalMeleeTargetAC)
|
||
{
|
||
iOurHit = GlobalOurBaseAttackBonus + i100;// Massive amount - we act as if we rolled 100!
|
||
}
|
||
// We turn off hiding/searching as we will at least do ActionAttack...
|
||
AI_ActionTurnOffHiding();
|
||
|
||
// Ranged weapon?
|
||
if(iRanged && ValidFeats && !AI_GetAIHaveEffect(GlobalEffectPolymorph))
|
||
{
|
||
// RANGED: Add dexterity
|
||
iOurHit += GetAbilityModifier(ABILITY_DEXTERITY);
|
||
|
||
// We see if it is a weapon which we can use power attack with >:-D
|
||
if(GetBaseItemType(oEquipped) == BASE_ITEM_THROWINGAXE)
|
||
{
|
||
if((iOurHit - i5) >= GlobalMeleeTargetAC)// Power attack
|
||
{
|
||
if((iOurHit - i10) >= GlobalMeleeTargetAC)// Improved power attack
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_IMPROVED_POWER_ATTACK);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_IMPROVED_POWER_ATTACK;
|
||
}
|
||
AI_SetMeleeMode(ACTION_MODE_POWER_ATTACK);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_POWER_ATTACK;
|
||
}
|
||
}
|
||
// Rapid shot - This provides another attack, at -2 to hit.
|
||
if((iOurHit - i2) >= GlobalMeleeTargetAC && GetHasFeat(FEAT_RAPID_SHOT))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_RAPID_SHOT);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_RAPID_SHOT;
|
||
}
|
||
// Called shot is -4, but some good things...does it work though? Uncommented till sure
|
||
if(((iOurHit - i4) >= GlobalMeleeTargetAC) &&
|
||
GetHasFeat(FEAT_CALLED_SHOT) &&
|
||
!GetHasFeatEffect(FEAT_CALLED_SHOT, GlobalMeleeTarget))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_CALLED_SHOT, GlobalMeleeTarget);
|
||
return FEAT_CALLED_SHOT;
|
||
}
|
||
}
|
||
// Parry the enemy, if we have only 1 target attacking us, and have
|
||
// decent skill.
|
||
else if(!iRanged && !GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PARRYING, AI_OTHER_COMBAT_MASTER) &&
|
||
// 1 attacker, and the melee target is attacking us.
|
||
GlobalMeleeAttackers <= i1 && GetAttackTarget(GlobalMeleeTarget) == OBJECT_SELF &&
|
||
// Not got a ranged weapon - enemy target that is
|
||
!GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, GlobalMeleeTarget)) &&
|
||
// Got skill rank of at least our hit dice + 4.
|
||
((GetSkillRank(SKILL_PARRY) >= (GlobalOurHitDice + i4)) ||
|
||
// Or forced
|
||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PARRYING, AI_OTHER_COMBAT_MASTER)))
|
||
{
|
||
// Set parry mode
|
||
AI_SetMeleeMode(ACTION_MODE_PARRY);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return AI_PARRY_ATTACK;
|
||
}
|
||
else if(!iRanged)
|
||
{
|
||
// Death attack - if we are hidden, or we have invisiblity effects
|
||
// - A basic "seen or heard" check, backwards (slightly cheating!)
|
||
if(GetHasFeat(FEAT_PRESTIGE_DEATH_ATTACK_1) &&
|
||
(!GetObjectSeen(OBJECT_SELF, GlobalMeleeTarget) ||
|
||
!GetObjectHeard(OBJECT_SELF, GlobalMeleeTarget)))
|
||
{
|
||
// This doesn't seem to be "useable" and is meant to be automatic.
|
||
// However, using something that decreases our attack will be silly
|
||
// so we will just ActionAttack the target.
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_PRESTIGE_DEATH_ATTACK_1;
|
||
}
|
||
// Check for defensive stance
|
||
if(GlobalRangeToMeleeTarget < f3 &&
|
||
GetLastAttackMode() != COMBAT_MODE_DEFENSIVE_STANCE)
|
||
{
|
||
// Use defensive stance on self (Hopefully these checks won't override each other)
|
||
AI_ActionUseFeatOnObject(FEAT_DWARVEN_DEFENDER_DEFENSIVE_STANCE);
|
||
// Note - fall through and carry on
|
||
}
|
||
// Added check here for valid feats, because of defensive stance.
|
||
if(!ValidFeats)
|
||
{
|
||
// If we have not used any, well...oh well! Attack!!
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return AI_NORMAL_MELEE_ATTACK;
|
||
}
|
||
// MELEE
|
||
// - Add strength
|
||
iOurHit += GetAbilityModifier(ABILITY_STRENGTH);
|
||
|
||
// More things to declare.
|
||
int iTargetWeaponSize, iTargetCreatureSize, iTargetCreatureRace, iOurWeaponSize,
|
||
iCanUseMonks, iTargetAlignment, iAddingModifier, iMonkLevels;
|
||
// Monk levels
|
||
iMonkLevels = GetLevelByClass(CLASS_TYPE_MONK);
|
||
if(!iMonkLevels) iMonkLevels = i1;
|
||
iTargetCreatureRace = GetRacialType(GlobalMeleeTarget);//done later.
|
||
iTargetAlignment = GetAlignmentGoodEvil(GlobalMeleeTarget);
|
||
// Now, monk, can it be done...
|
||
if((!GetIsObjectValid(oEquipped) ||
|
||
GetBaseItemType(oEquipped) == BASE_ITEM_KAMA) &&
|
||
iTargetCreatureRace != RACIAL_TYPE_CONSTRUCT &&
|
||
iTargetCreatureRace != RACIAL_TYPE_UNDEAD)
|
||
{
|
||
iCanUseMonks = TRUE;
|
||
}
|
||
// Smite good, or evil!
|
||
if(iTargetAlignment != ALIGNMENT_NEUTRAL)
|
||
{
|
||
// For use against them evil pests! Top - one use only anyway.
|
||
if(iTargetAlignment == ALIGNMENT_EVIL && GetHasFeat(FEAT_SMITE_EVIL))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_SMITE_EVIL, GlobalMeleeTarget);
|
||
return FEAT_SMITE_EVIL;
|
||
}
|
||
// For use against them evil pests! Top - one use only anyway.
|
||
else if(iTargetAlignment == ALIGNMENT_GOOD && GetHasFeat(FEAT_SMITE_GOOD))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_SMITE_GOOD, GlobalMeleeTarget);
|
||
return FEAT_SMITE_GOOD;
|
||
}
|
||
}
|
||
// Ki damage :-) max roll of damage for weapons for Weapon Master - Hordes
|
||
// - Note that this a lot of uses. Test for usefulness!
|
||
if(GetHasFeat(FEAT_KI_DAMAGE))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_KI_DAMAGE, GlobalMeleeTarget);
|
||
return FEAT_KI_DAMAGE;
|
||
}
|
||
// We may use Expertiese if we are being attacked, and above we tried
|
||
// to equip a shield...
|
||
// - Need more help around us (some allies) and people atually attacking us, or low HP.
|
||
if((GlobalOurPercentHP <= i20) ||
|
||
(iNeedMoreAC && GlobalMeleeAttackers && GlobalTotalAllies >= i2))
|
||
{
|
||
// +10 AC, -10 BAB
|
||
if(GetHasFeat(FEAT_IMPROVED_EXPERTISE))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE);
|
||
return FEAT_IMPROVED_EXPERTISE;
|
||
}
|
||
else if(GetHasFeat(FEAT_EXPERTISE))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_EXPERTISE);
|
||
return FEAT_EXPERTISE;
|
||
}
|
||
}
|
||
// First, we have the little-known-about monk powerful feats...
|
||
// Instant death, what can be better? >:-D
|
||
if(iCanUseMonks && GetHasFeat(FEAT_QUIVERING_PALM) &&
|
||
iOurHit + i5 >= GlobalMeleeTargetAC &&
|
||
GlobalOurHitDice >= GetHitDice(GlobalMeleeTarget))
|
||
{
|
||
// Ok, not too random. Thier roll is not d20 + fort save, it is random(15) + 5.
|
||
// - Our hit is 10 + Monk class/2 + Wisdom...
|
||
if((i10 + (iMonkLevels / i2) + GetAbilityModifier(ABILITY_WISDOM)) >=
|
||
// - This must be over thier Fortitude save, add 5 and 0-15.
|
||
(GetFortitudeSavingThrow(GlobalMeleeTarget) + i5 + Random(i15)))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_QUIVERING_PALM, GlobalMeleeTarget);
|
||
return FEAT_QUIVERING_PALM;
|
||
}
|
||
}
|
||
// We see if we want to use Whirlwind attack!
|
||
// - Check for amount of melee attackers. If 5 or more, use this.
|
||
// - We may use it later at 2 or more. :-)
|
||
// If we don't have 5 or more, we use some of the better single target
|
||
// feats before. This requires no BAB check - it is done at max BAB
|
||
if(// 90% chance of using it with 6+ melee attackers, and 8+ enemies in 4M
|
||
(d10() <= i9 && (GlobalEnemiesIn4Meters >= i8 ||
|
||
GlobalMeleeAttackers >= i6)) ||
|
||
// OR, 70% chance of using if we'll get more attacks if we used
|
||
// whirlwind then if we used
|
||
(d10() <= i7 && (GlobalOurBaseAttackBonus/i5 < (GlobalEnemiesIn4Meters - i1))) ||
|
||
// Lastly 40% chance if we have 4+ melee, or 5+ in range
|
||
(d10() <= i4 && (GlobalEnemiesIn4Meters >= i5 ||
|
||
GlobalMeleeAttackers >= i4)))
|
||
{
|
||
// - Free attack to all in 10'! This should be anyone in 6.6M or so.
|
||
if(GetHasFeat(FEAT_IMPROVED_WHIRLWIND))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_IMPROVED_WHIRLWIND, OBJECT_SELF);
|
||
// And attack after (as it doesn't seem to take a round to use)
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_IMPROVED_WHIRLWIND;
|
||
}
|
||
// All in 5' is still alright. 5 Feet = 3.3M or so.
|
||
else if(GetHasFeat(FEAT_WHIRLWIND_ATTACK))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_WHIRLWIND_ATTACK, OBJECT_SELF);
|
||
// And attack after (as it doesn't seem to take a round to use)
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_WHIRLWIND_ATTACK;
|
||
}
|
||
}
|
||
// Sap can be used by anyone, any weapon, I think...
|
||
// Almost Auto stun, great stuff. Help From toolset:
|
||
/* Sap
|
||
Type of Feat: General
|
||
Prerequisite: Base Attack Bonus +1, Called Shot.
|
||
Required for: Stunning Fist.
|
||
Specifics: A character with this feat is able to make a special stun
|
||
attack in melee. He makes an attack roll with a -4 penalty, and if the hit
|
||
successfully deals damage the defender must make a Discipline check with a
|
||
DC equal to the attacker's attack roll. If the defender fails, he or she is
|
||
dazed for 12 seconds.
|
||
Use: Selected. */
|
||
if(!GetHasFeatEffect(FEAT_SAP, GlobalMeleeTarget) &&
|
||
GetHasFeat(FEAT_SAP) &&
|
||
iOurHit - i4 >= GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_SAP, GlobalMeleeTarget);
|
||
return FEAT_SAP;
|
||
}
|
||
// Knockdown is great! One of the best ever!(and VERY, VERY overpowered)
|
||
if(!GetHasFeatEffect(FEAT_KNOCKDOWN, GlobalMeleeTarget) &&
|
||
!GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN, GlobalMeleeTarget))
|
||
{
|
||
// These return 1-5, based on size.
|
||
iTargetCreatureSize = GetCreatureSize(GlobalMeleeTarget);
|
||
// By far the BEST feat to use - knocking them down lets you freely attack them!
|
||
if(GetHasFeat(FEAT_IMPROVED_KNOCKDOWN))
|
||
{
|
||
// Imporved affects like if we were 1 size larger - thus we can affect 2 sizes bigger targets
|
||
if((GlobalOurSize + i2) >= iTargetCreatureSize)
|
||
{
|
||
// Modifier, adds anything from -4 to 0 to 4.
|
||
// Test: Us, size 3, them size 1. 3 - 1 = 2. 2 * 4 = +8 to hit.
|
||
iAddingModifier = (GlobalOurSize - iTargetCreatureSize) * i4;
|
||
// We are 1 size bigger, so its evens (we add 4 onto -4)
|
||
if(iAddingModifier + iOurHit >= GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_IMPROVED_KNOCKDOWN, GlobalMeleeTarget);
|
||
return FEAT_IMPROVED_KNOCKDOWN;
|
||
}
|
||
}
|
||
}
|
||
// Knockdown, we can hit on 1 size above or smaller.
|
||
else if(GetHasFeat(FEAT_KNOCKDOWN))
|
||
{
|
||
// Only works on our size, above 1, or smaller.
|
||
if((GlobalOurSize + i1) >= iTargetCreatureSize)
|
||
{
|
||
// Same as above, but we always take 4 more.
|
||
iAddingModifier = ((GlobalOurSize - iTargetCreatureSize) * i4) - (i4);
|
||
// Calculate
|
||
if(iAddingModifier + iOurHit >= GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_KNOCKDOWN, GlobalMeleeTarget);
|
||
return FEAT_KNOCKDOWN;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Define sizes of weapons, ETC.
|
||
// Check if they are disarmable.
|
||
if(GetIsCreatureDisarmable(GlobalMeleeTarget))
|
||
{
|
||
iOurWeaponSize = AI_GetWeaponSize(oEquipped);
|
||
iTargetWeaponSize = AI_GetWeaponSize(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, GlobalMeleeTarget));
|
||
// No AOO, and only a -4 penalty to hit.
|
||
if(GetHasFeat(FEAT_IMPROVED_DISARM))
|
||
{
|
||
// We need to have valid sizes, so no odd weapons or shields to attack with...
|
||
if(iOurWeaponSize && iTargetWeaponSize)// Are both != 0?
|
||
{
|
||
// Apply weapon size penalites/bonuses to check - Use right weapons.
|
||
// We times it by 4.
|
||
// Test: Us, size 3, them size 1. (3 - 1 = 2) then (2 * 4 = 8) So +8 to hit.
|
||
iAddingModifier = (iOurWeaponSize - iTargetWeaponSize) * i4;
|
||
if((iAddingModifier + iOurHit - i4) >= GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_IMPROVED_DISARM, GlobalMeleeTarget);
|
||
return FEAT_IMPROVED_DISARM;
|
||
}
|
||
}
|
||
}
|
||
// Provokes an AOO. Improved does not, but this is -6,
|
||
// and bonuses depend on weapons used (sizes)
|
||
else if(GetHasFeat(FEAT_DISARM))
|
||
{
|
||
// We need to have valid sizes, so no odd weapons or shields to attack with...
|
||
if(iOurWeaponSize && iTargetWeaponSize)// Are both != 0?
|
||
{
|
||
// Apply weapon size penalites/bonuses to check - Use left weapons.
|
||
iAddingModifier = (iOurWeaponSize - iTargetWeaponSize) * i4;
|
||
// We take 6 and then 1 per melee attacker (AOOs)
|
||
if((iAddingModifier + iOurHit - i6 - GlobalMeleeAttackers) >= GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_DISARM, GlobalMeleeTarget);
|
||
return FEAT_DISARM;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Next, stunning fist.
|
||
// Stuns the target, making them unable to move. -4 attack.
|
||
// DC (fort) of 10 + HD/2 + wis mod.
|
||
if(iCanUseMonks &&
|
||
!GetHasFeatEffect(FEAT_STUNNING_FIST, GlobalMeleeTarget) &&
|
||
GetHasFeat(FEAT_STUNNING_FIST))
|
||
{
|
||
// Start adding modifier at 0.
|
||
iAddingModifier = i0;
|
||
// If not a monk, its -4 to hit. Monk levels defaults to 1 if 0
|
||
if(iMonkLevels == TRUE) iAddingModifier - i4;
|
||
// We hit ETC.
|
||
// Save is above
|
||
if(iOurHit >= GlobalMeleeTargetAC &&
|
||
// Save
|
||
(i10 + (GlobalOurHitDice / i2) + GetAbilityModifier(ABILITY_WISDOM)
|
||
>= GetFortitudeSavingThrow(GlobalMeleeTarget) + i5 + Random(i15)))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_STUNNING_FIST, GlobalMeleeTarget);
|
||
return FEAT_STUNNING_FIST;
|
||
}
|
||
}
|
||
// We see if we want to use Whirlwind attack!
|
||
// - Check for amount of melee attackers. If 5 or more, use this.
|
||
// - We may use it later at 2 or more. :-)
|
||
// If we don't have 5 or more, we use some of the better single target
|
||
// feats before. This requires no BAB check - it is done at max BAB
|
||
if(GlobalEnemiesIn4Meters >= i2)
|
||
{
|
||
// - Free attack to all in 10'! This should be anyone in 6.6M or so.
|
||
if(GetHasFeat(FEAT_IMPROVED_WHIRLWIND))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_IMPROVED_WHIRLWIND, OBJECT_SELF);
|
||
return FEAT_IMPROVED_WHIRLWIND;
|
||
}
|
||
// All in 5' is still alright. 5 Feet = 3.3M or so.
|
||
else if(GetHasFeat(FEAT_WHIRLWIND_ATTACK))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_WHIRLWIND_ATTACK, OBJECT_SELF);
|
||
return FEAT_WHIRLWIND_ATTACK;
|
||
}
|
||
}
|
||
// Next, flurry of blows...
|
||
if(iCanUseMonks &&
|
||
iOurHit - i2 >= GlobalMeleeTargetAC &&
|
||
GetHasFeat(FEAT_FLURRY_OF_BLOWS))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_FLURRY_OF_BLOWS);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_FLURRY_OF_BLOWS;
|
||
}
|
||
// Expertise, special case: If we have a high BAB verus thier AC.
|
||
// Only basic for now...
|
||
if(GetHasFeat(FEAT_IMPROVED_EXPERTISE))
|
||
{
|
||
// Our hit is over thier AC, and thier BAB hits us all the time...
|
||
// OR when there are 3+
|
||
if((iOurHit >= GlobalMeleeTargetAC &&
|
||
GlobalMeleeTargetBAB + i5 >= GlobalOurAC) || // Add 5 to thier hit
|
||
GlobalMeleeAttackers >= i3)
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_IMPROVED_EXPERTISE;
|
||
}
|
||
}
|
||
if(GetHasFeat(FEAT_EXPERTISE))
|
||
{
|
||
// Expertise, we may use even if we can hit only sometimes, and
|
||
// they always hit up sometiems (50% time)
|
||
// OR when there are 1 + 1/2HD attackers.
|
||
if((iOurHit >= GlobalMeleeTargetAC &&
|
||
GlobalMeleeTargetBAB + i10 >= GlobalOurAC) || // Add 10 to thier hit
|
||
GlobalMeleeAttackers >= i2)
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_EXPERTISE);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_EXPERTISE;
|
||
}
|
||
}
|
||
// At a -2 to hit, this can disarm the arms or legs...speed or attack bonus
|
||
if((iOurHit - i4) >= GlobalMeleeTargetAC &&
|
||
GetHasFeat(FEAT_CALLED_SHOT) &&
|
||
!GetHasFeatEffect(FEAT_CALLED_SHOT, GlobalMeleeTarget))
|
||
{
|
||
AI_SetMeleeMode();
|
||
ActionUseFeat(FEAT_CALLED_SHOT, GlobalMeleeTarget);
|
||
return FEAT_CALLED_SHOT;
|
||
}
|
||
// -10 to hit, for +10 damage. Good, I guess, in some circumstances.
|
||
// Uses base attack bonus, no additions.
|
||
if(GetHasFeat(FEAT_IMPROVED_POWER_ATTACK) &&
|
||
GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_IMPROVED_POWER_ATTACK);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_IMPROVED_POWER_ATTACK;
|
||
}
|
||
// is a -5 to hit. Uses random 5, to randomise a bit,
|
||
// I guess. Still means a massive BAB will use it.
|
||
// Uses base attack bonus, no additions.
|
||
if( GetHasFeat(FEAT_POWER_ATTACK) &&
|
||
((GlobalOurBaseAttackBonus + Random(i5)) >= GlobalMeleeTargetAC))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_POWER_ATTACK);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_POWER_ATTACK;
|
||
}
|
||
// Either: Bad chance to hit, or only one attack, we use this for 1d4 more damage
|
||
if(GetHasFeat(FEAT_DIRTY_FIGHTING) &&
|
||
GlobalOurBaseAttackBonus / i5 < i1 ||
|
||
GlobalOurBaseAttackBonus + i15 < GlobalMeleeTargetAC)
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_DIRTY_FIGHTING);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return FEAT_DIRTY_FIGHTING;
|
||
}
|
||
}
|
||
|
||
// If we have not used any, well...oh well! Attack!!
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return AI_NORMAL_MELEE_ATTACK;
|
||
}
|
||
// Wrapper for AI_AttemptAttack_MeleeAttack
|
||
// Includes debug string
|
||
int AI_AttemptMeleeAttackWrapper()
|
||
{
|
||
// Errors
|
||
// We will exit if no valid melee target/dead
|
||
if(!GetIsObjectValid(GlobalMeleeTarget) || GetIsDead(GlobalMeleeTarget))
|
||
{
|
||
// 3: "[DCR:Melee] Melee Code. No valid melee target/Dead. Exiting"
|
||
DebugActionSpeakByInt(3);
|
||
return FALSE;
|
||
}
|
||
int iFeat = AI_EquipAndAttack();
|
||
// 4: "[DCR:Melee] Melee attack. [Target] " + GetName(GlobalMeleeTarget) + " [Feat/Attack] " + IntToString(iFeat)
|
||
DebugActionSpeakByInt(4, GlobalMeleeTarget, iFeat);
|
||
return iFeat;
|
||
}
|
||
// Used with GlobalLastSpellValid. If GlobalLastSpellValid is 0, sets locals for
|
||
// use later, and sets GlobalLastSpellValid to the spell in question.
|
||
void AI_SetBackupCastingSpellValues(int iSpellID, int nTalent, object oTarget, int iLocation, int iRequirement, int iItemTalentValue, int iPotionTalentValue)
|
||
{
|
||
if(GlobalLastSpellValid <= FALSE)
|
||
{
|
||
// Set last spell
|
||
GlobalLastSpellValid = iSpellID;
|
||
// Set values using 1 name, + a number
|
||
int iCnt = i1;
|
||
// SET...
|
||
// talent
|
||
SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), nTalent);
|
||
// target
|
||
iCnt++;
|
||
SetAIObject(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), oTarget);
|
||
// location
|
||
iCnt++;
|
||
SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iLocation);
|
||
// Requirements
|
||
iCnt++;
|
||
SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iRequirement);
|
||
// item
|
||
iCnt++;
|
||
SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iItemTalentValue);
|
||
// potion
|
||
iCnt++;
|
||
SetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt), iPotionTalentValue);
|
||
}
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Spell casting functions
|
||
//::///////////////////////////////////////////////
|
||
Spell casting functions.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created by : Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name AttemptSpecialConcentrationCheck
|
||
//::///////////////////////////////////////////////
|
||
This is a good check against the enemies (highest AC one) Damage against concentration
|
||
Also, based on allies, enemies, and things, may move back (random chance, bigger with
|
||
more bad things, like no stoneskins, no invisibility etc.)
|
||
We will do this more at mid-intelligence, and better checks at most intelligence.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
|
||
int AI_AttemptConcentrationCheck(object oTarget)
|
||
{
|
||
// Total off check
|
||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_CONCENTRATION, AI_OTHER_MASTER) ||
|
||
// Or has no moving back needed.
|
||
GetHasFeat(FEAT_EPIC_IMPROVED_COMBAT_CASTING) ||
|
||
// Or has all spells quickened!
|
||
GetHasFeat(FEAT_EPIC_AUTOMATIC_QUICKEN_3) ||
|
||
// - Ignore 1, but not 3 or 2.
|
||
GetHasFeat(FEAT_EPIC_AUTOMATIC_QUICKEN_2))
|
||
{
|
||
return FALSE;
|
||
}
|
||
// Jump out if we use defensive casting!
|
||
if(GetHasSkill(SKILL_CONCENTRATION) &&
|
||
// If we have 15 + 9 skill (for a level 9 spell) so we'll never fail, we
|
||
// always turn it on.
|
||
((GetSkillRank(SKILL_CONCENTRATION) >= i24) ||
|
||
// Else we'll turn it on based on our class level. Over class level
|
||
(((GetSkillRank(SKILL_CONCENTRATION) >= GlobalOurHitDice + i6) ||
|
||
// Else, we'll turn it on if we have many melee attackers - the damage from
|
||
// them will be pretty high otherwise. (ONLY if they are a comparable level!)
|
||
((GlobalMeleeAttackers >= i4 && GlobalAverageEnemyBAB + i15 >= GlobalOurAC) ||
|
||
(GlobalMeleeAttackers >= i7))))))
|
||
{
|
||
// Turn it on
|
||
// 5: "[DCR:Caster] Defensive Casting Mode ON [Enemy] " + GetName(GlobalSpellTarget)
|
||
DebugActionSpeakByInt(5, GlobalSpellTarget);
|
||
AI_SetMeleeMode(ACTION_MODE_DEFENSIVE_CAST);
|
||
return FALSE;
|
||
}
|
||
else if(GetDefensiveCastingMode(OBJECT_SELF) == DEFENSIVE_CASTING_MODE_ACTIVATED)
|
||
{
|
||
// Turn it off
|
||
SetActionMode(OBJECT_SELF, ACTION_MODE_DEFENSIVE_CAST, FALSE);
|
||
}
|
||
|
||
if(// Check if we have the feat FEAT_EPIC_IMPROVED_COMBAT_CASTING - no AOO
|
||
!GetHasFeat(FEAT_EPIC_IMPROVED_COMBAT_CASTING) &&
|
||
// We will never do anything if no melee attackers, or no ally that can help repel them
|
||
GlobalMeleeAttackers > FALSE &&
|
||
// Target of the spell is not us, if it is us, we don't WANT to move!! (EG: Stoneskin casting)
|
||
oTarget != OBJECT_SELF &&
|
||
// Do not move if we have protection spells, as it will be as good as we can get from stopping damage
|
||
// - May change
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) &&
|
||
// We have an ally in 4M
|
||
GlobalValidAlly && GlobalRangeToAlly < f4 &&
|
||
// Intelligence AND class mage
|
||
((GlobalIntelligence >= i4 &&
|
||
(GlobalOurChosenClass == CLASS_TYPE_WIZARD ||
|
||
GlobalOurChosenClass == CLASS_TYPE_SORCERER ||
|
||
GlobalOurChosenClass == CLASS_TYPE_FEY)) ||
|
||
// Or override
|
||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_CONCENTRATION, AI_OTHER_MASTER)))
|
||
{
|
||
// First - checks concentration...
|
||
int iConcentration = GetSkillRank(SKILL_CONCENTRATION);
|
||
// If we have 0 concentration, we always run!
|
||
|
||
|
||
// Else we run back based on our concentration compared to how much
|
||
// damage we can take.
|
||
// - NOTE: Once we can activate defensive casting, we should do so and ignore AOO.
|
||
|
||
// We may walk back quite often if there are quite a few allies around
|
||
// who'd benifit (IE from a person running after someone and might gain
|
||
// AOO)
|
||
// - We count up the melee enemy
|
||
// - We see how many compared to our protections
|
||
// - We move back quite often if the enemy are comparable levels and
|
||
// they are of comparable BAB (Consider average AC and average BAB)
|
||
|
||
// We always move back if low concentration...under half our hit dice.
|
||
if(iConcentration <= GlobalOurHitDice/i2 ||
|
||
// Or that the average HD is quite high (compared to 2/3 our HD)
|
||
GlobalAverageEnemyHD >= ((GlobalOurHitDice * i3) / i2) ||
|
||
// Or that the average BAB is quite high (compared to 2/3 our AC)
|
||
GlobalAverageEnemyBAB >= ((GlobalOurAC * i3) / i2))
|
||
{
|
||
// We check the counter
|
||
int iCounter = GetAIInteger(AI_CONCENTRATIONMOVE_COUNTER);
|
||
iCounter++;
|
||
SetAIInteger(AI_CONCENTRATIONMOVE_COUNTER, iCounter);
|
||
// If the counter is <= 5, we will move back, else we've been moving
|
||
// back for 5 turns already! Stop and do something useful...
|
||
if(iCounter <= i5)
|
||
{
|
||
ClearAllActions();
|
||
// 6: "[DCR:Caster] Moving away from AOO's. [Enemy] " + GetName(GlobalSpellTarget)
|
||
DebugActionSpeakByInt(6, GlobalSpellTarget);
|
||
ActionMoveAwayFromLocation(GetLocation(GlobalMeleeTarget), TRUE, f10);
|
||
return TRUE;
|
||
}
|
||
else if(iCounter >= i10)
|
||
{
|
||
// Reset once we get to 10 rounds.
|
||
DeleteAIInteger(AI_CONCENTRATIONMOVE_COUNTER);
|
||
}
|
||
else
|
||
{
|
||
// Counter between 5 and 10 - do normal things
|
||
return FALSE;
|
||
}
|
||
}
|
||
// If we don't move back, we reset the counter for time we have moved back
|
||
DeleteAIInteger(AI_CONCENTRATIONMOVE_COUNTER);
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Special case - it checks the talent again, in EffectCutsceneImmobilize (if not already so) and
|
||
// uses the item, if it is an equal talent.
|
||
int AI_ActionCastItemEqualTo(object oTarget, int iSpellID, int iLocation)
|
||
{
|
||
// We need to get what one is actually the talent number :-D
|
||
// This is actually faster, as we know that iSpellID equals one of them :-D
|
||
// Set to local integers
|
||
int iCnt, nTalent, iReturn;
|
||
talent tBestOfIt;
|
||
for(iCnt = i1; iCnt <= i21; iCnt++)
|
||
{
|
||
// Check match...
|
||
if(GetAIConstant(ITEM_TALENT_VALUE + IntToString(iCnt)) == iSpellID)
|
||
{
|
||
// We break with set one.
|
||
nTalent = iCnt;
|
||
break;
|
||
}
|
||
}
|
||
// Check for valid talent (1+)
|
||
if(nTalent >= i1)
|
||
{
|
||
// Apply EffectCutsceneImmobilize. It only removes ones with no spell ID anyway :-D
|
||
AI_SpecialActionApplyItem();
|
||
|
||
tBestOfIt = GetCreatureTalentBest(nTalent, i20);
|
||
// JUST to make sure!
|
||
if(GetIsTalentValid(tBestOfIt) &&
|
||
GetIdFromTalent(tBestOfIt) == iSpellID &&
|
||
GetTypeFromTalent(tBestOfIt) == TALENT_TYPE_SPELL)
|
||
{
|
||
AI_SetTimeStopStored(iSpellID);
|
||
// 7: "[DCR:Casting] Talent(item) [TalentID] " + IntToString(GetIdFromTalent(tBestOfIt)) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(7, oTarget, GetIdFromTalent(tBestOfIt), IntToString(iLocation));
|
||
// Sets a sub spell whatever, just in case.
|
||
SetLocalInt(OBJECT_SELF, AI_SPELL_SUB_SPELL_CAST, iSpellID);
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Use this only for items, so we should not have the spell.
|
||
if(iLocation)
|
||
{
|
||
ActionUseTalentAtLocation(tBestOfIt, GetLocation(oTarget));
|
||
}
|
||
else //if(!GetObjectSeen(oTarget)) // Should be seen - checked before.
|
||
{
|
||
ActionUseTalentOnObject(tBestOfIt, oTarget);
|
||
}
|
||
iReturn = TRUE;
|
||
}
|
||
// remove the EffectCutsceneImmobilize, if so.
|
||
AI_SpecialActionRemoveItem();
|
||
}
|
||
// Return TRUE or FALSE.
|
||
return iReturn;
|
||
}
|
||
// This is used for INFLICT spells, as GetHasSpell also can return 1+ for
|
||
// any extra castings - like if we had 2 light wounds and 2 blesses, it'd return
|
||
// 4.
|
||
// Imput the iSpellID, oTarget in to cast the spell. TRUE if casted. No items checked.
|
||
int AI_ActionCastSpontaeousSpell(int iSpellID, int nTalent, object oTarget)
|
||
{
|
||
if(nTalent > i0 && GetHasSpell(iSpellID) && GetObjectSeen(oTarget))
|
||
{
|
||
// Note: Not stored or used in time stop
|
||
// 8: "[DCR: Casting] Workaround for Spontaeous [SpellID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget)
|
||
DebugActionSpeakByInt(8, oTarget, iSpellID);
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Decrement the spell being cast by one as we cheat cast it
|
||
DecrementRemainingSpellUses(OBJECT_SELF, iSpellID);
|
||
// Cheat cast, it'll remove inflict wounds if it is an inflict spell anyway.
|
||
ActionCastSpellAtObject(iSpellID, oTarget, METAMAGIC_NONE, TRUE);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical):
|
||
// 1. If they have the spell normally
|
||
// 2. If they have an item with the spell.
|
||
// 3. If they have a potion of the right type.
|
||
// - We always attack with a bow at ranged, but may attack normally after the spell.
|
||
// - If nTalent is 0, we do not check items.
|
||
// - If iRequirement is 0, it is considered innate.
|
||
// - Imput iItemTalentValue and iPotionTalentValue to check item talents.
|
||
// - iSummonLevel can be 0, but if 1+, it is set to AI_LAST_SUMMONED_LEVEL
|
||
int AI_ActionCastSpell(int iSpellID, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1)
|
||
{
|
||
// 1. We need nTalent to be over 0. -1 means an invalid spell for that talent,
|
||
// IE no spell for that talent
|
||
// - If iRequirement is 0, we consider it innate and no talent category for some
|
||
// reason.
|
||
if(nTalent > i0 || !iRequirement)
|
||
{
|
||
// Check GetHasSpell as long as we are not silenced, and have right modifier, and
|
||
// the object is seen (this is a backup for it!)
|
||
if(!GlobalSilenceSoItems &&
|
||
(!iRequirement || GlobalSpellAbilityModifier >= iRequirement) &&
|
||
GetHasSpell(iSpellID) &&
|
||
(iLocation || GetObjectSeen(oTarget)))
|
||
{
|
||
// Make sure it is a spell, not an ability
|
||
if(iRequirement > FALSE)
|
||
{
|
||
// Attempt Concentration Check (Casting a spell, not an item)
|
||
if(AI_AttemptConcentrationCheck(oTarget)) return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Turn off all modes - remember, we can't use expertise with spellcasting!
|
||
AI_SetMeleeMode();
|
||
}
|
||
// Set time stop stored to this spell.
|
||
AI_SetTimeStopStored(iSpellID);
|
||
// 9: "[DCR:Casting] NormalSpell [ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(9, oTarget, iSpellID, IntToString(iLocation));
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Note: 1.3 fix. Action Cast At Object will be used if we can see
|
||
// the target, even if it is a location spell
|
||
if(GetObjectSeen(oTarget))
|
||
{
|
||
// aim at the object directly!
|
||
// - See 1.3 fix below. Basically, this should use Meta Magic normally
|
||
ActionCastSpellAtObject(iSpellID, oTarget);
|
||
}
|
||
// If location...
|
||
else //if(iLocation)
|
||
{
|
||
// Fire ActionSpellAtLocation at the given location
|
||
// 1.3 fix - Until ActionCastSpellAtLocation works with METAMAGIC
|
||
// it will cheat-cast, and decrement the spell by one, with no metamagic.
|
||
ActionCastSpellAtLocation(iSpellID, GetLocation(oTarget), METAMAGIC_NONE, TRUE);
|
||
DecrementRemainingSpellUses(OBJECT_SELF, iSpellID);
|
||
}
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
// Alway stop - we use else here, so we always do an action! :-D
|
||
return TRUE;
|
||
}
|
||
// 2. Cast from potions, or items!
|
||
// This is made simpler by adding in iItemTalentValue and iPotionTalentValue
|
||
}
|
||
// Basic items - Wands, Scrolls that might pop up - Potions too
|
||
if(iItemTalentValue == iSpellID ||
|
||
iPotionTalentValue == iSpellID)
|
||
{
|
||
if(AI_ActionCastItemEqualTo(oTarget, iSpellID, iLocation))
|
||
{
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// This will cast the shadow conjuration/protection version of the spell, always if they have it.
|
||
// 1. If they have the spell normally (Using the iUpperSpell spell)
|
||
// 2. If they have an item with the spell
|
||
// - We always attack with a bow at ranged, but may attack normally after the spell.
|
||
// - If nTalent is 0, we do not check items.
|
||
// - If iRequirement is 0, it is considered innate.
|
||
// - Imput iItemTalentValue to check item talents.
|
||
int AI_ActionCastSubSpell(int iSubSpell, int nTalent = 0, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1)
|
||
{
|
||
// 1. We need nTalent to be over 0. -1 means an invalid spell for that talent,
|
||
// IE no spell for that talent
|
||
// - If iRequirement is 0, we consider it innate and no talent category for some
|
||
// reason.
|
||
if(nTalent > i0 || !iRequirement)
|
||
{
|
||
// Check GetHasSpell as long as we are not silenced, and have right modifier, and
|
||
// the object is seen (this is a backup for it!)
|
||
if(!GlobalSilenceSoItems &&
|
||
(!iRequirement || GlobalSpellAbilityModifier >= iRequirement) &&
|
||
GetHasSpell(iSubSpell) &&
|
||
(iLocation || GetObjectSeen(oTarget)))
|
||
{
|
||
// Make sure it is a spell, not an ability
|
||
if(iRequirement > FALSE)
|
||
{
|
||
// Attempt Concentration Check (Casting a spell, not an item)
|
||
if(AI_AttemptConcentrationCheck(oTarget)) return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Turn off all modes - remember, we can't use expertise with spellcasting!
|
||
AI_SetMeleeMode();
|
||
}
|
||
// Set time stop stored to this spell.
|
||
AI_SetTimeStopStored(iSubSpell);
|
||
// 11: "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(iSubSpell) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(11, oTarget, iSubSpell, IntToString(iLocation));
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// See 1.3 fix notes about metamagic not being used correctly with
|
||
// cast spell at location.
|
||
if(GetObjectSeen(oTarget))
|
||
{
|
||
// Aim at the object directly!
|
||
ActionCastSpellAtObject(iSubSpell, oTarget, METAMAGIC_ANY, TRUE);
|
||
}
|
||
// If location...
|
||
else// if(iLocation)
|
||
{
|
||
// Fire ActionSpellAtLocation at the given location
|
||
ActionCastSpellAtLocation(iSubSpell, GetLocation(oTarget), METAMAGIC_NONE, TRUE);
|
||
}
|
||
// Decrement
|
||
DecrementRemainingSpellUses(OBJECT_SELF, iSubSpell);
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
// Alway stop - we use else here, so we always do an action! :-D
|
||
return TRUE;
|
||
}
|
||
// 2. Cast from potions, or items!
|
||
// This is made simpler by adding in iItemTalentValue and iPotionTalentValue
|
||
}
|
||
// Basic items - Wands, Scrolls that might pop up - Potions too
|
||
if(iItemTalentValue == iSubSpell ||
|
||
iPotionTalentValue == iSubSpell)
|
||
{
|
||
if(AI_ActionCastItemEqualTo(oTarget, iSubSpell, iLocation))
|
||
{
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// This will cast the spell of ID in this order [Note: Always adds to time stop, as it will be on self, and benifical):
|
||
// 0. If d100() is <= iRandom.
|
||
// 1. If they have the spell normally
|
||
// 2. If they have an item with the spell.
|
||
// 3. If they have a potion of the right type.
|
||
// - If we are at range from nearest enemy, we attack with a ranged weapon, else do nothing more.
|
||
// - If nTalent is -1, we do not check items.
|
||
// - Sets GlobalLastSpellValid to iSpellID if we can cast it, but don't randomly.
|
||
// Then you can use AI_ActionCastBackupRandomSpell to see if we can cast it later.
|
||
int AI_ActionCastSpellRandom(int iSpellID, int nTalent, int iRandom, object oTarget = OBJECT_SELF, int iRequirement = 0, int iLocation = FALSE, int iItemTalentValue = -1, int iPotionTalentValue = -1)
|
||
{
|
||
// 1. We need nTalent to be over 0. -1 means an invalid spell for that talent,
|
||
// IE no spell for that talent
|
||
// - If iRequirement is 0, we consider it innate and no talent category for some
|
||
// reason.
|
||
if(nTalent > i0 || !iRequirement)
|
||
{
|
||
// Check GetHasSpell as long as we are not silenced, and have right modifier, and
|
||
// the object is seen (this is a backup for it!)
|
||
if(!GlobalSilenceSoItems &&
|
||
(!iRequirement || GlobalSpellAbilityModifier >= iRequirement) &&
|
||
GetHasSpell(iSpellID) &&
|
||
(iLocation || GetObjectSeen(oTarget)))
|
||
{
|
||
if(d100() <= iRandom + GlobalRandomCastModifier)
|
||
{
|
||
// Make sure it is a spell, not an ability
|
||
if(iRequirement > FALSE)
|
||
{
|
||
// Attempt Concentration Check (Casting a spell, not an item)
|
||
if(AI_AttemptConcentrationCheck(oTarget)) return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Turn off all modes - remember, we can't use expertise with spellcasting!
|
||
AI_SetMeleeMode();
|
||
}
|
||
// Set time stop stored to this spell.
|
||
AI_SetTimeStopStored(iSpellID);
|
||
// 12: "[DCR:Casting] NormalRandomSpell. [ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(12, oTarget, iSpellID, IntToString(iLocation));
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Note: 1.3 fix. Action Cast At Object will be used if we can see
|
||
// the target, even if it is a location spell
|
||
if(GetObjectSeen(oTarget))
|
||
{
|
||
// aim at the object directly!
|
||
// - See 1.3 fix below. Basically, this should use Meta Magic normally
|
||
ActionCastSpellAtObject(iSpellID, oTarget);
|
||
}
|
||
// If location...
|
||
else //if(iLocation)
|
||
{
|
||
// Fire ActionSpellAtLocation at the given location
|
||
// 1.3 fix - Until ActionCastSpellAtLocation works with METAMAGIC
|
||
// it will cheat-cast, and decrement the spell by one, with no metamagic.
|
||
ActionCastSpellAtLocation(iSpellID, GetLocation(oTarget), METAMAGIC_NONE, TRUE);
|
||
DecrementRemainingSpellUses(OBJECT_SELF, iSpellID);
|
||
}
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
// Alway stop - we use else here, so we always do an action! :-D
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Don't use acid fog as this (Spell 0). If we have one already set,
|
||
// this is a worse spell :-)
|
||
AI_SetBackupCastingSpellValues(iSpellID, nTalent, oTarget, iLocation, iRequirement, iItemTalentValue, iPotionTalentValue);
|
||
|
||
// Always return FALSE.
|
||
return FALSE;
|
||
}
|
||
}
|
||
// 2. Cast from potions, or items!
|
||
// This is made simpler by adding in iItemTalentValue and iPotionTalentValue
|
||
}
|
||
// Basic items - Wands, Scrolls that might pop up. Potions too.
|
||
if(iItemTalentValue == iSpellID ||
|
||
iPotionTalentValue == iSpellID)
|
||
{
|
||
if(d100() <= iRandom + GlobalRandomCastModifier)
|
||
{
|
||
if(AI_ActionCastItemEqualTo(oTarget, iSpellID, iLocation))
|
||
{
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Don't use acid fog as this (Spell 0). If we have one already set,
|
||
// this is a worse spell :-)
|
||
AI_SetBackupCastingSpellValues(iSpellID, nTalent, oTarget, iLocation, iRequirement, iItemTalentValue, iPotionTalentValue);
|
||
|
||
// Always return FALSE.
|
||
return FALSE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// This will used a stored GlobalLastSpellValid to see if it should cast that
|
||
// spell (or attempt to!) as a backup. Uses stored targets from when it did know
|
||
// it was valid.
|
||
int AI_ActionCastBackupRandomSpell()
|
||
{
|
||
// Need a valid spell
|
||
if(GlobalLastSpellValid > FALSE)
|
||
{
|
||
object oTarget;
|
||
int iSpell, iTalent, iLocation, iItem, iPotion, iRequirement;
|
||
iSpell = GlobalLastSpellValid;
|
||
// Delete again for other castings
|
||
GlobalLastSpellValid = FALSE;
|
||
// 13: "[DCR:Casting] Backup spell caught: " + IntToString(iSpell)
|
||
DebugActionSpeakByInt(13, OBJECT_INVALID, iSpell);
|
||
|
||
// Get things from GLOBAL_LAST_SPELL_INFORMATION1 to GLOBAL...ATION5
|
||
int iCnt = i1;
|
||
// GET...
|
||
// talent
|
||
iTalent = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt));
|
||
// target
|
||
iCnt++;
|
||
oTarget = GetAIObject(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt));
|
||
// location
|
||
iCnt++;
|
||
iLocation = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt));
|
||
// reqirement
|
||
iCnt++;
|
||
iRequirement = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt));
|
||
// item
|
||
iCnt++;
|
||
iItem = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt));
|
||
// potion
|
||
iCnt++;
|
||
iPotion = GetAIInteger(GLOBAL_LAST_SPELL_INFORMATION + IntToString(iCnt));
|
||
|
||
// Cast spell at 100% chance, and innate (already checked iRequirement)
|
||
// - Should cast.
|
||
if(AI_ActionCastSpell(iSpell, iTalent, oTarget, iRequirement, iLocation, iItem, iPotion)) return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
int AI_ActionCastSummonSpell(int iThingID, int iRequirement = 0, int iSummonLevel = 0)
|
||
{
|
||
// Feat
|
||
if(iRequirement == iM1)
|
||
{
|
||
if(GetHasFeat(iThingID))
|
||
{
|
||
// 14: "[DCR:Feat] [ID] " + IntToString(iFeat) + " [Enemy] " + GetName(oObject)
|
||
DebugActionSpeakByInt(14, OBJECT_SELF, iThingID);
|
||
// talent tFeat doesn't work.
|
||
ActionUseFeat(iThingID, OBJECT_SELF);
|
||
SetAIInteger(AI_LAST_SUMMONED_LEVEL, iSummonLevel);
|
||
return TRUE;
|
||
}
|
||
}
|
||
else if(SpellAllies)
|
||
{
|
||
// Check GetHasSpell as long as we are not silenced, and have right modifier, and
|
||
// the object is seen (this is a backup for it!)
|
||
if(!GlobalSilenceSoItems &&
|
||
(iRequirement == FALSE || GlobalSpellAbilityModifier >= iRequirement) &&
|
||
GetHasSpell(iThingID))
|
||
{
|
||
// Make sure it is a spell, not an ability
|
||
if(iRequirement > FALSE)
|
||
{
|
||
// Attempt Concentration Check (Casting a spell, not an item)
|
||
if(AI_AttemptConcentrationCheck(GlobalSpellTarget)) return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Turn off all modes - remember, we can't use expertise with spellcasting!
|
||
AI_SetMeleeMode();
|
||
}
|
||
// Set time stop stored to this spell.
|
||
AI_SetTimeStopStored(iThingID);
|
||
// 9: "[DCR:Casting] NormalSpell [ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(9, GlobalSpellTarget, iThingID);
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Fire ActionSpellAtLocation at the given location
|
||
// 1.3 fix - Until ActionCastSpellAtLocation works with METAMAGIC
|
||
// it will cheat-cast, and decrement the spell by one, with no metamagic.
|
||
ActionCastSpellAtLocation(iThingID, GlobalSummonLocation, METAMAGIC_NONE, TRUE);
|
||
DecrementRemainingSpellUses(OBJECT_SELF, iThingID);
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(SpellAllies))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(SpellAllies));
|
||
}
|
||
// Alway stop - we use else here, so we always do an action! :-D
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Basic items - Wands, Scrolls that might pop up
|
||
else if(ItemAllies == iThingID)
|
||
{
|
||
// We need to get what one is actually the talent number :-D
|
||
// This is actually faster, as we know that iSpellID equals one of them :-D
|
||
// Set to local integers
|
||
int nTalent, iReturn;
|
||
if(GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES)) == iThingID)
|
||
{
|
||
nTalent = ItemAllies;
|
||
}
|
||
// Check for valid talent (1+)
|
||
if(nTalent >= i1)
|
||
{
|
||
// Apply EffectCutsceneImmobilize. It only removes ones with no spell ID anyway :-D
|
||
AI_SpecialActionApplyItem();
|
||
|
||
talent tBestOfIt = GetCreatureTalentBest(nTalent, i20);
|
||
// JUST to make sure!
|
||
if(GetIsTalentValid(tBestOfIt) &&
|
||
GetIdFromTalent(tBestOfIt) == iThingID &&
|
||
GetTypeFromTalent(tBestOfIt) == TALENT_TYPE_SPELL)
|
||
{
|
||
AI_SetTimeStopStored(iThingID);
|
||
// 7: "[DCR:Casting] Talent(item) [TalentID] " + IntToString(GetIdFromTalent(tBestOfIt)) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(7, GlobalSpellTarget, GetIdFromTalent(tBestOfIt));
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Use this only for items, so we should not have the spell.
|
||
ActionUseTalentAtLocation(tBestOfIt, GlobalSummonLocation);
|
||
iReturn = TRUE;
|
||
}
|
||
// remove the EffectCutsceneImmobilize, if so.
|
||
AI_SpecialActionRemoveItem();
|
||
// Lasts...recheck items
|
||
if(AI_GetSpellCategoryHasItem(nTalent))
|
||
{
|
||
ActionDoCommand(AI_SetItemTalentValue(nTalent));
|
||
}
|
||
return iReturn;
|
||
}
|
||
}
|
||
// No summon created
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// If they have the feat, they will use it on the target, and return TRUE
|
||
// * iFeat - Feat ID to use
|
||
// * oObject - object to use it on (Can't target locations in the AI - not worth it)
|
||
// * iSummonLevel - when using a summoning feat (EG: blackguard undead) use a number here. If false, its ignored
|
||
int AI_ActionUseFeatOnObject(int iFeat, object oObject = OBJECT_SELF)
|
||
{
|
||
if(GetHasFeat(iFeat) && GetIsObjectValid(oObject))
|
||
{
|
||
if(!GetHasFeatEffect(iFeat, oObject))
|
||
{
|
||
// 14: "[DCR:Feat] [ID] " + IntToString(iFeat) + " [Enemy] " + GetName(oObject)
|
||
DebugActionSpeakByInt(14, oObject, iFeat);
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
ActionUseFeat(iFeat, oObject);
|
||
if(oObject == OBJECT_SELF)
|
||
{
|
||
if(GetIsObjectValid(GlobalMeleeTarget)) ActionAttack(GlobalMeleeTarget);
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// If they have nFeat, they cheat-cast nSpell at oTarget.
|
||
// - This is a workaround, as some epic spells, for some reason, won't work with
|
||
// a standard ActionUseFeatOnObject()! Dammit. Beta 3 addition.
|
||
int AI_ActionUseEpicSpell(int nFeat, int nSpell, object oTarget = OBJECT_SELF)
|
||
{
|
||
if(GetHasFeat(nFeat))
|
||
{
|
||
// 14: "[DCR:Feat] [ID] " + IntToString(nFeat) + " [Enemy] " + GetName(oTarget)
|
||
DebugActionSpeakByInt(14, oTarget, nFeat);
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
// Cheat cast the spell
|
||
ActionCastSpellAtObject(nSpell, oTarget, METAMAGIC_NONE, TRUE);
|
||
// Decrement casting of it.
|
||
DecrementRemainingFeatUses(OBJECT_SELF, nFeat);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// This attempts to check the talent TALENT_CATEGORY_HARMFUL_RANGED, 2, which
|
||
// holds grenades. Easy checks.
|
||
// TRUE if they fire a grenade.
|
||
int AI_AttemptGrenadeThrowing(object oTarget)
|
||
{
|
||
int iReturn = FALSE;
|
||
// Check if the items is a grenade:
|
||
// SPELL_GRENADE_ACID - 1d6 Acid Damge/Target, or 1 splash.
|
||
// SPELL_GRENADE_CALTROPS - Up to 25 normal damage, at 1 Damage/round to an AOE
|
||
// SPELL_GRENADE_CHICKEN - Chicken - fires a chicken and fireball
|
||
// SPELL_GRENADE_CHOKING - Stinking cloud type effect, dazes on fort save.
|
||
// SPELL_GRENADE_FIRE - 1d6 fire damage/target, or 1 splash
|
||
// SPELL_GRENADE_HOLY - 2d4 Divine Damage to undead, 1 to undead in splash
|
||
// SPELL_GRENADE_TANGLE - Entangles spell target, reflex save.
|
||
// SPELL_GRENADE_THUNDERSTONE - Deafens against a DC 15 fort save, AOE.
|
||
|
||
// 744 Grenade_FireBomb - Big fire 'nade. Has an AOE after
|
||
// 745 Grenade_AcidBomb - Big Acid 'nade. Has an AOE after damage
|
||
|
||
if((ItemHostRanged >= SPELL_GRENADE_FIRE &&
|
||
ItemHostRanged <= SPELL_GRENADE_CALTROPS) ||
|
||
ItemHostRanged == 744 || ItemHostRanged == 745)
|
||
{
|
||
// We have a valid item grenade. We then throw it (or attempt to!)
|
||
// - Check holy grenade not firing Versus non-undead
|
||
if(ItemHostRanged == SPELL_GRENADE_HOLY &&
|
||
GetRacialType(oTarget) != RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Stop as they are not undead
|
||
return FALSE;
|
||
}
|
||
|
||
// We fire the spell at the target if they are seen
|
||
// - If SpellTargetSeen is TRUE, fire at them
|
||
int iLocation = FALSE;
|
||
if(!GlobalSeenSpell)
|
||
{
|
||
iLocation = TRUE;
|
||
}
|
||
|
||
// Apply EffectCutsceneImmobilize. It only removes ones with no spell ID anyway :-D
|
||
AI_SpecialActionApplyItem();
|
||
|
||
talent tBestOfIt = GetCreatureTalentBest(TALENT_CATEGORY_HARMFUL_RANGED, i20);
|
||
// JUST to make sure!
|
||
if(GetIsTalentValid(tBestOfIt) &&
|
||
GetIdFromTalent(tBestOfIt) == ItemHostRanged)
|
||
{
|
||
// 15: "[DCR:Casting] Grenade [ID] " + IntToString(ItemHostRanged) + " [Target] " + GetName(oTarget) + " [Location] " + IntToString(iLocation)
|
||
DebugActionSpeakByInt(15, GlobalSpellTarget, ItemHostRanged, IntToString(iLocation));
|
||
// Equip the best shield we have
|
||
AI_EquipBestShield();
|
||
// Use this only for items, so we should not have the spell.
|
||
if(iLocation)
|
||
{
|
||
ActionUseTalentAtLocation(tBestOfIt, GetLocation(oTarget));
|
||
}
|
||
else //if(!GetObjectSeen(GlobalSpellTarget)) // Should be seen - checked before.
|
||
{
|
||
ActionUseTalentOnObject(tBestOfIt, oTarget);
|
||
}
|
||
iReturn = TRUE;
|
||
}
|
||
// remove the EffectCutsceneImmobilize, if so.
|
||
AI_SpecialActionRemoveItem();
|
||
|
||
// Lasts...recheck items
|
||
// TALENT_CATEGORY_HARMFUL_RANGED is always checked for items.
|
||
ActionDoCommand(AI_SetItemTalentValue(TALENT_CATEGORY_HARMFUL_RANGED));
|
||
}
|
||
return iReturn;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: GetBestSpontaeousHealingSpell
|
||
//::///////////////////////////////////////////////
|
||
// This will return the best spontaeous healing spell, so:
|
||
// - It uses just normal GetHasSpell for the clerical healing spells.
|
||
// - It gets set up at the start to the global "GlobalBestSpontaeousHealingSpell"
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_GetBestSpontaeousHealingSpell()
|
||
{
|
||
if(GetHasSpell(SPELL_CURE_CRITICAL_WOUNDS))
|
||
{
|
||
return SPELL_CURE_CRITICAL_WOUNDS;
|
||
}
|
||
else if(GetHasSpell(SPELL_CURE_SERIOUS_WOUNDS))
|
||
{
|
||
return SPELL_CURE_SERIOUS_WOUNDS;
|
||
}
|
||
else if(GetHasSpell(SPELL_CURE_MODERATE_WOUNDS))
|
||
{
|
||
return SPELL_CURE_MODERATE_WOUNDS;
|
||
}
|
||
else if(GetHasSpell(SPELL_CURE_LIGHT_WOUNDS))
|
||
{
|
||
return SPELL_CURE_LIGHT_WOUNDS;
|
||
}
|
||
else if(GetHasSpell(SPELL_CURE_MINOR_WOUNDS))
|
||
{
|
||
return SPELL_CURE_MINOR_WOUNDS;
|
||
}
|
||
// False = no spell
|
||
return FALSE;
|
||
}
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_SetUpUs
|
||
//::///////////////////////////////////////////////
|
||
This sets up US, the user! :-)
|
||
- Determines class to use, dragon or not.
|
||
- And some other things that don't need to check allies/enemies for.
|
||
- Intelligence and so on, global effects of us and so on ;-)
|
||
//:://///////////////////////////////////////////*/
|
||
void AI_SetUpUs()
|
||
{
|
||
int iLastSpellType, nClass1, nClass2, nClass3, nLevel1, nLevel2, nLevel3,
|
||
nState1, nState2, nState3, nUseClass, iCurrent;
|
||
object oSummon;
|
||
float fTotal;
|
||
// We set up what intelligence we have (level). See OnSpawn for more info
|
||
// Default is 10, top 10, bottom 1.
|
||
GlobalIntelligence = GetBoundriedAIInteger(AI_INTELLIGENCE, i10, i10, i1);
|
||
// Checks the 3 classes, and returns one of them randomly based on how many they
|
||
// have in that level compared to the other 2.
|
||
GlobalOurHitDice = GetHitDice(OBJECT_SELF);
|
||
GlobalThisArea = GetArea(OBJECT_SELF);
|
||
GlobalOurRace = GetRacialType(OBJECT_SELF);
|
||
// HP
|
||
GlobalOurCurrentHP = GetCurrentHitPoints();
|
||
GlobalOurMaxHP = GetMaxHitPoints();
|
||
// Use Floats to get Decimal places.
|
||
GlobalOurPercentHP = AI_GetPercentOf(GlobalOurCurrentHP, GlobalOurMaxHP);
|
||
GlobalOurSize = GetCreatureSize(OBJECT_SELF);
|
||
// AI - just normal. More added/subtracted be;pw
|
||
GlobalOurAC = GetAC(OBJECT_SELF);
|
||
switch(GlobalOurSize)
|
||
{
|
||
case CREATURE_SIZE_TINY: GlobalOurAC += i2; break;
|
||
case CREATURE_SIZE_SMALL: GlobalOurAC += i1; break;
|
||
case CREATURE_SIZE_LARGE: GlobalOurAC -= i1; break;
|
||
case CREATURE_SIZE_HUGE: GlobalOurAC -= i2; break;
|
||
}
|
||
GlobalOurAppearance = GetAppearanceType(OBJECT_SELF);
|
||
GlobalOurGoodEvil = GetAlignmentGoodEvil(OBJECT_SELF);// Used for alignment prot. Spells.
|
||
// Weapons (in places) or objects :-P
|
||
GlobalLeftHandWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND);
|
||
GlobalRightHandWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND);
|
||
GlobalOurBaseAttackBonus = GetBaseAttackBonus(OBJECT_SELF);
|
||
// Spell Ranged Attacking
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_LONGER_RANGED_SPELLS_FIRST, AI_COMBAT_MASTER))
|
||
{
|
||
SRA = TRUE;
|
||
}
|
||
// Set up the extra % to random cast
|
||
GlobalRandomCastModifier = GlobalIntelligence * i2;
|
||
// - 2% extra at 1, 20% at 10 :-)
|
||
|
||
// Set if we are a global buffer
|
||
GlobalWeAreBuffer = GetSpawnInCondition(AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS, AI_COMBAT_MASTER);
|
||
|
||
// Set if we only use items
|
||
GlobalSilenceSoItems = AI_GetAIHaveEffect(GlobalEffectSilenced);
|
||
|
||
// If we have any of the silent feats, epic, we ignore any silence we have.
|
||
if(GlobalSilenceSoItems)
|
||
{
|
||
if(GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_1) ||
|
||
GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_2) ||
|
||
GetHasFeat(FEAT_EPIC_AUTOMATIC_SILENT_SPELL_3))
|
||
{
|
||
GlobalSilenceSoItems = FALSE;
|
||
}
|
||
}
|
||
|
||
// Our reach is the distance we can immediantly attack, normally.
|
||
GlobalOurReach = IntToFloat(GlobalOurSize * i4) + f1;
|
||
// Sets up the class to use.
|
||
fTotal = IntToFloat(GlobalOurHitDice);
|
||
nClass1 = GetClassByPosition(i1);
|
||
nClass2 = GetClassByPosition(i2);
|
||
nClass3 = GetClassByPosition(i3);
|
||
nLevel1 = GetLevelByClass(nClass1);
|
||
nLevel2 = GetLevelByClass(nClass2);
|
||
nLevel3 = GetLevelByClass(nClass3);
|
||
// Set up how much % each class occupies.
|
||
nState1 = FloatToInt((IntToFloat(nLevel1) / fTotal) * i100);
|
||
nState2 = FloatToInt((IntToFloat(nLevel2) / fTotal) * i100) + nState1;
|
||
nState3 = FloatToInt((IntToFloat(nLevel3) / fTotal) * i100) + nState2;
|
||
// Randomise the % we pick
|
||
nUseClass = d100();
|
||
// Set the class, and that classes level.
|
||
if(nUseClass <= nState1)
|
||
{
|
||
GlobalOurChosenClass = nClass1;
|
||
GlobalOurChosenClassLevel = nLevel1;
|
||
}
|
||
else if(nUseClass > nState1 && nUseClass <= nState2)
|
||
{
|
||
GlobalOurChosenClass = nClass2;
|
||
GlobalOurChosenClassLevel = nLevel2;
|
||
}
|
||
else
|
||
{
|
||
GlobalOurChosenClass = nClass3;
|
||
GlobalOurChosenClassLevel = nLevel3;
|
||
}
|
||
// Intelligence based spellcaster.
|
||
if(GlobalOurChosenClass == CLASS_TYPE_WIZARD)
|
||
{
|
||
GlobalSpellAbilityModifier = GetAbilityScore(OBJECT_SELF, ABILITY_INTELLIGENCE);
|
||
GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_INTELLIGENCE);
|
||
}
|
||
// Wisdom based spellcaster
|
||
else if(GlobalOurChosenClass == CLASS_TYPE_DRUID ||
|
||
GlobalOurChosenClass == CLASS_TYPE_CLERIC)
|
||
{
|
||
GlobalSpellAbilityModifier = GetAbilityScore(OBJECT_SELF, ABILITY_WISDOM);
|
||
GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_WISDOM);
|
||
}
|
||
// Charisma
|
||
else if(GlobalOurChosenClass == CLASS_TYPE_BARD ||
|
||
GlobalOurChosenClass == CLASS_TYPE_SORCERER)
|
||
{
|
||
// Summoning specials (and some others). If we are a bard/sorceror, it means
|
||
// we cast 1 from X spells, not just "Only got that spell".
|
||
GlobalWeAreSorcerorBard = TRUE;
|
||
// Charisma based spellcaster
|
||
GlobalSpellAbilityModifier = GetAbilityScore(OBJECT_SELF, ABILITY_CHARISMA);
|
||
GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_CHARISMA);
|
||
}
|
||
else // Monster
|
||
{
|
||
// - We set ability modifier to 25, so that they cast all spells
|
||
// and monster abilties
|
||
GlobalSpellAbilityModifier = i25;
|
||
GlobalSpellBaseSaveDC = i10 + GetAbilityModifier(ABILITY_CHARISMA);
|
||
}
|
||
// Set up GlobalSaveStupidity, 10 - Intelligence
|
||
// 0 is better then 10! Bascially, if they are immune (EG: Massive fortitude
|
||
// fighter VS death save) then taking 10 from thier save stat means a lower
|
||
// intelligence caster will fire it against immune beings.
|
||
// - Also used in AOE checking.
|
||
GlobalSaveStupidity = i10 - GlobalIntelligence;
|
||
// Spontaeous healing spell
|
||
GlobalBestSpontaeousHealingSpell = AI_GetBestSpontaeousHealingSpell();
|
||
|
||
// Set up SR roll
|
||
// - 20 + Class level + 2 for spell penetration, +4 for greater.
|
||
// - We always take it as a 20 - but we set this for a huge amount.
|
||
// NOTE: we check HD only - because of monster abilities.
|
||
GlobalSpellPenetrationRoll = GlobalOurHitDice + i20;
|
||
// Check for feats
|
||
if(GetHasFeat(FEAT_EPIC_SPELL_PENETRATION))
|
||
{
|
||
GlobalSpellPenetrationRoll += i6;
|
||
}
|
||
else if(GetHasFeat(FEAT_GREATER_SPELL_PENETRATION))
|
||
{
|
||
GlobalSpellPenetrationRoll += i4;
|
||
}
|
||
else if(GetHasFeat(FEAT_SPELL_PENETRATION))
|
||
{
|
||
GlobalSpellPenetrationRoll += i2;
|
||
}
|
||
// Summon checking (special)
|
||
// Used for summoned creatures.
|
||
oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED);
|
||
if(GetIsObjectValid(oSummon) && !GetIsDead(oSummon))
|
||
{
|
||
// If valid, we can use the level of the last cast to check if valid.
|
||
int iCurrentSummonLevel = GetAIInteger(AI_LAST_SUMMONED_LEVEL);
|
||
// - Never replace epic, or elemental sparm, or balors (10, 11/12)
|
||
// - Never replace sorcerors or bards
|
||
if(!GlobalWeAreSorcerorBard && iCurrentSummonLevel <= i9)
|
||
{
|
||
// We check thier HP. If they are under x6% and under 30HP, we may summon
|
||
// a summon over this one that exsists.
|
||
iCurrent = GetCurrentHitPoints(oSummon);
|
||
if(((iCurrent * i6) < GetMaxHitPoints(oSummon)) && (iCurrent <= i30))
|
||
{
|
||
// Make it -1, so that we will say, summon a level 5 summon
|
||
// over a damaged level 6, but never a level 2 summon in replacement.
|
||
GlobalCanSummonSimilarLevel = iCurrentSummonLevel - i1;
|
||
}
|
||
}
|
||
// If we have not set GlobalCanSummonSimilarLevel, we set it so we
|
||
// should not summon anything at all!
|
||
if(!GlobalCanSummonSimilarLevel)
|
||
{
|
||
GlobalCanSummonSimilarLevel = i100;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Reset to 0, false, to summon any monster
|
||
DeleteAIInteger(AI_LAST_SUMMONED_LEVEL);
|
||
GlobalCanSummonSimilarLevel = FALSE;
|
||
}
|
||
|
||
// Right:
|
||
// - If we have valid category, we will set it to the talent value.
|
||
// - We then use this in the spells, tightens up some things :-)
|
||
// - Use 16 as another "any other" category. This isn't checked for items/
|
||
|
||
// Sets each one to TRUE if we have any of that category (and a spell)
|
||
int iLocalSpellInteger = GetLocalInt(OBJECT_SELF, AI_VALID_SPELLS);
|
||
// Any?
|
||
if(iLocalSpellInteger & AI_VALID_OTHER_SPELL) SpellOtherSpell = i23;// New one
|
||
// Conditional
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_AREAEFFECT) SpellConSinTar = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_SINGLE) SpellConAre = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT;
|
||
// Enchancement
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_AREAEFFECT) SpellEnhAre = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SELF) SpellEnhSelf = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_ENHANCEMENT_SINGLE) SpellEnhSinTar = TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE;
|
||
// Other
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_OBTAIN_ALLIES) SpellAllies = TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_PERSISTENT_AREA_OF_EFFECT) SpellAura = TALENT_CATEGORY_PERSISTENT_AREA_OF_EFFECT;
|
||
// Protection
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_PROTECTION_AREAEFFECT) SpellProAre = TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_PROTECTION_SELF) SpellProSelf = TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_PROTECTION_SINGLE) SpellProSinTar = TALENT_CATEGORY_BENEFICIAL_PROTECTION_SINGLE;
|
||
// Hostile/Harmful.
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_AREAEFFECT_DISCRIMINANT) SpellHostAreaDis = TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_AREAEFFECT_INDISCRIMINANT) SpellHostAreaInd = TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_RANGED) SpellHostRanged = TALENT_CATEGORY_HARMFUL_RANGED;
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_TOUCH) SpellHostTouch = TALENT_CATEGORY_HARMFUL_TOUCH;
|
||
|
||
// Breath weapon
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_DRAGONS_BREATH) SpellHostBreath = TALENT_CATEGORY_DRAGONS_BREATH;
|
||
|
||
// ANY spells valid?
|
||
if(iLocalSpellInteger & AI_VALID_ANY_SPELL) SpellAnySpellValid = TRUE;
|
||
|
||
// Hostile feats
|
||
if(iLocalSpellInteger & AI_VALID_TALENT_HARMFUL_MELEE) ValidFeats = TRUE;
|
||
|
||
// Now, what about, say, ITEMS?!?!
|
||
// Items/Spells to reduce lag.
|
||
|
||
// If no items, then we will set iLastSpellType to 0 so that none are reset
|
||
if(!GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_ITEMS, AI_OTHER_MASTER))
|
||
{
|
||
//*1*/ SpellHostAreaDis, ItemHostAreaDis,
|
||
ItemHostAreaDis = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_AREAEFFECT_DISCRIMINANT));
|
||
//*2*/ SpellHostRanged, ItemHostRanged,
|
||
ItemHostRanged = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_RANGED));
|
||
//*3*/ SpellHostTouch, ItemHostTouch,
|
||
ItemHostTouch = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_TOUCH));
|
||
//*6*/ SpellConAre, ItemConAre,
|
||
ItemConAre = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT));
|
||
//*7*/ SpellConSinTar, ItemConSinTar,
|
||
ItemConSinTar = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE));
|
||
//*8*/ SpellEnhAre, ItemEnhAre,
|
||
ItemEnhAre = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_AREAEFFECT));
|
||
//*9*/ SpellEnhSinTar, ItemEnhSinTar,
|
||
ItemEnhSinTar = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SINGLE));
|
||
//*10*/ SpellEnhSelf, ItemEnhSelf,
|
||
ItemEnhSelf = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_SELF));
|
||
//*11*/ SpellHostAreaInd, ItemHostAreaInd,
|
||
ItemHostAreaInd = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_HARMFUL_AREAEFFECT_INDISCRIMINANT));
|
||
//*12*/ SpellProSelf, ItemProSelf,
|
||
ItemProSelf = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_PROTECTION_SELF));
|
||
//*13*/ SpellProSinTar, ItemProSinTar,
|
||
ItemProSinTar = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_PROTECTION_SINGLE));
|
||
//*14*/ SpellProAre, ItemProAre,
|
||
ItemProAre = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_PROTECTION_AREAEFFECT));
|
||
//*15*/ SpellAllies, ItemAllies,
|
||
ItemAllies = GetAIConstant(ITEM_TALENT_VALUE + IntToString(TALENT_CATEGORY_BENEFICIAL_OBTAIN_ALLIES));
|
||
//*18*/ PotionCon,
|
||
//*20*/ PotionPro,
|
||
//*21*/ PotionEnh,
|
||
// These are general talents, Always set because we can use these parrallel to spells.
|
||
tPotionCon = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_POTION, i20);
|
||
tPotionPro = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_PROTECTION_POTION, i20);
|
||
tPotionEnh = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_ENHANCEMENT_POTION, i20);
|
||
// Potions. We do use talents for these to be safe!
|
||
// No worries if it is 0 or nothing, we compare them not to acid fog ever.
|
||
PotionCon = GetIdFromTalent(tPotionCon);
|
||
PotionPro = GetIdFromTalent(tPotionPro);
|
||
PotionEnh = GetIdFromTalent(tPotionEnh);
|
||
}
|
||
|
||
// We got any potions?
|
||
if(PotionCon || PotionPro || PotionEnh) GobalPotionsValid = TRUE;
|
||
|
||
// We got any items?
|
||
if(ItemAllies || ItemConSinTar || ItemConAre || ItemProSelf || ItemProSinTar ||
|
||
ItemProAre || ItemEnhSelf || ItemEnhSinTar || ItemEnhAre || ItemHostAreaDis ||
|
||
ItemHostAreaInd || ItemHostRanged || ItemHostTouch) GobalOtherItemsValid = TRUE;
|
||
|
||
// Healing Kits
|
||
int iHealLeft = GetAIInteger(AI_VALID_HEALING_KITS);
|
||
if(iHealLeft)
|
||
{
|
||
// Get the kit
|
||
GlobalHealingKit = GetAIObject(AI_VALID_HEALING_KIT_OBJECT);
|
||
// Oh, if we don't have one, re-set them, and only them.
|
||
if(!GetIsObjectValid(GlobalHealingKit) && iHealLeft >= i2)
|
||
{
|
||
SetAIInteger(RESET_HEALING_KITS, TRUE);
|
||
ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF);
|
||
}
|
||
}
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_GetNearbyFleeObject
|
||
//::///////////////////////////////////////////////
|
||
This returns an object, not seen not heard ally, who we
|
||
might flee to. Uses a loop, and runs only when we are going to flee for sure.
|
||
//:://///////////////////////////////////////////*/
|
||
object AI_GetNearbyFleeObject()
|
||
{
|
||
object oReturn, oGroup, oEndReturn;
|
||
int iCnt;
|
||
string sCheck;
|
||
if(GetSpawnInCondition(AI_FLAG_FLEEING_FLEE_TO_NEAREST_NONE_SEEN, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD,
|
||
CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
if(GetIsObjectValid(oReturn)) // Need LOS check
|
||
{
|
||
return oReturn;
|
||
}
|
||
}
|
||
if(GetSpawnInCondition(AI_FLAG_FLEEING_FLEE_TO_OBJECT, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
sCheck = GetLocalString(OBJECT_SELF, AI_FLEE_OBJECT);
|
||
if(sCheck != "")
|
||
{
|
||
// We need to get the nearest of sCheck objects we cannot see nor hear, and
|
||
// over 6 meters just in case.
|
||
iCnt = i1;
|
||
oReturn = GetNearestObjectByTag(sCheck, OBJECT_SELF, iCnt);
|
||
while(GetIsObjectValid(oReturn))
|
||
{
|
||
// this should be simple enough to break when the object is valid.
|
||
if(!GetObjectSeen(oReturn) && !GetObjectHeard(oReturn) &&
|
||
GetDistanceToObject(oReturn) > f6) // (must be same area)
|
||
{
|
||
// Stop if valid
|
||
return oReturn;
|
||
}
|
||
iCnt++;
|
||
oReturn = GetNearestObjectByTag(sCheck, OBJECT_SELF, iCnt);
|
||
}
|
||
if(!GetIsObjectValid(oReturn))
|
||
{
|
||
// Just get any!
|
||
oReturn = GetObjectByTag(sCheck);
|
||
if(GetIsObjectValid(oReturn))
|
||
{
|
||
return oReturn;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Reset
|
||
oReturn = OBJECT_INVALID;
|
||
oGroup = OBJECT_INVALID;
|
||
// By default:
|
||
// At 1-3 INT, we run to the nearest non-seen, non-heard.
|
||
// At 4-7, we run to the best ally group, within 35M, or an ally who is +5 our HD.
|
||
// At 8+, we run to the best group, in 70M, or an ally who is +8 our HD, and we shout for help (HB)
|
||
|
||
if(GlobalIntelligence <= i3)
|
||
{
|
||
// Don't care if not valid!
|
||
oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD,
|
||
CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
return oReturn;
|
||
}
|
||
|
||
// Counters ETC
|
||
int iBestAllyGroupTotal, iCurrentGroupHD, nCnt, iGroupCnt, IfHigherBreak;
|
||
// Set floats
|
||
float fMaxCheckForGroup, fMaxGroupRange;
|
||
// Check range (Ie people we get near to us, need to be in this range)
|
||
fMaxCheckForGroup = 100.0;// 10 tiles
|
||
if(GlobalIntelligence >= i8) fMaxCheckForGroup *= i2; // Double check range.
|
||
fMaxGroupRange = f15;// Default. No need to change.
|
||
|
||
// We break when we have a group totaling some value of our HD...
|
||
// It goes up as intelligence does.(IE 4, 5, 6, 7, 8, 9 or 10 * HD/2 + 1)
|
||
IfHigherBreak = GlobalOurHitDice * ((GlobalIntelligence / i2) + i1);
|
||
// Note to self: THis means highest intelligence runs futhest away, hopefully smarter.
|
||
// Note: Need an acceptable limit. 10 * 20 is 200, max 100 though.
|
||
if(IfHigherBreak > i100) IfHigherBreak = i100;
|
||
|
||
nCnt = i1;// Start at 1 nearest.
|
||
// Nearest ally is got...we use not seen/not heard, not PC and friendly.
|
||
// Making it oReturn might return something at the very least.
|
||
oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
OBJECT_SELF, nCnt,
|
||
CREATURE_TYPE_IS_ALIVE, TRUE,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
|
||
// Need to be valid, the things we get, and not in X float meters.
|
||
while(GetIsObjectValid(oReturn) && GetDistanceToObject(oReturn) <= fMaxCheckForGroup)
|
||
{
|
||
// Loop the people around him,
|
||
iCurrentGroupHD = GetHitDice(oReturn);
|
||
iGroupCnt = i1;
|
||
oGroup = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
oReturn, iGroupCnt,
|
||
CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
|
||
// Remeber 15M range limit.
|
||
while(GetIsObjectValid(oGroup) &&
|
||
GetDistanceBetween(oReturn, oGroup) <= fMaxGroupRange)
|
||
{
|
||
iCurrentGroupHD += GetHitDice(oGroup);
|
||
// Get next group object
|
||
iGroupCnt++;
|
||
oGroup = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
oReturn, iGroupCnt,
|
||
CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
|
||
}
|
||
// Sets the ally, if got lots of allies. (Or a mass of HD)
|
||
if(iCurrentGroupHD > iBestAllyGroupTotal)
|
||
{
|
||
iBestAllyGroupTotal = iCurrentGroupHD;
|
||
oEndReturn = oReturn;
|
||
// Break time.
|
||
// It is (Int * HD/2 + 1), max 100. Shouldn't be too bad.
|
||
if(iCurrentGroupHD >= IfHigherBreak)
|
||
{
|
||
// Return the object
|
||
return oReturn;
|
||
}
|
||
}
|
||
nCnt++;
|
||
oReturn = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
OBJECT_SELF, nCnt,
|
||
CREATURE_TYPE_IS_ALIVE, TRUE,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_NOT_SEEN_AND_NOT_HEARD);
|
||
}
|
||
// By default, return nothing (oEndReturn = OBJECT_INVALID unless set in loop)
|
||
return oEndReturn;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_CompareTimeStopStored
|
||
//::///////////////////////////////////////////////
|
||
TRUE if the spell is one recorded as being cast before in time stop.
|
||
- Checks Global "Are we in time stop?" and returns FALSE if not in time stop
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_CompareTimeStopStored(int nSpell, int nSpell2 = 0, int nSpell3 = 0, int nSpell4 = 0)
|
||
{
|
||
// Set array size is under 0, IE no array, stop.
|
||
if(GlobalTimeStopArraySize < i0) return FALSE;
|
||
if(GlobalInTimeStop)
|
||
{
|
||
int iSpell = iM1;
|
||
int iCnt;
|
||
if(GlobalTimeStopArraySize == i0)
|
||
{
|
||
GlobalTimeStopArraySize = GetAIInteger(TIME_STOP_LAST_ARRAY_SIZE);
|
||
if(GlobalTimeStopArraySize == i0)
|
||
{
|
||
GlobalTimeStopArraySize = iM1;
|
||
}
|
||
}
|
||
if(GlobalTimeStopArraySize > i0)
|
||
{
|
||
for(iCnt = i1; iCnt <= GlobalTimeStopArraySize; iCnt++)
|
||
{
|
||
iSpell = GetAIConstant(TIME_STOP_LAST_ + IntToString(iCnt));
|
||
if(iSpell == nSpell ||
|
||
iSpell == nSpell2 ||
|
||
iSpell == nSpell3 ||
|
||
iSpell == nSpell4)
|
||
{
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_GetBestFriendyAreaSpellTarget
|
||
//::///////////////////////////////////////////////
|
||
Returns the object to the specifications:
|
||
Within nRange (float)
|
||
The most targets around the creature in nRange, in nSpread.
|
||
Can be the caster, of course
|
||
//:://///////////////////////////////////////////*/
|
||
/* UNCOMMENT
|
||
object AI_GetBestFriendyAreaSpellTarget(float fRange, float fSpread, int nShape)
|
||
{
|
||
object oGroupies, oSpellTarget, oTarget;
|
||
int iCountOnPerson, iMostOnPerson, nCnt;
|
||
location lTarget;
|
||
// Will always at least return ourselves.
|
||
oSpellTarget = OBJECT_SELF;
|
||
nCnt = i1;
|
||
// Gets the nearest friend...the loops takes care of range.
|
||
oTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(nCnt));
|
||
// Start loop. Checks range here.
|
||
while(GetIsObjectValid(oTarget) && GetDistanceToObject(oTarget) <= fRange)
|
||
{
|
||
lTarget = GetLocation(oTarget);
|
||
// Starts the count at 0, as first object in shape will also include the target.
|
||
iCountOnPerson = i0;
|
||
// Reset/Start counting the spread on oTarget.
|
||
oGroupies = GetFirstObjectInShape(nShape, fSpread, lTarget);
|
||
// If oGroupies is invalid, nothing.
|
||
while(GetIsObjectValid(oGroupies))
|
||
{
|
||
// Only add one if the person is an friend
|
||
if(GetIsFriend(oGroupies))
|
||
{
|
||
iCountOnPerson++;
|
||
}
|
||
oGroupies = GetNextObjectInShape(nShape, fSpread, lTarget);
|
||
}
|
||
if(iCountOnPerson > iMostOnPerson)
|
||
{
|
||
iMostOnPerson = iCountOnPerson;
|
||
oSpellTarget = oTarget;
|
||
}
|
||
nCnt++;
|
||
oTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(nCnt));
|
||
}
|
||
// Will always return self if anything
|
||
return oSpellTarget;
|
||
} END UNCOMMENT*/
|
||
// If the target will always save against iSaveType, and will take no damage, returns TRUE
|
||
// * Target is GlobalSpellTarget. Save is GlobalSpellTargetWill ETC.
|
||
// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE
|
||
// * iSpellLevel - Level of the spell being cast.
|
||
int AI_SaveImmuneSpellTarget(int iSaveType, int iSpellLevel)
|
||
{
|
||
if(iSaveType == SAVING_THROW_FORT)
|
||
{
|
||
// Basic one here. Some addition and comparison.
|
||
if(GlobalSpellTargetFort - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE;
|
||
}
|
||
else if(iSaveType == SAVING_THROW_REFLEX)
|
||
{
|
||
// Evasion - full damaged saved if the save is sucessful.
|
||
if(GetHasFeat(FEAT_EVASION, GlobalSpellTarget) ||
|
||
GetHasFeat(FEAT_IMPROVED_EVASION, GlobalSpellTarget))
|
||
{
|
||
if(GlobalSpellTargetReflex - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE;
|
||
}
|
||
}
|
||
else if(iSaveType == SAVING_THROW_WILL)
|
||
{
|
||
// Slippery mind has a re-roll. 1.3 - Ignore
|
||
if(GlobalSpellTargetWill - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel))
|
||
{
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// If the target will always save against iSaveType, and will take no damage, returns TRUE
|
||
// * oTarget - who saving against spell.
|
||
// * iSaveType - SAVING_THROW WILL/REFLEX/FORTITUDE
|
||
// * The save used is GetReflexSavingThrow* ETC.
|
||
// * iSpellLevel - Level of the spell being cast.
|
||
int AI_SaveImmuneAOE(object oTarget, int iSaveType, int iSpellLevel)
|
||
{
|
||
// GlobalSaveStupidity = 10 - Intelligence. Basically, lower intellgence
|
||
// will fire spells which may do nothing more often.
|
||
|
||
// Return if no level (innate ability normally)
|
||
if(iSpellLevel == FALSE) return FALSE;
|
||
if(iSaveType == SAVING_THROW_FORT)
|
||
{
|
||
// Basic one here. Some addition and comparison.
|
||
if(GetFortitudeSavingThrow(oTarget) - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE;
|
||
}
|
||
else if(iSaveType == SAVING_THROW_REFLEX)
|
||
{
|
||
// Evasion - full damaged saved if the save is sucessful.
|
||
if(GetHasFeat(FEAT_EVASION, oTarget) ||
|
||
GetHasFeat(FEAT_IMPROVED_EVASION, oTarget))
|
||
{
|
||
if(GetReflexSavingThrow(oTarget)- GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel)) return TRUE;
|
||
}
|
||
}
|
||
else if(iSaveType == SAVING_THROW_WILL)
|
||
{
|
||
// Slippery mind has a re-roll. We ignore in 1.3
|
||
if(GetWillSavingThrow(oTarget) - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel))
|
||
{
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: SpellResistanceImmune
|
||
//::///////////////////////////////////////////////
|
||
This checks targets spell resistance. If our level + 20 is below thier
|
||
resistance, the spell won't affect them.
|
||
//::////////////////////////////////////////////*/
|
||
int AI_SpellResistanceImmune(object oTarget)
|
||
{
|
||
// Check the targets spell resistance VS our GlobalSpellPenetrationRoll
|
||
// GlobalSpellPenetrationRoll = Our Hit Dice + Special help feats.
|
||
if(GetSpellResistance(oTarget) >= GlobalSpellPenetrationRoll)
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Note: REmoved monk feat, and spell resistance spell, but not sure if
|
||
// it checks alignment-orientated SR's...
|
||
/* if(GlobalOurGoodEvil != ALIGNMENT_NEUTRAL)
|
||
{
|
||
// Alinment protection (highest) is a SR 25
|
||
if(GlobalOurGoodEvil == ALIGNMENT_GOOD)
|
||
{
|
||
if(GetHasSpellEffect(SPELL_UNHOLY_AURA))
|
||
{
|
||
if(i25 >= GlobalSpellPenetrationRoll) return TRUE;
|
||
}
|
||
}
|
||
else if(GlobalOurGoodEvil == ALIGNMENT_EVIL)
|
||
{
|
||
if(GetHasSpellEffect(SPELL_HOLY_AURA))
|
||
{
|
||
if(i25 >= GlobalSpellPenetrationRoll) return TRUE;
|
||
}
|
||
}
|
||
} */
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: GetSpellLevelEffect
|
||
//::///////////////////////////////////////////////
|
||
This returns a number, 1-4. This number is the levels
|
||
of spell they will be totally immune to.
|
||
//::////////////////////////////////////////////*/
|
||
int AI_GetSpellLevelEffect(object oTarget)
|
||
{
|
||
int iNatural = GetLocalInt(oTarget, AI_SPELL_IMMUNE_LEVEL);
|
||
// Stop here, if natural is over 4
|
||
if(iNatural > i4) return iNatural;
|
||
|
||
// Big globe affects 4 or lower spells
|
||
if(GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, oTarget) || iNatural >= i4)
|
||
return i4;
|
||
// Minor globe is 3 or under
|
||
if(GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oTarget) ||
|
||
// Shadow con version
|
||
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, oTarget) ||
|
||
iNatural >= i3)
|
||
return i3;
|
||
// 2 and under is ethereal visage.
|
||
if(GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget) || iNatural >= i2)
|
||
return i2;
|
||
// Ghostly Visarge affects 1 or 0 level spells, and any spell immunity.
|
||
if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget) || iNatural >= i1 ||
|
||
// Or shadow con version.
|
||
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE, oTarget))
|
||
return i1;
|
||
// Return iNatural, which is 0-9
|
||
return FALSE;
|
||
}
|
||
|
||
int AI_GetSpellLevelEffectAOE(object oTarget, int iLevel = 9)
|
||
{
|
||
// Return if no level (innate ability normally)
|
||
if(iLevel == FALSE) return FALSE;
|
||
// Checks any NPC natural total spell immunities (like globes, but on hides)
|
||
// - On PC's, the local will return 0. this saves an extra !GetIsPC check.
|
||
if(iLevel <= GetLocalInt(oTarget, AI_SPELL_IMMUNE_LEVEL))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Big globe affects 4 or lower spells
|
||
if(iLevel <= i4 && GetHasSpellEffect(SPELL_GLOBE_OF_INVULNERABILITY, oTarget))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Minor globe is 3 or under
|
||
if(iLevel <= i3 && (GetHasSpellEffect(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, oTarget) ||
|
||
// Shadow con version
|
||
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, oTarget)))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// 2 and under is ethereal visage.
|
||
if(iLevel <= i2 && GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// 1 and under is ghostly visage.
|
||
if(iLevel <= i1 && (GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget) ||
|
||
GetHasSpellEffect(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE)))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// False if a level 5+ spell
|
||
return FALSE;
|
||
}
|
||
|
||
// Returns TRUE if any of the checks the oGroupTarget is immune to.
|
||
int AI_AOEDeathNecroCheck(object oGroupTarget, int iNecromanticSpell, int iDeathImmune)
|
||
{
|
||
if(iNecromanticSpell)
|
||
{
|
||
if(AI_GetAIHaveSpellsEffect(GlobalHasDeathWardSpell, oGroupTarget)) return TRUE;
|
||
}
|
||
if(iDeathImmune)
|
||
{
|
||
return GetIsImmune(oGroupTarget, IMMUNITY_TYPE_DEATH);
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*:://////////////////////////////////////////////
|
||
//:: Name Get the best HOSTILE spell target
|
||
//:: Function Name AI_GetBestAreaSpellTarget
|
||
//:://////////////////////////////////////////////
|
||
Returns the object to the specifications:
|
||
* fRange - Within fRange (fTouchRange 2.25, fShortRange 8.0, fMediumRange 20.0, fLongRange = 40.0)
|
||
* fSpread - Radius Size - RADIUS_SIZE_* constants (1.67 to 10.0M)
|
||
* nLevel - Used for saving throws/globe checks. Level of the spell added to save checks
|
||
* iSaveType = FALSE - SAVING_THROW_FORT/REFLEX/WILL. Not type, but the main save applied with it.
|
||
* nShape = SHAPE_SPHERE - SHAPE_* constants.
|
||
* nFriendlyFire = FALSE - Can this hurt allies? Best thing is to put
|
||
GlobalFriendlyFireHostile - GetIsReactionTypeHostile(oTarget) == TRUE
|
||
GlobalFriendlyFireFriendly - GetIsReactionTypeFriendly(oTarget) == FALSE
|
||
FALSE - Cannot hurt allies (GetIsEnemy/!GetIsFriend used)
|
||
* iDeathImmune = FALSE - Does it use a death save? (!GetIsImmune)
|
||
* iNecromanticSpell = FALSE - Is it a necromancy spell? Undead are also checked in this.
|
||
//:://////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
object AI_GetBestAreaSpellTarget(float fRange, float fSpread, int nLevel, int iSaveType = FALSE, int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE, int iDeathImmune = FALSE, int iNecromanticSpell = FALSE)
|
||
{
|
||
// Before we start, if it can harm us, we don't fire it if there are no
|
||
// enemies out of the blast, if it was centered on us
|
||
if(nFriendlyFire)
|
||
{
|
||
if(GlobalRangeToFuthestEnemy <= fSpread) return OBJECT_INVALID;
|
||
}
|
||
// Delcare objects
|
||
// (Target = Loop of nearest creatures. oResturnTarget = Who to return,
|
||
// and oGroupTarget is GetFirst/Next in nShape around oTarget)
|
||
object oTarget, oReturnTarget, oGroupTarget;
|
||
// Delcare Integers
|
||
int iCnt, iCntEnemiesOnTarget, iCntAlliesOnTarget, iNoHittingAllies,
|
||
iMostOnPerson, iMaxAlliesToHit, iOurToughness;
|
||
// Declare Booleans
|
||
int bWillHitAlly, bNoHittingAllies, bCheckAlliesHP;
|
||
location lTarget;
|
||
// Float values
|
||
float fDistance, fTargetMustBeFrom;
|
||
|
||
// - We don't check specific immunities with cirtain properties/settings
|
||
// If we have NOT got the setting and <= 6 int
|
||
if(GlobalIntelligence <= i6 &&
|
||
!GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_IMMUNITY_CHECKING, AI_COMBAT_MASTER))
|
||
{
|
||
iDeathImmune = FALSE;
|
||
iNecromanticSpell = FALSE;
|
||
}
|
||
// NOT got extra checks in.
|
||
if(!GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SPECIFIC_SPELL_IMMUNITY, AI_COMBAT_MASTER))
|
||
{
|
||
nLevel = i5; // This makes the spell think it gets past globes.
|
||
// - Problem is that the save DC will increase. Won't affect much.
|
||
}
|
||
|
||
// We set up the distance that oTarget must be away from us, if it is
|
||
// no friendly fire, it will be 0.0 meters, so we always use them.
|
||
// - defaults to 0.0 meters unless FF is on
|
||
if(nFriendlyFire)
|
||
{
|
||
fTargetMustBeFrom = fSpread + 0.3;
|
||
}
|
||
|
||
// Can we hit allies?
|
||
bNoHittingAllies = GetSpawnInCondition(AI_FLAG_COMBAT_NEVER_HIT_ALLIES, AI_COMBAT_MASTER);
|
||
// Do we check thier HP? (Allies) to see if they survive?
|
||
bCheckAlliesHP = GetSpawnInCondition(AI_FLAG_COMBAT_AOE_DONT_MIND_IF_THEY_SURVIVE, AI_COMBAT_MASTER);
|
||
|
||
// We will subtract all non-enemies within -8 challenge, upwards.
|
||
iOurToughness = GlobalOurHitDice - GetBoundriedAIInteger(AI_AOE_HD_DIFFERENCE, -8, i0, -30);
|
||
// The maximum number of allies we can hit...
|
||
iMaxAlliesToHit = GetBoundriedAIInteger(AI_AOE_ALLIES_LOWEST_IN_AOE, i3, i1, i90);
|
||
// The target to return - set to invalid to start
|
||
oReturnTarget = OBJECT_INVALID;
|
||
|
||
iCnt = i1;
|
||
// - 1.3 Change - made to target only creatures (trying to better performance)
|
||
oTarget = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, iCnt);
|
||
// Need distance a lot.
|
||
fDistance = GetDistanceToObject(oTarget);
|
||
// Need to see the target, within nRange around self.
|
||
while(GetIsObjectValid(oTarget) && fDistance <= fRange)
|
||
{
|
||
// Need seen/heard target
|
||
if(GetObjectSeen(oTarget) || GetObjectHeard(oTarget))
|
||
{
|
||
// Will not fire on self, if it is too near.
|
||
if(fDistance > fTargetMustBeFrom)
|
||
{
|
||
// Reset Targets
|
||
// - The person starts the spread at 0, because the object in shape
|
||
// will return the target as well.
|
||
iCntEnemiesOnTarget = FALSE;
|
||
iCntAlliesOnTarget = FALSE;
|
||
bWillHitAlly = FALSE;
|
||
// Must sue this - needs to use the correct shape
|
||
// This only gets creatures in shape to check.
|
||
lTarget = GetLocation(oTarget);
|
||
oGroupTarget = GetFirstObjectInShape(nShape, fSpread, lTarget);
|
||
// If oGroupies is invalid, nothing. Should not - as target will be returned at least.
|
||
while(GetIsObjectValid(oGroupTarget) && bWillHitAlly != TRUE)
|
||
{
|
||
// Sanity check for checking oGroupTarget
|
||
if(oGroupTarget != OBJECT_SELF &&
|
||
!GetIsDead(oGroupTarget) && !GetPlotFlag(oGroupTarget) &&
|
||
!GetIgnore(oGroupTarget) && !GetIsDM(oGroupTarget) &&
|
||
!AI_SpellResistanceImmune(oGroupTarget))
|
||
{
|
||
// Check necromancy immunity, and death immunity.
|
||
// + Save check
|
||
// + Level Immunity check
|
||
if(!AI_AOEDeathNecroCheck(oGroupTarget, iNecromanticSpell, iDeathImmune) &&
|
||
!AI_SaveImmuneAOE(oGroupTarget, iSaveType, nLevel) &&
|
||
!AI_GetSpellLevelEffectAOE(oGroupTarget, nLevel))
|
||
{
|
||
if(GetIsEnemy(oGroupTarget))
|
||
{
|
||
// Only add one if the person is an enemy,
|
||
// and the spell will affect them
|
||
iCntEnemiesOnTarget++;
|
||
}
|
||
// But else if friendly fire, we will subract
|
||
// similar non-allies.
|
||
else if(nFriendlyFire &&
|
||
(GetIsFriend(oGroupTarget) || GetFactionEqual(oGroupTarget)) &&
|
||
GetHitDice(oGroupTarget) >= iOurToughness)
|
||
{
|
||
if(bNoHittingAllies)
|
||
{
|
||
bWillHitAlly = TRUE;
|
||
}
|
||
// We ignore if not got the setting to check HP,
|
||
// else, we take one from the iCntEnemiesOnTarget.
|
||
else if(bCheckAlliesHP && GetCurrentHitPoints(oGroupTarget) < i50)
|
||
{
|
||
iCntAlliesOnTarget++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Get next target in shape
|
||
oGroupTarget = GetNextObjectInShape(nShape, fSpread, lTarget);
|
||
}
|
||
// Make the spell target oTarget if so.
|
||
// If it is >= then we set it - it is a futher away target!
|
||
if(bWillHitAlly != TRUE && iCntEnemiesOnTarget >= i1 &&
|
||
iCntAlliesOnTarget < iMaxAlliesToHit &&
|
||
((iCntEnemiesOnTarget - iCntAlliesOnTarget) >= iMostOnPerson))
|
||
{
|
||
iMostOnPerson = iCntEnemiesOnTarget - iCntAlliesOnTarget;
|
||
oReturnTarget = oTarget;
|
||
}
|
||
}
|
||
}
|
||
// Gets the next nearest.
|
||
iCnt++;
|
||
oTarget = GetNearestObject(OBJECT_TYPE_CREATURE, OBJECT_SELF, iCnt);
|
||
fDistance = GetDistanceToObject(oTarget);
|
||
}
|
||
// Will OBJECT_INVALID, or the best target in range.
|
||
return oReturnTarget;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_SortSpellImmunities
|
||
//::///////////////////////////////////////////////
|
||
Sets the Global Hex for the enemy spell target immunties.
|
||
- Uses effects loop and GetIsImmune to comprehend the most.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
void AI_SortSpellImmunities()
|
||
{
|
||
// Error checking
|
||
if(!GetIsObjectValid(GlobalSpellTarget)) return;
|
||
// Check effects of GlobalSpellTarget
|
||
effect eCheck = GetFirstEffect(GlobalSpellTarget);
|
||
while(GetIsEffectValid(eCheck))
|
||
{
|
||
switch(GetEffectType(eCheck))
|
||
{
|
||
case EFFECT_TYPE_TURNED:
|
||
case EFFECT_TYPE_FRIGHTENED:
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunityFear);
|
||
}
|
||
break;
|
||
case EFFECT_TYPE_STUNNED:
|
||
case EFFECT_TYPE_PARALYZE: // Count as stun. Near as dammit
|
||
case EFFECT_TYPE_DAZED: // Here for now :-)
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunityStun);
|
||
}
|
||
break;
|
||
case EFFECT_TYPE_SLEEP:
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunitySleep);
|
||
}
|
||
break;
|
||
case EFFECT_TYPE_POISON:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityPoison);
|
||
break;
|
||
case EFFECT_TYPE_NEGATIVELEVEL:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityNegativeLevel);
|
||
break;
|
||
case EFFECT_TYPE_ENTANGLE:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityEntangle);
|
||
break;
|
||
case EFFECT_TYPE_DOMINATED:
|
||
case EFFECT_TYPE_CHARMED:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityMind);
|
||
break;
|
||
case EFFECT_TYPE_CONFUSED:
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunityConfusion);
|
||
}
|
||
break;
|
||
case EFFECT_TYPE_DISEASE:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityDisease);
|
||
break;
|
||
case EFFECT_TYPE_CURSE:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityCurse);
|
||
break;
|
||
case EFFECT_TYPE_BLINDNESS:
|
||
case EFFECT_TYPE_DEAF:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityBlindDeaf);
|
||
break;
|
||
case EFFECT_TYPE_SLOW:
|
||
AI_SetSpellTargetImmunity(GlobalImmunitySlow);
|
||
break;
|
||
// All
|
||
case EFFECT_TYPE_PETRIFY:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityPetrify);
|
||
break;
|
||
// Defualt, check spell ID
|
||
default:
|
||
{
|
||
switch(GetEffectSpellId(eCheck))
|
||
{
|
||
// Mantals
|
||
case SPELL_GREATER_SPELL_MANTLE:
|
||
case SPELL_SPELL_MANTLE:
|
||
case SPELL_LESSER_SPELL_MANTLE:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityMantalProtection);
|
||
break;
|
||
// If the target has death ward, they are immune.
|
||
case SPELL_DEATH_WARD:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityDeath);
|
||
break;
|
||
// Spells that stop negative energy spells: Shadow Shield, Negative energy protection
|
||
case SPELL_SHADOW_SHIELD:
|
||
case SPELL_UNDEATHS_ETERNAL_FOE:// New one SoU
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunityNegativeEnergy);
|
||
AI_SetSpellTargetImmunity(GlobalImmunityNecromancy);
|
||
}
|
||
break;
|
||
case SPELL_NEGATIVE_ENERGY_PROTECTION:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityNegativeEnergy);
|
||
break;
|
||
case SPELL_CLARITY:
|
||
case SPELL_MIND_BLANK:
|
||
case SPELL_LESSER_MIND_BLANK:// New one SoU
|
||
AI_SetSpellTargetImmunity(GlobalImmunityMind);
|
||
break;
|
||
case SPELL_UNHOLY_AURA:
|
||
case SPELL_MAGIC_CIRCLE_AGAINST_GOOD:
|
||
case SPELL_PROTECTION_FROM_GOOD:// New one SoU
|
||
if(GlobalOurGoodEvil == ALIGNMENT_GOOD) AI_SetSpellTargetImmunity(GlobalImmunityMind);
|
||
break;
|
||
case SPELL_HOLY_AURA:
|
||
case SPELL_PROTECTION_FROM_EVIL:
|
||
case SPELL_MAGIC_CIRCLE_AGAINST_EVIL:// New one SoU
|
||
if(GlobalOurGoodEvil == ALIGNMENT_EVIL) AI_SetSpellTargetImmunity(GlobalImmunityMind);
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
eCheck = GetNextEffect(GlobalSpellTarget);
|
||
}
|
||
// Immunity Things
|
||
// We only use these if 7+ Intelligence
|
||
if(GlobalIntelligence >= i7 ||
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_IMMUNITY_CHECKING, AI_COMBAT_MASTER))
|
||
{
|
||
// Death immunity
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DEATH))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityDeath);
|
||
// confusion immunity
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityConfusion) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_CONFUSED))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityConfusion);
|
||
// Negative level immunity
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_NEGATIVE_LEVEL))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityNegativeLevel);
|
||
// Mind immunity
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_MIND_SPELLS))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityMind);
|
||
// Old ones, I doubt needed with GetIsImmune in.
|
||
// - Dragon race, Construct, Undead and Empty Body.
|
||
// Fear checking
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityFear) &&
|
||
(AI_GetSpellTargetImmunity(GlobalImmunityMind) ||
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_FEAR)))
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunityFear);
|
||
}
|
||
// Old ones for fear.
|
||
// - Undead, Dragon, Construct, Outsider
|
||
// - Feat Aura of courage, resist natures lure
|
||
// This stops curses.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityCurse) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_CURSED))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityCurse);
|
||
// This stops poison.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityPoison) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_POISON))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityPoison);
|
||
// This will stop blindness/deafness spells
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) &&
|
||
(GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_BLINDNESS) ||
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DEAFNESS)))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityBlindDeaf);
|
||
// Stun (And paralsis)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
(GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_STUN) ||
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_PARALYSIS)))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityStun);
|
||
// This stops entanglement.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityEntangle) &&
|
||
(GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_ENTANGLE) ||
|
||
GetHasFeat(FEAT_WOODLAND_STRIDE, GlobalSpellTarget)))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityEntangle);
|
||
// Sleep
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunitySleep) &&
|
||
(GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_SLEEP) ||
|
||
GetHasFeat(FEAT_IMMUNITY_TO_SLEEP, GlobalSpellTarget)))
|
||
AI_SetSpellTargetImmunity(GlobalImmunitySleep);
|
||
// Poison
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityPoison) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_POISON))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityPoison);
|
||
// Disease
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDisease) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DISEASE))
|
||
AI_SetSpellTargetImmunity(GlobalImmunityDisease);
|
||
// We don't check these feats for immune disease/poison till checked
|
||
// if the immune doesn't include htem
|
||
// GetHasFeat(FEAT_RESIST_DISEASE, GlobalSpellTarget)
|
||
// GetHasFeat(FEAT_VENOM_IMMUNITY, GlobalSpellTarget)
|
||
// GetHasFeat(FEAT_DIAMOND_BODY, GlobalSpellTarget)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunitySlow) &&
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_SLOW))
|
||
AI_SetSpellTargetImmunity(GlobalImmunitySlow);
|
||
// Domination (+Charm)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination))
|
||
{
|
||
if(GetIsObjectValid(GetMaster(GlobalSpellTarget)) ||
|
||
GlobalSpellTargetRace == RACIAL_TYPE_ELEMENTAL ||
|
||
GlobalSpellTargetRace == RACIAL_TYPE_UNDEAD ||
|
||
GlobalSpellTargetRace == RACIAL_TYPE_VERMIN ||
|
||
GlobalSpellTargetRace == RACIAL_TYPE_CONSTRUCT ||
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_DOMINATE) ||
|
||
GetIsImmune(GlobalSpellTarget, IMMUNITY_TYPE_CHARM) ||
|
||
GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_DOMINATED)))
|
||
{
|
||
AI_SetSpellTargetImmunity(GlobalImmunityDomination);
|
||
}
|
||
}
|
||
}
|
||
int iApperance = GetAppearanceType(GlobalSpellTarget);
|
||
// We won't use petrify on those immune, though. This is taken from
|
||
// x0_i0_spells. Stops silly basalisks or something. :-)
|
||
// - ANY intelligence
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify))
|
||
{
|
||
switch(GetAppearanceType(GlobalSpellTarget))
|
||
{
|
||
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:
|
||
AI_SetSpellTargetImmunity(GlobalImmunityPetrify);
|
||
break;
|
||
}
|
||
}
|
||
// Earthquake check removed.
|
||
/* Still got:
|
||
IMMUNITY_TYPE_ABILITY_DECREASE IMMUNITY_TYPE_AC_DECREASE
|
||
IMMUNITY_TYPE_ATTACK_DECREASE IMMUNITY_TYPE_CRITICAL_HIT
|
||
IMMUNITY_TYPE_DAMAGE_DECREASE IMMUNITY_TYPE_DAMAGE_IMMUNITY_DECREASE
|
||
IMMUNITY_TYPE_MOVEMENT_SPEED_DECREASE IMMUNITY_TYPE_SAVING_THROW_DECREASE
|
||
IMMUNITY_TYPE_SILENCE IMMUNITY_TYPE_SKILL_DECREASE
|
||
IMMUNITY_TYPE_SPELL_RESISTANCE_DECREASE IMMUNITY_TYPE_TRAP */
|
||
|
||
}
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_ActionDispelAOE
|
||
//::///////////////////////////////////////////////
|
||
Dispels or moves out of the oAOE.
|
||
- If we have >= iMax AND <= iDamage HP...
|
||
AND under iPercent total HP...
|
||
- We Dispel, or move out of the AOE. Move if friendly.
|
||
Note: We get nearest AOE of sAOE tag. Must be valid as well.
|
||
|
||
1.3 - Added
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_ActionDispelAOE(int iSpell, int iDamageOnly, float fRange, int iDamage, int iMax, int iPercent)
|
||
{
|
||
object oAOE = GetAIObject(AI_TIMER_AOE_SPELL_EVENT + IntToString(iSpell));
|
||
// Get damage done
|
||
int iLastDamage = GetAIInteger(ObjectToString(oAOE));
|
||
// Delete/clean up
|
||
DeleteAIInteger(ObjectToString(oAOE));
|
||
// Check we will be affected, or are affected, by the AOE at all!
|
||
if(GetIsObjectValid(oAOE) && GetDistanceToObject(oAOE) <= fRange &&
|
||
// + Damaged from that AOE, or affected by that AOE.
|
||
(iLastDamage >= TRUE || (GetHasSpellEffect(iSpell) && !iDamageOnly)))
|
||
{
|
||
// Either Under iMax AND under
|
||
if((GlobalOurMaxHP >= iMax && GlobalOurCurrentHP <= iDamage) ||
|
||
GlobalOurPercentHP <= iPercent || !GlobalAnyValidTargetObject)
|
||
{
|
||
// 16: "[AOE Call] Moving out of/Dispeling an AOE. [Tag] " + GetTag(oAOE)
|
||
DebugActionSpeakByInt(16, oAOE);
|
||
// If an enemy, Dispel it.
|
||
if(!GetIsFriend(GetAreaOfEffectCreator(oAOE)))
|
||
{
|
||
// 11 = Harmful AOE indis. 2 = Harmful ranged.
|
||
// Gust of Wind. Level 3 (Bard/Mage) Dispels all AOE's in radius, and save VS fort for 3 round knockdown.
|
||
if(AI_ActionCastSpell(SPELL_GUST_OF_WIND, SpellHostAreaInd, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
// Worst to best, then move...wind gust best!
|
||
// - Note that they have a % chance to remove stuff...
|
||
if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, oAOE, i13, TRUE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, oAOE, i16, TRUE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, oAOE, i19, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
// Else move. We force this until we are out of the AOE
|
||
SetAIObject(AI_AOE_FLEE_FROM, oAOE);
|
||
SetLocalFloat(OBJECT_SELF, AI_AOE_FLEE_FROM_RANGE, fRange + GlobalOurReach);
|
||
SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE);
|
||
ActionMoveAwayFromLocation(GetLocation(oAOE), TRUE, fRange + GlobalOurReach);
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: Special Checks
|
||
//::///////////////////////////////////////////////
|
||
This will check for darkness, AOE spells,
|
||
time stop stored, and a few other things.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptSpecialChecks()
|
||
{
|
||
// Delete stored ints if not in time stop.
|
||
if(!GlobalInTimeStop) AI_DeleteTimeStopStored();
|
||
|
||
// - Fleeing is performed in ClearAllAction()'s wrapper
|
||
|
||
// Darkness! the ENEMY OF THE ARTIFICAL INTELlIGENCE!!! MUAHAHAH! WE COMBAT IT WELL! (sorta)
|
||
if((AI_GetAIHaveSpellsEffect(GlobalEffectDarkness) &&
|
||
!AI_GetAIHaveEffect(GlobalEffectUltravision) &&
|
||
!AI_GetAIHaveEffect(GlobalEffectTrueSeeing))
|
||
// Check nearest enemy with darkness effect. Use heard only (they won't be seen!)
|
||
|| GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_DARKVISION, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD)))
|
||
{
|
||
// Ultravision - Talent category 10 (Benificial enchancement Self).
|
||
if(AI_ActionCastSpell(SPELL_DARKVISION, SpellEnhSelf, OBJECT_SELF, i13, FALSE, ItemEnhSelf, PotionEnh)) return TRUE;
|
||
// 1.30/SoU trueeing should Dispel it. (Benificial conditional AOE)
|
||
if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, FALSE, ItemConAre, PotionCon)) return TRUE;
|
||
|
||
// If we are a rubbish fightingclass OR we cannot hear any enemy to attack...move or something.
|
||
if(!GlobalValidNearestSeenEnemy &&
|
||
// If only someone else has it, don't worry, its only if we cannot see anyone
|
||
(GlobalOurChosenClass == CLASS_TYPE_WIZARD ||
|
||
GlobalOurChosenClass == CLASS_TYPE_SORCERER ||
|
||
GlobalOurChosenClass == CLASS_TYPE_FEY))
|
||
{
|
||
// 17: "[DCR:Special] Darkness + Caster. No seen enemy. Dispel/Move."
|
||
DebugActionSpeakByInt(17);
|
||
// Dispel it - trying nearest creature, if has darkness as well
|
||
// 11 = Harmful AOE indis. 2 = Harmful ranged.
|
||
// WORST to BEST as all of them never check caster levels for AOEs (Silly bioware!)
|
||
if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, OBJECT_SELF, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, OBJECT_SELF, i13, TRUE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, OBJECT_SELF, i16, TRUE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, OBJECT_SELF, i19, TRUE, ItemHostAreaInd)) return TRUE;
|
||
|
||
ActionMoveAwayFromLocation(GetLocation(OBJECT_SELF), TRUE, f6);
|
||
return TRUE;
|
||
}
|
||
}
|
||
object oInvisible = GetAIObject(AI_LAST_TO_GO_INVISIBLE);
|
||
DeleteAIObject(AI_LAST_TO_GO_INVISIBLE);
|
||
// Intelligence: 8+ means HIPS check. There is a 80% chance.
|
||
if((!GetIsObjectValid(oInvisible) || GetObjectSeen(oInvisible)) &&
|
||
GlobalIntelligence >= i8 && d10() <= i8)
|
||
{
|
||
// Makes the AI cast seeing spells against shadowdancers who will HIPS
|
||
oInvisible = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_SHADOWDANCER, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
DeleteLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE);
|
||
}
|
||
// If we have a previously set invisible enemy or if high intelligence, a shadowdancer in range.
|
||
if(GetIsObjectValid(oInvisible))
|
||
{
|
||
// 1.30/SoU trueeing should Dispel it. (Benificial conditional AOE)
|
||
if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, FALSE, ItemConAre, PotionCon)) return TRUE;
|
||
// Invisiblity purge! SpellConAre
|
||
if(AI_ActionCastSpell(SPELL_INVISIBILITY_PURGE, SpellConAre, OBJECT_SELF, i14, FALSE, ItemConAre, PotionCon)) return TRUE;
|
||
// This should work well as well! SpellConSinTar
|
||
if(AI_ActionCastSpell(SPELL_SEE_INVISIBILITY, SpellConSinTar, OBJECT_SELF, i12, FALSE, ItemConSinTar, PotionCon)) return TRUE;
|
||
|
||
location lTarget = GetLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE);
|
||
DeleteLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE);
|
||
|
||
// Check location validness
|
||
if(GetIsObjectValid(GetAreaFromLocation(lTarget)))
|
||
{
|
||
// We will cast at thier previous location, using a temp object we actually
|
||
// create there for a few seconds.
|
||
oInvisible = CreateObject(OBJECT_TYPE_PLACEABLE, "", GetLocalLocation(OBJECT_SELF, AI_LAST_TO_GO_INVISIBLE));
|
||
// Check range
|
||
if(GetIsObjectValid(oInvisible) &&
|
||
GetDistanceToObject(oInvisible) < f40)
|
||
{
|
||
// Best to worst
|
||
if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, oInvisible, i19, TRUE, ItemHostAreaInd)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, oInvisible, i16, TRUE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, oInvisible, i13, TRUE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, oInvisible, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
// Destroy the placeable after we have got the location ETC.
|
||
DestroyObject(oInvisible);
|
||
}
|
||
}
|
||
}
|
||
// AOE spells
|
||
// - Either we Dispel it or move out of it.
|
||
// - Dispeling more if higher INT.
|
||
// - We always move if it does great damage
|
||
// - Low intelligence ignores this (probably to thier doom).
|
||
|
||
// We have already:
|
||
// - Set if there is an AOE that affects us cast at us.
|
||
// - Checked any saves avalible, and immunities (Intelligence based)
|
||
|
||
// Delcarations.
|
||
object oAOE = GetNearestObject(OBJECT_TYPE_AREA_OF_EFFECT);
|
||
float fDistance = GetDistanceToObject(oAOE);
|
||
|
||
// First check: We randomly check this if INT 1-5, and always check 6+
|
||
if(GlobalIntelligence >= (i6 - d6()) &&
|
||
// Second: Need a valid AOE, of course!
|
||
GetIsObjectValid(oAOE) &&
|
||
// Third: Needs to be in 10.0 M
|
||
fDistance <= RADIUS_SIZE_COLOSSAL)
|
||
{
|
||
int iElemental = AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections);
|
||
int iPhisical = AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections);
|
||
// We can check the timers added to spells, to check for AOEs we are in.
|
||
|
||
// Long range ones...already made sure of collosal range!
|
||
// Storm of vengance...a lot of damage (so we need only 30HP left)
|
||
// Stats: Raduis 10! d6(3) , Get Is Friend/Friendly.
|
||
// Either: Electical (reflex) and stun (failed save), else Acid, d6(6).
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_STORM_OF_VENGEANCE)) && !iElemental)
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_STORM_OF_VENGEANCE, TRUE, RADIUS_SIZE_COLOSSAL, i30, i50, i20)) return TRUE;
|
||
}
|
||
// Next range...6.7
|
||
if(fDistance <= RADIUS_SIZE_HUGE)
|
||
{
|
||
// Creeping doom!
|
||
// This can get a lot of damage. We might check the counter status...
|
||
// Stats: Radius 6.7. Initial d20 damage (can't stop), Slow as well. Reaction Type Friendly.
|
||
// d6(Amount of rounds in effect) each round after that, HURTS! (up to 1000)
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_CREEPING_DOOM)) && !iPhisical)
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_CREEPING_DOOM, TRUE, RADIUS_SIZE_HUGE, GetLocalInt(oAOE, "NW_SPELL_CONSTANT_CREEPING_DOOM1" + ObjectToString(GetAreaOfEffectCreator(oAOE))) * i6, i40, i40)) return TRUE;
|
||
}
|
||
// Stinking cloud!
|
||
// No damage, only daze. If we are immune to it - fine.
|
||
// Note: PATCH 1.30 should make daze walkable - we can wait
|
||
// to be affected then walk out of the area :-)
|
||
// Only fortitude save.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_STINKING_CLOUD)))
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_STINKING_CLOUD, TRUE, RADIUS_SIZE_HUGE, i25, i60, i35)) return TRUE;
|
||
}
|
||
// Grease
|
||
// Stats: Radius 6. (count as 6.7) We get slowed (unless Woodland Stride) in it always.
|
||
// On a reflex save each round, knockdown effect (4 seconds). Reaction Type Friendly.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_GREASE)))
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_GREASE, FALSE, RADIUS_SIZE_HUGE, i10, i25, i15)) return TRUE;
|
||
}
|
||
// Wall of fire...
|
||
// Stats: 10M x 2M rectangle. d6(4) Fire damage (Reflex save).
|
||
// Notes: level 5/6 spell. Not bad, up to 24 damage! Reaction Type Frendly.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_WALL_OF_FIRE)) && !iElemental)
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_WALL_OF_FIRE, TRUE, RADIUS_SIZE_HUGE, i20, i50, i25)) return TRUE;
|
||
}
|
||
// Blade Barrier (special shape!)
|
||
// Stats: 10M x 1M rectangle. d6(CasterLevel) Piercing damage (Reflex save).
|
||
// Notes: LOTs of damage! warning about this one! Reaction Type friendly.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_BLADE_BARRIER)) && !iPhisical)
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_BLADE_BARRIER, TRUE, RADIUS_SIZE_HUGE, i30, i50, i35)) return TRUE;
|
||
}
|
||
// Next range...5.0
|
||
if(fDistance <= RADIUS_SIZE_LARGE)
|
||
{
|
||
// Acid Fog spell
|
||
// Stats: Radius 5. Damage: d6(2), Fort/2. Slow on failed Fort save (On Enter)
|
||
// Notes: Level 6 spell. Uses ReactionType, not GetFriend.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_ACID_FOG)))
|
||
{
|
||
// Ignore slowing effect
|
||
if(AI_ActionDispelAOE(SPELL_ACID_FOG, TRUE, RADIUS_SIZE_HUGE, i20, i40, i25)) return TRUE;
|
||
}
|
||
// Inceniary Cloud spell
|
||
// Stats: Radius 5. Damage: d6(4), Reflex. Slow always.
|
||
// Notes: Level 8 spell. Uses ReactionTypeFriendly, not GetFriend.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_INCENDIARY_CLOUD)) && !iElemental)
|
||
{
|
||
// Ignore slow
|
||
if(AI_ActionDispelAOE(SPELL_INCENDIARY_CLOUD, TRUE, RADIUS_SIZE_HUGE, i30, i50, i35)) return TRUE;
|
||
}
|
||
// Cloudkill spell
|
||
// Stats: Radius 5. Under 3HD Die. 4-7 Save VS Death (Fort)
|
||
// All Damage d10(), no save.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_CLOUDKILL)))
|
||
{
|
||
// Damage only
|
||
if(AI_ActionDispelAOE(SPELL_CLOUDKILL, TRUE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE;
|
||
}
|
||
// Evards Black Tenticals.
|
||
// Stats: Radius 5. 1d6 damage if d20 + 5 hits the target. (Reaction Type Hostile)
|
||
// May paralize them as well, on a fort save. Up to 5 of these tenticals BTW.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_EVARDS_BLACK_TENTACLES)) && !iPhisical)
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_EVARDS_BLACK_TENTACLES, TRUE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE;
|
||
}
|
||
// Mind Fog spell
|
||
// Stats: Radius 5.
|
||
// -10 to will saves, if fail a will save (and it continues out of it).
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_MIND_FOG)) &&
|
||
GetLocalInt(OBJECT_SELF, AI_ABILITY_DECREASE) > FALSE)
|
||
{
|
||
// No damage
|
||
if(AI_ActionDispelAOE(SPELL_MIND_FOG, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE;
|
||
}
|
||
// Entangle
|
||
// Stats: Radius 5. Reaction Type hostile used.
|
||
// Entangle effect against a reflex save each round (for 12 seconds).
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_ENTANGLE)) &&
|
||
AI_GetAIHaveEffect(GlobalEffectEntangle))
|
||
{
|
||
// No damage
|
||
if(AI_ActionDispelAOE(SPELL_ENTANGLE, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE;
|
||
}
|
||
// Vine mine
|
||
// Stats: Radius as Entangle - Radius 5
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_VINE_MINE_ENTANGLE)) &&
|
||
AI_GetAIHaveEffect(GlobalEffectEntangle))
|
||
{
|
||
// " "
|
||
if(AI_ActionDispelAOE(SPELL_VINE_MINE_ENTANGLE, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE;
|
||
}
|
||
// Hamper movement one
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_VINE_MINE_HAMPER_MOVEMENT)) &&
|
||
AI_GetAIHaveEffect(GlobalEffectMovementSpeedDecrease))
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_VINE_MINE_HAMPER_MOVEMENT, FALSE, RADIUS_SIZE_HUGE, i15, i30, i25)) return TRUE;
|
||
}
|
||
}
|
||
// Web
|
||
// Stats: 6.67 Radius
|
||
// Notes: Just a large entange + Slow for anyone.
|
||
if(GetLocalTimer(AI_TIMER_AOE_SPELL_EVENT + IntToString(SPELL_WEB)) &&
|
||
AI_GetAIHaveEffect(GlobalEffectEntangle))
|
||
{
|
||
if(AI_ActionDispelAOE(SPELL_WEB, FALSE, RADIUS_SIZE_HUGE, i30, i50, i35)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
// End AOE
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: ReturnHealingInfo
|
||
//::///////////////////////////////////////////////
|
||
This will do 1 of two things, with the spell ID
|
||
1. If iHealAmount is FALSE, it will return what number (rank) in order, which is also used for level checking
|
||
2. If TRUE, it will return the average damage it will heal.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created by: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_ReturnHealingInfo(int iSpellID, int iSelf = FALSE, int iHealAmount = FALSE)
|
||
{
|
||
// On error - return 0 rank, or 0 heal.
|
||
if(iSpellID <= FALSE) return FALSE;
|
||
switch(iSpellID)
|
||
{
|
||
// RANK - NAME = D8 AMOUNTs + RANGE OF CLERIC LEVELS ADDED. MAX.
|
||
// AVERAGE OF DICE. ABOUT 2/3 OF MODIFIERS.
|
||
case SPELL_CURE_CRITICAL_WOUNDS:
|
||
{
|
||
// 20 - Critical = 4d8 + 7-20. Max of 32. Take as 24. Take modifiers as 10.
|
||
if(iHealAmount){ return i35; } else { return i10; }
|
||
}
|
||
break;
|
||
case SPELLABILITY_LESSER_BODY_ADJUSTMENT:
|
||
case SPELL_CURE_LIGHT_WOUNDS:
|
||
{
|
||
// 10 - Lesser Bodily Adjustment = 1d8 + 1-5. Max of 8. Take as 6. Take modifiers as 3.
|
||
// NOTE: same spell script as Cure Light Wounds, but no concentration check!
|
||
// 8 - Light = 1d8 + 2-5. Max of 8. Take as 6. Take modifiers as 3.
|
||
if(iHealAmount){ return i9; } else { return i2; }
|
||
}
|
||
break;
|
||
case SPELL_CURE_MINOR_WOUNDS:
|
||
{
|
||
// 4 - Minor = 1. Max of 1. Take as 1. Take modifiers as 0.
|
||
// No need for check - healing and rank are both 1.
|
||
return i1;
|
||
}
|
||
break;
|
||
case SPELL_CURE_MODERATE_WOUNDS:
|
||
{
|
||
// 12 - Moderate = 2d8 + 3-10. Max of 16. Take as 12. Take modifiers as 5.
|
||
if(iHealAmount){ return i17; } else { return i3; }
|
||
}
|
||
break;
|
||
case SPELL_CURE_SERIOUS_WOUNDS:
|
||
{
|
||
// 16 - Serious = 3d8 + 5-15. Max of 24. Take as 18. Take modifiers as 7.
|
||
if(iHealAmount){ return i25; } else { return i4; }
|
||
}
|
||
break;
|
||
case SPELL_HEALING_CIRCLE:
|
||
{
|
||
// 14 - Healing circle = 1d8 + 9-20. Max of 8. Take as 8. Take modifiers as 10.
|
||
if(iHealAmount){ return i18; } else { return i4; }
|
||
}
|
||
break;
|
||
case AI_SPELLABILITY_CURE_CRITICAL_WOUNDS_OTHER:
|
||
{
|
||
// As cure critical wounds, but cures another and damages us...
|
||
// No idea.. :-)
|
||
if(iSelf == TRUE)
|
||
{
|
||
// 20 - Critical = 4d8 + 7-20. Max of 32. Take as 24. Take modifiers as 9.
|
||
if(iHealAmount){ return i35; } else { return i9; }
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
// On error - return 0 rank, or 0 heal.
|
||
return FALSE;
|
||
}
|
||
// Wrappers action Use Talent with debug string
|
||
void AI_ActionUseTalentDebug(talent tChosenTalent, object oTarget)
|
||
{
|
||
// 18: "[DRC:Talent] Using Talent (Healing). [TalentID] " + IntToString(GetIdFromTalent(tChosenTalent)) + " [Target] " + GetName(oTarget)
|
||
DebugActionSpeakByInt(18, oTarget, GetIdFromTalent(tChosenTalent));
|
||
ActionUseTalentOnObject(tChosenTalent, oTarget);
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_ActionHealObject
|
||
//::///////////////////////////////////////////////
|
||
This will heal oTarget using the best spell it can, even ones it can
|
||
spontaeously cast.
|
||
- Just does best spell.
|
||
- May use spontaneous ones as well. :-)
|
||
- Called from AI_AttemptHealing_Self and AI_AttemptHealing_Ally
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_ActionHealObject(object oTarget)
|
||
{
|
||
// Set healing talents
|
||
talent tAOEHealing, tTouchHealing, tPotionHealing;
|
||
// These are left at 0 if we have no SpawnInCondition setting them.
|
||
int iAOEHealingValue, iTouchHealingValue, iPotionValue;
|
||
|
||
// We set up OnSpawn if any of the 3 talents are valid
|
||
if(GetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_HEALING_AREAEFFECT, AI_VALID_SPELLS))
|
||
{
|
||
tAOEHealing = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_AREAEFFECT, i20);
|
||
iAOEHealingValue = GetIdFromTalent(tAOEHealing);
|
||
}
|
||
if(GetSpawnInCondition(AI_VALID_TALENT_BENEFICIAL_HEALING_TOUCH, AI_VALID_SPELLS))
|
||
{
|
||
tTouchHealing = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_TOUCH, i20);
|
||
iTouchHealingValue = GetIdFromTalent(tTouchHealing);
|
||
}
|
||
// First, we check for heal spells in the talents we have. They are special cases!
|
||
// We set this to the right value if the target is us.
|
||
int iTargetCurrentHP = GlobalOurCurrentHP;
|
||
int iTargetMaxHP = GlobalOurMaxHP;
|
||
int iTargetPercentHP = GlobalOurPercentHP;
|
||
int iTargetSelf = TRUE;
|
||
// Check...might still be minus one, anyway.
|
||
if(oTarget == OBJECT_SELF)
|
||
{
|
||
tPotionHealing = GetCreatureTalentBest(TALENT_CATEGORY_BENEFICIAL_HEALING_POTION, i20);
|
||
iPotionValue = GetIdFromTalent(tPotionHealing);
|
||
}
|
||
else // If not us, we don't know HP values.
|
||
{
|
||
iTargetCurrentHP = GetCurrentHitPoints(oTarget);
|
||
iTargetMaxHP = GetMaxHitPoints(oTarget);
|
||
// Percent = Current/Max * 100
|
||
iTargetPercentHP = AI_GetPercentOf(iTargetCurrentHP, iTargetMaxHP);
|
||
iTargetSelf = FALSE;
|
||
}
|
||
|
||
// Check for heal amounts, oTarget might have to be X damaged.
|
||
// - If we have someone using knockdown on us, we make SURE we cast it sooner
|
||
if(iTargetCurrentHP < i25 || iTargetPercentHP < i30 ||
|
||
// High damage at once for target (over 40)
|
||
(GetLocalInt(oTarget, AI_INTEGER + AI_HIGHEST_DAMAGE_AMOUNT) > i40 && iTargetPercentHP < i40) ||
|
||
// Had knockdown used on us lately
|
||
(GetLocalTimer(AI_TIMER_KNOCKDOWN) &&
|
||
(iTargetCurrentHP < i40 || iTargetPercentHP < i40)))
|
||
{
|
||
if(iPotionValue == SPELL_HEAL)
|
||
{
|
||
AI_EquipBestShield();
|
||
// Potion spell
|
||
AI_ActionUseTalentDebug(tPotionHealing, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
else if(iTouchHealingValue == SPELL_HEAL)
|
||
{
|
||
AI_EquipBestShield();
|
||
// Touch heal spell
|
||
AI_ActionUseTalentDebug(tTouchHealing, oTarget);
|
||
return TRUE;
|
||
}
|
||
else if(iAOEHealingValue == SPELL_MASS_HEAL)
|
||
{
|
||
AI_EquipBestShield();
|
||
// Mass heal spell
|
||
AI_ActionUseTalentDebug(tAOEHealing, oTarget);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Else, another talent - IE critical wounds and under.
|
||
|
||
// Are any of them valid?
|
||
if(GlobalBestSpontaeousHealingSpell >= i1 ||
|
||
iAOEHealingValue >= i1 || iTouchHealingValue >= i1 || iPotionValue >= i1)
|
||
{
|
||
// We must work out the rank of each thing we can use, IE 10 best,
|
||
// 1 worst or whatever :-D
|
||
int iSpontaeousRank, iPotionRank, iAOERank, iTouchRank, iRank,
|
||
iDamageNeededToBeDone, iRankOfSpell, // iRank must be 0, which it starts at.
|
||
// Spontaeous or a talent? 1 = Talent, 2 = Spontaeous, 0 = Error/none
|
||
iTalentOrSpon;
|
||
// What talent should we use?
|
||
talent tToUse;
|
||
// We check if we have a spon. spell, and set its rank. Same with others.
|
||
iSpontaeousRank = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf);
|
||
iAOERank = AI_ReturnHealingInfo(iAOEHealingValue, iTargetSelf);
|
||
iTouchRank = AI_ReturnHealingInfo(iTouchHealingValue, iTargetSelf);
|
||
iPotionRank = AI_ReturnHealingInfo(iPotionValue, iTargetSelf);
|
||
|
||
// Need a valid rank - we COULD get healing feats with these talents
|
||
if(iAOERank >= i1 || iAOERank >= i1 || iPotionRank >= i1 || iSpontaeousRank >= i1)
|
||
{
|
||
// Determine what to use...
|
||
// Potion VS area.
|
||
if(iPotionRank >= iAOERank && iPotionRank >= i1)// Make sure we have a potion rank
|
||
{
|
||
// Potion VS touch and so on.
|
||
if(iPotionRank >= iTouchRank)
|
||
{
|
||
if(iPotionRank >= iSpontaeousRank)// IE beats all 3
|
||
{
|
||
iTalentOrSpon = i1;
|
||
tToUse = tPotionHealing;
|
||
iRank = iPotionRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(iPotionValue, iTargetSelf, TRUE);
|
||
}
|
||
else if(iSpontaeousRank >= i1)
|
||
{
|
||
iTalentOrSpon = i2;
|
||
iRank = iSpontaeousRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE);
|
||
}
|
||
}
|
||
else if(iTouchRank >= iSpontaeousRank && iTouchRank >= i1)// Make sure we have a touch rank
|
||
{
|
||
iTalentOrSpon = i1;
|
||
tToUse = tTouchHealing;
|
||
iRank = iTouchRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(iTouchHealingValue, iTargetSelf, TRUE);
|
||
}
|
||
else if(iSpontaeousRank >= i1)
|
||
{
|
||
iTalentOrSpon = i2;
|
||
iRank = iSpontaeousRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE);
|
||
}
|
||
}
|
||
// Area VS Touch
|
||
else if(iAOERank >= iTouchRank && iAOERank >= i1)// Make sure we have an AOE rank
|
||
{
|
||
if(iAOERank >= iSpontaeousRank)
|
||
{
|
||
iTalentOrSpon = i1;
|
||
tToUse = tAOEHealing;
|
||
iRank = iPotionRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(iAOEHealingValue, iTargetSelf, TRUE);
|
||
}
|
||
else if(GlobalBestSpontaeousHealingSpell)
|
||
{
|
||
iTalentOrSpon = i2;
|
||
iRank = iSpontaeousRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE);
|
||
}
|
||
}
|
||
// Touch VS Spontaeous
|
||
else if(iTouchRank >= iSpontaeousRank && iTouchRank >= i1)
|
||
{
|
||
iTalentOrSpon = i1;
|
||
tToUse = tTouchHealing;
|
||
iRank = iTouchRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(iTouchHealingValue, iTargetSelf, TRUE);
|
||
}
|
||
// It should be Spontaeous at the very end, BUT, we need to make sure its valid
|
||
else if(iSpontaeousRank >= i1)
|
||
{
|
||
iTalentOrSpon = i2;
|
||
iRank = iSpontaeousRank;
|
||
iDamageNeededToBeDone = AI_ReturnHealingInfo(GlobalBestSpontaeousHealingSpell, iTargetSelf, TRUE);
|
||
}
|
||
// Now, have we a talent or spell to use?
|
||
if(iTalentOrSpon && iRank >= i1)
|
||
{
|
||
// If the current HP is under the damage that is healed,
|
||
// or under 25% left :-)
|
||
if(iTargetCurrentHP <= (iTargetMaxHP - iDamageNeededToBeDone) ||
|
||
(iTargetPercentHP < i25))
|
||
{
|
||
// Level check. Our HD must be a suitble amount, or no melee attackers.
|
||
// Even if we are healing others - we don't bother with bad healing
|
||
|
||
// Rank of the spell, can be in a range over our HD -5.
|
||
if(((iRank - GlobalOurHitDice) >= -i5) ||
|
||
// OR Rank of 16+ (critical wounds or more) are always done.
|
||
(iRank >= i16) ||
|
||
// OR no valid nearest enemy.
|
||
(!GlobalAnyValidTargetObject) ||
|
||
// OR 0 Melee attackers, and a larger range (up to -10 our HD)
|
||
((GlobalMeleeAttackers < i1) && ((iRank - GlobalOurHitDice) >= -10)))
|
||
{
|
||
// Use it...and attack with a bow. Always return TRUE.
|
||
// Need only one debug :-P
|
||
// 19: "[DCR:Healing] (Should) Healing [Target]" + GetName(oTarget) + " [CurrentHP|Max|ID|Rank|Power] " + IntToString((10000 * iTargetCurrentHP) + (1000 * iTargetMaxHP) + (100 * GetIdFromTalent(tToUse)) + (10 * iRank) + (iDamageNeededToBeDone))
|
||
DebugActionSpeakByInt(19, oTarget, ((10000 * iTargetCurrentHP) + (1000 * iTargetMaxHP) + (100 * GetIdFromTalent(tToUse)) + (10 * iRank) + (iDamageNeededToBeDone)));
|
||
|
||
// Talent!
|
||
if(iTalentOrSpon == i1)
|
||
{
|
||
if(GetIsTalentValid(tToUse))
|
||
{
|
||
AI_EquipBestShield();
|
||
// No AI_ActionUseTalentDebug, just normal. Debug string above.
|
||
DeleteLocalInt(OBJECT_SELF, AI_SPONTAEUOUSLY_CAST_HEALING);
|
||
ActionUseTalentOnObject(tToUse, oTarget);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Spontaeous Spell!
|
||
else //if(iTalentOrSpon == i2) // (is always 2 or 1 or 0)
|
||
{
|
||
// Use the same thing as the inflict spell
|
||
if(AI_ActionCastSpontaeousSpell(GlobalBestSpontaeousHealingSpell, TRUE, oTarget)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// Else, no healing is valid...ranks, ETC.
|
||
else
|
||
// Check if it is us and so on.
|
||
// If it is us, and we have no potion spell,
|
||
if(oTarget == OBJECT_SELF &&
|
||
GlobalOurPercentHP < i40 && !iPotionRank &&
|
||
GetSpawnInCondition(AI_FLAG_OTHER_CHEAT_MORE_POTIONS, AI_OTHER_MASTER))
|
||
{
|
||
if(!GetIsObjectValid(GetItemPossessedBy(OBJECT_SELF, "nw_it_mpotion003")))
|
||
{
|
||
// 20: "[DCR Healing] Boss Action, create Critical Wounds potion"
|
||
DebugActionSpeakByInt(20);
|
||
CreateItemOnObject("nw_it_mpotion003");
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AI_ActionHealUndeadObject
|
||
//::///////////////////////////////////////////////
|
||
This will heal oTarget using the best negative energy spell it can use,
|
||
- Used best spell (including harm, ETC)
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_ActionHealUndeadObject(object oTarget)
|
||
{
|
||
// First, we check for heal spells in the talents we have. They are special cases!
|
||
// We set this to the right value if the target is us.
|
||
int iTargetCurrentHP = GlobalOurCurrentHP;
|
||
int iTargetMaxHP = GlobalOurMaxHP;
|
||
int iTargetPercentHP = GlobalOurPercentHP;
|
||
// If not us, we don't know HP values.
|
||
if(oTarget != OBJECT_SELF)
|
||
{
|
||
iTargetCurrentHP = GetCurrentHitPoints(oTarget);
|
||
iTargetMaxHP = GetMaxHitPoints(oTarget);
|
||
// Percent = Current/Max * 100
|
||
iTargetPercentHP = AI_GetPercentOf(iTargetCurrentHP, iTargetMaxHP);
|
||
}
|
||
// Check for Harm amounts, oTarget might have to be X damaged.
|
||
if(iTargetCurrentHP < i25 || iTargetPercentHP < i30 ||
|
||
// High damage at once for target (over 40)
|
||
(GetLocalInt(oTarget, AI_INTEGER + AI_HIGHEST_DAMAGE_AMOUNT) > i40 && iTargetPercentHP < i40) ||
|
||
// Had knockdown used on us lately
|
||
(GetLocalTimer(AI_TIMER_KNOCKDOWN) &&
|
||
(iTargetCurrentHP < i40 || iTargetPercentHP < i40)))
|
||
{
|
||
// If us, use harm self
|
||
if(oTarget == OBJECT_SELF)
|
||
{
|
||
// Innate ability. Under healing self, so leave as innate.
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_UNDEAD_HARM_SELF, FALSE, oTarget)) return TRUE;
|
||
}
|
||
// Use it...if we have it...and attack with a bow.
|
||
if(AI_ActionCastSpell(SPELL_HARM, SpellHostTouch, oTarget, i16, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Other Under things!
|
||
// Inflict range...always use top 2.
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_CRITICAL_WOUNDS, SpellHostTouch, oTarget)) return TRUE;
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_SERIOUS_WOUNDS, SpellOtherSpell, oTarget)) return TRUE;
|
||
// Circle of doom: d8 + Caster level heal. Category 1.
|
||
if(AI_ActionCastSpell(SPELL_CIRCLE_OF_DEATH, SpellHostRanged, oTarget, i15, TRUE, ItemHostRanged)) return TRUE;
|
||
// Negative Energy Burst...this is good enough to always use normally...same as Circle of doom! (d8 + caster)
|
||
if(AI_ActionCastSpell(SPELL_NEGATIVE_ENERGY_BURST, SpellHostRanged, oTarget, i13, TRUE, ItemHostRanged)) return TRUE;
|
||
// Lower ones ain't too good for some HD (ours!)
|
||
if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i12)
|
||
{
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MODERATE_WOUNDS, SpellOtherSpell, oTarget)) return TRUE;
|
||
if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i6)
|
||
{
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_LIGHT_WOUNDS, SpellOtherSpell, oTarget)) return TRUE;
|
||
if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i2)
|
||
{
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MINOR_WOUNDS, SpellOtherSpell, oTarget)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
// No undead healing cast
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: TalentHealingSelf
|
||
//::///////////////////////////////////////////////
|
||
Uses the best it can.
|
||
1. If it is heal, they need to be under half HP and under 40 HP
|
||
2. If not, it has to be under half HP and not be heal/mass heal
|
||
3. Testing to see if harm will be cast by undead
|
||
|
||
1.3 changes: A few bug fixes...not sure what was wrong before.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
int AI_AttemptHealingSelf()
|
||
{
|
||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER)) return FALSE;
|
||
|
||
// Determine what we should heal at...
|
||
int iPercent = GetBoundriedAIInteger(AI_HEALING_US_PERCENT, i50, i100, i1);
|
||
|
||
// What % are we at? It is GlobalOurPercentHP
|
||
// Are we under the right %?
|
||
if(GlobalOurPercentHP < iPercent)
|
||
{
|
||
// We can't be a silly race!
|
||
if(GlobalOurRace != RACIAL_TYPE_UNDEAD &&
|
||
GlobalOurRace != RACIAL_TYPE_CONSTRUCT)
|
||
{
|
||
int nWillHeal;
|
||
// If we can heal self with feats...use them! No AOO
|
||
if(GetHasFeat(FEAT_WHOLENESS_OF_BODY) &&
|
||
((GlobalOurCurrentHP <= GetLevelByClass(CLASS_TYPE_MONK, OBJECT_SELF) * i2) ||
|
||
GlobalOurPercentHP < i20))
|
||
{
|
||
if(AI_ActionUseFeatOnObject(FEAT_WHOLENESS_OF_BODY)) return TRUE;
|
||
}
|
||
// Only use this on ourselves.
|
||
if(GetHasFeat(FEAT_LAY_ON_HANDS))
|
||
{
|
||
// This does the actual formula...note, putting ones to stop DIVIDE BY ZERO errors
|
||
int nChr = GetAbilityModifier(ABILITY_CHARISMA);
|
||
if(nChr < i1) nChr = i1;// Error checking
|
||
int nLevel = GetLevelByClass(CLASS_TYPE_PALADIN);
|
||
if(nLevel < i1) nLevel = i1;// Error checking
|
||
//Caluclate the amount needed to be at, to use.
|
||
int nHeal = nLevel * nChr;
|
||
if(nHeal < i1) nHeal = i1;// Error checking
|
||
// We can be under the amount healed, or under 30
|
||
if(GlobalOurCurrentHP < nHeal ||
|
||
GlobalOurPercentHP < i30)
|
||
{
|
||
if(AI_ActionUseFeatOnObject(FEAT_LAY_ON_HANDS)) return TRUE;
|
||
}
|
||
}
|
||
// Note: Feat Lesser Bodily Adjustment uses cure light wounds spell script.
|
||
// Odd classes mean no potions.
|
||
int iPotions = TRUE;
|
||
if(GlobalOurRace == RACIAL_TYPE_ABERRATION ||
|
||
GlobalOurRace == RACIAL_TYPE_BEAST || GlobalOurRace == RACIAL_TYPE_ELEMENTAL ||
|
||
GlobalOurRace == RACIAL_TYPE_VERMIN || GlobalOurRace == RACIAL_TYPE_MAGICAL_BEAST ||
|
||
GlobalOurRace == RACIAL_TYPE_DRAGON || GlobalOurRace == RACIAL_TYPE_ANIMAL)
|
||
iPotions = FALSE;
|
||
|
||
// Lets see if we can use a healing kit! Only a valid race (as potions)
|
||
if(iPotions && GetAIInteger(AI_VALID_HEALING_KITS) &&
|
||
!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_USING_HEALING_KITS, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
// Need a valid kit and skill to be worth using :-)
|
||
if(GetIsObjectValid(GlobalHealingKit) &&
|
||
(GetSkillRank(SKILL_HEAL) >= (GlobalOurHitDice / i3) ||
|
||
GlobalOurPercentHP < i30))
|
||
{
|
||
AI_EquipBestShield();
|
||
// 21: "[DCR:Casting] Healing self with healing kit, [Kit] " + GetName(GlobalHealingKit)
|
||
DebugActionSpeakByInt(21, GlobalHealingKit);
|
||
ActionUseSkill(SKILL_HEAL, OBJECT_SELF, i0, GlobalHealingKit);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Finally, heal self with potions, spells or whatever.
|
||
return AI_ActionHealObject(OBJECT_SELF);
|
||
}
|
||
else if(GlobalOurRace == RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Undead can cast some spells to heal...
|
||
return AI_ActionHealUndeadObject(OBJECT_SELF);
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Get the nearest seen ally creature with the effect.
|
||
// - Checks us first
|
||
// - Then checks leader
|
||
// - Then loops seen allies within 20M.
|
||
object AI_GetNearestAllyWithEffect(int iEffectHex)
|
||
{
|
||
// Check self
|
||
if(AI_GetAIHaveEffect(iEffectHex)) return OBJECT_SELF;
|
||
// Check leader
|
||
if(AI_GetAIHaveEffect(iEffectHex, GlobalNearestLeader)) return GlobalNearestLeader;
|
||
|
||
// Check seen allies in 20M
|
||
int iCnt = i1;
|
||
object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= f20)
|
||
{
|
||
// Don't target undead
|
||
if(GetRacialType(oAlly) != RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Has it got the effect?
|
||
// - Stop and return oAlly.
|
||
if(AI_GetAIHaveEffect(iEffectHex, oAlly)) return oAlly;
|
||
}
|
||
// Carry on loop
|
||
iCnt++;
|
||
oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
return OBJECT_INVALID;
|
||
}
|
||
// Returns the nearest ally with iMin effects, and X more based on HD.
|
||
// - Checks us first. True if (HD/6) Effects and (iMin - 1) effects
|
||
// - Checks leader next. True if (HD/5) Effects and (iMin - 2) effects
|
||
// - Checks allies after. True if (HD/4) Effects and (iMin) effects
|
||
object AI_GetNearestAllyWithRestoreEffects(int iMin)
|
||
{
|
||
// Check self /6 (need much less)
|
||
int iAmount = GetLocalInt(OBJECT_SELF, AI_ABILITY_DECREASE);
|
||
if(iAmount >= iMin - i1 && iAmount >= GlobalOurHitDice / i6) return OBJECT_SELF;
|
||
// Check leader - /5 (need less)
|
||
if(GlobalValidLeader)
|
||
{
|
||
iAmount = GetLocalInt(GlobalNearestLeader, AI_ABILITY_DECREASE);
|
||
if(iAmount >= iMin - i2 && iAmount >= GetHitDice(GlobalNearestLeader) / i5) return GlobalNearestLeader;
|
||
}
|
||
// Check seen allies in 20M - until we get one with /2 effects, minimum 4.
|
||
int iCnt = i1;
|
||
object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oAlly) && GetDistanceToObject(oAlly) <= f20)
|
||
{
|
||
// Don't target undead
|
||
if(GetRacialType(oAlly) != RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Check ally
|
||
iAmount = GetLocalInt(oAlly, AI_ABILITY_DECREASE);
|
||
if(iAmount >= iMin && iAmount >= GetHitDice(oAlly) / i4) return oAlly;
|
||
}
|
||
// Carry on loop
|
||
iCnt++;
|
||
oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
return OBJECT_INVALID;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: TalentCureCondition
|
||
//::///////////////////////////////////////////////
|
||
Uses spells to cure conditions. Needs to be checked fully
|
||
- Uses allies own integers to check thier effects
|
||
- Loops spells (Best => worst) and if we havn't cast it in an amount of time
|
||
we check effects (Us, leader, allies seen) until we find someone (nearest)
|
||
who we can cast it on.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptCureCondition()
|
||
{
|
||
// Set OnSpawn if we have any of the spells we use.
|
||
if(!GetSpawnInCondition(AI_VALID_CURE_CONDITION_SPELLS, AI_VALID_SPELLS) ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_CURING_ALLIES, AI_OTHER_MASTER) ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER)) return FALSE;
|
||
|
||
// Check timer
|
||
if(GetLocalTimer(AI_TIMER_CURE_CONDITION)) return FALSE;
|
||
|
||
int iTimer = GetAIInteger(SECONDS_BETWEEN_STATUS_CHECKS);
|
||
if(iTimer >= i1)
|
||
{
|
||
// Set timer
|
||
SetLocalTimer(AI_TIMER_CURE_CONDITION, IntToFloat(iTimer));
|
||
}
|
||
|
||
// Cure spells in order:
|
||
// SPELL_GREATER_RESTORATION - Ability Decrease, AC decrease, Attack decrease,
|
||
// Damage Decrease, Damage Immunity Decrease, Saving Throw Decrease, Spell
|
||
// resistance Decrease, Skill decrease, Blindness, Deaf, Curse, Disease, Poison,
|
||
// Charmed, Dominated, Dazed, Confused, Frightened, Negative level, Paralyze,
|
||
// Slow, Stunned.
|
||
// SPELL_FREEDOM - Paralyze, Entangle, Slow, Movement speed decrease. (+Immunity!)
|
||
// SPELL_RESTORATION - Ability Decrease, AC decrease, Attack Decrease,
|
||
// Damage Decrease, Damage Immunity Decrease, Saving Throw Decrease,
|
||
// Spell Resistance Decrease, Skill Decrease, Blindess, Deaf, Paralyze, Negative level
|
||
// SPELL_REMOVE_BLINDNESS_AND_DEAFNESS - Blindess, Deaf.
|
||
// SPELL_NEUTRALIZE_POISON - Poison
|
||
// SPELL_REMOVE_DISEASE - Disease
|
||
// SPELL_REMOVE_CURSE - Curse
|
||
// SPELL_LESSER_RESTORATION - Ability Decrease, AC decrease, Attack Decrease,
|
||
// Damage Decrease, Damage Immunity Decrease, Saving throw Decrease,
|
||
// Spell Resistance Decrease, Skill decrease.
|
||
|
||
// Note: We like to dispel anything that stuns us - EG paralyze effects,
|
||
// stunning, confusion, dazedness VIA Greater Restoration, after that,
|
||
// blindness (A great inhibiter) and then poison disease and curse.
|
||
|
||
// Lastly, ability damage ETC can come in after the stuns, or right at
|
||
// the end, depending on the amount.
|
||
object oHeal;
|
||
// Petrify!
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectPetrify);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Only one
|
||
if(AI_ActionCastSpell(SPELL_STONE_TO_FLESH, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// First, Paralysis should be removed.
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectParalyze);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Freedom first
|
||
if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Daze
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectDazed);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Freedom first
|
||
if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Next? blindness
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectBlindness);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Remove first
|
||
if(AI_ActionCastSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Lots of negative effects.
|
||
oHeal = AI_GetNearestAllyWithRestoreEffects(i7);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Only these can remove ability decreases ETC.
|
||
if(AI_ActionCastSpell(SPELL_LESSER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Negative level is tough
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectNegativeLevel);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Only these can remove it
|
||
if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Slow
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectSlowed);
|
||
if(GetIsObjectValid(oHeal) && !AI_GetAIHaveEffect(GlobalEffectHaste, oHeal))
|
||
{
|
||
// haste - we don't set we have this OnSpawn, because if we have not
|
||
// got any other healing cure spells, we're probably not meant to cast it on slowed people.
|
||
if(AI_ActionCastSpell(SPELL_MASS_HASTE, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_HASTE, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Entangle/Move slowdown
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectEntangle);
|
||
if(!GetIsObjectValid(oHeal))
|
||
{
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectMovementSpeedDecrease);
|
||
}
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Freedom first
|
||
if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Deafness
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectDeaf);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Remove first
|
||
if(AI_ActionCastSpell(SPELL_REMOVE_BLINDNESS_AND_DEAFNESS, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_FREEDOM_OF_MOVEMENT, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Quite a few negative effects.
|
||
oHeal = AI_GetNearestAllyWithRestoreEffects(i5);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Only these can remove ability decreases ETC.
|
||
if(AI_ActionCastSpell(SPELL_LESSER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Curse
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectCurse);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Remove first
|
||
if(AI_ActionCastSpell(SPELL_REMOVE_CURSE, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Disease
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectDisease);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Remove first
|
||
if(AI_ActionCastSpell(SPELL_REMOVE_DISEASE, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// Poison
|
||
oHeal = AI_GetNearestAllyWithEffect(GlobalEffectPoison);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Remove first
|
||
if(AI_ActionCastSpell(SPELL_NEUTRALIZE_POISON, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
// See if there is anyone with a decent amount of effects.
|
||
// Some negative effects.
|
||
oHeal = AI_GetNearestAllyWithRestoreEffects(i3);
|
||
if(GetIsObjectValid(oHeal))
|
||
{
|
||
// Only these can remove ability decreases ETC.
|
||
if(AI_ActionCastSpell(SPELL_LESSER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_RESTORATION, FALSE, oHeal)) return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: TalentHeal
|
||
//::///////////////////////////////////////////////
|
||
HEAL ALL ALLIES
|
||
Only if they are in sight, and are under a percent%. Always nearest...
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptHealingAlly()
|
||
{
|
||
// Are we able to raise allies?
|
||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_WILL_RAISE_ALLIES_IN_BATTLE, AI_OTHER_COMBAT_MASTER) && SpellConSinTar)
|
||
{
|
||
// Leader - if dead, make sure we raise them!
|
||
if(GetIsObjectValid(GlobalNearestLeader) &&
|
||
GetIsDead(GlobalNearestLeader) &&
|
||
!GetLocalInt(GlobalNearestLeader, AI_INTEGER + I_AM_TOTALLY_DEAD))
|
||
{
|
||
// Talent 7 - Conditional single.
|
||
if(AI_ActionCastSpell(SPELL_RESURRECTION, SpellConSinTar, GlobalNearestLeader, i17, FALSE, ItemConSinTar)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RAISE_DEAD, SpellConSinTar, GlobalNearestLeader, i15, FALSE, ItemConSinTar)) return TRUE;
|
||
}
|
||
// Get the nearest ally, seen, who is dead
|
||
// - This can cause problems as no looping dead people, as there is a
|
||
// "totally dead" flag, used for bioware's lootable corpses.
|
||
object oDead = GetNearestCreature(CREATURE_TYPE_IS_ALIVE, FALSE,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
if(GetIsObjectValid(oDead) && !GetIgnore(oDead) &&
|
||
!GetLocalInt(oDead, AI_INTEGER + I_AM_TOTALLY_DEAD))
|
||
{
|
||
if(AI_ActionCastSpell(SPELL_RESURRECTION, SpellConSinTar, oDead, i17, FALSE, ItemConSinTar)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_RAISE_DEAD, SpellConSinTar, oDead, i15, FALSE, ItemConSinTar)) return TRUE;
|
||
}
|
||
}
|
||
// Do we heal allies?
|
||
if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_ONLY_CURE_SELF, AI_OTHER_COMBAT_MASTER) &&
|
||
!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_CURING, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
// We always heal leaders
|
||
// - Might make % HP checks.
|
||
if(GlobalValidLeader)
|
||
{
|
||
if(AI_ActionHealObject(GlobalNearestLeader)) return TRUE;
|
||
}
|
||
// Else we may heal a set ally
|
||
else if(GlobalValidSeenAlly)
|
||
{
|
||
// We run through a loop, and get who we might heal ;-)
|
||
int iNeedToBeAt = GetBoundriedAIInteger(AI_HEALING_ALLIES_PERCENT, i60, i100, i1);
|
||
object oAllyToHeal, oLoopTarget;
|
||
int iCnt, iPercentHitPoints, iChosenPercentHitPoints;
|
||
float fDistance, fChosenLastDistance;
|
||
// iSummonHeal will be TRUE to heal associates only if we have <= 3 allies in total
|
||
int iSummonHeal = FALSE;
|
||
if(GlobalTotalAllies <= i3)
|
||
{
|
||
iSummonHeal = TRUE;
|
||
}
|
||
fChosenLastDistance = f60;// So 1st target in health band gets picked
|
||
iCnt = i1;
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oLoopTarget))
|
||
{
|
||
if(GetRacialType(oLoopTarget) != RACIAL_TYPE_CONSTRUCT &&
|
||
(GetAssociateType(oLoopTarget) == ASSOCIATE_TYPE_NONE || iSummonHeal == TRUE))
|
||
{
|
||
// We do actually not ALWAYS use the nearest dead person, nor
|
||
// the most damaged in 20M or so. We use a mixture - the most
|
||
// damaged in a cirtain range.
|
||
iPercentHitPoints = AI_GetPercentOf(GetCurrentHitPoints(oLoopTarget), GetMaxHitPoints(oLoopTarget));
|
||
// Do we consider them needing healing?
|
||
if(iPercentHitPoints <= iNeedToBeAt)
|
||
{
|
||
// Distance to them - we may not just heal the nearest under 60%
|
||
fDistance = GetDistanceToObject(oLoopTarget);
|
||
if(fDistance < fChosenLastDistance ||
|
||
(fDistance < (fChosenLastDistance + f5) &&
|
||
iPercentHitPoints < iChosenPercentHitPoints - i10))
|
||
{
|
||
oAllyToHeal = oLoopTarget;
|
||
fChosenLastDistance = fDistance;
|
||
iChosenPercentHitPoints = iPercentHitPoints;
|
||
}
|
||
}
|
||
}
|
||
iCnt++;
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
// Did we find someone to heal?
|
||
if(iChosenPercentHitPoints > i0)
|
||
{
|
||
int iAllyHealRace = GetRacialType(oAllyToHeal);
|
||
// Undead - negative energy heals!
|
||
if(iAllyHealRace == RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Stop now whatever, true or false.
|
||
return AI_ActionHealUndeadObject(oAllyToHeal);
|
||
}
|
||
else //if(iAllyHealRace != RACIAL_TYPE_CONSTRUCT) // Checked earlier
|
||
{
|
||
if(AI_ActionHealObject(oAllyToHeal))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// This will, if they have any (Standard ones! unless you add them in)
|
||
// healing potions, they will, if close and no cleric, pass them to
|
||
// people who need them. It uses ActionGiveItem so may move to target.
|
||
// There are extra speakstrings for these events.
|
||
else if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GIVE_POTIONS_TO_HELP, AI_OTHER_COMBAT_MASTER) &&
|
||
// Can they use potions anyway?
|
||
iAllyHealRace != RACIAL_TYPE_ABERRATION && iAllyHealRace != RACIAL_TYPE_BEAST &&
|
||
iAllyHealRace != RACIAL_TYPE_ELEMENTAL && iAllyHealRace != RACIAL_TYPE_VERMIN &&
|
||
iAllyHealRace != RACIAL_TYPE_MAGICAL_BEAST && iAllyHealRace != RACIAL_TYPE_DRAGON &&
|
||
iAllyHealRace != RACIAL_TYPE_ANIMAL)
|
||
{
|
||
// So we pass over a potion. Obviously, we do not need healing,
|
||
// so any potion is good!
|
||
// NW_IT_MPOTION001 = Light
|
||
// NW_IT_MPOTION002 = Serious
|
||
// NW_IT_MPOTION003 = Critical
|
||
// NW_IT_MPOTION012 = Heal
|
||
// NW_IT_MPOTION020 = Moderate
|
||
|
||
// Do we have any of the above? Eh?
|
||
// Heal
|
||
object oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION012");
|
||
if(!GetIsObjectValid(oPotionToPass))
|
||
{
|
||
// Critical
|
||
oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION003");
|
||
if(!GetIsObjectValid(oPotionToPass))
|
||
{
|
||
// Serious
|
||
oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION002");
|
||
if(!GetIsObjectValid(oPotionToPass))
|
||
{
|
||
// Moderate
|
||
oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION020");
|
||
if(!GetIsObjectValid(oPotionToPass))
|
||
{
|
||
// Light
|
||
oPotionToPass = GetItemPossessedBy(OBJECT_SELF, "NW_IT_MPOTION001");
|
||
if(!GetIsObjectValid(oPotionToPass))
|
||
{
|
||
// If NONE are valid, we delete this spawn in condition
|
||
DeleteSpawnInCondition(AI_FLAG_OTHER_COMBAT_GIVE_POTIONS_TO_HELP, AI_OTHER_COMBAT_MASTER);
|
||
// Stop - passing potions is last thing to try
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// If any valid, we pass it.
|
||
if(GetIsObjectValid(oPotionToPass))
|
||
{
|
||
// Pass it over with ActionGiveItem
|
||
ActionGiveItem(oPotionToPass, oAllyToHeal);
|
||
// Speak arrays.
|
||
string sSpeak = GetLocalString(OBJECT_SELF, AI_TALK_WE_PASS_POTION + s1);
|
||
if(sSpeak != "")
|
||
{
|
||
SpeakString(sSpeak);
|
||
}
|
||
sSpeak = GetLocalString(OBJECT_SELF, AI_TALK_WE_GOT_POTION + s1);
|
||
if(sSpeak != "")
|
||
{
|
||
AssignCommand(oAllyToHeal, SpeakString(sSpeak));
|
||
}
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: DoSummonFamiliar
|
||
//::///////////////////////////////////////////////
|
||
This will, if it can, summon a familiar, or
|
||
animal companion, and if set to want to.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptFeatSummonFamiliar()
|
||
{
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_SUMMON_FAMILIAR, AI_COMBAT_MASTER))
|
||
{
|
||
if(GetHasFeat(FEAT_SUMMON_FAMILIAR) && !GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_FAMILIAR)))
|
||
{
|
||
// 1: "[DCR:Feat] Summoning my familiar"
|
||
DebugActionSpeakByInt(22);
|
||
// We do not bother turning off hiding/searching
|
||
ActionUseFeat(FEAT_SUMMON_FAMILIAR, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
else if(GetHasFeat(FEAT_ANIMAL_COMPANION) && !GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION)))
|
||
{
|
||
// 23: "[DCR:Feat] Summoning my animal companion"
|
||
DebugActionSpeakByInt(23);
|
||
ActionUseFeat(FEAT_ANIMAL_COMPANION, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name AI_ActionFleeScared
|
||
//::///////////////////////////////////////////////
|
||
Makes the person move away from the nearest enemy, or the defined target.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:: Created On: 13/01/03
|
||
//:://///////////////////////////////////////////*/
|
||
void AI_ActionFleeScared()
|
||
{
|
||
// Sets to flee mode
|
||
SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE);
|
||
SpeakArrayString(AI_TALK_ON_STUPID_RUN);
|
||
ClearAllActions();
|
||
// 24: "[DCR:Fleeing] Stupid/Panic/Flee moving from enemies/position - We are a commoner/no morale/failed < 3 int"
|
||
DebugActionSpeakByInt(24);
|
||
|
||
// Get a target, or run from position
|
||
object oTarget = GlobalNearestEnemySeen;
|
||
if(!GetIsObjectValid(oTarget))
|
||
{
|
||
oTarget = GlobalNearestEnemyHeard;
|
||
if(!GetIsObjectValid(oTarget))
|
||
{
|
||
oTarget = GetLastHostileActor();
|
||
if(!GetIsObjectValid(oTarget) || GetIsDead(oTarget))
|
||
{
|
||
// Away from position
|
||
ActionMoveAwayFromLocation(GetLocation(OBJECT_SELF), TRUE, f50);
|
||
return;// Stop, no move away from enemy
|
||
}
|
||
}
|
||
}
|
||
ActionMoveAwayFromObject(oTarget, TRUE, f50);
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name Flee
|
||
//::///////////////////////////////////////////////
|
||
Makes checks, and may flee.
|
||
|
||
SpawnInCondition(TURN_OFF_GROUP_MORALE, AI_TARGETING_FLEE_MASTER);
|
||
// This turns OFF any sort of group morale bonuses.
|
||
|
||
|
||
SpawnInCondition(FLEE_TO_NEAREST_NONE_SEEN, AI_TARGETING_FLEE_MASTER);
|
||
// This is a simple thing, and will, instead of moving to the best
|
||
// group of allies, will just move to the nearest non-seen and non-heard
|
||
// ally. This may help resources...if fleeing is activated.
|
||
|
||
SpawnInCondition(FLEE_TO_OBJECT, AI_TARGETING_FLEE_MASTER);
|
||
// This will flee to an object, with the correct tag.
|
||
// ONLY if they are not there already, of course. Gets nearest (but if
|
||
// not valid, any one). This will get any object - placeables, monsters,
|
||
// well - anything! Also, waypoints, so this is easily useful.
|
||
// Can be dynamic: Instead of "BOS.." it could be GetTag(OBJECT_SELF) + "_FLEE" or something
|
||
// Ideas: Fleeing to thier leader, fleeing to a cage or a door (can be other area).
|
||
|
||
LocalString(OBJECT_SELF, AI_FLEE_OBJECT, "BOSS_TAG_OR_WHATEVER");
|
||
// This needs setting if the above is to work.
|
||
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
// Only true if we have a flee object.
|
||
int AI_ActionFlee(string sArray)
|
||
{
|
||
object oToFlee = AI_GetNearbyFleeObject();
|
||
if(GetIsObjectValid(oToFlee))
|
||
{
|
||
// Speak string...
|
||
if(sArray != "") SpeakArrayString(sArray);
|
||
// Set it so we will not return for a bit.
|
||
SetAIObject(AI_FLEE_TO, oToFlee);
|
||
// Sets to flee mode
|
||
SetCurrentAction(AI_SPECIAL_ACTIONS_FLEE);
|
||
// 25: [DCR:Fleeing] Fleeing to allies. [ID Array] " + sArray + " [Ally] " + GetName(oToFlee)
|
||
DebugActionSpeakByInt(25, oToFlee, FALSE, sArray);
|
||
// Move too them.
|
||
ActionMoveToObject(oToFlee, TRUE);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Fleeing - set OnSpawn for setup. Uses a WILL save!
|
||
// - Bonuses in groups
|
||
// - May go get help
|
||
// - Won't run on a CR/HD threshold.
|
||
// - Leaders help! And all is intelligence based.
|
||
int AI_AttemptMoraleFlee()
|
||
{
|
||
int iMoraleValue = GetAIInteger(AI_MORALE);
|
||
// Commoner flee
|
||
if(iMoraleValue < FALSE)
|
||
{
|
||
AI_ActionFleeScared();
|
||
return TRUE;
|
||
}
|
||
// Declare vaiables...
|
||
// We only flee every few rounds, or at least check too!
|
||
// - This is set On Spawn if we are fearless by race or immunity
|
||
if(!GetSpawnInCondition(AI_FLAG_FLEEING_FEARLESS, AI_TARGETING_FLEE_MASTER) &&
|
||
// - Its on a timer.
|
||
(!GetLocalTimer(AI_TIMER_FLEE) ||
|
||
// - We check each round if under 40 and under 10% HP
|
||
(GlobalOurCurrentHP <= i40 && GlobalOurPercentHP <= i10)))
|
||
{
|
||
// Re-set timer even if we don't do the morale check.
|
||
if(!GetLocalTimer(AI_TIMER_FLEE))
|
||
{
|
||
SetLocalTimer(AI_TIMER_FLEE, f20);
|
||
}
|
||
// Bosses, when they flee, make others flee.
|
||
int iBoss = GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER);
|
||
// Specials first: Never fight against impossible odds...or leader sense.
|
||
if(GlobalAverageEnemyHD > GlobalOurHitDice + i8)
|
||
{
|
||
if(GetSpawnInCondition(AI_FLAG_FLEEING_NEVER_FIGHT_IMPOSSIBLE_ODDS, AI_TARGETING_FLEE_MASTER) || iBoss)
|
||
{
|
||
// You see, if there is no valid object ,we stop the script so not to call GetNearbyFleeObject more then once
|
||
if(AI_ActionFlee(AI_TALK_ON_MORALE_BREAK))
|
||
{
|
||
if(iBoss)
|
||
{
|
||
// AI command - makes everyone flee.
|
||
AISpeakString(LEADER_FLEE_NOW);
|
||
}
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
// HP check
|
||
int iHPBeAt = GetBoundriedAIInteger(HP_PERCENT_TO_CHECK_AT, i80, i100, i1);
|
||
// Do the check
|
||
if(GlobalOurPercentHP <= iHPBeAt ||
|
||
// Either HP OR one of these:
|
||
(!GetSpawnInCondition(AI_FLAG_FLEEING_NO_OVERRIDING_HP_AMOUNT, AI_TARGETING_FLEE_MASTER) &&
|
||
// - If the enemy is very strong, we do a will save (8 HD over us)
|
||
(GlobalAverageEnemyHD > GlobalOurHitDice + i8 ||
|
||
// - If we have a morerate morale penalty (Set by massive damage/many deaths around us)
|
||
// - We are alone
|
||
GetAIInteger(AI_MORALE_PENALTY) >= i10 ||
|
||
GlobalTotalAllies < i1)))
|
||
{
|
||
// Difference of HD to cancle out check...
|
||
int iDifference = GetBoundriedAIInteger(AMOUNT_OF_HD_DIFFERENCE_TO_CHECK, -2, -50, i50);// Default to -2.
|
||
int iAlliesBonus = FALSE;
|
||
// We add X amount per ally present.
|
||
// - We can add up to a minimum of 6, maximum of our hit dice/2.
|
||
if(!GetSpawnInCondition(AI_FLAG_FLEEING_TURN_OFF_GROUP_MORALE, AI_TARGETING_FLEE_MASTER) &&
|
||
GlobalTotalAllies >= i1)
|
||
{
|
||
if(GlobalTotalAllies > i6)
|
||
{
|
||
iAlliesBonus = i6;
|
||
}
|
||
else if(iAlliesBonus > GlobalOurHitDice/i2)
|
||
{
|
||
iAlliesBonus = GlobalOurHitDice/i2;
|
||
}
|
||
else
|
||
{
|
||
iAlliesBonus = GlobalTotalAllies;
|
||
}
|
||
iMoraleValue += iAlliesBonus;
|
||
}
|
||
// Double value if leader present!
|
||
if(GlobalValidLeader) iMoraleValue *= i2;
|
||
// Check...
|
||
if((GlobalOurHitDice - iDifference) < GlobalAverageEnemyHD)
|
||
{
|
||
// Save DC from 0 to 50. Default 20. This is the base.
|
||
int iSaveDC = GetBoundriedAIInteger(BASE_MORALE_SAVE, i20, i100, i1);
|
||
// Change it as with our leader, morale and group morale bonuses.
|
||
iSaveDC -= iMoraleValue;
|
||
// Note we will add DC based on difference in HD between us
|
||
// and our Global Melee Target (as the fact is, it is normally
|
||
// the nearest, and is always valid. Also, if you are fighting
|
||
// something you can defeat (EG: Badger summon) you don't run,
|
||
// but you will run against the level 20 mage who summoned it, if
|
||
// you were levle 5 or so.
|
||
|
||
// Add thiers to DC, take ours. Basically Ours - Theres added on.
|
||
iSaveDC += GetHitDice(GlobalMeleeTarget);
|
||
iSaveDC -= GlobalOurHitDice;
|
||
|
||
// If we have a save DC of over 1...we check!
|
||
if(iSaveDC >= i1)
|
||
{
|
||
if(!WillSave(OBJECT_SELF, iSaveDC, SAVING_THROW_TYPE_FEAR, GlobalMeleeTarget))
|
||
{
|
||
// If we fail the will save, VS fear...
|
||
// You see, if there is no valid object ,we stop the script so not to call GetNearbyFleeObject more then once
|
||
if(AI_ActionFlee(AI_TALK_ON_MORALE_BREAK))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Intelligence 1-3 runs when we fail and no allies.
|
||
// 4+ means we don't run, and stay and fight (IE FALSE)
|
||
else if(GlobalIntelligence <= i3)
|
||
{
|
||
// Stupid run
|
||
AI_ActionFleeScared();
|
||
return TRUE;
|
||
}
|
||
else
|
||
// Higher intelligence don't run from them, but does say something
|
||
{
|
||
SpeakArrayString(AI_TALK_ON_CANNOT_RUN);
|
||
}
|
||
return FALSE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: GoForTheKill
|
||
//::///////////////////////////////////////////////
|
||
Very basic at the moment. This will, if thier inteligence is
|
||
high and they can be hit relitivly easily, and at low HP
|
||
will attack in combat. May add some spells, if I can be bothered.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptGoForTheKill()
|
||
{
|
||
// Turn off script
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_NO_GO_FOR_THE_KILL, AI_COMBAT_MASTER) ||
|
||
GlobalIntelligence <= i8)
|
||
{
|
||
return FALSE;
|
||
}
|
||
int nSleepingOnly = FALSE;
|
||
// Finnish off a dead PC, or dying one, out of combat.
|
||
object oTempEnemy = GetNearestCreature(
|
||
CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_IS_ALIVE, FALSE,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
// Check if valid. If not, check for one with the spell "sleep" on them.
|
||
if(!GetIsObjectValid(oTempEnemy))
|
||
{
|
||
// This means we only attack in melee (no spells, no ranged)
|
||
nSleepingOnly = TRUE;
|
||
oTempEnemy = GetNearestCreature(
|
||
CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_SLEEP,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
// Dragon sleep breath
|
||
if(!GetIsObjectValid(oTempEnemy))
|
||
{
|
||
oTempEnemy = GetNearestCreature(
|
||
CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_HAS_SPELL_EFFECT, SPELLABILITY_DRAGON_BREATH_SLEEP,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
}
|
||
}
|
||
float fDistance = GetDistanceToObject(oTempEnemy);
|
||
// Valid, and is not "dead dead"
|
||
if(GetIsObjectValid(oTempEnemy) &&
|
||
// -11 is TOTALLY dead.
|
||
GetCurrentHitPoints(oTempEnemy) > iM10 &&
|
||
// Intelligence 9+. 10 will attack with many enemies, 9 with 1 or under
|
||
((GlobalMeleeAttackers <= i3 &&
|
||
GlobalIntelligence >= i10) ||
|
||
GlobalMeleeAttackers <= i1) &&
|
||
// Make sure it isn't too far, and they are only sleeping
|
||
(nSleepingOnly == FALSE ||
|
||
fDistance < f6) &&
|
||
// Big AC check, but mages with +1, VS ac of 30 won't hit :-)
|
||
GlobalOurBaseAttackBonus >= GetAC(oTempEnemy) - i25)
|
||
{
|
||
// 26: "[DCR:GFTK] Attacking a PC who is dying/asleep! [Enemy]" + GetName(oTempEnemy)
|
||
DebugActionSpeakByInt(26, oTempEnemy);
|
||
// Attempt default most damaging to try and do most damage.
|
||
if(fDistance > GlobalRangeToMeleeTarget)
|
||
{
|
||
ActionEquipMostDamagingRanged(oTempEnemy);
|
||
}
|
||
else
|
||
{
|
||
ActionEquipMostDamagingMelee(oTempEnemy);
|
||
}
|
||
ActionAttack(oTempEnemy);
|
||
return TRUE;
|
||
}
|
||
|
||
// Now, we check for most damaged member of the nearest enemies faction.
|
||
// - Uses bioware function - just...simpler
|
||
// - Only with 9+ int
|
||
oTempEnemy = GetFactionMostDamagedMember(GlobalMeleeTarget);
|
||
// - Effect, if lots of people can attack this, like with ranged things,
|
||
// it can lead to some bloody deaths, especially for some classes.
|
||
// - Note: Intelligence of 9+ shouldn't be used for low levels!
|
||
if(GetIsObjectValid(oTempEnemy))
|
||
{
|
||
// From 1-6, or 1-10 HP they need.
|
||
int iHP = GetCurrentHitPoints(oTempEnemy);
|
||
int iBeBelow = i6 + Random(i4);
|
||
int iHTH = FALSE;// TRUE = melee
|
||
fDistance = GetDistanceToObject(oTempEnemy);
|
||
// If in melee range, we set to melee, and add stength to what we can knock off!
|
||
if(fDistance <= f4)
|
||
{
|
||
iHTH = TRUE;
|
||
iBeBelow += GetAbilityModifier(ABILITY_STRENGTH);
|
||
}
|
||
// Check...
|
||
if(GetCurrentHitPoints(oTempEnemy) <= iBeBelow &&
|
||
GetMaxHitPoints(oTempEnemy) >= i20)
|
||
{
|
||
// We also need to check if we can hit them to kill them!
|
||
// - Either high BAB against thier AC (take roll as 15+)
|
||
if(GlobalOurBaseAttackBonus >= GetAC(oTempEnemy) - i15 ||
|
||
// - or it is 0.75 of our hit dice (IE cleric or better).
|
||
GlobalOurBaseAttackBonus >= ((GetHitDice(oTempEnemy) * i3) / i2))
|
||
{
|
||
if(iHTH)
|
||
{
|
||
ActionEquipMostDamagingMelee(oTempEnemy);
|
||
}
|
||
// We take a big risk here... we equip a ranged (default call)
|
||
// not how good it is, things in the way, or
|
||
// if we don't have ammo or even have it!
|
||
// - EquipsMelee Anyway.
|
||
// - Still needs to be valid, else we do normal actions.
|
||
else if(GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED)))
|
||
{
|
||
ActionEquipMostDamagingRanged(oTempEnemy);
|
||
}
|
||
else // If we have no ranged wepaon, ignore this.
|
||
{
|
||
return FALSE;
|
||
}
|
||
ActionAttack(oTempEnemy);
|
||
return TRUE;
|
||
}
|
||
// Else, we do spells! Just a few...
|
||
else
|
||
{
|
||
// This stops us being unprotected, as there may be melee attackers.
|
||
if(!iHTH || GlobalMeleeAttackers < i3)
|
||
{
|
||
// Spells are done with little consideration for immunities.
|
||
// - Saves power, and shows fury to knock out.
|
||
// - All auto-hit, no-save spells.
|
||
// - No items.
|
||
// - Checks immunity level, incase of globes.
|
||
int iImmuneLevel = AI_GetSpellLevelEffect(oTempEnemy);
|
||
|
||
// Issacs storms are long range beasts!
|
||
// Greater Missile Storm. Level 6 (Wizard). 2d6 * Caster level dice up to 20, divided between targets.
|
||
if(AI_ActionCastSpell(SPELL_ISAACS_GREATER_MISSILE_STORM, SpellHostRanged, oTempEnemy, i16)) return TRUE;
|
||
|
||
if(iImmuneLevel < i4)
|
||
{
|
||
// Lesser Missile Storm. Level 4 (Wizard). 1d6 * Caster level dice up to 10, divided between targets.
|
||
if(AI_ActionCastSpell(SPELL_ISAACS_LESSER_MISSILE_STORM, SpellHostRanged, oTempEnemy, i14)) return TRUE;
|
||
|
||
if(iImmuneLevel < i3)
|
||
{
|
||
// Searing light. Level 3 (Cleric). Full level: 1-10d6 VS undead. Half Level: 1-5d6 VS constructs. 1-5d8 VS Others.
|
||
if(AI_ActionCastSpell(SPELL_SEARING_LIGHT, SpellHostRanged, oTempEnemy, i13)) return TRUE;
|
||
|
||
if(iImmuneLevel < i2)
|
||
{
|
||
// Acid arrow. Level 2 (Wizard). 3d6 damage on hit. 1d6 for more rounds after.
|
||
if(AI_ActionCastSpell(SPELL_MELFS_ACID_ARROW, SpellHostRanged, oTempEnemy, i12)) return TRUE;
|
||
|
||
if(iImmuneLevel < i1)
|
||
{
|
||
// Magic missile. Good knockout spell. Shield stops it.
|
||
if(!GetHasSpellEffect(SPELL_SHIELD, oTempEnemy))
|
||
{
|
||
// Magic Missile. Level 1 (Wizard). 1-5 missiles, 1-9 levels. 1d4+1 damage/missile.
|
||
if(AI_ActionCastSpell(SPELL_MAGIC_MISSILE, SpellHostRanged, oTempEnemy, i11)) return TRUE;
|
||
}
|
||
|
||
// Negative energy ray - note will save!
|
||
if(fDistance < fMediumRange &&
|
||
!AI_SaveImmuneAOE(oTempEnemy, SAVING_THROW_WILL, i1))
|
||
{
|
||
// Negative Energy Ray. Level 1 (Mage) 2 (Cleric) 1d6(CasterLevel/2) to 5d6 negative damage.
|
||
if(AI_ActionCastSpell(SPELL_NEGATIVE_ENERGY_RAY, SpellHostRanged, oTempEnemy, i11)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name: AbilityAura
|
||
//::///////////////////////////////////////////////
|
||
This will use all abilties.
|
||
|
||
Note:
|
||
|
||
- They are cheat cast. If removed, as DMG, they are instantly re-applied as
|
||
a free action!
|
||
- They are cast instantly.
|
||
-
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
void AI_AttemptCastAuraSpell(int iSpellID)
|
||
{
|
||
if(GetHasSpell(iSpellID) && !GetHasSpellEffect(iSpellID))
|
||
{
|
||
// Cheat/fast cast the aura.
|
||
ActionCastSpellAtObject(iSpellID, OBJECT_SELF, METAMAGIC_NONE, TRUE, i20, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
|
||
}
|
||
}
|
||
|
||
void AI_ActionAbilityAura()
|
||
{
|
||
if(SpellAura)
|
||
{
|
||
int iCnt;
|
||
// We can loop around a few:
|
||
// 195 AURA_BLINDING
|
||
// 196 Aura_Cold
|
||
// 197 Aura_Electricity
|
||
// 198 Aura_Fear
|
||
// 199 Aura_Fire
|
||
// 200 Aura_Menace
|
||
// 201 Aura_Protection
|
||
// 202 Aura_Stun
|
||
// 203 Aura_Unearthly_Visage
|
||
// 204 Aura_Unnatural
|
||
for(iCnt = SPELLABILITY_AURA_BLINDING; iCnt <= SPELLABILITY_AURA_UNNATURAL; iCnt++)
|
||
{
|
||
// Wrapper function.
|
||
AI_AttemptCastAuraSpell(iCnt);
|
||
}
|
||
// Other auras we would have unlimited uses in
|
||
AI_AttemptCastAuraSpell(SPELLABILITY_DRAGON_FEAR);
|
||
AI_AttemptCastAuraSpell(SPELLABILITY_TYRANT_FOG_MIST);
|
||
AI_AttemptCastAuraSpell(AI_SPELLABILITY_AURA_OF_HELLFIRE);
|
||
|
||
// Then, ones that are limited duration, or limited uses.
|
||
if(!GetHasSpellEffect(SPELLABILITY_MUMMY_BOLSTER_UNDEAD) &&
|
||
GetHasSpell(SPELLABILITY_MUMMY_BOLSTER_UNDEAD))
|
||
{
|
||
ActionCastSpellAtObject(SPELLABILITY_MUMMY_BOLSTER_UNDEAD, OBJECT_SELF);
|
||
}
|
||
if(!GetHasSpellEffect(SPELLABILITY_EMPTY_BODY) && GetHasFeat(FEAT_EMPTY_BODY))
|
||
{
|
||
ActionUseFeat(FEAT_EMPTY_BODY, OBJECT_SELF);
|
||
}
|
||
}
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name LeaderActions
|
||
//::///////////////////////////////////////////////
|
||
This will make the leader "command" allies. Moving
|
||
one to get others, orshout attack my target.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
void AI_ActionLeaderActions()
|
||
{
|
||
// We only advance if we are the group leader.
|
||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
// Does the enemy out class us by a random amount? (2-6 HD difference).
|
||
// Sends a runner.
|
||
if(GlobalAverageEnemyHD > (GlobalAverageFriendlyHD + d4() + i2))
|
||
{
|
||
if(GlobalValidAlly && !GetLocalTimer(AI_TIMER_LEADER_SENT_RUNNER))
|
||
{
|
||
object oBestAllies = AI_GetNearbyFleeObject();
|
||
if(GetIsObjectValid(oBestAllies)) // Send a runner...
|
||
{
|
||
// Speak array.
|
||
SpeakArrayString(AI_TALK_ON_LEADER_SEND_RUNNER);
|
||
// All we need to do is set an action on the nearest ally,
|
||
// and the object to run to.
|
||
SetLocalObject(GlobalNearestAlly, AI_OBJECT + AI_RUNNER_TARGET, oBestAllies);
|
||
// Set action.
|
||
SetLocalInt(GlobalNearestAlly, AI_CURRENT_ACTION, AI_SPECIAL_ACTIONS_ME_RUNNER);
|
||
// Don't send another for 50 seconds.
|
||
SetLocalTimer(AI_TIMER_LEADER_SENT_RUNNER, f50);
|
||
}
|
||
}
|
||
}
|
||
// Second, we shout for people to target this person - IF we need help
|
||
// - Example, low HP us, high HP them.
|
||
// - Uses melee target.
|
||
int iLeaderCountForShout = GetAIInteger(AI_LEADER_SHOUT_COUNT);
|
||
iLeaderCountForShout++;
|
||
if(iLeaderCountForShout > i4)
|
||
{
|
||
// Check HD difference
|
||
if(GetHitDice(GlobalMeleeTarget) - i5 > GlobalAverageEnemyHD)
|
||
{
|
||
// Set who we want them to attack.
|
||
SetAIObject(AI_ATTACK_SPECIFIC_OBJECT, GlobalMeleeTarget);
|
||
// Shout counter re-set
|
||
iLeaderCountForShout = FALSE;
|
||
// Speak random custom shout (like "You lot, attack this one!")
|
||
SpeakArrayString(AI_TALK_ON_LEADER_ATTACK_TARGET);
|
||
|
||
// Speak a silent AI shout
|
||
AISpeakString(LEADER_ATTACK_TARGET);
|
||
}
|
||
}
|
||
SetAIInteger(AI_LEADER_SHOUT_COUNT, iLeaderCountForShout);
|
||
}
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name ArcherRetreat
|
||
//::///////////////////////////////////////////////
|
||
This may make the archer retreat - if they are set to, and have a ranged weapon
|
||
and don't have point blank shot, and has a nearby ally. (INT>=1 if set to does
|
||
it more if higher).
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptArcherRetreat()
|
||
{
|
||
//SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER);
|
||
// This is quite specifically for archers - ones good at using bows.
|
||
// They will, quite often (with support from others) retreat to use
|
||
// bows much better (IE no AOO). they perfere ranged combat, and only
|
||
// draw HTH weapons (If they have any, else they use thier bow) in the end.
|
||
// Note: Below, you can set it to ALWAYs move back, if an archer.
|
||
//SetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER);
|
||
|
||
// Many conditions - we must have it to start!
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER) ||
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER))
|
||
{
|
||
// Enemy range must be close (HTH distance, roughly) and we must have
|
||
// a close ally. ELSE we'll just get lots of AOO and die running (pointless)
|
||
if((GlobalEnemiesIn4Meters > i1 && GlobalValidAlly && GlobalRangeToAlly < f4) ||
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER))
|
||
{
|
||
// We may, if at the right range (and got a ranged weapon) equip it.
|
||
object oRanged = GetAIObject(AI_WEAPON_RANGED);
|
||
if(GetIsObjectValid(oRanged) &&
|
||
GetItemPossessor(oRanged) == OBJECT_SELF)
|
||
{
|
||
// Is the ranged weapon valid? And iRangedAttack == TRUE
|
||
// We need valid ammo, else we eqip nothing!
|
||
int iValidAmmo = GetAIInteger(AI_WEAPON_RANGED_AMMOSLOT);
|
||
if(iValidAmmo == INVENTORY_SLOT_RIGHTHAND ||
|
||
GetIsObjectValid(GetItemInSlot(iValidAmmo)))
|
||
{
|
||
// 27: "[DCR:Moving] Archer Retreating back from the enemy [Enemy]" + GetName(GlobalMeleeTarget)
|
||
DebugActionSpeakByInt(27, GlobalMeleeTarget);
|
||
// - Equip the rnaged weapon if not already
|
||
if(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND) != oRanged)
|
||
{
|
||
ActionEquipItem(oRanged, INVENTORY_SLOT_RIGHTHAND);
|
||
}
|
||
ActionMoveAwayFromObject(GlobalMeleeTarget, TRUE, f15);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name UseTurning
|
||
//::///////////////////////////////////////////////
|
||
This, if we have the feat, will turn things it can.
|
||
Need to really make sure it works well, and maybe
|
||
make an interval between one and another.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Bioware. Edited by Jasperre.
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptFeatTurning()
|
||
{
|
||
// We loop the targets and see who we come up with to hit!
|
||
// - Use ranged enemy checks, seen/heard and only if we have any
|
||
// valid races in the area.
|
||
if(GetHasFeat(FEAT_TURN_UNDEAD))
|
||
{
|
||
// The turn level is set OnSpawn if we have FEAT_TURN_UNDEAD
|
||
int nTurnLevel = GetAIInteger(AI_TURNING_LEVEL);
|
||
// Error checking
|
||
if(nTurnLevel <= i0) return FALSE;
|
||
|
||
// We then loop through targets nearby, up to 20M. We can easily use
|
||
// GetHasSpellEffect(AI_SPELL_FEAT_TURN_UNDEAD)
|
||
// and for the epic feat
|
||
// GetHasSpellEffect(AI_SPELL_FEAT_PLANAR_TURNING)
|
||
|
||
// Ok, so how does turning work?
|
||
// - Basically, all undead/virmin/elementals or outsiders are turned,
|
||
// with constructs being damaged.
|
||
// - Works agasints non-friends.
|
||
// - If we have nClassLevel/2 >= nHD (nHD = GetHitDice(oTarget) + GetTurnResistanceHD(oTarget)
|
||
// then kill, else turn.
|
||
// - Only can turn if nHD <= nTurnLevel.
|
||
// - 20M range.
|
||
// Flags for bonus turning types
|
||
int nElemental = GetHasFeat(FEAT_AIR_DOMAIN_POWER) + GetHasFeat(FEAT_EARTH_DOMAIN_POWER) + GetHasFeat(FEAT_FIRE_DOMAIN_POWER) + GetHasFeat(FEAT_WATER_DOMAIN_POWER);
|
||
int nVermin = GetHasFeat(FEAT_PLANT_DOMAIN_POWER) + GetHasFeat(FEAT_ANIMAL_COMPANION);
|
||
int nConstructs = GetHasFeat(FEAT_DESTRUCTION_DOMAIN_POWER);
|
||
int nPlanarTurning = GetHasFeat(AI_FEAT_EPIC_PLANAR_TURNING);
|
||
// Outsiders can be turned via. Epic Planar Turning too.
|
||
int nOutsider = GetHasFeat(FEAT_GOOD_DOMAIN_POWER) + GetHasFeat(FEAT_EVIL_DOMAIN_POWER) + nPlanarTurning;
|
||
int nHD, nRacial, iValid;
|
||
// Loop, using a sort of loop used for the turning check.
|
||
// - Add GetObjectSeen/GetObjectHeard.
|
||
// - We stop if, when we would use it, it'd re-apply effects to one
|
||
// already turned.
|
||
// - We stop if we get to something we can turn!
|
||
int nCnt = i1;
|
||
object oTarget = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC , OBJECT_SELF, nCnt);
|
||
while(GetIsObjectValid(oTarget) && iValid == FALSE && GetDistanceToObject(oTarget) <= f20)
|
||
{
|
||
if(!GetIsFriend(oTarget) && (GetObjectSeen(oTarget) || GetObjectHeard(oTarget)))
|
||
{
|
||
nHD = GetHitDice(oTarget) + GetTurnResistanceHD(oTarget);
|
||
nRacial = GetRacialType(oTarget);
|
||
// Planar creatures add spell resistance (don't bother if can't turn them)
|
||
if(nRacial == RACIAL_TYPE_OUTSIDER && nOutsider)
|
||
{
|
||
if(nPlanarTurning)
|
||
{
|
||
// Half with the epic feat
|
||
nHD += (GetSpellResistance(oTarget)/i2);
|
||
}
|
||
else
|
||
{
|
||
// Full SR added without the epic feat.
|
||
nHD += GetSpellResistance(oTarget);
|
||
}
|
||
}
|
||
if(nHD <= nTurnLevel)
|
||
{
|
||
// If we would try and re-apply effects, then break and do
|
||
// not use
|
||
if(GetHasSpellEffect(AI_SPELL_FEAT_TURN_UNDEAD, oTarget) ||
|
||
GetHasSpellEffect(AI_SPELL_FEAT_PLANAR_TURNING, oTarget))
|
||
{
|
||
iValid = i2;
|
||
}
|
||
// Else Check the various domain turning types
|
||
else if(nRacial == RACIAL_TYPE_UNDEAD ||
|
||
(nRacial == RACIAL_TYPE_VERMIN && nVermin) ||
|
||
(nRacial == RACIAL_TYPE_ELEMENTAL && nElemental) ||
|
||
(nRacial == RACIAL_TYPE_CONSTRUCT && nConstructs) ||
|
||
(nRacial == RACIAL_TYPE_OUTSIDER && nOutsider))
|
||
{
|
||
iValid = TRUE;
|
||
}
|
||
}
|
||
}
|
||
nCnt++;
|
||
oTarget = GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_NOT_PC, OBJECT_SELF, nCnt);
|
||
}
|
||
// If valid, use it
|
||
if(iValid == TRUE)
|
||
{
|
||
// 28: "[DCR:Turning] Using Turn Undead"
|
||
DebugActionSpeakByInt(28);
|
||
// We do not bother turning off hiding/searching - but we do check
|
||
// expertise!
|
||
if(GlobalIntelligence >= i4)
|
||
{
|
||
if(GetHasFeat(FEAT_IMPROVED_EXPERTISE))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE);
|
||
}
|
||
else if(GetHasFeat(FEAT_EXPERTISE))
|
||
{
|
||
AI_SetMeleeMode(ACTION_MODE_EXPERTISE);
|
||
}
|
||
}
|
||
ActionUseFeat(FEAT_TURN_UNDEAD, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Else nothing to do it against or no feat.
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name TalentBardSong
|
||
//::///////////////////////////////////////////////
|
||
This, if we have the feat, and not the effects,
|
||
will use it on ourselves.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Bioware
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptFeatBardSong()
|
||
{
|
||
// Got it and not silenced
|
||
if(GetHasFeat(FEAT_BARD_SONGS) && !AI_GetAIHaveEffect(GlobalEffectSilenced))
|
||
{
|
||
// the spell script used is 411
|
||
// Get nearest without this spell's effect to check too
|
||
object oSonged = GetNearestCreature(CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, AI_SPELL_BARD_SONG, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_FRIEND, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
if((!GetHasSpellEffect(AI_SPELL_BARD_SONG) &&
|
||
!GetHasFeatEffect(FEAT_BARD_SONGS) &&
|
||
// Do not use if we are deaf...
|
||
!AI_GetAIHaveEffect(GlobalEffectDeaf, oSonged)) ||
|
||
// ...but we could use it if we have an undeaf ally in 5M
|
||
(GetIsObjectValid(oSonged) && GetDistanceToObject(oSonged) < f5 &&
|
||
!AI_GetAIHaveEffect(GlobalEffectDeaf, oSonged)))
|
||
{
|
||
// 29: "[DCR:Bard Song] Using"
|
||
DebugActionSpeakByInt(29);
|
||
// We do not bother turning off hiding/searching
|
||
ActionUseFeat(FEAT_BARD_SONGS, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
// We may use curse song!
|
||
else if(GetHasFeat(FEAT_CURSE_SONG))
|
||
{
|
||
// get nearest without this spell's effect
|
||
oSonged = GetNearestCreature(CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, AI_SPELL_CURSE_SONG, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
// If they are not cursed, IE valid, in 6M, and not deaf, we try it.
|
||
if(GetIsObjectValid(oSonged) &&
|
||
GetDistanceToObject(oSonged) <= f6 &&
|
||
!AI_GetAIHaveEffect(GlobalEffectDeaf, oSonged))
|
||
{
|
||
// 30: "[DCR:Bard Curse Song] Using"
|
||
DebugActionSpeakByInt(30);
|
||
// We do not bother turning off hiding/searching
|
||
ActionUseFeat(FEAT_CURSE_SONG, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// Wrappers Greater and Lesser spell breach.
|
||
int AI_ActionCastBreach()
|
||
{
|
||
// Greater Spell Breach. Level 6 (Wizard). Dispels cirtain protections automatically + lowers spell resistance
|
||
if(AI_ActionCastSpell(SPELL_GREATER_SPELL_BREACH, SpellHostRanged, GlobalDispelTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
// Lesser Spell Breach. Level 4 (Wizard) Dispels cirtain protections automatically + lowers spell resistance
|
||
if(AI_ActionCastSpell(SPELL_LESSER_SPELL_BREACH, SpellHostRanged, GlobalDispelTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
return FALSE;
|
||
}
|
||
// Wrappers all dispel spells
|
||
int AI_ActionCastDispel()
|
||
{
|
||
// Mordenkainens Disjunction. Level 9 (Wizard). Acts like a breach, dispel AND lowers SR, in a big area.
|
||
if(AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, GlobalDispelTarget, i19, FALSE, ItemHostAreaInd)) return TRUE;
|
||
// Greater Dispeling. Level 5 (Bard/Innate) 6 (Cleric/Druid/Mage) General dispel up to 15 caster levels.
|
||
if(AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, GlobalDispelTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
// Dispel Magic. Level 3 (Bard/Cleric/Paladin/Mage/Innate) 6 (Druid) General dispel up to 10 caster levels.
|
||
if(AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostRanged, GlobalDispelTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
// Only lesser if under 15 HD, because it may not be worth it (DC wise)
|
||
if(GlobalAverageEnemyHD < i15)
|
||
{
|
||
// Lesser Dispel. Level 1 (Bard) 2 (Cleric/Druid/Mage/Innate). General dispel up to 5 caster levels.
|
||
if(AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, GlobalDispelTarget, i12, FALSE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers Premonition, Greater Stoneskin and Stoneskin.
|
||
// Includes Shades Stoneskin too. SPELL_SHADES_STONESKIN
|
||
// - iLowest - 8 = Prem, 6 = Greater, 4 = Stoneskin
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)
|
||
int AI_SpellWrapperPhisicalProtections(int iLowest = 1)
|
||
{
|
||
// Stoneskin - protection from attackers.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections))
|
||
{
|
||
// Epic Warding (Mage Only) - Damage reduction 50/+20. Lasts 1 round per level.
|
||
if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_EPIC_WARDING, AI_SPELL_EPIC_WARDING)) return TRUE;
|
||
|
||
// Psionic Inertial Barrier
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER, SpellProSelf)) return TRUE;
|
||
|
||
if(iLowest <= i8)
|
||
{
|
||
// Preminition is 30/+5 damage resistance. Level 8 spell. Always class
|
||
// as level 9. (Mage)
|
||
if(AI_ActionCastSpell(SPELL_PREMONITION, SpellProSelf, OBJECT_SELF, i18, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
if(iLowest <= i6)
|
||
{
|
||
// Then, greater stoneskin protects a lot of damage - 20/+5.
|
||
// We also consider this to be a high-level spell. Level 6 (Mage) 7 (druid)
|
||
if(AI_ActionCastSpell(SPELL_GREATER_STONESKIN, SpellProSelf, OBJECT_SELF, i16, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
if(iLowest <= i4)
|
||
{
|
||
// Shades stoneskin. SPELL_SHADES_STONESKIN
|
||
if(AI_ActionCastSubSpell(SPELL_SHADES_STONESKIN, SpellProSinTar, OBJECT_SELF, i16, FALSE, ItemProSinTar)) return TRUE;
|
||
|
||
// Stoneskin is next, level 4, but 10/+5 resistance Level 4 (Mage) 5 (Druid)
|
||
if(AI_ActionCastSpell(SPELL_STONESKIN, SpellProSinTar, OBJECT_SELF, i14, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers Energy Buffer, Protection From Elements, Resist Elements, Endure elements
|
||
// - iLowest - Goes 5, 3, 2, 1.
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections)
|
||
int AI_SpellWrapperElementalProtections(int iLowest = 1)
|
||
{
|
||
// Elemental protections (fireball ETC)
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) && iLowest <= i5)
|
||
{
|
||
// Energy buffer - 40/- resistance to the damage.
|
||
// 6 for druids ETC, but 5 Mage/Innate.
|
||
if(AI_ActionCastSpell(SPELL_ENERGY_BUFFER, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
if(iLowest <= i3)
|
||
{
|
||
// Protection from elements - 30/- resistance to the damage. Level 3
|
||
if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_ELEMENTS, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
if(iLowest <= i2)
|
||
{
|
||
// Resist elements - 20/- resistance to the damage. Level 2
|
||
if(AI_ActionCastSpell(SPELL_RESIST_ELEMENTS, SpellProSinTar, OBJECT_SELF, i12, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
if(iLowest <= i1)
|
||
{
|
||
// Endure elements - 10/- resistance to the damage. Level 1
|
||
if(AI_ActionCastSpell(SPELL_ENDURE_ELEMENTS, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers Haste and Mass Haste.
|
||
// - iLowest - 6 = Mass, 3 = Haste
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)
|
||
int AI_SpellWrapperHasteEnchantments(int iLowest = 1)
|
||
{
|
||
// Excelent to cast this normally - +4AC. Probably normally best after protections from
|
||
// phisical attacks.
|
||
if(!AI_GetAIHaveEffect(GlobalEffectHaste) && iLowest <= i6)
|
||
{
|
||
// The feat, for haste, called blinding speed.
|
||
if(AI_ActionUseFeatOnObject(FEAT_EPIC_BLINDING_SPEED)) return TRUE;
|
||
|
||
// Mass haste, level 6 spell. (Mage/Bard) Affects allies, and adds +1 action, +4 AC and +150% move
|
||
if(AI_ActionCastSpell(SPELL_MASS_HASTE, SpellEnhAre, OBJECT_SELF, i16, FALSE, ItemEnhAre, PotionEnh)) return TRUE;
|
||
if(iLowest <= i3)
|
||
{
|
||
// Haste, Level 3 spell. (Mage/Bard)
|
||
if(AI_ActionCastSpell(SPELL_HASTE, SpellEnhSinTar, OBJECT_SELF, i13, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers Shadow Shield, Ethereal Visage and Ghostly Visage.
|
||
// Includes Greater Shadow Conjuration Ghostly Visage (SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE
|
||
// - iLowest - 7 = Shadow, 6 = Ethereal 2 = Ghostly
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections)
|
||
int AI_SpellWrapperVisageProtections(int iLowest = 1)
|
||
{
|
||
// Visages are generally lower DR, with some spell-resisting or effect-immunty extras.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections) && iLowest <= i7)
|
||
{
|
||
// Shadow Shield - has 10/+3 damage reduction
|
||
// Level 7 (Mage)
|
||
if(AI_ActionCastSpell(SPELL_SHADOW_SHIELD, SpellProSelf, OBJECT_SELF, i17, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
if(iLowest <= i6)
|
||
{
|
||
// Ethereal Visage (level 6 (Mage)) is 20/+3 damage reduction - no limit!
|
||
// + Immunity to level 2, 1, 0 level spells.
|
||
if(AI_ActionCastSpell(SPELL_ETHEREAL_VISAGE, SpellEnhSelf, OBJECT_SELF, i16, FALSE, ItemEnhSelf, PotionEnh)) return TRUE;
|
||
if(iLowest <= i2)
|
||
{
|
||
// This is the shadow dancer evade
|
||
if(AI_ActionUseFeatOnObject(FEAT_SHADOW_EVADE)) return TRUE;
|
||
|
||
// This is the assassin ghostly visage.
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_SPELL_GHOSTLY_VISAGE)) return TRUE;
|
||
|
||
// This is ghostly visage, dispite the spell constant name. G.Shadow conjuration
|
||
if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_MIRROR_IMAGE, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar)) return TRUE;
|
||
|
||
// Ghostly Visage is only 5/+1, but could work at the lower levels! Immunity to 1-0 spells too.
|
||
// Level 2 (Mage).
|
||
if(AI_ActionCastSpell(SPELL_GHOSTLY_VISAGE, SpellProSelf, OBJECT_SELF, i12, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers All Mantals (Greater, Normal, Lesser) (Spell Mantals)
|
||
// - iLowest - 9 = Greater, 7 = Normal. 5 = Lesser
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections)
|
||
int AI_SpellWrapperMantalProtections(int iLowest = 1)
|
||
{
|
||
// Provides total spell immunity (for most spells, say, 99%) until runs out/dispeled.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) && iLowest <= i9)
|
||
{
|
||
// Greater Spell mantal. Level 9 (Mage), for d12() + 10 spell levels protected.
|
||
if(AI_ActionCastSpell(SPELL_GREATER_SPELL_MANTLE, SpellProSelf, OBJECT_SELF, i19, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
if(iLowest <= i7)
|
||
{
|
||
// Normal, level 7 spell (Mage). d6() + 8 spell levels protected.
|
||
if(AI_ActionCastSpell(SPELL_SPELL_MANTLE, SpellProSelf, OBJECT_SELF, i17, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
if(iLowest <= i5)
|
||
{
|
||
// Lesser, level 5 spell (Mage). d4() + 6 spell levels protected.
|
||
if(AI_ActionCastSpell(SPELL_LESSER_SPELL_MANTLE, SpellProSelf, OBJECT_SELF, i15, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers All Globes (Greater, Shadow Conjuration, Minor)
|
||
// - iLowest - 6 = Greater, 4 = Shadow/Minor
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasGlobeProtections)
|
||
int AI_SpellWrapperGlobeProtections(int iLowest = 1)
|
||
{
|
||
// Immunity to most spells of X or under. Good because level 4 spells have a large selection of AOE ones.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasGlobeProtections) && iLowest <= i6)
|
||
{
|
||
// Normal globe, level 6 spell (Mage). Protects vurses 4 or lower spells.
|
||
if(AI_ActionCastSpell(SPELL_GLOBE_OF_INVULNERABILITY, SpellProSinTar, OBJECT_SELF, i16, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
// Note! Ethereal Visage protects VS 0, 1, 2 levels spells.
|
||
if(iLowest <= i4)
|
||
{
|
||
// SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE.
|
||
// As Minor globe, except shadow version
|
||
if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE, SpellProSelf, OBJECT_SELF, i15, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
|
||
// Minor globe, level 4 spell (Mage). Protects vurses 3 or lower spells.
|
||
if(AI_ActionCastSpell(SPELL_MINOR_GLOBE_OF_INVULNERABILITY, SpellProSelf, OBJECT_SELF, i14, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers All Shields - Elemental Shield, Wounding Whispers
|
||
// - iLowest - 4 = Elemental, 3 = Wounding.
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasElementalShieldSpell)
|
||
int AI_SpellWrapperShieldProtections(int iLowest = 1)
|
||
{
|
||
// These help deflect damage :-D
|
||
if(!AI_GetAIHaveEffect(GlobalEffectDamageShield) && iLowest <= i4)
|
||
{
|
||
// Elemental Shield. Level 4 (Mage). Does some damage to melee attackers (Casterlvl + d6(), Fire). +50% cold/fire resistance.
|
||
if(AI_ActionCastSpell(SPELL_ELEMENTAL_SHIELD, SpellProSinTar, OBJECT_SELF, i14, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
if(iLowest <= i3)
|
||
{
|
||
// Acid Sheath. Level 3 (Mage) Does some damage to melee attackers (2 * CasterLvl + 1d6, Acid).
|
||
if(AI_ActionCastSpell(SPELL_MESTILS_ACID_SHEATH, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
|
||
// Wounding Whispers. Level 3 (bard) Does some damage to melee attackers (Casterlvl + d6(), Sonic).
|
||
if(AI_ActionCastSpell(SPELL_WOUNDING_WHISPERS, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
|
||
if(iLowest <= i2)
|
||
{
|
||
// Death Armor. Level 2 (Mage) Does some damage to melee attackers (Caster Level/2 (to +5) + 1d4, Magical)
|
||
if(AI_ActionCastSpell(SPELL_DEATH_ARMOR, SpellProSinTar, OBJECT_SELF, i13, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Wrappers All Mind resistance spells - Mind blank, Lesser and Clarity. bioware likes 3's...
|
||
// - iLowest - 8 = Mind Blank, 5 = Lesser, 2 = Clarity
|
||
// - Checks AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections)
|
||
int AI_SpellWrapperMindResistanceProtections(int iLowest = 1)
|
||
{
|
||
// immunties against mind - cool :-D (but not 100% useful).
|
||
// Might add more "if they cast a mind spell, we cast this to stop more" in sometime
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections) && iLowest <= i8)
|
||
{
|
||
// Mind Blank. Level 8 (Mage) Mind immunity (and clean up)
|
||
if(AI_ActionCastSpell(SPELL_MIND_BLANK, SpellEnhSinTar, OBJECT_SELF, i18, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
if(iLowest <= i5)
|
||
{
|
||
// Lesser Mind Blank. Level 5 (Mage) Mind immunity (and clean up)
|
||
if(AI_ActionCastSpell(SPELL_LESSER_MIND_BLANK, SpellEnhSinTar, OBJECT_SELF, i15, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
if(iLowest <= i2)
|
||
{
|
||
// Clarity. Level 2 (Bard/Innate) 3 (Cleric/Mage) Mind immunity (and clean up)
|
||
if(AI_ActionCastSpell(SPELL_CLARITY, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// Wrappers All Consealment spells - Improved Invisiblity. Displacement.
|
||
// - iLowest - 4 = Improved Invisiblit, 3 = Displacement
|
||
// - Checks !AI_GetAIHaveEffect(GlobalEffectInvisible, oTarget) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oTarget)
|
||
int AI_SpellWrapperConsealmentEnhancements(object oTarget, int iLowest = 1)
|
||
{
|
||
if(!AI_GetAIHaveEffect(GlobalEffectInvisible, oTarget) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oTarget))
|
||
{
|
||
// Shadow dragon special consealment
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_SHADOWBLEND)) return TRUE;
|
||
|
||
if(iLowest <= i4)
|
||
{
|
||
// Improved Invis - assassin
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_INVISIBILITY_2)) return TRUE;
|
||
|
||
// Improved Invis. Level 4 (Bard, Mage). 50% consealment + invisibility.
|
||
if(AI_ActionCastSpell(SPELL_IMPROVED_INVISIBILITY, SpellEnhSinTar, OBJECT_SELF, i14, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
|
||
if(iLowest <= i3)
|
||
{
|
||
// Displacement. Level 3 (Mage). 50% consealment.
|
||
if(AI_ActionCastSpell(SPELL_DISPLACEMENT, SpellEnhSinTar, OBJECT_SELF, i13, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Shades darkness, assassin feat, normal spell.
|
||
int AI_SpellWrapperDarknessSpells(object oTarget)
|
||
{
|
||
// Special Assassin Version
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_DARKNESS, oTarget)) return TRUE;
|
||
// Shades one
|
||
if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_DARKNESS, SpellOtherSpell, oTarget, i14, TRUE)) return TRUE;
|
||
// Darkness. Area of consealment. Level 2 (Cleric/Bard/Mage) Specail Assassin.
|
||
if(AI_ActionCastSpell(SPELL_DARKNESS, SpellOtherSpell, oTarget, i12, TRUE)) return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
// Invisibility spells + feats
|
||
int AI_SpellWrapperNormalInvisibilitySpells()
|
||
{
|
||
// Special Harper Version
|
||
if(AI_ActionUseFeatOnObject(FEAT_HARPER_INVISIBILITY)) return TRUE;
|
||
// Special Assassin Version
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_INVISIBILITY_1)) return TRUE;
|
||
// Cast invisiblity spells!
|
||
// Invisiblity Sphere. Level 3 (Bard, Mage). Invisibility till attacked for an area!
|
||
if(AI_ActionCastSpell(SPELL_INVISIBILITY_SPHERE, SpellEnhAre, OBJECT_SELF, i13, FALSE, ItemEnhAre)) return TRUE;
|
||
// Shad. conjuration
|
||
if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_INIVSIBILITY, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
// Invisiblity. Level 2 (Bard, Mage). Invisibility till attacked.
|
||
if(AI_ActionCastSpell(SPELL_INVISIBILITY, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
|
||
return FALSE;
|
||
}
|
||
|
||
// This will loop seen allies in a cirtain distance, and get the first one without
|
||
// the spells effects of iSpell1 to iSpell4 (and do not have the spells).
|
||
// - Quite expensive loop. Only used if we have the spell (iSpellToUse1+)
|
||
// in the first place (no items!) and not the timer which stops it for a few rounds (on iSpellToUse1)
|
||
// - TRUE if it casts its any of iSpellToUseX's.
|
||
// * It has only a % chance to cast if GlobalWeAreBuffer is not TRUE.
|
||
int AI_ActionCastAllyBuffSpell(float fMaxRange, int iPercent, int iSpellToUse1, int iSpellToUse2 = -1, int iSpellToUse3 = -1, int iSpellToUse4 = -1, int iSpellOther1 = -1, int iSpellOther2 = -1)
|
||
{
|
||
// Not in time stop
|
||
if(GlobalInTimeStop) return FALSE;
|
||
|
||
// Check buff ally is valid
|
||
if(!GetIsObjectValid(GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + s1))) return FALSE;
|
||
|
||
// Check % (buffers add 150, so always pass this)
|
||
if(d100() > (iPercent + // Default %
|
||
(i150 * GlobalWeAreBuffer) - // Always cast if buffer
|
||
(GlobalWeAreSorcerorBard * i50) + // Much less (50%) if sorceror/bard
|
||
(FloatToInt(GlobalRangeToMeleeTarget))// Add a little for range to melee target
|
||
)) return FALSE;
|
||
|
||
// Check local timer for the top spell to cast against the ally. Only the
|
||
// top spell is timed.
|
||
if(GetLocalTimer(AI_TIMER_BUFF_ALLY_SPELL + IntToString(iSpellToUse1))) return FALSE;
|
||
|
||
// Set local timer to not use it for a while
|
||
SetLocalTimer(AI_TIMER_BUFF_ALLY_SPELL + IntToString(iSpellToUse1), f18);
|
||
|
||
// Check if we have the spell
|
||
if(!GetHasSpell(iSpellToUse1) && !GetHasSpell(iSpellToUse2) &&
|
||
!GetHasSpell(iSpellToUse3) && !GetHasSpell(iSpellToUse4)) return FALSE;
|
||
|
||
// - This lets real-hardcode buffers go to a longer range.
|
||
float fRangeToGoTo = fMaxRange + GlobalBuffRangeAddon;
|
||
// Loop nearest to futhest allies
|
||
int iCnt = i1;
|
||
object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + IntToString(iCnt));
|
||
// Loop Targets
|
||
// - iCnt is our breaker. We only check 10 nearest allies, and set to 20 if break.
|
||
while(GetIsObjectValid(oAlly) && iCnt <= i10 && GetDistanceToObject(oAlly) <= fRangeToGoTo)
|
||
{
|
||
// Check for thier effects
|
||
if((iSpellToUse1 == iM1 || !GetHasSpellEffect(iSpellToUse1, oAlly)) &&
|
||
(iSpellToUse2 == iM1 || !GetHasSpellEffect(iSpellToUse2, oAlly)) &&
|
||
(iSpellToUse3 == iM1 || !GetHasSpellEffect(iSpellToUse3, oAlly)) &&
|
||
(iSpellToUse4 == iM1 || !GetHasSpellEffect(iSpellToUse4, oAlly)) &&
|
||
(iSpellOther1 == iM1 || !GetHasSpellEffect(iSpellOther1, oAlly)) &&
|
||
(iSpellOther2 == iM1 || !GetHasSpellEffect(iSpellOther2, oAlly)) &&
|
||
!GetHasSpell(iSpellToUse1, oAlly) && !GetHasSpell(iSpellToUse2, oAlly) &&
|
||
!GetHasSpell(iSpellToUse3, oAlly) && !GetHasSpell(iSpellToUse4, oAlly))
|
||
{
|
||
// Break with this ally as target
|
||
iCnt = i20;
|
||
}
|
||
else
|
||
{
|
||
// Get Next ally
|
||
iCnt++;
|
||
oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + IntToString(iCnt));
|
||
}
|
||
}
|
||
// If valid, cast at the target and return TRUE.
|
||
if(iCnt == i20)
|
||
{
|
||
// oAlly is our buff target - cast the best to worst on it!
|
||
if(AI_ActionCastSpell(iSpellToUse1, FALSE, oAlly)) return TRUE;
|
||
if(AI_ActionCastSpell(iSpellToUse2, FALSE, oAlly)) return TRUE;
|
||
if(AI_ActionCastSpell(iSpellToUse3, FALSE, oAlly)) return TRUE;
|
||
if(AI_ActionCastSpell(iSpellToUse4, FALSE, oAlly)) return TRUE;
|
||
}
|
||
// Return FALSE - no spell cast
|
||
return FALSE;
|
||
}
|
||
|
||
// This will run through most avalible protections spells.
|
||
// - TRUE if we cast any.
|
||
// Used when invisible to protect before we break the invisibility.
|
||
// - We may cast many on allies too
|
||
int AI_ActionCastWhenInvisible()
|
||
{
|
||
// We run through some spells. Primarily, protection then buffs.
|
||
// We don't target others else we'd break the invisiblity.
|
||
|
||
// Haste's
|
||
if(AI_SpellWrapperHasteEnchantments()) return TRUE;
|
||
|
||
// Stoneskin - protection from attackers.
|
||
if(AI_SpellWrapperPhisicalProtections()) return TRUE;
|
||
|
||
// Mantals
|
||
if(AI_SpellWrapperMantalProtections()) return TRUE;
|
||
|
||
// Elemental protections (fireball ETC)
|
||
if(AI_SpellWrapperElementalProtections()) return TRUE;
|
||
|
||
// Visages
|
||
if(AI_SpellWrapperVisageProtections()) return TRUE;
|
||
|
||
// Globes
|
||
if(AI_SpellWrapperGlobeProtections()) return TRUE;
|
||
|
||
// Shields
|
||
if(AI_SpellWrapperShieldProtections()) return TRUE;
|
||
|
||
// Mind resistances
|
||
if(AI_SpellWrapperMindResistanceProtections()) return TRUE;
|
||
|
||
// Thats us done, what about allies?
|
||
// - Buffs for allies (Reference!)
|
||
// Stoneskin (+ Greater)
|
||
// Improved Invisibility, Displacement
|
||
// Haste, Mass Haste
|
||
// Energy Protections (Buffer, Protection From, Resist, Endure)
|
||
// Ultravision (special case: Nearest seen ally with Darkness and not this effect)
|
||
// Spell Resistance
|
||
// Death Ward (If we can see arcane spellcasters, as it only provides death immunity)
|
||
// Regenerate, Monstourous Regeneration
|
||
// Negative Energy Protection (If we see any clerics, druids, with harm, else only self)
|
||
|
||
// Only if buffer:
|
||
// Mage armor, Barkskin
|
||
// Bless, Aid
|
||
// Bulls Strength, Cats Grace, Endurance
|
||
// Stone bones - On undead only.
|
||
|
||
// Not cast:
|
||
// Weapon spells (Blackstaff ETC) - We wouldn't have many.
|
||
// Protection From Spells - AOE, and the AOE includes us anyway (so gets captured when we cast it)
|
||
// Mind blanks - Mind protection, not worth casting on allies, its more for removal.
|
||
// Normal invisilbilties - They would normally be broken right away.
|
||
// Natures Balance - Healing AOE only really, mostly enemy SR lowering in AOE.
|
||
// Prayer - Cast on self
|
||
// Freedom of movement - More removal if anyone needs it. Doesn't stop too much.
|
||
|
||
// Much lower % as sorceror or bard.
|
||
|
||
// Try stoneskins as a main one
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_GREATER_STONESKIN, SPELL_STONESKIN)) return TRUE;
|
||
|
||
// Hastes!
|
||
if(AI_ActionCastAllyBuffSpell(f8, i100, SPELL_HASTE, SPELL_MASS_HASTE)) return TRUE;
|
||
|
||
// Consealment spells
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) return TRUE;
|
||
|
||
// Elemental protections
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_ENERGY_BUFFER, SPELL_PROTECTION_FROM_ELEMENTS, SPELL_RESIST_ELEMENTS, SPELL_ENDURE_ELEMENTS)) return TRUE;
|
||
|
||
// If we have the setting to buff allies, we carry on buffing with some
|
||
// other spells.
|
||
if(GlobalWeAreBuffer)
|
||
{
|
||
// Some AC protections
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_MAGE_ARMOR, SPELL_BARKSKIN)) return TRUE;
|
||
|
||
// Bulls Strength, Cats Grace, Endurance
|
||
if(AI_ActionCastAllyBuffSpell(f10, i100, SPELL_ENDURANCE, SPELL_CATS_GRACE, SPELL_ENDURANCE, iM1, SPELL_GREATER_BULLS_STRENGTH, SPELL_GREATER_CATS_GRACE)) return TRUE;
|
||
|
||
// Bless, Aid
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_AID, SPELL_BLESS)) return TRUE;
|
||
}
|
||
// Return FALSE - nothing cast
|
||
return FALSE;
|
||
}
|
||
|
||
// Using the array ARRAY_ENEMY_RANGE, we return a % of seen/heard people who
|
||
// DO have any of the spells which see through the invisiblity spells.
|
||
// * iLimit - when we get to this number of people who have invisiblity, we stop and return 100%
|
||
// If ANY of the people are attacking us and have the effect, we return +30% for each.
|
||
int AI_GetSpellWhoCanSeeInvis(int iLimit)
|
||
{
|
||
// Loop LOS range enemies.
|
||
int iCnt = i1;
|
||
int iHasSeeingTotal, iTotal, iAdditional;
|
||
// Loop start
|
||
object oEnemy = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oEnemy) && iAdditional < i100)
|
||
{
|
||
// Seen/heard check is already done
|
||
// Add one to total.
|
||
iTotal++;
|
||
// Make this the total of any seeing spells.
|
||
if(GetHasSpellEffect(SPELL_SEE_INVISIBILITY, oEnemy) ||
|
||
GetHasSpellEffect(SPELL_TRUE_SEEING, oEnemy) ||
|
||
// - We obviously can tell with creatures with true seeing hides. Only checking hides!
|
||
GetItemHasItemProperty(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oEnemy), ITEM_PROPERTY_TRUE_SEEING))
|
||
{
|
||
iHasSeeingTotal++;
|
||
// Limit checking
|
||
if(iHasSeeingTotal >= iLimit)
|
||
{
|
||
iAdditional += i100;
|
||
}
|
||
// Special: If they are attacking us (with it) we add 30%
|
||
// to outcome, and add 1.
|
||
else if(GetAttackTarget(oEnemy) == OBJECT_SELF)
|
||
{
|
||
iAdditional += i30;
|
||
}
|
||
}
|
||
iCnt++;
|
||
oEnemy = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
if(iHasSeeingTotal > FALSE)
|
||
{
|
||
return AI_GetPercentOf(iHasSeeingTotal, iTotal) + iAdditional;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// Returns a dismissal target - a target with a master, and they
|
||
// are a Familiar, Animal companion or summon.
|
||
// - Nearest one in 10 M. Seen ones only.
|
||
object AI_GetDismissalTarget()
|
||
{
|
||
object oMaster, oReturn;
|
||
int iCnt = i1;
|
||
string sCnt = IntToString(iCnt);
|
||
object oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
float fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
while(GetIsObjectValid(oLoopTarget) && fDistance <= f10)
|
||
{
|
||
// Check if they are a valid summon/familar/comapnion
|
||
oMaster = GetMaster(oLoopTarget);
|
||
//Is that master valid and is he an enemy
|
||
if(GetIsObjectValid(oMaster) && GetIsEnemy(oMaster))
|
||
{
|
||
//Is the creature a summoned associate
|
||
if(GetAssociate(ASSOCIATE_TYPE_SUMMONED, oMaster) == oLoopTarget ||
|
||
GetAssociate(ASSOCIATE_TYPE_FAMILIAR, oMaster) == oLoopTarget ||
|
||
GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION, oMaster) == oLoopTarget)
|
||
{
|
||
// Stop and break
|
||
oReturn = oLoopTarget;
|
||
break;
|
||
}
|
||
}
|
||
// Get next target
|
||
iCnt++;
|
||
sCnt = IntToString(iCnt);
|
||
fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
}
|
||
return oReturn;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name ImportAllSpells
|
||
//::///////////////////////////////////////////////
|
||
Taken from Jugulators improved spell AI (but now, hardly any of it remains!).
|
||
This is a very heavily changed version. If they can cast spells
|
||
or abilities, this will run though them and choose which
|
||
to cast. It will cast location spells at location, meaning heard
|
||
enemies may still be targeted.
|
||
|
||
Incudes, nearer the bottom, if we are not a spellcaster, and
|
||
our BAB is high compared to thier AC, we will HTH attack
|
||
rather than carry on casting low level spells.
|
||
|
||
Now: 1.3, it uses setups of what to do from OnSpawn to the best of its
|
||
abilities. It already has chosen an appropriate target (And decerned its
|
||
properties, immunities).
|
||
|
||
Note: These are some of the non-verbal and non-stomatic componants:
|
||
|
||
- Non-Verbal Spells -
|
||
Clarity, Lesser Dispel, and Ethereal Visage.
|
||
|
||
- Non-Somatic Spells -
|
||
Darkness, Knock, Light, Mass Charm, Mordenkainen's Disjunction, Polymorph
|
||
Self, Power Word Kill, Power Word Stun, Wail of the Banshee, Word of Faith,
|
||
and War Cry.
|
||
|
||
We ALWAYS use the nearest seen, if no one else :-P.
|
||
|
||
Note: On Setup, like fighter choices, we normally perfere futher away targets
|
||
which we can see :-) as fighters go for nearer ones, but they must be the
|
||
best!
|
||
|
||
This attempts to cast a spell, running down the lists.
|
||
The only variable is iLowest and iBAB level's, targets are globally set.
|
||
- iLowestSpellLevel - If it is set to more than 1, then all spells of that
|
||
level and lower are just ignored. Used for dragons. Default 0.
|
||
- iBABCheckHighestLevel - We check our BAB to see if attacking them would probably
|
||
be a better thing to do. Default 3, it adds 5DC for each level.
|
||
- iLastCheckedRange - 1 = Minimum, 4 = Longest. If it runs through once, and
|
||
with "ranged attacking", doesn't find any spells to cast at the "long" range,
|
||
then it will attempt to see if there are any spells for "meduim" etc.
|
||
- Range: Long 40, Medium: 20, Short: 10, Touch: 2.25 (Game meters). Personal = 0
|
||
NOTE 1: If GlobalItemsOnly is set, we only check for item talents!
|
||
NOTE 2: It uses the SAME target, but for ranged attacking, we use a float range to check range :-)
|
||
|
||
Notes on touch attacks:
|
||
[Quote]
|
||
Here's how the AC is added up for touch attacks:
|
||
|
||
Armor bonus: no
|
||
Shield bonus: no
|
||
Deflection bonus: yes
|
||
Natural bonus: no
|
||
Dodge bonus: yes
|
||
Dexterity modifier: yes
|
||
Monk wisdom: yes
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jugulator, Modified (very) Heavily: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptAllSpells(int iLowestSpellLevel = 0, int iBABCheckHighestLevel = 3, int iLastCheckedRange = 1, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE)
|
||
{
|
||
// Checks for valid numbers, ETC.
|
||
if(AI_GetAIHaveEffect(GlobalEffectPolymorph) ||
|
||
(GlobalSpellAbilityModifier < i10 && !GobalOtherItemsValid && !GobalPotionsValid) ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_LAG_NO_SPELLS, AI_OTHER_MASTER) ||
|
||
// Do we have any spells to cast?
|
||
(GlobalSilenceSoItems && !GobalOtherItemsValid && !GobalPotionsValid) ||
|
||
(!SpellAnySpellValid && !GobalOtherItemsValid && !GobalPotionsValid) ||
|
||
!GetIsObjectValid(GlobalSpellTarget) || GetIsDead(GlobalSpellTarget))
|
||
{
|
||
// 31: "[DCR: All Spells] Error! No casting (No spells, items, target Etc)."
|
||
DebugActionSpeakByInt(31);
|
||
return FALSE;
|
||
}
|
||
// 11: "[DCR: All Spells] [Modifier|BaseDC|SRA] " + IntToString(iInput)
|
||
// Input: 100 * GlobalSpellAbilityModifier) + 10 * GlobalSpellBaseSaveDC + SRA
|
||
DebugActionSpeakByInt(32, OBJECT_INVALID, (i100 * GlobalSpellAbilityModifier) + (i10 * GlobalSpellBaseSaveDC) + (SRA));
|
||
|
||
// Sets up AOE target object to get.
|
||
object oAOE, oRandomSpellNotUsedAOE, oPurge;
|
||
// (oRandomSpellNotUsedAOE is set when we radomise a spell, and the spell
|
||
// isn't cast, but could be!)
|
||
|
||
// Notes on targets:
|
||
// Uses 1.
|
||
// - Ranged attacking may IGNORE shorter ranged spells IF they are not in range.
|
||
|
||
// - Range: Long 40, Medium: 20, Short: 10, Touch: 2.25 (Game meters). Personal = 0
|
||
// Range valids:
|
||
int RangeLongValid, RangeMediumValid, RangeShortValid, RangeTouchValid,
|
||
iCnt, iBreak, /* Counter things */
|
||
iPercentWhoSeeInvis, iNeedToBeBelow, SingleSpellsFirst,
|
||
SingleSpellOverride, MultiSpellOverride, IsFirstRunThrough;
|
||
// Range LONG should ALWAYS be used, IE we can't get a longer ranged spell than that!
|
||
// SRA!
|
||
|
||
if(!SRA)
|
||
{
|
||
RangeLongValid = TRUE;
|
||
RangeMediumValid = TRUE;
|
||
RangeShortValid = TRUE;
|
||
RangeTouchValid = TRUE;
|
||
}
|
||
// We stop if all set to TRUE, IE, already checked.
|
||
else if(InputRangeLongValid && InputRangeMediumValid &&
|
||
InputRangeShortValid && InputRangeTouchValid)
|
||
{
|
||
// All done, stop
|
||
return FALSE;
|
||
}
|
||
// Ranges...
|
||
// If set to TRUE, it is ignored.
|
||
// If set to FALSE, we check the pass number or the range, and set to true.
|
||
else
|
||
{
|
||
// Long
|
||
if(InputRangeLongValid == i0)
|
||
{
|
||
// No range check, RangeLongValid is of course always true.
|
||
RangeLongValid = TRUE;
|
||
}
|
||
// At 1, we do not do anything, and ignore long range spells.
|
||
// Medium
|
||
if(InputRangeMediumValid == i0)
|
||
{
|
||
// Range check for medium - or if the run is at number 2+
|
||
if(GlobalSpellTargetRange <= fMediumRange ||
|
||
iLastCheckedRange >= i2)
|
||
{
|
||
RangeMediumValid = TRUE;
|
||
}
|
||
}
|
||
// At 1, we ignore medium
|
||
// Short
|
||
if(InputRangeShortValid == i0)
|
||
{
|
||
// Range check for short - or if it is >= pass 3
|
||
if(GlobalSpellTargetRange <= fShortRange ||
|
||
iLastCheckedRange >= i3)
|
||
{
|
||
RangeShortValid = TRUE;
|
||
}
|
||
}
|
||
// At 1, we ignore short ranged spells
|
||
// Touch
|
||
if(InputRangeTouchValid == i0)
|
||
{
|
||
// Range check for touch - or >= pass 4
|
||
if(GlobalSpellTargetRange <= fTouchRange ||
|
||
iLastCheckedRange >= i4)
|
||
{
|
||
RangeTouchValid = TRUE;
|
||
}
|
||
}
|
||
// At 1, we ignore touch ranged spells
|
||
}
|
||
|
||
// IsFirstRunThrough is TRUE if this is the first run through, if
|
||
// last checked range is == 1
|
||
// - Used, if TRUE, for defensive spells to stop checking the same things
|
||
// up to 4 times!
|
||
if(iLastCheckedRange == i1)
|
||
{
|
||
IsFirstRunThrough = TRUE;
|
||
}
|
||
|
||
// these force the use of AOE spells, or single spells, over the other.
|
||
SingleSpellOverride = GetSpawnInCondition(AI_FLAG_COMBAT_SINGLE_TARGETING, AI_COMBAT_MASTER);
|
||
MultiSpellOverride = GetSpawnInCondition(AI_FLAG_COMBAT_MANY_TARGETING, AI_COMBAT_MASTER);
|
||
|
||
// Saves. Stops them firing spells they would ALWAYs save against.
|
||
// GlobalSpellTargetWill, GobalSpellTargetFort, GlobalSpellTargetReflex.
|
||
|
||
// SingleSpellsFirst = if TRUE, we try our single spells before the
|
||
// AOE spells. If false, don't bother. Not set if not seen target.
|
||
if(GlobalSeenSpell && (GlobalTotalSeenHeardEnemies < (GlobalOurHitDice / i3) ||
|
||
GlobalTotalSeenHeardEnemies < i2 ||
|
||
// Single targeting override.
|
||
SingleSpellOverride))
|
||
{
|
||
SingleSpellsFirst = TRUE;
|
||
}
|
||
|
||
// There is also the case of people immune to normal spells (such as
|
||
// Fireball) but not spells like Gaze of Death.
|
||
// - Set to the level of silence we cannot use, BTW.
|
||
// - Set to 10 if we have 90% or more spell failure.
|
||
// - Also includes any globes, ETC.
|
||
// - We use "if(GlobalNormalSpellsNoEffectLevel < 9)" for level 9 spells.
|
||
//GlobalNormalSpellsNoEffectLevel = 1-10. 10 being absolutely nothing.
|
||
// - EG: Set to 3, means 0, 1, 2, 3 have 0% chance of affecting them.
|
||
|
||
/*::9999999999999999999999999999999999999999999999999999999999999999999999999999
|
||
//:: Level 9 spells.
|
||
//::9999999999999999999999999999999999999999999999999999999999999999999999999999
|
||
NOTE ABOUT HOW I DO SPELL LEVELS OF EACH SPELL!
|
||
Because there are spells for several classes, the same spell, such as Wall
|
||
of Fire, I take the MAGE (Sorceror/Wizard) class. This is because it will
|
||
then vary the sorcerors spells he casts. I then, if it is not a Mage spell,
|
||
generally take the lowest spell number (EG: Death Ward, level 4 cleric/bard,
|
||
5 druid, and I'l take it as a level 4). This does not mean it will be cast
|
||
in the bracket for spell level X - especially if it is not a hostile spell.
|
||
|
||
H = Hordes only (And I think includes SoU spells)
|
||
S = SoU only
|
||
|
||
Protections are important, and are cast sometimes many levels above other
|
||
spells of thier level, like casting Stoneskin before Wail of the Banshee.
|
||
|
||
Thoughts -level 9 spells are the powerhouses, or so they say. They are actually
|
||
not...good. Most are death save based, and so many higher-level PC's will
|
||
be immune with there being Death Ward handy/Shadow shield. Some good spells
|
||
however - energy drain is powerful and has maximum save DC, even if it is
|
||
necromantic. Storm of Vengance is a really powerful AOE persistant spell,
|
||
and the damaging spells are not half bad. Also, powerful summons at level 9!
|
||
|
||
Epic:
|
||
H [Epic Mage Armor] - +5 of the 4 different AC types. Just +5 dodge is a great asset.
|
||
H [Hellball] - Long range AOE - 10d6 sonic, acid, fire and lightning damage to all in area. Reflex only halves.
|
||
H [Ruin] - 35d6 divine damage, fort save for half.
|
||
H [Mummy Dust] - Powerful summon that cannot be dispelled. 24 Hours.
|
||
H [Dragon Knight] - Powerful dragon summon that cannot be dispelled. 20 rounds.
|
||
H [Epic Warding] - Damage reduction 50/+20. Lasts 1 round per level.
|
||
|
||
AOE:
|
||
[Wail Of the Banshee] - Save VS death (fort) or die. Doesn't affect caster. 10M Range.
|
||
[Wierd] - Save VS Will & Fort. Doesn't affect allies. 10M AOE.
|
||
[Meteor Swarm] - Non-friends in 10M are done with 20d6 damage. Reflex Save.
|
||
[Storm of Vengance] - Anyone in the AOE (10M where cast) gets reflex-electic, and alway-acid damage.
|
||
[Implosion] - +3 Save DC, VS death, medium AOE. Not affect self (but affects anyone else)
|
||
|
||
[Modenkainens Disjunction] - Powerful Dispel. VERY powerful - acts like a breach as well!
|
||
|
||
Single:
|
||
[Dominate Monster] - Save VS Will else dominated - anything! (3 turns +1 per caster level.)
|
||
[Energy Drain] - Save VS Fort or negative levels - 2d4. If it goes to 0 levels, kills. Also lots of negative stats! (Supernatural Effect)
|
||
S [Bigby's Crushing Hand] - (2d6 + 12 damage/round)
|
||
|
||
Defender:
|
||
[Greater Spell Mantal] - Stops spells. d12 + 10
|
||
[Time Stop] - 9 Seconds (Default amount anyway) that we stop everyone but ourselves.
|
||
S [Undeath's Eternal Foe] Stops negative damage, ability score draining, negative levels, immunity poisons, immunity diseases.
|
||
|
||
Summon:
|
||
[Gate] - Balor. Short duration, powerful, (has spells) but need prot. From evil.
|
||
[Elemental Swarm] - Great! After one powerful one dies, another is summoned. Greater Elements
|
||
[Summon Creature 9] - Summons a random Elder Elemental. Normally 24HRS duration.
|
||
[Innate Summons] - Almost all innate ones are so easy to cast (no concentration) we cast them here.
|
||
- Summon Celestial (One Will'o'whisp)
|
||
- Summon Mephit (One Mephit)
|
||
- Summon Slaad (One red slaad)
|
||
- Summon Tanarri (One subbucus)
|
||
H - Summon Baatezu (One Erinyes) (Hordes)
|
||
H [Black Blade of Disaster] - A powerful greatsword fights. Please note: AI abuses the "concentration" rules for it!
|
||
|
||
Other:
|
||
|
||
These will be cast all the time :-) [Dismissal] is included - a special case
|
||
for any dismissal targets, of course.
|
||
|
||
Same with [Haste]/[Mass Haste]. These are almost too good to miss!
|
||
|
||
S [Etherealness] is a VERY powerful spell - if we cast this, we can normally
|
||
cast defensive spells while invisible - cool :-)
|
||
- We cast quite a few invisible-based spells near the top of our list.
|
||
|
||
[Time stop] is a special case - we cast it first if we have 2 or more, cast
|
||
haste, then we are able to cast it again and get the maximum, safe, usage
|
||
out of it.
|
||
|
||
H [Crumble] - Constructs only.
|
||
|
||
Also note that [Stoneskin] (and variants) are usually cast at an upmost prioritory,
|
||
as mages have bad armor, saves and HP :-)
|
||
|
||
Creatures with very powerful enchantments to dispel are dispeled, if they have
|
||
level 5 defenses.
|
||
|
||
Harm/Heal are also cast here, to knock out enemies early on (if in range,
|
||
of course)
|
||
|
||
Do Prismatic AC bonus adding here
|
||
//::99999999999999999999999999999999999999999999999999999999999999999999999999*/
|
||
|
||
// No BAB check.
|
||
|
||
// Not in time stop
|
||
if(IsFirstRunThrough && !GlobalInTimeStop && GlobalIntelligence >= (i6 - d4()))
|
||
{
|
||
// We do this once, mind you, when we start at iLastCheckedRange == 4.
|
||
// Get a nearby enemy summon - 10M range.
|
||
oAOE = AI_GetDismissalTarget();
|
||
// Is it valid?
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Banishment. Level 6 (Cleric/Innate) 7 (Mage). Destroys outsiders as well as all summons in area around caster (10M)
|
||
if(AI_ActionCastSpell(SPELL_BANISHMENT, SpellHostAreaInd, OBJECT_SELF, i17, TRUE, ItemHostAreaInd)) return TRUE;
|
||
|
||
// Dismissal is short range anyway. Enemy must be within 5M to be targeted.
|
||
// Dismissal. Level 4 (Bard/Cleric/Innate) 5 (Mage). At a will save (VS DC + 6) destroy summons/familiars/companions in area.
|
||
if(AI_ActionCastSpell(SPELL_DISMISSAL, SpellHostAreaDis, oAOE, i15, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Time Stop - Never casts again in a timestop
|
||
// This will cast it very first if we have 2 or more (Sorceror)
|
||
if(IsFirstRunThrough && !GlobalInTimeStop && GlobalIntelligence >= i10 &&
|
||
GetHasSpell(SPELL_TIME_STOP) >= i2)
|
||
{
|
||
// Time Stop. Level 9 (Mage). 9 Seconds (by default. Meant to be 1d4 + 1) of frozen time for caster. Meant to be a rare spell.
|
||
if(AI_ActionCastSpell(SPELL_TIME_STOP, SpellHostAreaDis, OBJECT_SELF, i19, FALSE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
|
||
// Special case - Lots of phisical damage requires maximum protectoin.
|
||
// For this, we may even consider visage's to be worth something over stoneskin
|
||
// if we have no stoneskins :-D
|
||
// - We cast this if we have many melee attackers or total attackers.
|
||
// - To save time stop checking, we don't do this in time stop.
|
||
if(GlobalIntelligence >= i7 && !GlobalInTimeStop && IsFirstRunThrough &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections) &&
|
||
// Formula - IE They do 40 damage, we must be level 10 or less. :-)
|
||
// - As this is mainly for mages, we don't bother about how much HP left.
|
||
(GetAIInteger(AI_HIGHEST_PHISICAL_DAMAGE_AMOUNT) >= GlobalOurHitDice * i4 ||
|
||
// - Do we have anyone in 20M?
|
||
(GlobalRangeToNearestEnemy <= f20 &&
|
||
// BAB check as well
|
||
GlobalAverageEnemyBAB >= GlobalOurHitDice / i2)))
|
||
{
|
||
// Stoneskins and so forth first, then visages.
|
||
|
||
// We think that Stoneskin (which has /+5) is better then 15/+3
|
||
if(AI_SpellWrapperPhisicalProtections(iLowestSpellLevel)) return TRUE;
|
||
|
||
// Visage - lowest of Ethereal (6) however. Ghostly? Don't bother
|
||
if(iLowestSpellLevel <= i6)
|
||
{
|
||
if(AI_SpellWrapperVisageProtections(i6)) return TRUE;
|
||
}
|
||
}
|
||
// END phisical protections override check
|
||
|
||
// Haste - First. Good one, I suppose. Should check for close (non-hasted)
|
||
// allies, to choose between them.
|
||
if(IsFirstRunThrough && !AI_GetAIHaveEffect(GlobalEffectHaste) &&
|
||
!AI_CompareTimeStopStored(SPELL_HASTE, SPELL_MASS_HASTE))
|
||
{
|
||
// I don't want to bother checking for allies for MASS_HASTE because
|
||
// mass haste is a good duration/harder to Dispel compared to haste
|
||
// anyway.
|
||
// - Should this be moved down?
|
||
if(AI_SpellWrapperHasteEnchantments(iLowestSpellLevel)) return TRUE;
|
||
}
|
||
|
||
// Now the normal time stop. Power to the lords of time! (sorry, a bit over the top!)
|
||
// - Top prioritory. If you don't want it to cast it first, don't give it to them!
|
||
if(IsFirstRunThrough && !GlobalInTimeStop)
|
||
{
|
||
// Time Stop. Level 9 (Mage). 9 Seconds (by default. Meant to be 1d4 + 1) of frozen time for caster. Meant to be a rare spell.
|
||
if(AI_ActionCastSpell(SPELL_TIME_STOP, SpellHostAreaDis, OBJECT_SELF, i19, FALSE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
|
||
// Special case - if lots of damage has been done elemetally wise, we will
|
||
// cast elemental protections (if not cast already).
|
||
// - To save time stop checking, we don't do this in time stop.
|
||
// - Only done if 4 or more intelligence.
|
||
if(SpellProSinTar /*Extra check here.*/ &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) &&
|
||
!GlobalInTimeStop && IsFirstRunThrough && GlobalIntelligence >= i4 &&
|
||
// Formula - IE They do 40 damage, we must be level 10 or less. :-)
|
||
// - As this is mainly for mages, we don't bother about how much HP left.
|
||
(GetAIInteger(MAX_ELEMENTAL_DAMAGE) >= GlobalOurHitDice * i4 ||
|
||
// OR last hit was major compared to total HP
|
||
GetAIInteger(LAST_ELEMENTAL_DAMAGE) >= GlobalOurMaxHP/i4))
|
||
{
|
||
if(AI_SpellWrapperElementalProtections(iLowestSpellLevel))
|
||
{
|
||
// Reset and return, if we cast something!
|
||
DeleteAIInteger(MAX_ELEMENTAL_DAMAGE);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// END special elemental override check
|
||
|
||
// Epic mage armor after specials. Not the best place...
|
||
// +20 AC is good, normally.
|
||
if(IsFirstRunThrough && !AI_GetAIHaveSpellsEffect(GlobalHasOtherACSpell))
|
||
{
|
||
// Epic Mage Armor. (Mage only) +5 of the 4 different AC types. Just +5 dodge is a great asset.
|
||
if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_EPIC_MAGE_ARMOR, SPELL_EPIC_MAGE_ARMOR)) return TRUE;
|
||
}
|
||
|
||
// Visibility Protections - going invisible!
|
||
// - We only do this not in time stop
|
||
// - We must be invisible
|
||
// - We must be of a decnt intelligence (5+)
|
||
// - We must make sure we don't already have all the protections listed
|
||
// - We MUST have not run through this once already.
|
||
if(!GlobalInTimeStop && GlobalIntelligence >= i5 && IsFirstRunThrough)
|
||
{
|
||
// First, check if we already have any effects.
|
||
// - If we have GlobalEffectEthereal, then we cast all (non-see through)
|
||
if(AI_GetAIHaveEffect(GlobalEffectEthereal) ||
|
||
// - If we have darkness, we'll protect ourselves. (Ultravision or not!)
|
||
// * We can ultra vision in the override special actions part of the AI.
|
||
AI_GetAIHaveEffect(GlobalEffectDarkness))
|
||
{
|
||
// Do some protection spells. :-)
|
||
// - And on allies!
|
||
if(AI_ActionCastWhenInvisible()) return TRUE;
|
||
}
|
||
// - If we have GlobalEffectInvisible, then we check who can see us.
|
||
// * If we have the timer Do not hide, and we didn't hide last
|
||
// turn, then we don't do it.
|
||
else if(AI_GetAIHaveEffect(GlobalEffectInvisible))
|
||
{
|
||
if(!GetObjectSeen(OBJECT_SELF, GlobalMeleeTarget) ||
|
||
GetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY))
|
||
{
|
||
// Do some protection spells. :-)
|
||
// - And on allies!
|
||
if(AI_ActionCastWhenInvisible()) return TRUE;
|
||
}
|
||
}
|
||
// Else, no invisibility, so we may well cast it >:-D
|
||
// - Only cast it if we are not a sorceror or bard, or we have not got
|
||
// cirtain protections (important ones!)
|
||
// - Cast if any class other then bard/sorceror because we get advantages
|
||
// to use other spells and attacks.
|
||
else if(!GlobalWeAreSorcerorBard ||
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasElementalProtections) ||
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections) ||
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) ||
|
||
GlobalOurPercentHP >= i100)
|
||
{
|
||
// Is it a good time? (EG we want to flee, or want more protections :-) ).
|
||
// - Do we HAVE THE SPELLS?!
|
||
// - Are we overwhelmed?
|
||
// - Is the enemy a lot stronger?
|
||
// * Fleeing that uses this does it in the flee section (not in yet)
|
||
// * More protections for concentation is done in that section (not in yet)
|
||
|
||
// 1. Etherealness.
|
||
// This is easily the best one there is! always works 100%%%!
|
||
// Etherealness. Total invisibility! Level 6 (Cleric) 8 (Mage) 7 (Innate).
|
||
if(AI_ActionCastSpell(SPELL_ETHEREALNESS, SpellOtherSpell, OBJECT_SELF, i17)) return TRUE;
|
||
|
||
// 2. Darkness
|
||
// We cast this hopefully most of the time. We will cast it a lot if we have
|
||
// many melee attackers, else we'll cast it if we have ultravision/trueseeing
|
||
if(GlobalMeleeAttackers >= GlobalOurHitDice / i3 ||
|
||
AI_GetAIHaveEffect(GlobalEffectUltravision) ||
|
||
AI_GetAIHaveEffect(GlobalEffectTrueSeeing) ||
|
||
GetHasSpell(SPELL_DARKVISION) || GetHasSpell(SPELL_TRUE_SEEING))
|
||
{
|
||
// Darkness's
|
||
AI_SpellWrapperDarknessSpells(OBJECT_SELF);
|
||
}
|
||
// 3. Normal invisiblity.
|
||
// We need to make sure it won't be Dispeled by invis purge.
|
||
oPurge = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY,
|
||
OBJECT_SELF, i1,
|
||
CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN,
|
||
CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_INVISIBILITY_PURGE);
|
||
if(!GetIsObjectValid(oPurge) || GetDistanceToObject(oPurge) > f10)
|
||
{
|
||
// Also, we can't go invisible if any enemies who are attacking us,
|
||
// can see us. We also won't if over 50 (or whatever)% of the enemy have got
|
||
// seeing spells.
|
||
// What % though? MORE if we have limited, selected spell (non-sorceror/bard)
|
||
// LESS if we do have other spells to take its place.
|
||
iPercentWhoSeeInvis = AI_GetSpellWhoCanSeeInvis(i4);
|
||
|
||
// Here, we make sure it is less then 80% for mages, and less then
|
||
// 30% for sorcerors or bards.
|
||
iNeedToBeBelow = i80;
|
||
// Use global
|
||
if(GlobalWeAreSorcerorBard) iNeedToBeBelow = i30;
|
||
|
||
if(iPercentWhoSeeInvis <= iNeedToBeBelow + d20())
|
||
{
|
||
// If within d20 of the needed amount, we do cast improved
|
||
// invisibility.
|
||
// Special Assassin Version
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_INVISIBILITY_2))
|
||
{
|
||
SetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY, f12);
|
||
return TRUE;
|
||
}
|
||
// Improved Invis. Level 4 (Bard, Mage). 50% consealment + invisibility.
|
||
if(AI_ActionCastSpell(SPELL_IMPROVED_INVISIBILITY, SpellEnhSinTar, OBJECT_SELF, i14, FALSE, ItemEnhSinTar, PotionEnh))
|
||
{
|
||
SetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY, f12);
|
||
return TRUE;
|
||
}
|
||
// Other invisibilities.
|
||
if(iPercentWhoSeeInvis <= iNeedToBeBelow)
|
||
{
|
||
// Invisibility
|
||
if(AI_SpellWrapperNormalInvisibilitySpells())
|
||
{
|
||
SetLocalTimer(AI_TIMER_JUST_CAST_INVISIBILITY, f12);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Massive summoning spells
|
||
// Normally, no...always, these are only owned by intelligent creatures, no
|
||
// need to random cast or anything.
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i12)
|
||
{
|
||
// Dragon Knight - Powerful dragon summon that cannot be dispelled. 20 rounds.
|
||
if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_DRAGON_KNIGHT, SPELL_EPIC_DRAGON_KNIGHT))
|
||
{
|
||
// Set level to 12
|
||
SetAIInteger(AI_LAST_SUMMONED_LEVEL, i12);
|
||
return TRUE;
|
||
}
|
||
}
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i11)
|
||
{
|
||
// Mummy Dust - Powerful summon that cannot be dispelled. 24 Hours.
|
||
if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_MUMMY_DUST, SPELL_EPIC_MUMMY_DUST))
|
||
{
|
||
// Set level to 11
|
||
SetAIInteger(AI_LAST_SUMMONED_LEVEL, i11);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// This is POWERFUL!
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i10)
|
||
{
|
||
// Black Blade of Disaster. Level 9 (Mage) A powerful greatsword fights. Please note: AI abuses the "concentration" rules for it!
|
||
if(AI_ActionCastSummonSpell(SPELL_BLACK_BLADE_OF_DISASTER, i19, i10)) return TRUE;
|
||
}
|
||
|
||
// If we are in time stop, or no enemy in 4 m, we will buff our appropriate stat.
|
||
// Level 2 spells. GLOBALINTELLIGENCE >= 6
|
||
// NOte: CHANGE TO RANDOM ROLL, MORE CHANCE AT INT 10
|
||
// Put just above first hostile spells.
|
||
// - Always cast if we have stoneskin effects.
|
||
if((GlobalInTimeStop || GlobalRangeToNearestEnemy > f4 ||
|
||
AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections)) &&
|
||
!GlobalIntelligence >= i6 && IsFirstRunThrough &&
|
||
!AI_CompareTimeStopStored(SPELL_FOXS_CUNNING, SPELL_OWLS_WISDOM,
|
||
SPELL_EAGLE_SPLEDOR))
|
||
{
|
||
if(GlobalOurChosenClass == CLASS_TYPE_WIZARD)
|
||
{
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasFoxesCunningSpell) &&
|
||
!AI_CompareTimeStopStored(SPELL_FOXS_CUNNING, SPELL_GREATER_FOXS_CUNNING))
|
||
{
|
||
// Greater first. This provides +2d4 + 1.
|
||
// foxes cunning :-) - No items, innate.
|
||
if(AI_ActionCastSpell(SPELL_GREATER_FOXS_CUNNING, SpellEnhSinTar)) return TRUE;
|
||
// Lesser one - but we have items for it.
|
||
if(AI_ActionCastSpell(SPELL_FOXS_CUNNING, SpellEnhSinTar, OBJECT_SELF, i12, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
else if(GlobalOurChosenClass == CLASS_TYPE_DRUID || GlobalOurChosenClass == CLASS_TYPE_CLERIC)
|
||
{
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasOwlsWisdomSpell) &&
|
||
!AI_CompareTimeStopStored(AI_SPELL_OWLS_INSIGHT, SPELL_GREATER_OWLS_WISDOM, SPELL_OWLS_WISDOM))
|
||
{
|
||
// Owls insight is cool - 2x caster level in wisdom = +plenty of DC.
|
||
// Owls Insight. Level 5 (Druid).
|
||
if(AI_ActionCastSpell(AI_SPELL_OWLS_INSIGHT, SpellEnhSinTar, OBJECT_SELF, i15, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
// Greater first. This provides +2d4 + 1.
|
||
// owls wisdom :-)
|
||
if(AI_ActionCastSpell(SPELL_GREATER_OWLS_WISDOM, SpellEnhSinTar)) return TRUE;
|
||
// Lesser one - but we have items for it.
|
||
if(AI_ActionCastSpell(SPELL_OWLS_WISDOM, SpellEnhSinTar, OBJECT_SELF, i12, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
else // Monsters probably benifit from this as well.
|
||
{
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasEaglesSpledorSpell) &&
|
||
!AI_CompareTimeStopStored(SPELL_GREATER_EAGLE_SPLENDOR, SPELL_EAGLE_SPLEDOR))
|
||
{
|
||
// Greater first. This provides +2d4 + 1.
|
||
// eagles splendor :-)
|
||
if(AI_ActionCastSpell(SPELL_GREATER_EAGLE_SPLENDOR, SpellEnhSinTar)) return TRUE;
|
||
// Lesser one - but we have items for it.
|
||
if(AI_ActionCastSpell(SPELL_EAGLE_SPLEDOR, SpellEnhSinTar, OBJECT_SELF, i12, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Special behaviour: Constructs...
|
||
// - These are bastards, and have either total immunity to spells or massive
|
||
// SR.
|
||
// - GlobalNormalSpellNoEffectLevel will already be set.
|
||
if(IsFirstRunThrough && GlobalIntelligence >= i5 &&
|
||
GlobalSpellTargetRace == CLASS_TYPE_CONSTRUCT)
|
||
{
|
||
// These spells go through any ristances (I mean, "Spell Immunity: Level 9 or lower")
|
||
|
||
// Ruin - (Epic) - 35d6 divine damage, fort save for half.
|
||
if(AI_ActionUseFeatOnObject(AI_FEAT_EPIC_SPELL_RUIN, GlobalSpellTarget)) return TRUE;
|
||
|
||
// Crumble. Level 6 (Druid) - Up to 15d6 damage to a construct.
|
||
if(AI_ActionCastSpell(SPELL_CRUMBLE, SpellHostRanged, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
// Cast prismatic AC bonus spell - defective force
|
||
if(!GetHasSpellEffect(AI_SPELLABILITY_PRISMATIC_DEFLECTING_FORCE))
|
||
{
|
||
// Deflecting Force - adds charisma bonus to defeltection AC.
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_PRISMATIC_DEFLECTING_FORCE, SpellProSinTar)) return TRUE;
|
||
}
|
||
|
||
// Try a finger/destruction spell, if their fortitude save is really, really low.
|
||
// Will not use these 2 twice in time stop, as they *should* die instantly
|
||
if(GlobalNormalSpellsNoEffectLevel < i7 && IsFirstRunThrough &&
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS, AI_COMBAT_MASTER) &&
|
||
GlobalSeenSpell && RangeShortValid &&
|
||
!AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH))
|
||
{
|
||
// Check low saves, IE always fails, no immunities and no mantals.
|
||
if((GlobalSpellTargetFort + i20) <= (GlobalSpellBaseSaveDC + i7) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMantalProtection))
|
||
{
|
||
// Note: No items, because it will be a much lower save.
|
||
|
||
// Destruction, level 7 (Cleric). Fort (Death) for Death if fail, or 10d6 damage if pass.
|
||
if(AI_ActionCastSpell(SPELL_DESTRUCTION, SpellHostRanged, GlobalSpellTarget, i17)) return TRUE;
|
||
// Finger of Death. Leve 7 (Mage). Fort (Death) for death if fail, or d6(3) + nCasterLvl damage if pass.
|
||
if(AI_ActionCastSpell(SPELL_FINGER_OF_DEATH, SpellHostRanged, GlobalSpellTarget, i17)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Now will cast mantal if not got one and nearest enemy is a spellcaster...
|
||
if(GlobalMeleeAttackers <= i1 /*We won't cast it with melee attackers - cast phisicals first*/ &&
|
||
!GlobalInTimeStop && GlobalIntelligence >= i7 && IsFirstRunThrough &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasMantalProtections) &&
|
||
/* Check for mage classes...spell target only */
|
||
(GetLevelByClass(CLASS_TYPE_WIZARD, GlobalSpellTarget) >= GlobalSpellTargetHitDice/i3) &&
|
||
(GetLevelByClass(CLASS_TYPE_SORCERER, GlobalSpellTarget) >= GlobalSpellTargetHitDice/i3))
|
||
{
|
||
// Cast mantals, or spell resistance...or Protection from spells.
|
||
if(AI_SpellWrapperMantalProtections()) return TRUE;
|
||
|
||
// Protection from spells. Level 7 (Mage), for +8 on all saves (Area effect too!)
|
||
if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_SPELLS, SpellProAre, OBJECT_SELF, i17, FALSE, ItemProAre)) return TRUE;
|
||
|
||
// Spell resistance. Level 5 (Cleric/Druid) 12 + Caster level (no limit) in spell resistance.
|
||
if(AI_ActionCastSpell(SPELL_SPELL_RESISTANCE, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Haste for allies
|
||
// - 60% chance of casting. More below somewhere (near stoneskin
|
||
if(AI_ActionCastAllyBuffSpell(f6, i60, SPELL_MASS_HASTE, SPELL_HASTE)) return TRUE;
|
||
// Ultravision for allies
|
||
// - 90% chance of casting.
|
||
// - Only cast if a valid enemy in darkness is near
|
||
if(GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_DARKVISION, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD)))
|
||
{
|
||
// Darkvision (Ultravision) but not trueseeing, is cast.
|
||
if(AI_ActionCastAllyBuffSpell(f8, i90, SPELL_DARKVISION, iM1, iM1, iM1, SPELL_TRUE_SEEING)) return TRUE;
|
||
}
|
||
}
|
||
// Epic killing spells. Hellball is very important to fire off!
|
||
// - We fire this off at the futhest target in 40M
|
||
|
||
// Futhest one away.
|
||
if(IsFirstRunThrough && GlobalSeenSpell)
|
||
{
|
||
// Hellball. (Epic) Long range AOE - 10d6 sonic, acid, fire and lightning damage to all in area. Reflex only halves.
|
||
// We just use spell target. Tough luck, we probably will be in the AOE
|
||
// whatever target we want!
|
||
if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_HELLBALL, SPELL_EPIC_HELLBALL, GlobalSpellTarget)) return TRUE;
|
||
|
||
// Ruin - a lot of damage, no SR, and only a half save thing for damage
|
||
if(AI_ActionUseEpicSpell(AI_FEAT_EPIC_SPELL_RUIN, SPELL_EPIC_RUIN, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// These 3 will not be wasted on mantals.
|
||
if(IsFirstRunThrough && GlobalNormalSpellsNoEffectLevel < i9 &&// PW kill is level 9
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMantalProtection) &&
|
||
!AI_CompareTimeStopStored(SPELL_CLOUDKILL, SPELL_POWER_WORD_KILL, SPELL_CIRCLE_OF_DEATH))
|
||
{
|
||
// INSTANT DEATH SPELLS...if conditions are met.
|
||
// - These will instantly kill a target. It does cheat to do it. Only
|
||
// does this if set (Not AI intelligence related)
|
||
// Most useful for high-levels not getting killed by much lower levels
|
||
// (EG: Lich getting defeated by a enemy with only knockdown or something)
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS, AI_COMBAT_MASTER))
|
||
{
|
||
// Cloudkill here - if average HD is < 7
|
||
// Random is < 7, always if under 4
|
||
// Damage, slow, long range. Also, AOE :-)
|
||
if(RangeLongValid && (GlobalAverageEnemyHD < i4) ||
|
||
((GlobalAverageEnemyHD < i7) && (d10() < i4)) &&
|
||
GlobalNormalSpellsNoEffectLevel < i5)
|
||
{
|
||
// Cloud Kill. Level 5 (Mage). Kills if under level 7, else acid damage.
|
||
if(AI_ActionCastSpell(SPELL_CLOUDKILL, SpellHostAreaInd, GlobalSpellTarget, i15, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
// Power Word: Kill
|
||
// If improved, will check HP, else just casts it.
|
||
if(RangeShortValid && GlobalSpellTargetCurrentHitPoints < i100)
|
||
{
|
||
// Power Word Kill. Level 9 (Mage). If target has less then 100HP, dies.
|
||
if(AI_ActionCastSpell(SPELL_POWER_WORD_KILL, SpellHostAreaInd, GlobalSpellTarget, i19, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Circle of Death
|
||
// CONDITION: Average enemy party hd less than 10
|
||
// This spell only effects level 1-9 people. Good cleaner for lower monsters!
|
||
// Meduim ranged spell.
|
||
if(RangeMediumValid && GlobalAverageEnemyHD <= i9 &&
|
||
GlobalNormalSpellsNoEffectLevel < i6)
|
||
{
|
||
// Circle of death. Level 6 (Mage). Medium ranged, Large AOE, kills 10HD or less people (to d4(casterlevel)).
|
||
if(AI_ActionCastSpell(SPELL_CIRCLE_OF_DEATH, SpellHostAreaDis, GlobalSpellTarget, i16, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Physical Damage Protections (Self)
|
||
// Stoneskins, (Greater, normal) + Premonition provide instant damage reduction.
|
||
if(AI_SpellWrapperPhisicalProtections(iLowestSpellLevel)) return TRUE;
|
||
}
|
||
|
||
|
||
// Do not check spell resistance (GlobalNormalSpellsNoEffectLevel) here.
|
||
// We only use pulses 80% of the time.
|
||
if(RangeTouchValid && !GlobalInTimeStop &&
|
||
GlobalSpellTargetRange < fTouchRange && d10() <= i8)
|
||
{
|
||
/* These are the pulses. Not much I can be bothered to check for. All good stuff!
|
||
281 | Pulse_Drown - None Constructs, Undeads, Elementals DIE if fail a fort check, DC: 20.
|
||
282 | Pulse_Spores - Soldier Shakes (Disease) is applied to those in the area.
|
||
283 | Pulse_Whirlwind - Large. Reflex DC 14 check, or Knockdown (2 rounds) and d3(HD) Damage.
|
||
284 | Pulse_Fire - Huge. d6(HD) in fire damage. Reflex Save, DC: 10 + HD
|
||
285 | Pulse_Lightning - Huge. d6(HD) in electrical damage. Reflex Save, DC: 10 + HD
|
||
286 | Pulse_Cold - Huge. d6(HD) in cold damage. Reflex Save, DC: 10 + HD
|
||
287 | Pulse_Negative - Large. Heals (Undead) allies. Harms all non-undead. Damage/Heal: d4(HD). Reflex Save, DC: 10 + HD.
|
||
288 | Pulse_Holy - Large. Heals (Non-undead) allies. Harms all undead. Damage/Heal: d4(HD). Reflex Save, DC: 10 + HD.
|
||
289 | Pulse_Death - Large. Death if fail Fort Save, DC: 10 + HD
|
||
290 | Pulse_Level_Drain - 1 Negative Level, if fail Fort Save, DC: 10 + HD.
|
||
291 | Pulse_Ability_Drain_Intelligence - Large. -(HD/5) INT, if fail fort save VS: 10 + HD.
|
||
292 | Pulse_Ability_Drain_Charisma - Large. -(HD/5) CHA, if fail fort save VS: 10 + HD.
|
||
293 | Pulse_Ability_Drain_Constitution - Large. -(HD/5) CON, if fail fort save VS: 10 + HD.
|
||
294 | Pulse_Ability_Drain_Dexterity - Large. -(HD/5) DEX, if fail fort save VS: 10 + HD.
|
||
295 | Pulse_Ability_Drain_Strength - Large. -(HD/5) STR, if fail fort save VS: 10 + HD.
|
||
296 | Pulse_Ability_Drain_Wisdom - Large. -(HD/5) WIS, if fail fort save VS: 10 + HD.
|
||
297 | Pulse_Poison - Varying Poison instantly applied. Check nw_s1_pulspois.nss, and poison.2da files.
|
||
298 | Pulse_Disease - Varying Save. See diseases.2da, and it is based on the
|
||
race of the caster, below are the diseases applied:
|
||
Vermin = Vermin Madness. Undead = Filth Fever. Outsider = Demon Fever.
|
||
Magical Beast = Soldier Shakes. Aberration = Blinding Sickness.
|
||
ANYTHING ELSE = Mindfire. */
|
||
for(iCnt = SPELLABILITY_PULSE_DROWN/*281*/; iCnt <= SPELLABILITY_PULSE_DISEASE/*289*/; iCnt++)
|
||
{
|
||
// All innate, so no matter about talents really.
|
||
if(AI_ActionCastSpell(iCnt)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Then harm/heal. (Needs 20 HP, and be challenging to us).
|
||
if((GlobalAverageEnemyHD >= (GlobalOurHitDice - i5)) &&
|
||
GlobalSpellTargetCurrentHitPoints > i20 && RangeTouchValid &&
|
||
!AI_CompareTimeStopStored(SPELL_HARM, SPELL_MASS_HEAL, SPELL_HEAL) &&
|
||
GlobalNormalSpellsNoEffectLevel < i8)// Mass heal = 8.
|
||
{
|
||
if(GlobalSeenSpell && GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD &&
|
||
GlobalSpellTargetRace != RACIAL_TYPE_CONSTRUCT &&
|
||
GlobalSpellTargetRace != RACIAL_TYPE_INVALID)
|
||
{
|
||
// If we are undead, we make sure we leave at least 1 for our own healing.
|
||
if(GlobalNormalSpellsNoEffectLevel < i6 &&
|
||
(GlobalOurRace != RACIAL_TYPE_UNDEAD || GetHasSpell(SPELL_HARM) >= i2))
|
||
{
|
||
// Harm
|
||
// CONDITION: 6+ hit dice and NOT undead! :) Also checks HP
|
||
// Harm Level 6 (Cleric) 7 (Druid) Makes the target go down to 1d4HP (or heals undead as heal)
|
||
if(AI_ActionCastSpell(SPELL_HARM, SpellHostTouch, GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
}
|
||
// (Mass) Heal (used as Harm for undead)
|
||
// CONDITION: Undead at 4+ hd. Never casts twice in time stop, and not over 20 HP.
|
||
else if(GlobalSpellTargetRace == RACIAL_TYPE_UNDEAD &&
|
||
GlobalIntelligence >= i7)
|
||
{
|
||
// Really, talent 4, heal area effect, no items are set in this though.
|
||
// Mass Heal. Level 8 (Cleric) 9 (Druid) mass "heal" damage/healing.
|
||
if(AI_ActionCastSpell(SPELL_MASS_HEAL, FALSE, GlobalSpellTarget, i18, TRUE)) return TRUE;
|
||
|
||
// Never use last 2 heals for harming. Level 6 (Cleric) 7 (Druid)
|
||
// - 1.3, changed to 3+ only, because, basically, healing self will
|
||
// probably be better. Undead + Constructs ignore this and use all of them.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i6 &&
|
||
(GetHasSpell(SPELL_HEAL) >= i3 || GlobalOurRace == RACIAL_TYPE_UNDEAD ||
|
||
GlobalOurRace != RACIAL_TYPE_CONSTRUCT))
|
||
{
|
||
// Heal. Level 6 (Cleric) 7 (Druid). For undead: As harm, else full healing.
|
||
if(AI_ActionCastSpell(SPELL_HEAL, FALSE, GlobalSpellTarget, i16)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Power Word: Stun
|
||
// Is not immune to mind spell (I think this is a valid check) and not already stunned.
|
||
// Really, if under < 151 HP to be affected
|
||
// Wierdly, this is considered a "Area effect" spell. Nope - jsut a VERY nice normal one. (I like it!)
|
||
// - We can cast this later. Here, we only cast if low amount of enemies :-D
|
||
if(GlobalSpellTargetCurrentHitPoints <= i150 &&
|
||
GlobalNormalSpellsNoEffectLevel < i7 &&
|
||
GlobalTotalSeenHeardEnemies < i3 && GlobalAverageEnemyHD > i10 &&
|
||
GlobalSeenSpell && RangeTouchValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_CompareTimeStopStored(SPELL_POWER_WORD_STUN))
|
||
{
|
||
// Power Word Stun. Level 7 (Wizard). Stun duration based on HP.
|
||
if(AI_ActionCastSpell(SPELL_POWER_WORD_STUN, SpellHostAreaInd, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
|
||
// Elemental shield here, if over 0 melee attackers (and 30% chace) or
|
||
// over 1-4 attackers (level based). Doesn't double cast, and not more then 1 at once.
|
||
if(IsFirstRunThrough &&
|
||
// - Checks if we have the effects or not in a second
|
||
((GlobalMeleeAttackers > (GlobalOurHitDice / i4) ||
|
||
(GlobalMeleeAttackers > i0 && d10() > i6))) &&
|
||
!AI_CompareTimeStopStored(SPELL_ELEMENTAL_SHIELD, SPELL_WOUNDING_WHISPERS,
|
||
SPELL_DEATH_ARMOR, SPELL_MESTILS_ACID_SHEATH))
|
||
{
|
||
if(AI_SpellWrapperShieldProtections(iLowestSpellLevel)) return TRUE;
|
||
}
|
||
|
||
// Dispel Good spells on the enemy.
|
||
// - Dispel Level 5 here.
|
||
// Basically, level 5 spells are mantals and spell-stoppers, or an alful
|
||
// lot of lower level spells.
|
||
if(RangeMediumValid && !GlobalInTimeStop)// All medium spells.
|
||
{
|
||
// Dispel number need to be 5 for breach
|
||
if(GlobalDispelTargetHighestBreach >= i5)
|
||
{
|
||
// Wrapers Greater and Lesser Breach.
|
||
if(AI_ActionCastBreach()) return TRUE;
|
||
}
|
||
// Dispel >= 5
|
||
if(GlobalDispelTargetHighestDispel >= i5)
|
||
{
|
||
// Wrappers the dispel spells
|
||
if(AI_ActionCastDispel()) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Consealment Protections
|
||
// We do displacement then blindness/deafness. We only attempt blindness/deafness
|
||
// if we are not a sorceror/bard mind you.
|
||
if(IsFirstRunThrough && !GlobalInTimeStop && // No Consealment in time stop.
|
||
GetObjectSeen(OBJECT_SELF, GlobalSpellTarget) &&
|
||
!AI_GetAIHaveEffect(GlobalEffectInvisible) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells))
|
||
{
|
||
// Imp. Invis and displacement...
|
||
if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF, iLowestSpellLevel)) return TRUE;
|
||
|
||
// Cast darkness on any enemy in range, if we have ultravision (or its
|
||
// effects)
|
||
// Need range check, with SRA, of course
|
||
if(RangeLongValid && (AI_GetAIHaveEffect(GlobalEffectUltravision) ||
|
||
AI_GetAIHaveEffect(GlobalEffectTrueSeeing) ||
|
||
GetHasSpell(SPELL_DARKVISION) || GetHasSpell(SPELL_TRUE_SEEING)))
|
||
{
|
||
// Cast at nearest without darkness!
|
||
oAOE = GetNearestCreature(CREATURE_TYPE_DOES_NOT_HAVE_SPELL_EFFECT, SPELL_DARKVISION, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
if(GetIsObjectValid(oAOE) && !GetHasSpellEffect(SPELL_TRUE_SEEING, oAOE)
|
||
&& !GetHasSpellEffect(SPELL_DARKVISION, oAOE))
|
||
{
|
||
// Darkness
|
||
if(AI_SpellWrapperDarknessSpells(oAOE)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Blindness/deafness. No casting in time stop is simpler.
|
||
if(RangeMediumValid && GlobalNormalSpellsNoEffectLevel < i8 &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) &&
|
||
!GlobalWeAreSorcerorBard &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i8))
|
||
{
|
||
// Mass Blindness and Deafness. Level 8 (Mage) AOE fort save, enemies only, who save or are blinded and deafened.
|
||
if(AI_ActionCastSpell(SPELL_MASS_BLINDNESS_AND_DEAFNESS, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Ally spells. A great variety that we cast above.
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Haste again - 90% chance
|
||
if(AI_ActionCastAllyBuffSpell(f6, i90, SPELL_MASS_HASTE, SPELL_HASTE)) return TRUE;
|
||
// Cast phisical stoneskins on allies - 80% chance, as it is quite good.
|
||
if(AI_ActionCastAllyBuffSpell(f6, i80, SPELL_GREATER_STONESKIN, SPELL_STONESKIN)) return TRUE;
|
||
// Consealment spells
|
||
if(AI_ActionCastAllyBuffSpell(f6, i60, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) return TRUE;
|
||
}
|
||
|
||
//Gate
|
||
//CONDITION: Protection from Evil active on self
|
||
// Balors rock, literally! If not, I kill dem all!! >:-D
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i10 &&
|
||
SpellAllies && AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell))
|
||
{
|
||
// Gate. Level 9 (Mage/Innate). Summons a balor, who would normally attack caster if not protected from evil, else powerful summon
|
||
if(AI_ActionCastSummonSpell(SPELL_GATE, i19, i10)) return TRUE;
|
||
}
|
||
|
||
//Protection from Evil / Magic Circle Against Evil
|
||
// ... in preparation for Gate!
|
||
if(IsFirstRunThrough &&GetHasSpell(SPELL_GATE) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell))
|
||
{
|
||
// Magic Circle against Alignment. Level 3 (Mage/Cleric/Bard/Paladin/Innate). +2 AC, mind spell immunity VS them.
|
||
if(AI_ActionCastSubSpell(SPELL_MAGIC_CIRCLE_AGAINST_EVIL, SpellEnhAre, OBJECT_SELF, i13, FALSE, ItemEnhAre)) return TRUE;
|
||
// Protection From Evil. Level 1(Mage/Cleric/Bard/Paladin/Innate). +4 AC, mind spell immunity VS them.
|
||
if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_EVIL, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
}
|
||
// None for allies. Won't bother (unless it is a problem!).
|
||
// - If adding, we will get the nearest without the spells effects.
|
||
|
||
// GlobalCanSummonSimilarLevel can be 1-12. (10 is elemental swarm, balor) (11, 12 epic)
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i9)
|
||
{
|
||
// Elemental swarm. Level 9 (Druid) Never replaced until no summon left - summons consecutive 4 huge elementals
|
||
if(AI_ActionCastSummonSpell(SPELL_ELEMENTAL_SWARM, i19, i10)) return TRUE;
|
||
// Summon an eldar elemental.
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_IX, i19, i9)) return TRUE;
|
||
// No-concentration summoned creatures.
|
||
if(AI_ActionCastSummonSpell(AI_SPELLABILITY_SUMMON_BAATEZU, FALSE, i9)) return TRUE;
|
||
if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_TANARRI, FALSE, i9)) return TRUE;
|
||
if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_SLAAD, FALSE, i9)) return TRUE;
|
||
if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_CELESTIAL, FALSE, i9)) return TRUE;
|
||
if(AI_ActionCastSummonSpell(SPELLABILITY_SUMMON_MEPHIT, FALSE, i9)) return TRUE;
|
||
if(AI_ActionCastSummonSpell(SPELLABILITY_NEGATIVE_PLANE_AVATAR, FALSE, i9)) return TRUE;
|
||
}
|
||
|
||
// Dominate spells
|
||
// - Dominate monster is level 9. Others are worth casting if valid.
|
||
// Needs to not be immune, and be of right race for DOM PERSON
|
||
// No time stop, and a valid will save to even attempt.
|
||
if(!GlobalInTimeStop && GlobalSeenSpell && RangeMediumValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
(GlobalOurHitDice - GlobalAverageEnemyHD) <= i8)
|
||
{
|
||
// Will save VS mind spells.
|
||
if(GlobalNormalSpellsNoEffectLevel < i9 &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i9))
|
||
{
|
||
// Dominate Monster. Level 9 (Mage) Dominates (VS. Mind will save) ANYTHING!
|
||
if(AI_ActionCastSpell(SPELL_DOMINATE_MONSTER, SpellHostRanged, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE;
|
||
|
||
if(GlobalNormalSpellsNoEffectLevel < i5 &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i5))
|
||
{
|
||
// Dominate person
|
||
if(!GetIsPlayableRacialType(GlobalSpellTarget))
|
||
{
|
||
// Dominate Person. Level 5 (Mage). Only affects PC races. Dominate on failed will.
|
||
if(AI_ActionCastSpell(SPELL_DOMINATE_PERSON, SpellHostRanged, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Mass charm. sorcerors/bards have better things (20% casting chance)
|
||
if(!GlobalWeAreSorcerorBard || d10() <= i2)
|
||
{
|
||
// For mass charm, we don't bother if they don't have a decent chance to fail (ues as level 5 spell for save DC)
|
||
// Mass Charm. Level 8 (Mage) 1 Round/2caster levels of charm. Affects PC races only + humanoids.
|
||
if(AI_ActionCastSpell(SPELL_MASS_CHARM, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Protection from Spells
|
||
// - CONDITION: Enemies less than 5 levels below me (powerful enemies)
|
||
// - CONDITION 2: At least 1 ally
|
||
// - We cast this lower down if we don't cast it here.
|
||
if(IsFirstRunThrough &&
|
||
(((GlobalOurHitDice - GlobalAverageEnemyHD) <= i5 &&
|
||
GlobalValidSeenAlly && GlobalRangeToAlly < f5) ||
|
||
((GlobalOurHitDice - GlobalAverageEnemyHD) <= i10)))
|
||
{
|
||
if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_SPELLS, SpellProAre, OBJECT_SELF, i17, FALSE, ItemProAre)) return TRUE;
|
||
}
|
||
|
||
//Mantle Protections
|
||
//CONDITION: Enemies less than 5 levels below me (powerful enemies)
|
||
// Will chance casting these anyway, if no melee attackers, and the enemy has a valid talent.
|
||
// Yes, talents include items...ahh well.
|
||
if(IsFirstRunThrough && ((GlobalOurHitDice - GlobalAverageEnemyHD) <= i5))
|
||
{
|
||
if(AI_SpellWrapperMantalProtections(iLowestSpellLevel)) return TRUE;
|
||
}
|
||
|
||
// All these are short-ranged death!
|
||
// - What do we want to cast? Eh? Well, we might as well randomly choose
|
||
// one of the level 9 main hostile spells. This includes single target spells,
|
||
// BUT we may use sigle target spells first if not many enemies.
|
||
|
||
// - Energy Drain (powerful, lowers statistics)
|
||
// - Bigby's Crushing Hand (2d6 + 12 damage/round)
|
||
|
||
// - Storm of vengance is a massive electrical/acid storm. Cast, we may move out later.
|
||
// - Implosion has a +3 fort Save DC (great!) (only medium radius)
|
||
// - Wail of the banshee has a collosal area and 1 save.
|
||
// - Wierd doesn't affect allies, 2 saves, collosal area.
|
||
// - Meteor Storm is like a huge fireball based on the caster, 20d6 damage.
|
||
|
||
// Single target spells first check
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i9)
|
||
{
|
||
// Crushing hand is indefinatly better, but depends...I think energy drain
|
||
// has its merits of being permament! :-D
|
||
|
||
// Explanation on bigby's spells - hitting, grappling.
|
||
// HIT: Beat GetAC(oTarget) with Primary Stat + Caster Level + d20 + 9-11 (7, 8, 9 level spells)
|
||
// GRAPPLE: Beat BAB + Size Mod of opposing. d20 + nCasterModifier + Caster Level + 14-16.
|
||
// Basically, we won't check this, as the caster level + the 9-11 + primary stat can beat AC.
|
||
// Ok, I can't be bothered to check it :-P
|
||
|
||
// 70% chance of Crushing Hand.
|
||
// We try and not cast it twice on the same target (for a starter, wasting spells)
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_CRUSHING_HAND, GlobalSpellTarget))
|
||
{
|
||
// Bigby's Crushing Hand. Level 9 (Mage). 2d6 + 12 damage/round.
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CRUSHING_HAND, SpellHostRanged, i60, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
if(RangeShortValid && GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i9))
|
||
{
|
||
// 40% chance of energy drain, as we think single target spells are better.
|
||
// Energy Drain. Level 9 (Mage). 2d4 negative levels! -BAB, Saves, Stats! :-)
|
||
if(AI_ActionCastSpellRandom(SPELL_ENERGY_DRAIN, SpellHostRanged, i40, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// AOE targets. Most...but not all...are short-range spells.
|
||
if(RangeShortValid) // No GlobalNormalSpellsNoEffectLevel for this. AOE spells may affect someone who is not immune!
|
||
{
|
||
// Storm - a very good AOE spell. May as well use here!
|
||
// AOE is 10M across.
|
||
// - May add in elemental protections check.
|
||
// 40% chance (especially as it is only a clerical spell)
|
||
if(!GlobalInTimeStop && GlobalSpellTargetRange < f6)
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELL_STORM_OF_VENGEANCE, SpellHostAreaDis, i40, OBJECT_SELF, i19, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
// Implosion - great spell! Instant death on a +3 save (!). Short range.
|
||
if(SpellHostAreaInd && (GetHasSpell(SPELL_IMPLOSION) ||
|
||
ItemHostAreaInd == SPELL_IMPLOSION))
|
||
{
|
||
// Its save is at 9 + 3 = 12 DC. Death save, and can kill allies not in PvP
|
||
// - Note that because we check natural "globes" in this, the level is set to 9.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_MEDIUM, i9, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly, TRUE);
|
||
// If valid, 40% chance of casting this one before others.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Implosion. Level 9 (Cleric). Instant death at +3 save. Note: Medium radius (others are collosal)
|
||
if(AI_ActionCastSpellRandom(SPELL_IMPLOSION, SpellHostAreaInd, i30, oAOE, i19, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Wail of the Banshee
|
||
// Fort save, else death, and it never affects us, but can kill allies.
|
||
if(SpellHostAreaDis && (GetHasSpell(SPELL_WAIL_OF_THE_BANSHEE) ||
|
||
ItemHostAreaDis == SPELL_WAIL_OF_THE_BANSHEE))
|
||
{
|
||
// Collosal range, fortitude, necromancy and death saves.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly, TRUE, TRUE);
|
||
// If valid, 40% of firing.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Wail of the Banshee. Level 9 (Mage/Innate). Caster cries out, kills everything that cannot save in area affected.
|
||
if(AI_ActionCastSpellRandom(SPELL_WAIL_OF_THE_BANSHEE, SpellHostAreaDis, i30, oAOE, i19, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Weird - item immunity fear? Need to test
|
||
// Never affects allies. Will save type - if the will is always
|
||
// saved, it does nothing at all.
|
||
if(SpellHostAreaDis && (GetHasSpell(SPELL_WEIRD) ||
|
||
ItemHostAreaDis == SPELL_WEIRD))
|
||
{
|
||
// Get AOE object - this is a small (8M) range, collosal size, doesn't affect allies.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_WILL);
|
||
// Is it valid? 40% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Wierd. Level 9 (Wizard/Innate). 2 saves (will/fort) against death. Doesn't kill allies! (Illusion)
|
||
if(AI_ActionCastSpellRandom(SPELL_WEIRD, SpellHostAreaDis, i30, oAOE, i19, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
//Meteor Swarm
|
||
// CONDITION: Closest enemy 5 ft. (1.5 meters) from me
|
||
// Changed to 10M ... the collosal sized actially used (on self)
|
||
// But only if the enemy is. Removed enemy fire, for now.
|
||
// 40% chance.
|
||
if(GlobalSpellTargetRange < f5)
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELL_METEOR_SWARM, SpellHostAreaDis, i30, OBJECT_SELF, i19, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Finally, the single target spells again. IE cast them after the AOE ones
|
||
// if we don't choose an AOE one.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i9)
|
||
{
|
||
// 50% chance of Crushing Hand.
|
||
// We try and not cast it twice on the same target (for a starter, wasting spells)
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_CRUSHING_HAND, GlobalSpellTarget))
|
||
{
|
||
// Bigby's Crushing Hand. Level 9 (Mage). 2d6 + 12 damage/round.
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CRUSHING_HAND, SpellHostRanged, i40, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
// Short range, not undead ETC.
|
||
if(RangeShortValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i9))
|
||
{
|
||
// 30% chance of energy drain
|
||
// Energy Drain. Level 9 (Mage). 2d4 negative levels! -BAB, Saves, Stats! :-)
|
||
if(AI_ActionCastSpellRandom(SPELL_ENERGY_DRAIN, SpellHostRanged, i30, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Backup, obviously, that we might not randomly choose Implision, if it
|
||
// fails the 40% check. Might sitll have the item/spell though!
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Finally, we might as well cast that new (SoU) cleric spell which stops
|
||
// many negative effects :-)
|
||
// Undeath's Eternal Foe. Stops negative damage, ability score draining,
|
||
// negative levels, immunity poisons, immunity diseases.
|
||
// Level 9 (Cleric)
|
||
if(AI_ActionCastSpell(SPELL_UNDEATHS_ETERNAL_FOE, SpellEnhSinTar, OBJECT_SELF, i19, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
/*::8888888888888888888888888888888888888888888888888888888888888888888888888888
|
||
Level 8 Spells.
|
||
//::8888888888888888888888888888888888888888888888888888888888888888888888888888
|
||
Thoughts - LOTS of AOE spells here for all classes. *shrugs* all good too!
|
||
massive damage and decent saves. Will random cast most of them. Ones for
|
||
Sorcerors that they'll want to random cast are [Sunburst] (100% chance VS undead!)
|
||
[Incendiary Cloud], [Horrid Wilting] (both are decent damage) [Clenched fist].
|
||
|
||
AOE:
|
||
[Natures Balance] - -1d4(CasterLevel/5) SR for enemies. 3d8 + Caster Level in healing for allies. Large area. Short range. Druid only.
|
||
[Sunbeam] - Blindness VS Reflex. d6(CasterLevel) divine VS undead, else 3d6 others. ReactionType.
|
||
S [Sun Burst] - Similar to sunbeam. 6d6 to others. Vampires die + all undead d6(Caster level) damage.
|
||
S [Earthquake] - 1d6(caster level) in damage to AOE, except caster.
|
||
[Fire Storm] - 1d6(Caster level) in fire/divine damage. No allies. collosal over caster.
|
||
S [Bombardment] - 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal
|
||
X [Mass Charm] - Charm a lot of enemies in an area. Cast above
|
||
X [Mass Blindness/Deafness] - Blind and death on fortitude save. Cast above
|
||
[Incendiary Cloud] - 4d6 Fire damage/round in the large AOE.
|
||
[Horrid Wilting] - d8(CasterLevel) in negative energy. Fort for half. Necromancy.
|
||
|
||
Single:
|
||
S [Bigby's Clenched Fist] - Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well.
|
||
|
||
Defender:
|
||
[Aura Versus Alignment] - d6 + d8 damage shield, 25SR, 4Saves, 4AC, mind immune VS alignemnt
|
||
X [Premonition] - 30/+5 DR. Cast up above this, though.
|
||
X [Mind blank] - Mind immunity/cleansing. Not cast normally, except in invisbility.
|
||
S X [Etherealness] - Total invisiblity (Bugged I say). Cast above.
|
||
|
||
Summon:
|
||
[Create Greater Undead] - Create Vampire, Doom knight, Lich, Mummy Cleric.
|
||
[Summon Creature VIII (8)] - Greater Elemental.
|
||
[Greater Planar Binding] - Death Slaad (Evil) Vrock (Neutral) Trumpet Archon (Good)
|
||
|
||
Other:
|
||
|
||
Undead are targeted here by undead-specific spells more often the PC's.
|
||
|
||
Regenerate is cast if we have damage.
|
||
|
||
Gazes (monster gazes) are powerful AOE cone spells. Cast at the taget.
|
||
//::88888888888888888888888888888888888888888888888888888888888888888888888888*/
|
||
|
||
// Jump out if we don't want to cast level 8 spells/abilities.
|
||
if(iLowestSpellLevel > i8) return FALSE;
|
||
|
||
int iEnemyAlignment = GetAlignmentGoodEvil(GlobalSpellTarget);
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Aura Vs. Alignment. d6 + d8 damage shield, 25SR, 4Saves, 4AC, mind immune VS alignemnt
|
||
if(GetAlignmentGoodEvil(GlobalSpellTarget) == ALIGNMENT_EVIL)
|
||
{
|
||
// Holy: Versus Evil. Level 8 (Cleric)
|
||
if(AI_ActionCastSubSpell(SPELL_HOLY_AURA, SpellEnhSelf, OBJECT_SELF, i18, FALSE, ItemEnhSelf)) return TRUE;
|
||
}
|
||
else if(iEnemyAlignment == ALIGNMENT_GOOD)
|
||
{
|
||
// Unholy Holy: Versus Good. Level 8 (Cleric)
|
||
if(AI_ActionCastSubSpell(SPELL_UNHOLY_AURA, SpellEnhSelf, OBJECT_SELF, i18, FALSE, ItemEnhSelf)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Cast regeneration + Natures Balance here if we are lacking HP.
|
||
if(IsFirstRunThrough && GlobalOurPercentHP <= i70)
|
||
{
|
||
if(!GetHasSpellEffect(SPELL_REGENERATE))
|
||
{
|
||
// Regeneration. Level 6 (Druid) 7 (Cleric). 6HP/Round. Good for persistant healing.
|
||
if(AI_ActionCastSpell(SPELL_REGENERATE, SpellEnhSelf, OBJECT_SELF, i17, FALSE, ItemEnhSelf, PotionEnh)) return TRUE;
|
||
}
|
||
// Cast at us, might as well.
|
||
// Natures Balance. Level 8 (Druid). Lowers SR of enemies by 1d4(CasterLevel/5) + Healing (3d6 + CasterLevel) for allies.
|
||
if(AI_ActionCastSpell(SPELL_NATURES_BALANCE, SpellHostAreaDis, OBJECT_SELF, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
|
||
// Undead spells. We can cast sunbeam/sunburst (VERY similar!) and Searing Light.
|
||
// We do cast this against non-undead, but later on.
|
||
// We only do this if at 5+ intelligence. Those below are stupid :-)
|
||
if(GlobalSpellTargetRace == RACIAL_TYPE_UNDEAD &&
|
||
GlobalIntelligence >= i5 && GlobalNormalSpellsNoEffectLevel < i8)
|
||
{
|
||
// First, Undead to Death - Slays 1d4HD worth of undead/level.
|
||
// (Max 20d4). Lowest first.
|
||
// - Will save!
|
||
// - 20M radius (but this we can ignore)
|
||
// - Takes into account all SR and so forth.
|
||
if(GlobalNormalSpellsNoEffectLevel < i6 &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i6) &&
|
||
RangeMediumValid)
|
||
{
|
||
// Undead to Death. Level 6 (Cleric) Slays 1d4HD worth of undead/level. (Max 20d4). Lowest first.
|
||
if(AI_ActionCastSpell(SPELL_UNDEATH_TO_DEATH, SpellHostAreaDis, GlobalSpellTarget, i16, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
// SUNBURST: 6d6 to non-undead. Kills vampires. Blindness. Limit of 25 dice for undead damage. Medium range/colosal size
|
||
// SUNBEAM: 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size
|
||
// Really silly. Only druids/mages get SunBurst...and druids get Sunbeam anyway!
|
||
// *sigh* Only difference is sunburst has a higher limit for damage, kills
|
||
// vampires, and does 6d6 damage to non-undead, so marginally better.
|
||
|
||
// Won't even randomly choose between them. Not worth it.
|
||
if(RangeShortValid)
|
||
{
|
||
// Sunburst. Level 8 (Druid/Mage) 6d6 to non-undead. Kills vampires. Blindness. Limit of 25 dice for undead damage. Medium range/colosal size
|
||
if(AI_ActionCastSpell(SPELL_SUNBURST, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
// Sunbeam. Level 8 (Cleric/Druid) 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size
|
||
if(AI_ActionCastSpell(SPELL_SUNBEAM, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
|
||
// If we can't destroy them, dominate them!
|
||
if(GlobalSpellTargetHitDice < GlobalOurChosenClassLevel * i3 &&
|
||
GlobalNormalSpellsNoEffectLevel < i8 &&
|
||
!GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_DOMINATED)))
|
||
{
|
||
// Control undead. Level 6 (Cleric) 7 (Mage). Dominates 1 undead up to 3x Caster level.
|
||
if(AI_ActionCastSpell(SPELL_CONTROL_UNDEAD, SpellHostRanged, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
// Medium spell
|
||
if(GlobalNormalSpellsNoEffectLevel < i3)
|
||
{
|
||
// Searing light. Level 3 (Cleric). Full level: 1-10d6 VS undead. Half Level: 1-5d6 VS constructs. 1-5d8 VS Others.
|
||
if(AI_ActionCastSpell(SPELL_SEARING_LIGHT, SpellHostRanged, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Gazes have short ranges - 80% chance.
|
||
// Why this high? (compared to say, fire storm?) because they are innate
|
||
// and so no concentration checks, and pretty good DC's.
|
||
if(RangeShortValid && d10() <= i8) // No GlobalNormalSpellsNoEffectLevel check
|
||
{
|
||
// We cast all of these, but randomly. It works through with most powerful
|
||
// getting the highest %'s of course :-)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDeath))
|
||
{
|
||
// Death gazes first - Golem one is the most deadly!
|
||
// 50% chance of either.
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GOLEM_BREATH_GAS, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DEATH, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
// Petrify - SoU. 50% chance (almost like death!)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_PETRIFY, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
// Destroy X are powerful. 50% chance each. They are basically as DEATH but for alignments.
|
||
if(iEnemyAlignment == ALIGNMENT_GOOD)
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_GOOD, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
else if(iEnemyAlignment == ALIGNMENT_EVIL)
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_EVIL, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
if(GetAlignmentLawChaos(GlobalSpellTarget) == ALIGNMENT_CHAOTIC)
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_CHAOS, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
else if(GetAlignmentLawChaos(GlobalSpellTarget) == ALIGNMENT_LAWFUL)
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DESTROY_LAW, FALSE, i40, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
// Can't be immune to mind
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityMind))
|
||
{
|
||
// Fear (and fixed: Added Krenshar Scare) 30%
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityFear))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_KRENSHAR_SCARE, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_FEAR, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
// Domination/Charm 30%
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DOMINATE, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_CHARM, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
// Other random mind things
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_PARALYSIS, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_STUNNED, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_CONFUSION, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DOOM, FALSE, i30, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Level 8 summons. 20HD or under, or 2 melee enemy and under.
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i8 &&
|
||
(GlobalOurHitDice <= i20 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Create Greater undead - Pale Master
|
||
if(AI_ActionCastSummonSpell(AI_FEAT_PM_CREATE_GREATER_UNDEAD, iM1, i8)) return TRUE;
|
||
|
||
// Create Greater Undead. Level 8 (Cleric) Create Vampire, Doom knight, Lich, Mummy Cleric.
|
||
if(AI_ActionCastSummonSpell(SPELL_CREATE_GREATER_UNDEAD, i18, i8)) return TRUE;
|
||
// Summon an Greater elemental. Summon 8 - Druid/Cleric/Bard/Mage.
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VIII, i18, i8)) return TRUE;
|
||
// Greater Planar Binding. Level 8 (Mage) Death Slaad (Evil) Vrock (Neutral) Trumpet Archon (Good)
|
||
if(AI_ActionCastSummonSpell(SPELL_GREATER_PLANAR_BINDING, i18, i8)) return TRUE;
|
||
}
|
||
|
||
// Level 8 general attack spells.
|
||
// Randomly pick one:
|
||
// [Sunbeam] - Blindness VS Reflex. d6(CasterLevel) divine VS undead, else 3d6 others. ReactionType.
|
||
// [Sun Burst] - Similar to sunbeam. 6d6 to others. Vampires die + all undead d6(Caster level) damage.
|
||
// [Earthquake] - 1d6(caster level) in damage to AOE, except caster.
|
||
// [Fire Storm] - 1d6(Caster level) in fire/divine damage. No allies. collosal over caster.
|
||
// [Bombardment] - 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal
|
||
// [Incendiary Cloud] - 4d6 Fire damage/round in the large AOE.
|
||
// [Horrid Wilting] - d8(CasterLevel) in negative energy. Fort for half. Necromancy.
|
||
|
||
// Single:
|
||
// [Bigby's Clenched Fist] - Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well.
|
||
|
||
// Is it best to cast Single Spells First?
|
||
// 70% if favourable.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i8)
|
||
{
|
||
if(!GetHasSpellEffect(SPELL_BIGBYS_CLENCHED_FIST, GlobalSpellTarget) &&
|
||
!AI_CompareTimeStopStored(SPELL_BIGBYS_CLENCHED_FIST) &&
|
||
RangeLongValid)
|
||
{
|
||
// Bigby's Clenched Fist. Level 8 (Mage) Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well.
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CLENCHED_FIST, SpellHostRanged, i60, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// No other single spells for level 8.
|
||
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
|
||
// Area of effect spells. Go through - some do have higher %'s then othres.
|
||
|
||
// Fire storm - cast on self with a 10M range around caster (circle). 60% chance.
|
||
if(RangeShortValid && GlobalSpellTargetRange <= f8 &&
|
||
GlobalNormalSpellsNoEffectLevel < i8 &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i8))
|
||
{
|
||
// Fire storm. Level 8 (Cleric). 1d6(Caster level) in fire/divine damage. No allies. collosal over caster.
|
||
if(AI_ActionCastSpellRandom(SPELL_FIRE_STORM, SpellHostAreaDis, i50, OBJECT_SELF, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
// Horrid Wilting
|
||
// Never affects allies. Fortitude - necromancy spell too. Lots of damage.
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_HORRID_WILTING) || ItemHostAreaDis == SPELL_HORRID_WILTING))
|
||
{
|
||
// Won't cast if got lots of undead. 20M range, huge radius.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i8, SAVING_THROW_FORT, SHAPE_SPHERE, FALSE, FALSE, TRUE);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Horrid Wilting. Level 8 (Mage). d8(CasterLevel) in negative energy. Fort for half. Necromancy.
|
||
if(AI_ActionCastSpellRandom(SPELL_HORRID_WILTING, SpellHostAreaDis, i50, oAOE, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Bombardment. Similar to the above. Long range, relfex save, not affect allies.
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_BOMBARDMENT) || ItemHostAreaInd == SPELL_BOMBARDMENT))
|
||
{
|
||
// 40M range, collosal area. Relfex save.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Bombardment. Level 8 (Druid) 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal
|
||
if(AI_ActionCastSpellRandom(SPELL_BOMBARDMENT, SpellHostAreaInd, i50, oAOE, i18, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Earthquake. No SR, long range, affects anyone except caster.
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_EARTHQUAKE) || ItemHostAreaInd == SPELL_EARTHQUAKE))
|
||
{
|
||
// 40M range, collosal area. Relfex save.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Earthquake] - 1d6(caster level) (to 10d6) in damage to AOE, except caster.
|
||
if(AI_ActionCastSpellRandom(SPELL_EARTHQUAKE, SpellHostAreaInd, i40, oAOE, i18, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Incendiary Cloud. Long range, the AOE is 5.0M across. 4d6Damage/round is quite good.
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
!AI_CompareTimeStopStored(SPELL_INCENDIARY_CLOUD) &&
|
||
(GetHasSpell(SPELL_INCENDIARY_CLOUD) || ItemHostAreaInd == SPELL_INCENDIARY_CLOUD))
|
||
{
|
||
// 40M range, lagre (5.0 across) area. Relfex saves.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i8, SAVING_THROW_REFLEX);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Earthquake] - 1d6(caster level) (to 10d6) in damage to AOE, except caster.
|
||
if(AI_ActionCastSpellRandom(SPELL_INCENDIARY_CLOUD, SpellHostAreaInd, i40, oAOE, i18, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Sunbeam and burst. Very similar spells and if they are not undead, then
|
||
// there should be very little chance of casting it.
|
||
// - Won't bother getting nearest undead.
|
||
if(SpellHostAreaInd && !GlobalInTimeStop && RangeLongValid &&
|
||
(GetHasSpell(SPELL_SUNBEAM) || GetHasSpell(SPELL_SUNBURST) ||
|
||
ItemHostAreaDis == SPELL_SUNBEAM || ItemHostAreaDis == SPELL_SUNBURST))
|
||
{
|
||
// 40M range, lagre (5.0 across) area. Relfex saves.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i8, SAVING_THROW_REFLEX);
|
||
// Is it valid? 20% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Sunburst. Level 8 (Druid/Mage) 6d6 to non-undead. Kills vampires. Blindness. Limit of 25 dice for undead damage. Medium range/colosal size
|
||
if(AI_ActionCastSpellRandom(SPELL_SUNBURST, SpellHostAreaDis, i10, oAOE, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
// Sunbeam. Level 8 (Cleric/Druid) 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size
|
||
if(AI_ActionCastSpellRandom(SPELL_SUNBEAM, SpellHostAreaDis, i10, oAOE, i18, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Single spells at end
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i8)
|
||
{
|
||
// 40% chance
|
||
if(!GetHasSpellEffect(SPELL_BIGBYS_CLENCHED_FIST, GlobalSpellTarget) &&
|
||
!AI_CompareTimeStopStored(SPELL_BIGBYS_CLENCHED_FIST) &&
|
||
RangeLongValid)
|
||
{
|
||
// Bigby's Clenched Fist. Level 8 (Mage) Attack Each Round. 1d8 + 12 damage/round. Fort VS stunning as well.
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CLENCHED_FIST, SpellHostRanged, i30, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// No other single spells for level 8.
|
||
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// Check if we had any of the above we didn't cast based on the %
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
/*::7777777777777777777777777777777777777777777777777777777777777777777777777777
|
||
Level 7 Spells.
|
||
//::7777777777777777777777777777777777777777777777777777777777777777777777777777
|
||
Thoughts - well, quite a varied amount of spells. Among classes, there are
|
||
some variety. SoU adds Banishment, and Bigby's. Quite a few death spells
|
||
but note: Word of faith is very effective against 11s and under - stun, for
|
||
under 4's, death!
|
||
|
||
Hordes adds Great Thunderclap - requires 3 saves! :-)
|
||
|
||
AOE:
|
||
S X [Banishment] - Destroys summons (familiars, creatures, spells) to a 2xCasters HD limit.
|
||
[Word of Faith] - Enemies only. 4 Or Down die. 4+ Confuse Stun, Blind. 8+ Stun + Blind. 12+ Only Blind. (1Round/2Casterlevels) outsiders killed.
|
||
[Creeping Doom] - Until 1000 damage, d6 + d6/round stayed in damage in an AOE.
|
||
[Delayed Fireball Blast] - Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!)
|
||
[Prismatic Spray] - Random damage/effects. Chance of doing double amount of effects.
|
||
H [Great Thunderclap] - Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown.
|
||
|
||
Single:
|
||
[Distruction] - Short ranged instant death for fort, else take 10d6 Negative energy. Death + Necromantic.
|
||
S [Bigby's Grasping Hand] - Hold target if sucessful grapple
|
||
X [Control Undead] - Yep, controls undead! Dominates them. Cast above near sunbeam.
|
||
[Finger of Death] - Short ranged instant Death on fort else 3d6 negative energy.
|
||
[Power Word, Stun] - Instant stun based on HP of target. Cast above if few targets. Here if more.
|
||
|
||
Defender:
|
||
[Aura of Vitality] - +4 STR, CON, DEX for all allies.
|
||
[Protection From Spells] +8 to all saves. Cast above, backup here.
|
||
[Shadow Shield] - Necromancy/negative energy immunity. Some other resistances and some DR.
|
||
X [Spell Mantle] - 1d8 + 8 spells stopped. Cast above (This is good VS anything that casts spells)
|
||
[Regenerate] - 6HP/Round healed. Cast above if damaged. Cast here anyway :-)
|
||
|
||
Summon:
|
||
[Summon Monster VII (7)] - Huge elemental
|
||
[Mordenkainen's Sword] - Good, nay, very good hitting summon.
|
||
|
||
Other:
|
||
X [Greater Restoration] - Heals lots of effects.
|
||
X [Resurrection] - Resurrection :-) cast on dead people not here.
|
||
|
||
Do Palemaster Death touch here. Always use it if they are not immune.
|
||
//::77777777777777777777777777777777777777777777777777777777777777777777777777*/
|
||
|
||
// Jump out if we don't want to cast level 7 spells.
|
||
if(iLowestSpellLevel > i7) return FALSE;
|
||
|
||
// Cast Shadow Shield only first. Good protections really :-)
|
||
// Visages are generally lower DR, with some spell-resisting or effect-immunty extras.
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Shadow Shield - has 10/+3 damage reduction + lots of stuff. Level 7 (Mage)
|
||
if(AI_SpellWrapperVisageProtections(i7)) return TRUE;
|
||
|
||
// Protection From Spells.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasProtectionSpellsSpell))
|
||
{
|
||
// Protection from spells. Level 7 (Mage), for +8 on all saves (Area effect too!)
|
||
if(AI_ActionCastSpell(SPELL_PROTECTION_FROM_SPELLS, SpellProAre, OBJECT_SELF, i17, FALSE, ItemProAre)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Palemaster death touch
|
||
// DC 17 + (Pale Master - 10) /2.
|
||
if(RangeTouchValid)
|
||
{
|
||
// Cannot affect creatures over large size
|
||
// - Module switch, but always checked here.
|
||
if(GetCreatureSize(GlobalSpellTarget) <= CREATURE_SIZE_LARGE &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
// Fort save DC 17
|
||
GlobalSpellTargetFort <= i16)
|
||
{
|
||
// Use the feat
|
||
if(AI_ActionUseFeatOnObject(FEAT_DEATHLESS_MASTER_TOUCH, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
// Undead graft paralyzes!
|
||
if(GlobalSpellTargetRace != RACIAL_TYPE_ELF &&
|
||
// Fort save - 14 + Palemaster levels / 2
|
||
!GlobalSpellTargetFort < (i14 + GetLevelByClass(CLASS_TYPE_PALEMASTER)/i2))
|
||
{
|
||
// Use the feat
|
||
if(AI_ActionUseFeatOnObject(FEAT_UNDEAD_GRAFT_1, GlobalSpellTarget)) return TRUE;
|
||
// 2 versions
|
||
if(AI_ActionUseFeatOnObject(FEAT_UNDEAD_GRAFT_2, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Hostile spells here.
|
||
// We randomly choose one to cast (with higher %'s for some!)
|
||
// AOE:
|
||
// [Word of Faith] - Enemies only. 4 Or Down die. 4+ Confuse Stun, Blind. 8+ Stun + Blind. 12+ Only Blind. (1Round/2Casterlevels) outsiders killed.
|
||
// [Creeping Doom] - Until 1000 damage, d6 + d6/round stayed in damage in an AOE.
|
||
// [Delayed Fireball Blast] - Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!)
|
||
// [Prismatic Spray] - Random damage/effects. Chance of doing double amount of effects.
|
||
// [Great Thunderclap] - Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown.
|
||
// [Stonehold] - AOE - Will Encase people in stone (Paralysis) VS Will Mind throw. (Save each round)
|
||
// Single:
|
||
// [Distruction] - Short ranged instant death for fort, else take 10d6 Negative energy. Death + Necromantic.
|
||
// [Bigby's Grasping Hand] - Hold target if sucessful grapple
|
||
// [Finger of Death] - Short ranged instant Death on fort else 3d6 negative energy.
|
||
// [Power Word, Stun] - Instant stun based on HP of target. Cast above if few targets. Here if more.
|
||
|
||
// Is it best to target with single-target spells first? Most are pretty good :-D
|
||
// 60-70% if favourable.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i7)
|
||
{
|
||
// Killing spells. These, if sucessful, rend all status-effect spells redundant.
|
||
// - Only fired if they are not immune :-D
|
||
if(RangeShortValid && !AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i7) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath))
|
||
{
|
||
// Destruction, level 7 (Cleric). Fort (Death) for Death if fail, or 10d6 damage if pass.
|
||
if(AI_ActionCastSpellRandom(SPELL_DESTRUCTION, SpellHostRanged, i60, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE;
|
||
// Finger of Death. Level 7 (Mage). Fort (Death) for death if fail, or d6(3) + nCasterLvl damage if pass.
|
||
if(AI_ActionCastSpellRandom(SPELL_FINGER_OF_DEATH, SpellHostRanged, i50, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Is not immune to mind spell (I think this is a valid check) and not already stunned.
|
||
// Really, if under < 151 HP to be affected - short ranged
|
||
if (GlobalSpellTargetCurrentHitPoints <= i150 && RangeShortValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_CompareTimeStopStored(SPELL_POWER_WORD_STUN))
|
||
{
|
||
// Power Word Stun. Level 7 (Wizard). Stun duration based on HP.
|
||
if(AI_ActionCastSpellRandom(SPELL_POWER_WORD_STUN, SpellHostAreaInd, i60, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
// Bigbiy's Grasping hand last. We don't bother checking for grapple checks -
|
||
// they mainly work anyway, if anything. Powerful in its own right, as
|
||
// it holds on a sucessful check.
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_GRASPING_HAND, GlobalSpellTarget) &&
|
||
!AI_CompareTimeStopStored(SPELL_BIGBYS_GRASPING_HAND) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun))
|
||
{
|
||
// Bigby's Grasping Hand. Level 7 (Mage) Hold target if sucessful grapple
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_GRASPING_HAND, SpellHostRanged, i40, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// AOE spells now.
|
||
// [Word of Faith] - Enemies only. 4 Or Down die. 4+ Confuse Stun, Blind. 8+ Stun + Blind. 12+ Only Blind. (1Round/2Casterlevels) outsiders killed.
|
||
// [Creeping Doom] - Until 1000 damage, d6 + d6/round stayed in damage in an AOE.
|
||
// [Delayed Fireball Blast] - Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!)
|
||
// [Prismatic Spray] - Random damage/effects. Chance of doing double amount of effects.
|
||
// [Great Thunderclap] - Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown.
|
||
|
||
// Word of faith
|
||
// Doesn't hurt allies, will-based save, at the very least blindness :-D Medium range, collosal size
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_WORD_OF_FAITH) || ItemHostAreaDis == SPELL_WORD_OF_FAITH))
|
||
{
|
||
// 20M medium range, colossal size, will save to deflect.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_COLOSSAL, i7, SAVING_THROW_WILL);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Word of Faith. Level 7 (Cleric) Enemies only affected. 4 Or Down die. 4+ [Confuse|Stun|Blind]. 8+ [Stun|Blind]. 12+ [Blind]. (1Round/2Casterlevels) outsiders killed.
|
||
if(AI_ActionCastSpellRandom(SPELL_WORD_OF_FAITH, SpellHostAreaDis, i60, oAOE, i17, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Creeping Doom
|
||
// Damage to all in AOE, to 1000 damage. d6(rounds in it) basically. Good AOE spell.
|
||
// Only 50% chance of casting, to not cast too many.
|
||
if(SpellHostAreaDis && RangeMediumValid && !GlobalInTimeStop &&
|
||
(GetHasSpell(SPELL_CREEPING_DOOM) || ItemHostAreaDis == SPELL_CREEPING_DOOM))
|
||
{
|
||
// 20M medium range, we'll say a huge size. No save can stop all damage ;-)
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i7, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Creeping Doom. Level 7 (Druid) Until 1000 damage, d6 + d6/round stayed in damage in an AOE.
|
||
if(AI_ActionCastSpellRandom(SPELL_CREEPING_DOOM, SpellHostAreaDis, i40, oAOE, i17, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Delayed Fireball Blast. Big fireball. Lots of fire damage - reflex saves.
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
!AI_CompareTimeStopStored(SPELL_DELAYED_BLAST_FIREBALL, SPELL_FIREBALL) &&
|
||
(GetHasSpell(SPELL_DELAYED_BLAST_FIREBALL) || ItemHostAreaInd == SPELL_DELAYED_BLAST_FIREBALL))
|
||
{
|
||
// 20M medium range, blast is RADIUS_SIZE_HUGE, lower then Fireball, but more deadly (both save + damage)
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i7, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Delayed Fireball Blast. Level 7 (Mage) Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!)
|
||
if(AI_ActionCastSpellRandom(SPELL_DELAYED_BLAST_FIREBALL, SpellHostAreaInd, i50, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Great Thunderclap is OK, but doesn't really do too much. If anything, the
|
||
// 3 saves are cool :-P
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
!AI_CompareTimeStopStored(SPELL_GREAT_THUNDERCLAP) &&
|
||
(GetHasSpell(SPELL_GREAT_THUNDERCLAP) || ItemHostAreaInd == SPELL_GREAT_THUNDERCLAP))
|
||
{
|
||
// 20M medium range, hits a gargantuan area.
|
||
// - We ignore saves for this.
|
||
// - Doesn't actually hit ourselves. Won't bother checking this though.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_GARGANTUAN, i7, SAVING_THROW_ALL, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Great Thunderclap. Level 7 (Mage) Will VS 1 round stun. Fort VS 1 Turn Deaf. Reflex VS 1 Round Knockdown.
|
||
if(AI_ActionCastSpellRandom(SPELL_GREAT_THUNDERCLAP, SpellHostAreaInd, i40, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Lastly, AOE wise, it is prismatic spray. Comparable, if a little (or very!)
|
||
// erratic. We might as well cast this whatever (So if they all are immune
|
||
// to the delay fireballs we have, this does good damage compared to 0!)
|
||
if(SpellHostAreaInd && RangeShortValid &&
|
||
!AI_CompareTimeStopStored(SPELL_PRISMATIC_SPRAY) &&
|
||
(GetHasSpell(SPELL_PRISMATIC_SPRAY) || ItemHostAreaInd == SPELL_PRISMATIC_SPRAY))
|
||
{
|
||
// 8M short range, blast is a cone, and no save. Spell script has fSpread at 11.0
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, i7, FALSE, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 40% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Prismatic Spray. Level 7 (Mage) Random damage/effects. Chance of doing double amount of effects.
|
||
if(AI_ActionCastSpellRandom(SPELL_PRISMATIC_SPRAY, SpellHostAreaInd, i30, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Level 7 summon spells. Also cast the unique class-based summons (like
|
||
// a blackguards undead, a shadowdancers shadow ETC).
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i7)
|
||
{
|
||
// Always use our feat-based ones (not level dependant) as they increase
|
||
// with level.
|
||
// - Shadow based on level
|
||
if(AI_ActionCastSummonSpell(FEAT_SUMMON_SHADOW, iM1, i7)) return TRUE;
|
||
// - Undead warrior (EG: doomknight) based on level
|
||
if(AI_ActionCastSummonSpell(AI_FEAT_BG_CREATE_UNDEAD, iM1, i7)) return TRUE;
|
||
// - Shadow based on level
|
||
if(AI_ActionCastSummonSpell(AI_FEAT_BG_FIENDISH_SERVANT, iM1, i7)) return TRUE;
|
||
// Pale master
|
||
if(AI_ActionCastSummonSpell(AI_FEAT_PM_CREATE_UNDEAD, iM1, i7)) return TRUE;
|
||
|
||
// Then, the normal summons.
|
||
if(GlobalOurHitDice <= i18 || GlobalMeleeAttackers <= i2)
|
||
{
|
||
// Mordenkainen's Sword Level 7 (Mage). Good, nay, very good hitting summon.
|
||
if(AI_ActionCastSummonSpell(SPELL_MORDENKAINENS_SWORD, i17, i7)) return TRUE;
|
||
|
||
// Summon Monster VII (7). Level 7 (Cleric, Mage, Druid, etc) Huge elemental
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VII, i17, i7)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Now, back to single spell targets again.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i7)
|
||
{
|
||
// Killing spells. These, if sucessful, rend all status-effect spells redundant.
|
||
// - Only fired if they are not immune :-D
|
||
if(RangeShortValid && !AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i7) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath))
|
||
{
|
||
// Destruction, level 7 (Cleric). Fort (Death) for Death if fail, or 10d6 damage if pass.
|
||
if(AI_ActionCastSpellRandom(SPELL_DESTRUCTION, SpellHostRanged, i30, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE;
|
||
// Finger of Death. Leve 7 (Mage). Fort (Death) for death if fail, or d6(3) + nCasterLvl damage if pass.
|
||
if(AI_ActionCastSpellRandom(SPELL_FINGER_OF_DEATH, SpellHostRanged, i20, GlobalSpellTarget, i17, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Bigbiy's Grasping hand last. We don't bother checking for grapple checks -
|
||
// they mainly work anyway, if anything. Powerful in its own right, as
|
||
// it holds on a sucessful check.
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_GRASPING_HAND, GlobalSpellTarget) &&
|
||
!AI_CompareTimeStopStored(SPELL_BIGBYS_GRASPING_HAND) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun))
|
||
{
|
||
// Bigby's Grasping Hand. Level 7 (Mage) Hold target if sucessful grapple
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_GRASPING_HAND, SpellHostRanged, i20, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Is not immune to mind spell (I think this is a valid check) and not already stunned.
|
||
// Really, if under < 151 HP to be affected - short ranged
|
||
if (GlobalSpellTargetCurrentHitPoints <= i150 && RangeShortValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_CompareTimeStopStored(SPELL_POWER_WORD_STUN))
|
||
{
|
||
// Power Word Stun. Level 7 (Wizard). Stun duration based on HP.
|
||
if(AI_ActionCastSpellRandom(SPELL_POWER_WORD_STUN, SpellHostAreaInd, i20, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Backup casting of the level 7 hostle spells, just below the last
|
||
// level 7 summon spells :-)
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
/*::6666666666666666666666666666666666666666666666666666666666666666666666666666
|
||
Level 6 Spells.
|
||
//::6666666666666666666666666666666666666666666666666666666666666666666666666666
|
||
Thoughts - a great variety of spells here, from defenses like Greater Stoneskin,
|
||
down to versitile Shades, Flesh to stone and chain lightning. For all classes,
|
||
level 6 spells give the greatest variety.
|
||
|
||
Hordes adds a few, as does SoU.
|
||
|
||
AOE:
|
||
[Acid Fog] - Acid damage in an AOE, including slow in AOE. 1d6/round in fog.
|
||
[Chain Lightning] - Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage.
|
||
X [Circle of Death] - Under 9HD people save or die. 1d4 creatures/caster leve. Cast above
|
||
S [Isaac's Greater Missile Storm] - !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save!
|
||
[Blade Barrier] - Lots of spikes = Lots of damage. Piercing, relfex saves.
|
||
H X [Undead to Death] - Slays 1d4HD worth of undead/level. (Max 20d4). Lowest first.
|
||
|
||
Cast as single target:
|
||
|
||
H [Evil Blight] - All in a collosal area have a curse (-3 stats) on them. Will save.
|
||
|
||
Mobile AOE:
|
||
|
||
S [Dirge] - Continual Strength Damage to those in the AOE (Mobile, on self)
|
||
|
||
[Greater Dispeling] - Dispels all effects on 1 target, or 1 on an area of targets. (to 15 caster levels)
|
||
[Greater Spell Breach] - Strips 6 protection spells, then lowers SR of target.
|
||
|
||
Single:
|
||
S [Bigby's Forceful Hand] - Bullrush to make the target Knockdowned and Dazed all in one.
|
||
S [Flesh to Stone] - Petrifies a target on a fortitude save.
|
||
[Harm] - 1d4 HP left on target, if touch attacked.
|
||
S [Drown] - Up to 90% of current HP damage done. Fort save for none.
|
||
H X [Crumble] - 1d6/level damage (to 15d6) only to constructs.
|
||
|
||
Defender:
|
||
[Ethereal Visage] - 25% consealment. 20/+3 DR. 0/1/2 spells immune to.
|
||
[Globe of Invulnerability] - Immunity to 4, 3, 2, 1, 0 level spells.
|
||
[Greater Stoneskin] - 20/+5 DR. We cast this here, if we have not got it, to prepare for Premonition going down.
|
||
X [Mass Haste] - Haste for allies in area.
|
||
|
||
Summon:
|
||
[Summon Monster VI (6)] - Dire Tiger
|
||
[Planar Binding] - Summons Subbucus (Evil), Hound Arcon (Good), Green Slaad (Neutral). AI won't target outsiders specifically.
|
||
[Create Undead] - Creates a lowish undead to aid the caster.
|
||
[Planar Ally] - Waoh! Same as planar binding! Exactly! :-) (except no stopping outsiders)
|
||
|
||
Other:
|
||
XXXX[Legend Lore] - Lots of lore. Never cast.
|
||
X [Shades] - Offense and defense spells. NPC's can cast these right now. Cast same time as normal versions
|
||
X [Stone to Flesh] - Un-petrifies a target.
|
||
X [Tenser's Transformation] - Uses polymorph, so cast after spells. Massive HP boost and BAB boost.
|
||
X [True Seeing] - See invisible creatures/hidden ones (thats a bug) meant to stop illusions. Cast in special cases.
|
||
X [Heal] - Heals all damage/harms undead.
|
||
X [Healing Circle] - Heals damage as critcal wounds in an AOE.
|
||
|
||
Note that there are some monster abilities - the howl attacks, here.
|
||
Sorta like pulses, 80% chance to check them, that sort of thing :-D
|
||
|
||
We also cast visage protections here. Ghoslty, no, not until level 3 spells.
|
||
|
||
We also cast, as a starter, elemental protections :-)
|
||
|
||
We check if we can cast Golem Ranged Slam here too.
|
||
|
||
Trying to do dragon diciple breath too! :-)
|
||
//::66666666666666666666666666666666666666666666666666666666666666666666666666*/
|
||
|
||
// Jump out if we don't want to cast level 6 spells.
|
||
if(iLowestSpellLevel > i6) return FALSE;
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Best elemental protection (maybe % chance here...and later do it 100%)
|
||
if(AI_SpellWrapperElementalProtections(iLowestSpellLevel)) return TRUE;
|
||
|
||
// Visage - eathereal :-)
|
||
// Ethereal Visage. Level 6 (Mage) 25% consealment. 20/+3 DR. 0/1/2 spells immune to.
|
||
if(AI_SpellWrapperVisageProtections(i6)) return TRUE;
|
||
|
||
// Globe of Invunrability. We cast minor globe if we have under 10HD (Else we'll try
|
||
// it lower down anyway)
|
||
if(GlobalOurHitDice <= i10)
|
||
{
|
||
if(AI_SpellWrapperGlobeProtections(iLowestSpellLevel)) return TRUE;
|
||
}
|
||
else
|
||
{
|
||
if(AI_SpellWrapperGlobeProtections(i6)) return TRUE;
|
||
}
|
||
|
||
// Cast Greater Stoneskin if not got the specific greater stoneskin spell
|
||
// because we want to prepare for premonition going down, and we must have
|
||
// checked all level 7, 8 and 9 spells anyway
|
||
if(!GetHasSpellEffect(SPELL_GREATER_STONESKIN))
|
||
{
|
||
// Then, greater stoneskin protects a lot of damage -
|
||
// Greater Stoneskin. Level 6 (Mage) 7 (druid) 20/+5 phiscial damage reduction
|
||
if(AI_ActionCastSpell(SPELL_GREATER_STONESKIN, SpellProSelf, OBJECT_SELF, i16, FALSE, ItemProSelf, PotionPro)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Dispel level 4 protections (Hastes, regenerates, tensors...)
|
||
if(RangeMediumValid && !GlobalInTimeStop)// All medium spells.
|
||
{
|
||
// Dispel number need to be 4 for breach
|
||
if(GlobalDispelTargetHighestBreach >= i4)
|
||
{
|
||
// Wrapers Greater and Lesser Breach.
|
||
if(AI_ActionCastBreach()) return TRUE;
|
||
}
|
||
// Dispel >= 4
|
||
if(GlobalDispelTargetHighestDispel >= i4)
|
||
{
|
||
// Wrappers the dispel spells
|
||
if(AI_ActionCastDispel()) return TRUE;
|
||
}
|
||
}
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Cast more buff spells
|
||
// Buffing spells (energy buffer and downwards)
|
||
// 40% chance.
|
||
if(AI_ActionCastAllyBuffSpell(f6, i40, SPELL_ENERGY_BUFFER, SPELL_PROTECTION_FROM_ELEMENTS, SPELL_RESIST_ELEMENTS, SPELL_ENDURE_ELEMENTS)) return TRUE;
|
||
}
|
||
|
||
// Dirge.
|
||
// - Cast always, basically. Boring :-) but its an OK spell
|
||
if(IsFirstRunThrough && !GetHasSpellEffect(SPELL_DIRGE))
|
||
{
|
||
// Dirge. Level 6 (Bard). Continual Strength Damage to those in the AOE (Mobile, on self)
|
||
if(AI_ActionCastSpell(SPELL_DIRGE, SpellHostAreaInd, GlobalSpellTarget, i15, FALSE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
|
||
// Golem ranged slam. 80% chance of using.
|
||
if(RangeLongValid && GlobalSeenSpell && d10() <= i8)
|
||
{
|
||
// Golem Ranged Slam. Long ranged, Random(30) + 30 Blud damage. Can do knockdown too.
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_GOLEM_RANGED_SLAM, SpellHostRanged, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// Dragon Diciple breath.
|
||
if(RangeTouchValid &&
|
||
// Reflex save. 19 + 1 per 4 levels after 10.
|
||
GlobalSpellTargetReflex < (i19 + (GetLevelByClass(CLASS_TYPE_DRAGONDISCIPLE) - i10)/i4))
|
||
{
|
||
// Dragon diciple breath - x2_s2_descbreath
|
||
if(AI_ActionUseFeatOnObject(FEAT_DRAGON_DIS_BREATH, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// Howl! HOOOOOOOOWWWWWWWWWWWLLLLLLLLL! Collosal range on self.
|
||
// Most are decent enough to cast as level 6 spells, centred on self, 80% chance to cast.
|
||
// We also randomly choose one (and always cast one if we can cast one :-) )
|
||
if(RangeTouchValid && GlobalSeenSpell && d10() <= i8)
|
||
{
|
||
// We cast all of these, but randomly. It works through with most powerful
|
||
// getting the highest %'s of course :-)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDeath))
|
||
{
|
||
// 50% chance of death howl.
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_DEATH, SpellHostAreaInd, i40)) return TRUE;
|
||
}
|
||
// Sonic damage is powerful - 40%
|
||
// Fortitude save or sonic damage d6(HD/4) :-)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_SONIC, SpellHostAreaInd, i30)) return TRUE;
|
||
|
||
// Can't be immune to mind for most of the rest
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityMind))
|
||
{
|
||
// Mind blast
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_MINDFLAYER_MINDBLAST_10, SpellHostAreaInd, i20)) return TRUE;
|
||
// Other one
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_MINDFLAYER_PARAGON_MINDBLAST, SpellHostAreaInd, i20)) return TRUE;
|
||
// Other one 2
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_PSIONIC_MASS_CONCUSSION, SpellHostAreaInd, i20)) return TRUE;
|
||
|
||
// Fear howl. 40% chance
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityFear))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_FEAR, SpellHostAreaInd, i20)) return TRUE;
|
||
}
|
||
// Paralisis and daze (mind effects, and stunning ones)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityStun))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_PARALYSIS, SpellHostAreaInd, i20)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_DAZE, SpellHostAreaInd, i20)) return TRUE;
|
||
}
|
||
}
|
||
// Doom last. -X's amounts of stats. Also note, don't cast if already
|
||
// affected :-)
|
||
if(!GetHasSpellEffect(SPELLABILITY_HOWL_DOOM))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_DOOM, SpellHostAreaInd, i20)) return TRUE;
|
||
}
|
||
|
||
// Harpy song.
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_HARPYSONG, SpellHostAreaInd, i20)) return TRUE;
|
||
|
||
// Finally, if we got the 80% chance of using one, and none of them
|
||
// random cast, backup with this.
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// Now, randomish attack spells we possess.
|
||
// AOE:
|
||
// [Acid Fog] - Acid damage in an AOE, including slow in AOE. 1d6/round in fog.
|
||
// [Chain Lightning] - Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage.
|
||
// [Isaac's Greater Missile Storm] - !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save!
|
||
// [Blade Barrier] - Lots of spikes = Lots of damage. Piercing, relfex saves.
|
||
|
||
// Single:
|
||
// [Bigby's Forceful Hand] - Bullrush to make the target Knockdowned and Dazed all in one.
|
||
// [Flesh to Stone] - Petrifies a target on a fortitude save.
|
||
// [Harm] - 1d4 HP left on target, if touch attacked.
|
||
// [Drown] Up to 90% of current HP damage done. Fort save for none.
|
||
// [Evil Blight] - All in a collosal area have a curse (-3 stats) on them. Will save.
|
||
|
||
// Is it best to target with single-target spells first? Flesh to stone
|
||
// alone makes single target level 6 spells decent enough :-)
|
||
// 60-70% if favourable.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i6)
|
||
{
|
||
// Drown! We don't do this if they are under 30HP already.
|
||
// 80% chance to cast if we have it, and not immune to the save.
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_DROWN) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i6) &&
|
||
GlobalSpellTargetCurrentHitPoints >= i30)
|
||
{
|
||
// Drown. level 6 (Druid) Up to 90% of current HP damage done. Fort save for none.
|
||
if(AI_ActionCastSpellRandom(SPELL_DROWN, SpellHostRanged, i70, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Flesh to Stone is a good spell - petrify attack.
|
||
// 70% chance to cast if we have it, and not immune to the save.
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_FLESH_TO_STONE) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityPetrify) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i6))
|
||
{
|
||
// Flesh to Stone. - Petrifies a target on a fortitude save.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLESH_TO_STONE, SpellHostRanged, i60, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Evil Blight. This is an AOE curse, but note that we cannot check
|
||
// if an AOE already has it.
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(AI_SPELL_EVIL_BLIGHT) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityCurse) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i6))
|
||
{
|
||
// Evil Blight. Level 6 (Mage) -3 Curse to all in AOE (Will save)
|
||
if(AI_ActionCastSpellRandom(AI_SPELL_EVIL_BLIGHT, SpellHostTouch, i60, GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Bigby's Forceful hand. No need to check for bullrush attack. It
|
||
// will knockdown and daze a target :-) Quite powerful as no save.
|
||
// (Count as stun for immunity - that is anyting that stops them moving (daze included))
|
||
// - Should affect mind-immune people. Bioware will fix this, been told. No mind check
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_FORCEFUL_HAND, GlobalSpellTarget) &&
|
||
!AI_CompareTimeStopStored(SPELL_BIGBYS_FORCEFUL_HAND) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun))
|
||
{
|
||
// Bigby's Forceful Hand. Level 6 (Mage) Bullrush to make the target Knockdowned and Dazed all in one.
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_FORCEFUL_HAND, SpellHostRanged, i30, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
// AOE spells - including the now-infamous Isaac's Greater Missile Storm!
|
||
// [Acid Fog] - Acid damage in an AOE, including slow in AOE. 1d6/round in fog.
|
||
// [Chain Lightning] - Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage.
|
||
// [Isaac's Greater Missile Storm] - !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save!
|
||
// [Blade Barrier] - Lots of spikes = Lots of damage. Piercing, relfex saves.
|
||
|
||
// Isaac's Greater Missile Storm - only hits enemies, targeted on an area
|
||
// around the caster, lots of missiles which do 2d6 MAGICAL energy each!!!
|
||
// Ok, this is sad, but because it goes well targeting many, or one target,
|
||
// we will target this at the spell target regardless of range (which is
|
||
// long anyway!) or better (more in AOE) targets. 80% chance of casting.
|
||
// Cast at ground.
|
||
|
||
if(RangeLongValid && GlobalNormalSpellsNoEffectLevel < i6)
|
||
{
|
||
// Isaac's Greater Missile Storm. Level 6 (Mage) !!! To 20 missiles, 2d6 Damage/Missile, hits only enemies in AOE around target. No save!
|
||
if(AI_ActionCastSpellRandom(SPELL_ISAACS_GREATER_MISSILE_STORM, SpellHostRanged, i70, GlobalSpellTarget, i16, TRUE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
// Chain lightning is a decent-damage, no, good-damage spell, like fireball,
|
||
// but a better way - it damages all enemies only :-D
|
||
// Because it damages enemies only, we aim it at the current spell target,
|
||
// because it needs a target.
|
||
if(RangeLongValid && !AI_CompareTimeStopStored(SPELL_CHAIN_LIGHTNING) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i6))
|
||
{
|
||
// Collosal area, long range, reflex throw. 60% chance of casting.
|
||
// Chain Lightning. Level 6 (Mage). Target enemies only. Up to 20d6 for primary, 10d6 for secondary in reflex electical damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_CHAIN_LIGHTNING, SpellHostAreaDis, i50, GlobalSpellTarget, i16, FALSE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
// Blade Barrier - lots of damage, up to 20d6 damage to targets :-)
|
||
// Wierd shape, however. It is 2M across one way, but 10M long, retangle.
|
||
// We just target a large (5.0) area, and as long as it hits an enemy object,
|
||
// it is great!
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
!AI_CompareTimeStopStored(SPELL_BLADE_BARRIER) &&
|
||
(GetHasSpell(SPELL_BLADE_BARRIER) || SpellHostAreaInd == SPELL_BLADE_BARRIER))
|
||
{
|
||
// 20M medium range, large area we'll say. Reflex save
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i6, SAVING_THROW_REFLEX);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Blade Barrier. Level 6 (Cleric) Lots of spikes = Lots of damage (to 20d6). Piercing, relfex saves.
|
||
if(AI_ActionCastSpellRandom(SPELL_BLADE_BARRIER, SpellHostAreaInd, i40, oAOE, i16, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Level 6 summons
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i6 &&
|
||
(GlobalOurHitDice <= i16 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Summon Monster VI (6). Level 6 (Most classes) Dire Tiger
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VI, i16, i6)) return TRUE;
|
||
// Planar Binding. Level 6 (Mage). Summons Subbucus (Evil), Hound Arcon (Good), Green Slaad (Neutral). AI won't target outsiders specifically.
|
||
if(AI_ActionCastSummonSpell(SPELL_PLANAR_BINDING, i16, i6)) return TRUE;
|
||
// Create Undead. Level 6 (Cleric) 8 (Mage). Creates a lowish undead to aid the caster.
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_VI, i16, i6)) return TRUE;
|
||
// Planar Ally. Level 6 (Cleric) - Waoh! Same as planar binding! Exactly! :-) (except no stopping outsiders)
|
||
if(AI_ActionCastSummonSpell(SPELL_PLANAR_ALLY, i16, i6)) return TRUE;
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Lastly, the single-target spells again :-)
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i6)
|
||
{
|
||
// Flesh to Stone is a good spell - petrify attack.
|
||
// 50% chance to cast if we have it, and not immune to the save.
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_FLESH_TO_STONE) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityPetrify) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i6))
|
||
{
|
||
// Flesh to Stone. - Petrifies a target on a fortitude save.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLESH_TO_STONE, SpellHostRanged, i40, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Evil Blight. This is an AOE curse, but note that we cannot check
|
||
// if an AOE already has it.
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(AI_SPELL_EVIL_BLIGHT) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityCurse) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i6))
|
||
{
|
||
// Evil Blight. Level 6 (Mage) -3 Curse to all in AOE (Will save)
|
||
if(AI_ActionCastSpellRandom(AI_SPELL_EVIL_BLIGHT, SpellHostTouch, i30, GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Bigby's Forceful hand. No need to check for bullrush attack. It
|
||
// will knockdown and daze a target :-) Quite powerful as no save.
|
||
// (Count as stun for immunity - that is anyting that stops them moving (daze included))
|
||
// - Should affect mind-immune people. Bioware will fix this, been told. No mind check
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_BIGBYS_FORCEFUL_HAND, GlobalSpellTarget) &&
|
||
!AI_CompareTimeStopStored(SPELL_BIGBYS_FORCEFUL_HAND) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun))
|
||
{
|
||
// Bigby's Forceful Hand. Level 6 (Mage) Bullrush to make the target Knockdowned and Dazed all in one.
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_FORCEFUL_HAND, SpellHostRanged, i20, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// % casting recast again, before next set
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Acid fog - slow, and damage in acid fog.
|
||
// Decent..but ...well, its alright :-)
|
||
// THIS is spell 0 spell :-) we cast 100% and no backup casting.
|
||
if(RangeLongValid && SpellHostAreaInd && !GlobalInTimeStop &&
|
||
(GetHasSpell(SPELL_ACID_FOG) || SpellHostAreaInd == SPELL_ACID_FOG))
|
||
{
|
||
// 40M spell range, 5M radius (large) fortitude save, but doesn't stop damage.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i6);
|
||
// Is it valid? 40% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Acid Fog. Level 6 (Mage) Acid damage in an AOE, including slow in AOE. 1d6/round in fog.
|
||
if(AI_ActionCastSpell(SPELL_ACID_FOG, SpellHostAreaInd, oAOE, i16, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
/*::5555555555555555555555555555555555555555555555555555555555555555555555555555
|
||
Level 5 Spells.
|
||
//::5555555555555555555555555555555555555555555555555555555555555555555555555555
|
||
Thoughts - A good variety of AOE spells, but lacking good single target spells
|
||
generally. Of course, some spells are cast at different levels :-P . Slay
|
||
living is probably the lowest Save-Or-Die spell, while the rest give decent
|
||
damage (AOE spells that is). Some specilist spells include Mind Fog and
|
||
Feeble Mind :-)
|
||
|
||
AOE:
|
||
[Cloudkill] - Acid damage, and kills level 7s or below. AOE.
|
||
[Cone of Cold] - Cone of damage, up to 15d6 to those in the cone. Reflex for none.
|
||
X [Dismissal] - Destroys summons in AOE against a will save + 6DC. Cast above
|
||
S [Firebrand] - Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage.
|
||
[Mind Fog] - Minus 10 to all will saves within the AOE.
|
||
[Circle of Doom] - 1d8 + 1/caster level in negative damage
|
||
[Flame Strike] - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area.
|
||
H [Ball Lightning] - 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms)
|
||
HXXX[Vine mine] - Entangle, 50% movement, or Camoflage. Note: Not cast ever! Stupid spell!
|
||
|
||
[Battletide] - +2 Save/Attack/Damage to cast. -2 to enemies who enter. Mobile AOE on self.
|
||
|
||
Single:
|
||
S [Bigby's Interposing Hand] -10 to hit for 1 target.
|
||
X [Dominate Person] - Dominates 1 humanoid person. Cast above
|
||
[Feeblemind] - 1d4/4 caster levels in intelligence decrease, if fail will save.
|
||
[Hold Monster] - Paralyze's an enemy monster (any race)
|
||
[Slay Living] - Touch attack + Fortitude Save, creature dies, else d6(3) negative damage.
|
||
H [Inferno] - 2d6 Fire damage/round like acid arrow. No save, only SR.
|
||
|
||
Defender:
|
||
[Energy Buffer] - 40/- elemental resistance to Cold, Acid, Fire, Sonic and electicity, to 60 damage total.
|
||
X [Lesser Spell Mantle] - 1d4 + 6 spells immune to. Cast above
|
||
[Spell Resistance] - 12 + Caster Level in spell resistance.
|
||
H X [Mestil's Acid Sheath] - 1d6 + 2/level in acid damage to attackers.
|
||
|
||
Summon:
|
||
[Summon Creature V (5)] - Dire Bear
|
||
[Lesser Planar Binding] - Imp (Evil), Slaad Red (Neutral), Lantern Archon (Good)
|
||
|
||
Other:
|
||
[Greater Shadow Conjuration] - Variety of spells, illision based.
|
||
[Lesser Mind Blank] - Protection VS mind spells and rids bad mind things.
|
||
[Raise Dead] - Raises a dead person :-)
|
||
[Awaken] - Helps animal companion greatly.
|
||
S [Owl's Insight] - + Half caster level in wisdom.
|
||
H [Monsterous Regeneration] - +3 Regeneration for CasterLevel/2 + 1.
|
||
|
||
We check level 3 dispels here.
|
||
|
||
Resistance to fire ETc (energy buffer, elemental resistances) are cast
|
||
in the level 6 set.
|
||
|
||
Cones breath things too here.
|
||
|
||
Giant Hurl Rocks are also here (and some other hurling things)
|
||
//::55555555555555555555555555555555555555555555555555555555555555555555555555*/
|
||
|
||
// Jump out if we don't want to cast level 5 spells.
|
||
if(iLowestSpellLevel > i5) return FALSE;
|
||
|
||
// Monsterous Regeneration if we have 70% or less HP
|
||
if(IsFirstRunThrough && GlobalOurPercentHP <= i70 &&
|
||
!GetHasSpellEffect(SPELL_MONSTROUS_REGENERATION))
|
||
{
|
||
// Monsterous Regeneration. Level 5 (Cleric/Druid) +3 Regeneration for CasterLevel/2 + 1.
|
||
if(AI_ActionCastSpell(SPELL_MONSTROUS_REGENERATION, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar)) return TRUE;
|
||
}
|
||
|
||
// Spell reistance - feel the vibes :-) Cast above also if mages around, or need spell protections.
|
||
if(IsFirstRunThrough && !AI_GetAIHaveSpellsEffect(GlobalHasSpellResistanceSpell))
|
||
{
|
||
// Spell Resistance. Level 5 (Druid/Cleric) 12 + Caster Level in spell resistance.
|
||
if(AI_ActionCastSpell(SPELL_SPELL_RESISTANCE, SpellProSinTar, OBJECT_SELF, i15, FALSE, ItemProSinTar)) return TRUE;
|
||
}
|
||
|
||
// Dispel level 3 protections (Hastes, regenerates, tensors...)
|
||
if(RangeMediumValid && !GlobalInTimeStop)// All medium spells.
|
||
{
|
||
// Dispel number need to be 3 for breach
|
||
if(GlobalDispelTargetHighestBreach >= i3)
|
||
{
|
||
// Wrapers Greater and Lesser Breach.
|
||
if(AI_ActionCastBreach()) return TRUE;
|
||
}
|
||
// Dispel >= 3
|
||
if(GlobalDispelTargetHighestDispel >= i3)
|
||
{
|
||
// Wrappers the dispel spells
|
||
if(AI_ActionCastDispel()) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Battletide is not bad. Level 5 (Cleric)
|
||
// - Oddly classed under SpellHostAreaDis
|
||
if(IsFirstRunThrough && !GetHasSpellEffect(SPELL_BATTLETIDE))
|
||
{
|
||
// Battletide. Level 5 (Cleric). -2 Attack/Saves/damage to enemies who come in. +2 to same for caster.
|
||
if(AI_ActionCastSpell(SPELL_BATTLETIDE, SpellHostAreaDis, GlobalSpellTarget, i15, FALSE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
|
||
// Giant Hurl Rock
|
||
if(RangeLongValid && GlobalSeenSpell)
|
||
{
|
||
// Giant Hurl Rock is for d6(HD/5) + Str. Damage. Huge AOE and bludgeoning damage.
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_GIANT_HURL_ROCK, SpellHostRanged, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
|
||
// Battle Boulder Toss - from Campaign, but is an ability too. d6(3)+5 Bud dam.
|
||
if(AI_ActionCastSpell(AI_SPELLABILITY_BATTLE_BOULDER_TOSS, SpellHostRanged, GlobalSpellTarget, FALSE, TRUE)) return TRUE;
|
||
}
|
||
|
||
// Monster cones - these ignore the GlobalNormalSpellsNoEffectLevel toggle.
|
||
if(RangeShortValid)
|
||
{
|
||
// Small-distance, cone-based spell.
|
||
// - Take it as no level, and no save. These scale up with the HD of the
|
||
// monster, so on average should be used all the time.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% for each.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Cones
|
||
// Uses the AOE object for cone of cold.
|
||
// These are the "Cones". Appropriate to put it here, don'tca think?
|
||
for(iCnt = SPELLABILITY_CONE_ACID; iCnt <= SPELLABILITY_CONE_SONIC; iCnt++)
|
||
{
|
||
if(AI_ActionCastSpellRandom(iCnt, SpellHostAreaInd, i40, oAOE, FALSE, TRUE)) return TRUE;
|
||
}
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_HELL_HOUND_FIREBREATH, SpellHostAreaInd, i40, oAOE, FALSE, TRUE)) return TRUE;
|
||
}
|
||
}
|
||
|
||
|
||
// Mind fog cast here, lower prioritory, if the spell target has high will.
|
||
// Long range.
|
||
if(RangeLongValid && (GlobalSpellTargetWill / i2 >= GlobalSpellAbilityModifier))
|
||
{
|
||
// Mind Fog. Level 5 (Mage/Bard) - Minus 10 to all will saves within the AOE.
|
||
if(AI_ActionCastSpell(SPELL_MIND_BLANK, SpellHostAreaInd, GlobalSpellTarget, i15, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
|
||
// Feeblemind is Good if in medium range, and is a mage :-)
|
||
if(RangeMediumValid && GlobalSeenSpell &&
|
||
GetLevelByClass(CLASS_TYPE_WIZARD, GlobalSpellTarget) >= GlobalOurHitDice/i4)
|
||
{
|
||
// Feeblemind. Level 5 (Mage) 1d4/4 caster levels in intelligence decrease, if fail will save.
|
||
if(AI_ActionCastSpell(SPELL_FEEBLEMIND, SpellHostRanged, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Randomise one of the many level 5 spells.
|
||
// AOE:
|
||
// [Cloudkill] - Acid damage, and kills level 7s or below. AOE.
|
||
// [Cone of Cold] - Cone of damage, up to 15d6 to those in the cone. Reflex for none.
|
||
// [Firebrand] - Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage.
|
||
// [Circle of Doom] - 1d8 + 1/caster level in negative damage
|
||
// [Flame Strike] - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area.
|
||
// [Ball Lightning] - 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms)
|
||
// Single:
|
||
// [Bigby's Interposing Hand] -10 to hit for 1 target.
|
||
// [Hold Monster] - Paralyze's an enemy monster (any race)
|
||
// [Slay Living] - Touch attack + Fortitude Save, creature dies, else d6(3) negative damage.
|
||
// [Inferno] - 2d6 Fire damage/round like acid arrow. No save, only SR.
|
||
|
||
// Is it best to target with single-target spells first? Some pretty
|
||
// good level 5 spells - Hold monster and Slay Living are useful :-)
|
||
// 60-70% if favourable.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i5)
|
||
{
|
||
// Slay living. 60% chance to cast - it is a touch spell.
|
||
if(RangeTouchValid && !AI_CompareTimeStopStored(SPELL_SLAY_LIVING) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i5))
|
||
{
|
||
// Slay Living. Level 5 (Cleric). Touch attack + Fortitude Save, creature dies, else d6(3) negative damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_SLAY_LIVING, SpellHostRanged, i50, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Hold monster - Paralyze's an enemy monster (any race)
|
||
// Decent enough, if not stunned already - 60%
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_HOLD_MONSTER) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i5))
|
||
{
|
||
// Hold monster - Paralyze's an enemy monster (any race)
|
||
if(AI_ActionCastSpellRandom(SPELL_HOLD_MONSTER, SpellHostRanged, i50, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Inferno - the Druids Acid Arrow (more damage, however, each round).
|
||
// ---- No save!!
|
||
if(RangeShortValid && !GetHasSpellEffect(SPELL_INFERNO))
|
||
{
|
||
// Inferno. Level 5 (Druid) 2d6 Fire damage/round like acid arrow. No save, only SR.
|
||
if(AI_ActionCastSpellRandom(SPELL_INFERNO, SpellHostRanged, i50, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Bigby's Interposing Hand. No save, only SR. -10 attack rolls for them
|
||
// is good if they have highish BAB. Check here.
|
||
// (Count as stun for immunity - that is anyting that stops them moving (daze included))
|
||
// - Should affect mind-immune people. Bioware will fix this, been told. No mind check
|
||
if(RangeLongValid && !AI_CompareTimeStopStored(SPELL_BIGBYS_INTERPOSING_HAND) &&
|
||
!GetHasSpellEffect(SPELL_BIGBYS_INTERPOSING_HAND, GlobalSpellTarget) &&
|
||
GetBaseAttackBonus(GlobalSpellTarget) >= GlobalOurHitDice/i2)
|
||
{
|
||
// Bigby's Interposing Hand. Level 5 (Mage) No save, only SR. -10 attack rolls for target
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_INTERPOSING_HAND, SpellHostRanged, i50, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
// AOE:
|
||
// [Cloudkill] - Acid damage, and kills level 7s or below. AOE.
|
||
// [Cone of Cold] - Cone of damage, up to 15d6 to those in the cone. Reflex for none.
|
||
// [Firebrand] - Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage.
|
||
// [Circle of Doom] - 1d8 + 1/caster level in negative damage
|
||
// [Flame Strike] - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area.
|
||
// [Ball Lightning] - 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms)
|
||
|
||
// Firebrand - good, because it hits only enemies (up to 15! thats plenty)
|
||
// and 1d6 damage each. Mainly, it doesn't hit allies :-D
|
||
if(SpellHostRanged && RangeMediumValid && !AI_CompareTimeStopStored(SPELL_FIREBRAND) &&
|
||
(GetHasSpell(SPELL_FIREBRAND) || ItemHostRanged == SPELL_FIREBRAND))
|
||
{
|
||
// 20M medium range, colossal area. Reflex save - doesn't hit allies too.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_COLOSSAL, i5, SAVING_THROW_REFLEX);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Firebrand Level 5 (Mage) Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_FIREBRAND, SpellHostRanged, i50, oAOE, i15, TRUE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
// Ball lightning - Medium spell, and missile storm. We cast this at the target.
|
||
if(RangeMediumValid && GlobalSeenSpell &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i5))
|
||
{
|
||
// Ball Lightning. Level 5 (Mage) 1d6/missile, to 15 missiles. Reflex based. Cast at singal target (like missile storms)
|
||
if(AI_ActionCastSpellRandom(SPELL_BALL_LIGHTNING, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
|
||
// (Shades) Cone of Cold. Shades = Never hits allies (Still same saves ETC).
|
||
if(SpellHostAreaInd && RangeShortValid &&
|
||
(GetHasSpell(SPELL_SHADES_CONE_OF_COLD) || ItemHostAreaInd == SPELL_SHADES_CONE_OF_COLD ||
|
||
GetHasSpell(SPELL_CONE_OF_COLD) || ItemHostAreaInd == SPELL_CONE_OF_COLD))
|
||
{
|
||
// Small-distance, cone-based spell. Reflex save
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, i5, SAVING_THROW_REFLEX, SHAPE_SPELLCONE);
|
||
// Is it valid? 100% chance of casting (Can't be bothered to create a special random version of SubSpell)
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Cone of Cold. Level 5 (Mage). Cone of damage, up to 15d6 to those in the cone. Reflex for none.
|
||
// Shades vesion is great though - never affects allies.
|
||
if(AI_ActionCastSubSpell(SPELL_SHADES_CONE_OF_COLD, SpellHostAreaInd, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE;
|
||
|
||
// Cone of Cold. Level 5 (Mage). Cone of damage, up to 15d6 to those in the cone. Reflex for none.
|
||
if(AI_ActionCastSpellRandom(SPELL_CONE_OF_COLD, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Flame Strike - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area.
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_FLAME_STRIKE) || ItemHostAreaInd == SPELL_FLAME_STRIKE))
|
||
{
|
||
// Small-distance, cone-based spell. Reflex save
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Flame Strike. Level 5 (Cleric) Up to 15d6 in Fire + Divine damage. Reflex based. Medium area.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLAME_STRIKE, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Circle of Doom - 1d8 + 1/caster level in negative damage
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_CIRCLE_OF_DOOM) || ItemHostAreaDis == SPELL_CIRCLE_OF_DOOM))
|
||
{
|
||
// Shpere, medium and reflex save.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Circle of Doom. Level 5 (Cleric) 1d8 + 1/caster level in negative damage
|
||
if(AI_ActionCastSpellRandom(SPELL_CIRCLE_OF_DOOM, SpellHostAreaDis, i40, oAOE, i15, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Cloudkill. Acid damage, and kills level 7s or below. AOE. Quite good persistant damage.
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_CLOUDKILL) || ItemHostAreaInd == SPELL_CLOUDKILL))
|
||
{
|
||
// No save (fortitude only halfs damage) but large (5M across) AOE - and long range
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_LARGE, i5, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Cloudkill. Level 5 (Mage) Acid damage, and kills level 7s or below. AOE. Quite good persistant damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_CLOUDKILL, SpellHostAreaInd, i40, oAOE, i15, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Level 5 summons
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i5 &&
|
||
(GlobalOurHitDice <= i14 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Summon Monster V (5). Level 5 (Most classes) Dire Bear
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_V, i15, i5)) return TRUE;
|
||
// Lesser Planar Binding. Level 5 (Mage). Summons Imp (Evil), Slaad Red (Neutral), Lantern Archon (Good)
|
||
if(AI_ActionCastSummonSpell(SPELL_LESSER_PLANAR_BINDING, i15, i5)) return TRUE;
|
||
}
|
||
|
||
// Single spells again.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i5)
|
||
{
|
||
// Slay living. 60% chance to cast - it is a touch spell.
|
||
if(RangeTouchValid && !AI_CompareTimeStopStored(SPELL_SLAY_LIVING) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i5))
|
||
{
|
||
// Slay Living. Level 5 (Cleric). Touch attack + Fortitude Save, creature dies, else d6(3) negative damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_SLAY_LIVING, SpellHostRanged, i30, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Hold monster - Paralyze's an enemy monster (any race)
|
||
// Decent enough, if not stunned already - 60%
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_HOLD_MONSTER) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i5))
|
||
{
|
||
// Hold monster - Paralyze's an enemy monster (any race)
|
||
if(AI_ActionCastSpellRandom(SPELL_HOLD_MONSTER, SpellHostRanged, i20, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Inferno - the Druids Acid Arrow (more damage, however, each round).
|
||
// ---- No save!!
|
||
if(RangeShortValid)
|
||
{
|
||
// Inferno. Level 5 (Druid) 2d6 Fire damage/round like acid arrow. No save, only SR.
|
||
if(AI_ActionCastSpellRandom(SPELL_INFERNO, SpellHostRanged, i20, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Bigby's Interposing Hand. No save, only SR. -10 attack rolls for them
|
||
// is good if they have highish BAB. Check here.
|
||
// (Count as stun for immunity - that is anyting that stops them moving (daze included))
|
||
// - Should affect mind-immune people. Bioware will fix this, been told. No mind check
|
||
if(RangeLongValid && !AI_CompareTimeStopStored(SPELL_BIGBYS_INTERPOSING_HAND) &&
|
||
!GetHasSpellEffect(SPELL_BIGBYS_INTERPOSING_HAND, GlobalSpellTarget) &&
|
||
GetBaseAttackBonus(GlobalSpellTarget) >= GlobalOurHitDice/i2)
|
||
{
|
||
// Bigby's Interposing Hand. Level 5 (Mage) No save, only SR. -10 attack rolls for target
|
||
if(AI_ActionCastSpellRandom(SPELL_BIGBYS_INTERPOSING_HAND, SpellHostRanged, i20, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Pass random spell not cast, but have, here.
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
/*::4444444444444444444444444444444444444444444444444444444444444444444444444444
|
||
Level 4 Spells.
|
||
//::4444444444444444444444444444444444444444444444444444444444444444444444444444
|
||
Thoughts -Quite a few decent enough spells here. Nothing to catch your eye,
|
||
except maybe the overused Ice Storm or Wall of Fire. hammer of the gods
|
||
is a good clerical AOE spell and phantasmal killer is probably the first
|
||
Instant-death spells (but requires 2 saves1).
|
||
|
||
AOE:
|
||
[Confusion] - confuse people Vs Mind Will in an area.
|
||
[Evard's Black Tentacles] - Rolls VS
|
||
[Fear] - Save VS mind will or fear, in an area.
|
||
[Ice Storm] - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save!
|
||
S [Isaac's Lesser Missile Storm] - 1d6 per each 1-10 missile divided around enemies in AOE.
|
||
[Wall of Fire] - 4d6 fire reflex damage in a retangle persistant AOE
|
||
[Hammer of the Gods] - divine damage d8(half caster level) to 5. Can Daze. Will save.
|
||
[War Cry] Will save or fear (like a howl) and all allies get +2 attack/damage
|
||
|
||
Single:
|
||
[Bestow Curse] - Curse = -2 in all stats against a fortitude save.
|
||
[Charm Monster] - Charm a single monster (any race) VS will and mind
|
||
[Contagion] - Disease (Static DC!) if fail fortitude save
|
||
[Enervation] - 1d4 negative levels, fortitude save
|
||
[Phantasmal Killer] Will save (Illusion) then fort save, or death.
|
||
S [Inflict Critical Wounds] - 3d8 + 1/caster level to +20, in negative energy.
|
||
[Poison] - Inflicts poison (sadly, static fortitude save)
|
||
|
||
X [Lesser Spell Breach] - Breach an amount of spell protections and lowers SR.
|
||
|
||
Defender:
|
||
[Elemental Shield] +50% Cold/Fire resistance. 1d6 + Caster level reflected damage to melee attackers.
|
||
X [Improved Invisibility] +50% consealment, invisiblity (unseen) until hostile action. Cast above to at least conseal
|
||
[Minor Globe of Invulnerability] - 0/1/2/3 level hostile spells immune to.
|
||
[Stoneskin] - 10/+5 DR. We cast this here, sorceror behaviour and backup for greater stoneskin
|
||
[Death Ward] - Death Immunity - death magic.
|
||
|
||
Summon:
|
||
[Summon Creature IV (4)] Summons a Dire Spider
|
||
|
||
Other:
|
||
X [Polymorph Self] - Troll, umber Hulk, Pixie, Zombie, Giant spider. Cast after spells to fight.
|
||
X [Shadow Conjuration] - Shadow spells.
|
||
[Cure Critical Wounds] - Cures wounds
|
||
X [Divine Power] + Temp HP, +BAB, +Strength to 18. Done before melee
|
||
[Freedom of Movement] - Slowing removed. Immunity to slowing effects and stuff.
|
||
[Neutralize Poison] - Rids poison
|
||
[Restoration] - Restores lost statistics
|
||
S [Mass Camouflage] +10 Hide
|
||
H X [Holy Sword] - Paladins sword gains the "Holy Avenger" property - +1d6 divine, +5 enchant, 25% dispel.
|
||
//::44444444444444444444444444444444444444444444444444444444444444444444444444*/
|
||
|
||
// Jump out if we don't want to cast level 4 spells.
|
||
if(iLowestSpellLevel > i4) return FALSE;
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Cast normal stoneskin to prepare for greater stoneskin going down.
|
||
// as par greater stoneskin, we must also have used level 5, 6, 7 8 and 9 spells
|
||
// anyway! That, and this is good for sorcerors.
|
||
if(!GetHasSpellEffect(SPELL_STONESKIN))
|
||
{
|
||
// Stoneskin. Level 4 (Mage) 5 (Druid) 10/+5 phisical damage reduction
|
||
if(AI_ActionCastSpell(SPELL_STONESKIN, SpellProSinTar, OBJECT_SELF, i14, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
|
||
// Check for Arcane Archer feats here
|
||
if(GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER))
|
||
{
|
||
// Arrow of death. DC20 or die.
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_ARROW_OF_DEATH, GlobalSpellTarget)) return TRUE;
|
||
// Fireball arrow - won't harm allies
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_IMBUE_ARROW, GlobalSpellTarget)) return TRUE;
|
||
// Seeker Arrow is cool
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_SEEKER_ARROW_2, GlobalSpellTarget)) return TRUE;
|
||
// Hail of arrows is neat
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_HAIL_OF_ARROWS, GlobalSpellTarget)) return TRUE;
|
||
// Seeker Arrow is cool
|
||
if(AI_ActionUseFeatOnObject(FEAT_PRESTIGE_SEEKER_ARROW_1, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// Elemental Shield is a good spell - reflected damage. It is normally
|
||
// cast above with more melee attackers. Here by backup. Shield-based spell
|
||
// (EffectDamageShield())
|
||
|
||
// Elemental Shield. Level 5 (Mage) +50% Cold/Fire resistance. 1d6 + Caster level reflected damage to melee attackers.
|
||
// (Use this but with imput i4)
|
||
if(AI_SpellWrapperShieldProtections(i4)) return TRUE;
|
||
|
||
// Minor globe backup casting.
|
||
if(AI_SpellWrapperGlobeProtections(iLowestSpellLevel)) return TRUE;
|
||
|
||
// Not bad, immunity to death/
|
||
// - Might add in opposing casting if they start casting death spells.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasDeathWardSpell))
|
||
{
|
||
// Death Ward. Level 4 (Cleric/Paladin) 5 (Druid). Immunity to death (Death-based spells nromally, like Wail).
|
||
if(AI_ActionCastSpell(SPELL_DEATH_WARD, SpellEnhSinTar, OBJECT_SELF, i14, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
|
||
// Some lower end ally buffs
|
||
|
||
// Spell Resistance.
|
||
if(AI_ActionCastAllyBuffSpell(f10, i60, SPELL_SPELL_RESISTANCE)) return TRUE;
|
||
|
||
if(GetBaseAttackBonus(GlobalSpellTarget) < GlobalSpellTargetHitDice - i2)
|
||
{
|
||
// Death Ward if we can see an enemy spellcaster.
|
||
if(AI_ActionCastAllyBuffSpell(f10, i60, SPELL_DEATH_WARD)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// BAB check
|
||
// - BAB checks check our BASE attack bonus, no modifiers. Basically, as
|
||
// normally even mages have a +X to attack, this provides a good indicator
|
||
// if we are going to easy, or very easily, hit the enemy.
|
||
// - Clerics, Druids and Bards must be able to hit even better then normal.
|
||
if(IsFirstRunThrough && !SRA &&
|
||
GlobalOurChosenClass != CLASS_TYPE_WIZARD &&
|
||
GlobalOurChosenClass != CLASS_TYPE_SORCERER &&
|
||
GlobalOurChosenClass != CLASS_TYPE_FEY)
|
||
{
|
||
// If a druid, cleric, and so on, have to be able to hit better then
|
||
// more then normal
|
||
if(GlobalOurChosenClass == CLASS_TYPE_CLERIC ||
|
||
GlobalOurChosenClass == CLASS_TYPE_DRUID ||
|
||
GlobalOurChosenClass == CLASS_TYPE_BARD)
|
||
{
|
||
// BAB check for level 4 spells.
|
||
if(GlobalOurBaseAttackBonus - i5 >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
// Demons, fighters, anything else really.
|
||
else
|
||
{
|
||
// BAB check for level 4 spells.
|
||
if(GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
}
|
||
|
||
// Hostile spells
|
||
// Single:
|
||
// [Enervation] - 1d4 negative levels, fortitude save
|
||
// [Phantasmal Killer] Will save (Illusion) then fort save, or death.
|
||
|
||
// [Poison] - Inflicts poison (sadly, static fortitude save)
|
||
// [Bestow Curse] - Curse = -2 in all stats against a fortitude save.
|
||
// [Charm Monster] - Charm a single monster (any race) VS will and mind
|
||
// [Contagion] - Disease (Static DC!) if fail fortitude save
|
||
|
||
// Is it best to target with single-target spells first?
|
||
// Inflict critical, as well as some others, are quite powerful for level 3.
|
||
// 60-70% if favourable.
|
||
// - We don't cast Bestow Curse, Charm Monster, Poison, or Contagion above
|
||
// AOE spells. We cast critical wounds only at the very end.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i4 &&
|
||
// All level 4, all fort saves
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i4))
|
||
{
|
||
// Phantismal Killer. Will and Fortitude save VS death. :-)
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_PHANTASMAL_KILLER) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i4))
|
||
{
|
||
// Phantasmal Killer. Level 4 (Mage). Will save (Illusion) then fort save, or death.
|
||
if(AI_ActionCastSpellRandom(SPELL_PHANTASMAL_KILLER, SpellHostRanged, i50, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Enervation. 1d4 negative levels. Short range
|
||
if(RangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Enervation. Level 4 (Mage). 1d4 negative levels, fortitude save
|
||
if(AI_ActionCastSpellRandom(SPELL_ENERVATION, SpellHostRanged, i40, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// AOE:
|
||
// [Confusion] - confuse people Vs Mind Will in an area.
|
||
// [Evard's Black Tentacles] - Rolls VS AC and damage 1-5 lots of 2d6 blud.
|
||
// [Fear] - Save VS mind will or fear, in an area.
|
||
// [Ice Storm] - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save!
|
||
// [Isaac's Lesser Missile Storm] - 1d6 per each 1-10 missile divided around enemies in AOE.
|
||
// [Wall of Fire] - 4d6 fire reflex damage in a retangle persistant AOE
|
||
// [Hammer of the Gods] - divine damage d8(half caster level) to 5. Can Daze. Will save.
|
||
// [War Cry] Will save or fear (like a howl) and all allies get +2 attack/damage
|
||
|
||
if(GlobalNormalSpellsNoEffectLevel < i4)
|
||
{
|
||
if(RangeLongValid)
|
||
{
|
||
// Lesser missile storm. 1d6/missile. 1-10 missiles basically. Enemies only!
|
||
// Just cast at the enemy.
|
||
// Isaac's Lesser Missile Storm. Level 4 (Mage) 1d6 per each 1-10 missile divided around enemies in AOE.
|
||
if(AI_ActionCastSpellRandom(SPELL_ISAACS_LESSER_MISSILE_STORM, SpellHostRanged, i70, GlobalSpellTarget, i14, TRUE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
// War cry here. No need to check if they run off - helps allies.
|
||
if(GlobalSpellTargetRange <= RADIUS_SIZE_COLOSSAL)
|
||
{
|
||
// War Cry. Level 4 (Bard) Will save or fear (like a howl) and all allies get +2 attack/damage
|
||
if(AI_ActionCastSpellRandom(SPELL_WAR_CRY, SpellHostAreaDis, i30, OBJECT_SELF, i14, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Ice storm. plenty of damage :-)
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_ICE_STORM) || ItemHostAreaInd == SPELL_ICE_STORM))
|
||
{
|
||
// Shpere, huge and no save :-) - long range too.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i4, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Ice Storm. Level 4 (Mage) 5 (Druid) 6 (Bard) - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save!
|
||
if(AI_ActionCastSpellRandom(SPELL_ICE_STORM, SpellHostAreaInd, i50, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Confusion - confusion! Decent especially against PC's.
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_CONFUSION) || ItemHostAreaDis == SPELL_CONFUSION))
|
||
{
|
||
// Shpere, huge and no save :-)
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireHostile);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Ice Storm. Level 4 (Mage) 5 (Druid) 6 (Bard) - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save!
|
||
if(AI_ActionCastSpellRandom(SPELL_CONFUSION, SpellHostAreaDis, i50, oAOE, i14, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Hammer of the Gods. Divine damage is good, as well as save VS daze.
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_HAMMER_OF_THE_GODS) || ItemHostAreaDis == SPELL_HAMMER_OF_THE_GODS))
|
||
{
|
||
// Shpere, no friends affected, we cast even if saves VS will. :-)
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i4);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Hammer of the Gods. Level 4 (Cleric). Divine damage d8(half caster level) to 5d8. Can Daze. Will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_HAMMER_OF_THE_GODS, SpellHostAreaDis, i40, oAOE, i14, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Fear - fear!
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_FEAR) || ItemHostAreaDis == SPELL_FEAR))
|
||
{
|
||
// Shpere, no friends affected, we cast even if saves VS will. :-)
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireHostile);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Fear. Level 4 (Mage) 3 (Bard). Save VS mind will or fear, in an area.
|
||
if(AI_ActionCastSpellRandom(SPELL_FEAR, SpellHostAreaDis, i40, oAOE, i14, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Wall of Fire.
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_WALL_OF_FIRE) || ItemHostAreaInd == SPELL_WALL_OF_FIRE) &&
|
||
(GetHasSpell(SPELL_SHADES_WALL_OF_FIRE) || ItemHostAreaInd == SPELL_SHADES_WALL_OF_FIRE))
|
||
{
|
||
// Ok, retangle. Take it as a medium sized sphere.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i4, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Shades version
|
||
if(AI_ActionCastSubSpell(SPELL_SHADES_WALL_OF_FIRE, SpellHostAreaInd, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE;
|
||
|
||
// Wall of Fire. Level 4 (Mage) 5 (Druid) 4d6 fire reflex damage in a retangle persistant AOE
|
||
if(AI_ActionCastSpellRandom(SPELL_WALL_OF_FIRE, SpellHostAreaInd, i40, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Evard's Black Tentacles. AC attack and damage (fort or paralysis on enter)
|
||
// Casts if average HD is under 10
|
||
if(SpellHostAreaInd && RangeMediumValid && GlobalAverageEnemyHD <= i10 &&
|
||
(GetHasSpell(SPELL_EVARDS_BLACK_TENTACLES) || ItemHostAreaInd == SPELL_EVARDS_BLACK_TENTACLES))
|
||
{
|
||
// 5M sized AOE spell, medium range.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i4, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 40% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Evard's Black Tentacles. Level 4 (Mage) AC attack and damage (2d6 x 1-5 hits) (fort or paralysis on enter)
|
||
if(AI_ActionCastSpellRandom(SPELL_EVARDS_BLACK_TENTACLES, SpellHostAreaInd, i30, oAOE, i14, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Level 4 summons
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i4 &&
|
||
(GlobalOurHitDice <= i12 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Summon Monster IV (4). Level 4 (Most classes) Summons a Dire Spider
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_IV, i14, i4)) return TRUE;
|
||
}
|
||
|
||
// [Enervation] - 1d4 negative levels, fortitude save
|
||
// [Phantasmal Killer] Will save (Illusion) then fort save, or death.
|
||
// [Inflict Critical Wounds] - 3d8 + 1/caster level to +20, in negative energy.
|
||
|
||
// [Poison] - Inflicts poison (sadly, static fortitude save)
|
||
// [Bestow Curse] - Curse = -2 in all stats against a fortitude save.
|
||
// [Charm Monster] - Charm a single monster (any race) VS will and mind
|
||
// [Contagion] - Disease (Static DC!) if fail fortitude save
|
||
|
||
// All single target spells, even poison and so on.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i4)
|
||
{
|
||
// All but charm are fortitude based.
|
||
if(!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i4))
|
||
{
|
||
// Phantismal Killer. Will and Fortitude save VS death. :-)
|
||
if(RangeMediumValid && !AI_CompareTimeStopStored(SPELL_PHANTASMAL_KILLER) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDeath) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i4))
|
||
{
|
||
// Phantasmal Killer. Level 4 (Mage). Will save (Illusion) then fort save, or death.
|
||
if(AI_ActionCastSpellRandom(SPELL_PHANTASMAL_KILLER, SpellHostRanged, i50, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Enervation. 1d4 negative levels. Short range
|
||
if(RangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Enervation. Level 4 (Mage). 1d4 negative levels, fortitude save
|
||
if(AI_ActionCastSpellRandom(SPELL_ENERVATION, SpellHostRanged, i40, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Contagion. Disease! Quite good, but still check spell save DC at fort above.
|
||
// Necromancy.
|
||
if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDisease))
|
||
{
|
||
// Blackguard Ability
|
||
if(AI_ActionUseFeatOnObject(FEAT_CONTAGION, GlobalMeleeTarget)) return TRUE;
|
||
|
||
// Contagion. Level 4 (Mage) 3 (Cleric/Bard). Random disease (Set DC's!) against a fortitude saving throw.
|
||
if(AI_ActionCastSpellRandom(SPELL_CONTAGION, SpellHostTouch, i40, GlobalSpellTarget, i14, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Poison - Inflicts poison (sadly, static fortitude save) Necro spell.
|
||
if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityPoison))
|
||
{
|
||
// Poison. Level 4 (Cleric) 3 (Druid) Large Scorion Venom poison (Set DC!) against a fortitude saving throw.
|
||
if(AI_ActionCastSpellRandom(SPELL_POISON, SpellHostRanged, i40, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Bestow Curse - Inflicts poison (sadly, static fortitude save) Necro spell.
|
||
if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityCurse))
|
||
{
|
||
// Bestow Curse. Level 4 (Mage) 3 (Cleric/Bard) -2 to all stats VS Fortitude save.
|
||
if(AI_ActionCastSpellRandom(SPELL_BESTOW_CURSE, SpellHostTouch, i40, GlobalSpellTarget, i14, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
}
|
||
// Charm Monster. Charms any monster. Will save, mind based.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) && RangeShortValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i4))
|
||
{
|
||
// Charm Monster. Level 4 (mage) 3 (Bard). Charm any monster, will save to resist.
|
||
if(AI_ActionCastSpellRandom(SPELL_CHARM_MONSTER, SpellHostRanged, i30, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Backup cast anything we didn't choose to before
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Critical wounds - damage at a touch attack.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i4 && RangeTouchValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Blackguard ability
|
||
if(AI_ActionUseFeatOnObject(FEAT_INFLICT_CRITICAL_WOUNDS, GlobalSpellTarget)) return TRUE;
|
||
|
||
// Inflict Critical Wounds. Level 4 (Cleric) 3d8 + 1/caster level to +20, in negative energy.
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_CRITICAL_WOUNDS, SpellHostTouch, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
/*::3333333333333333333333333333333333333333333333333333333333333333333333333333
|
||
Level 3 Spells.
|
||
//::3333333333333333333333333333333333333333333333333333333333333333333333333333
|
||
Thoughts - This is a level where mages pick up - IE fireball! Many decent AOE
|
||
and single-target spells, which scale up pretty well for mid and high level
|
||
casters. Also dispels level 2 protections before other things.
|
||
|
||
AOE:
|
||
[Fireball] - THE ONE SPELL TO RULE THEM ALL! - ok, basically the 1 D&D spell
|
||
which is most famous - and frequently the last word a mage says before it kills him!
|
||
up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...)
|
||
S [Gust of Wind] Knockdown enemies VS reflex. Main thing is Dispeling AOE's, and 1 is always kept in reserve.
|
||
[Lightning Bolt] An "oh, other spell" to fireball. Does a different damage type. can be more useful - different shape! Can't hit caster! 10d6 reflex electrical
|
||
[Negative Energy Burst] 1d8 + 1-20 (caster level) negative damage, and -1 STR/4 caster levels. Heals undead.
|
||
[Slow] -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies.
|
||
[Stinking Cloud] - Dazes those in the AOE, if fail VS will.
|
||
[Call Lightning] - Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball
|
||
S [Spike Growth] - Damage to those in the AOE, and slow for 24 Hours!
|
||
H [Mestals Acid Breath] - A cone of up to 10d6 (acid) damage. Reflex save.
|
||
H [Scintillating Sphere] - Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save)
|
||
H [Glyph of Warding] - AOE, if entered, does up to 1d6/2 caster levels (to 5d6) damage.
|
||
|
||
[Dispel Magic] - Dispels all magic (at a max of +10 check) or 1 from all in AOE
|
||
|
||
Single:
|
||
[Flame Arrow] 4d6 Reflex Fire Damage for each missile - 1/4 caster levels.
|
||
[Hold Person] Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save.
|
||
[Vampiric Touch] 1d6(caster level/2) in negative damage, heals us with temp HP.
|
||
[Dominate Animal] Dominates an animal only. Will save.
|
||
S [Quillfire] 1 quill at 1d8 + 1-5 damage. Scorpion poison too.
|
||
[Searing Light] Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead
|
||
S [Inflict Serious Wounds] - Touch attack, hit means 3d8 + 1-15 damage.
|
||
H [Healing Sting] - 1d6 + 1/Caster evel damage, and healed for that amount. Fort save for none.
|
||
H [Infestation of Maggots] - 1d4 Temp constitution damage/round. (1 round/caster level)
|
||
|
||
Defender:
|
||
S X [Displacement] - 50% consealment. Cast above to conseal ourselves sooner (50% missing is good! stoneskin lasts twice as long!)
|
||
[Magic Circle Against Alignment] +AC, +mind immunity etc. In a persistant AOE around caster.
|
||
X [Protection From Elements] 30/- elemental protection, until 40 damage.
|
||
[Negative Energy Protection] Immunity to negative energy
|
||
S [Wounding Whispers] 1d6 + Caster level in refelected sonic damage.
|
||
H [Magical Vestment] - Gives 1 suit of armor/shield a +1 AC bonus/3 caster levels (to +5)
|
||
|
||
Summon:
|
||
[Animate Dead] - Skeleton or zomie summon. Tough DR, long lasting, but hard to heal.
|
||
[Summon Creature III] - Summons a Dire Wolf.
|
||
|
||
Other:
|
||
X [Clairaudience/Clairvoyance] +20 spot.
|
||
[Clarity] - Mind resistance, clears hostile mind effects.
|
||
XXXX[Find Traps] - Finds and disarms traps. Never cast - unless I add it for PC traps.
|
||
X [Haste] +1 action. +4 AC. +150% speed. Good spell, cast right near top for maximum effect.
|
||
X [Invisibility Sphere] - Allies in area become invisible. Cast above (nearly first spell!) if want to go invis.
|
||
S [Greater Magic Fang] - Helps animal companion a lot :-)
|
||
X [Cure Serious Wounds] 3d8 + 1-15 damage healed.
|
||
X [Invisibility Purge] - AOE around us which removes Invisiblity effects. Cast as special
|
||
X [Prayer] +HP, +Attack +Damage for one person. Cast just before melee
|
||
X [Remove Blindness/Deafness] - Removes blindness and deafness!
|
||
X [Remove Curse] - " " curse
|
||
X [Remove Disease] - " " Disease
|
||
H X [Greater Magical Weapon] - Up to +5 enchantment for weapon (cast before melee)
|
||
H X [Keen Edge] - Keens a weapon.
|
||
H X [Blade Thirst] - +3 enchantment bonus to 1 slashing weapon.
|
||
H X [Darkfire] - +1d6 + 1/caster level (to +10) in fire damage applied to non-magic weapon.
|
||
|
||
Dispels all level 2 protections here.
|
||
|
||
Bolts cast here.
|
||
//::33333333333333333333333333333333333333333333333333333333333333333333333333*/
|
||
|
||
// Jump out if we don't want to cast level 3 spells.
|
||
if(iLowestSpellLevel > i3) return FALSE;
|
||
|
||
// Dispel level 2 protections
|
||
if(RangeMediumValid && !GlobalInTimeStop)// All medium spells.
|
||
{
|
||
// Dispel number need to be 2 for breach
|
||
if(GlobalDispelTargetHighestBreach >= i2)
|
||
{
|
||
// Wrapers Greater and Lesser Breach.
|
||
if(AI_ActionCastBreach()) return TRUE;
|
||
}
|
||
// Dispel >= 2
|
||
if(GlobalDispelTargetHighestDispel >= i2)
|
||
{
|
||
// Wrappers the dispel spells
|
||
if(AI_ActionCastDispel()) return TRUE;
|
||
}
|
||
}
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Greater magic fang
|
||
oAOE = GetAssociate(ASSOCIATE_TYPE_ANIMALCOMPANION);
|
||
if(GetIsObjectValid(oAOE) && !GetHasSpellEffect(SPELL_GREATER_MAGIC_FANG, oAOE))
|
||
{
|
||
// Greater MAgic fang. Level 3 (Ranger/Druid) +Attack, +DR to animal companion
|
||
if(AI_ActionCastSpell(SPELL_GREATER_MAGIC_FANG, SpellProSinTar, oAOE, i13, FALSE, ItemProSinTar)) return TRUE;
|
||
}
|
||
|
||
// Divine Shield - a feat, but a damn good one.
|
||
// Up to +5 Dodge AC.
|
||
if(GetHasFeat(FEAT_TURN_UNDEAD))
|
||
{
|
||
// Divine Shield
|
||
if(AI_ActionUseFeatOnObject(FEAT_DIVINE_SHIELD)) return TRUE;
|
||
}
|
||
|
||
// Magical vestment - this adds up to +5 AC to armor or shield!
|
||
// - Affects only our equipped armor.
|
||
oAOE = GetItemInSlot(INVENTORY_SLOT_CHEST);
|
||
// Makes sure it is valid
|
||
if(GetIsObjectValid(oAOE) && !GetHasSpellEffect(SPELL_MAGIC_VESTMENT))
|
||
{
|
||
// Cast it at the armor
|
||
// Magical Vestment. Level 3 (Cleric) Gives 1 suit of armor/shield a +1 AC bonus/3 caster levels (to +5)
|
||
if(AI_ActionCastSpell(SPELL_MAGIC_VESTMENT, SpellEnhSinTar, oAOE, i13, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
|
||
// Regenerations
|
||
if(AI_ActionCastAllyBuffSpell(f10, i50, SPELL_REGENERATE, SPELL_MONSTROUS_REGENERATION)) return TRUE;
|
||
|
||
// Bulls Strength, Cats Grace, Endurance
|
||
if(AI_ActionCastAllyBuffSpell(f10, i50, SPELL_ENDURANCE, SPELL_CATS_GRACE, SPELL_ENDURANCE, iM1, SPELL_GREATER_BULLS_STRENGTH, SPELL_GREATER_CATS_GRACE)) return TRUE;
|
||
}
|
||
|
||
// BAB check
|
||
// - BAB checks check our BASE attack bonus, no modifiers. Basically, as
|
||
// normally even mages have a +X to attack, this provides a good indicator
|
||
// if we are going to easy, or very easily, hit the enemy.
|
||
// - Clerics, Druids and Bards must be able to hit even better then normal.
|
||
if(IsFirstRunThrough && !SRA &&
|
||
GlobalOurChosenClass != CLASS_TYPE_WIZARD &&
|
||
GlobalOurChosenClass != CLASS_TYPE_SORCERER &&
|
||
GlobalOurChosenClass != CLASS_TYPE_FEY)
|
||
{
|
||
// If a druid, cleric, and so on, have to be able to hit better then
|
||
// more then normal
|
||
if(GlobalOurChosenClass == CLASS_TYPE_CLERIC ||
|
||
GlobalOurChosenClass == CLASS_TYPE_DRUID ||
|
||
GlobalOurChosenClass == CLASS_TYPE_BARD)
|
||
{
|
||
// BAB check for level 3 spells.
|
||
// Must be able to hit them 100% of the time.
|
||
if(GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
// Demons, fighters, anything else really.
|
||
else
|
||
{
|
||
// BAB check for level 3 spells.
|
||
// 75% chance of hitting them outright.
|
||
if(GlobalOurBaseAttackBonus + i5 >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
}
|
||
|
||
// Bolts 80% chance. Not too good, but oh well.
|
||
if(GlobalSeenSpell && RangeMediumValid && d10() <= i8)
|
||
{
|
||
// All ability Draining Bolts. All creature, so no limits.
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_DEXTERITY) >= i10)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_DEXTERITY, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_WISDOM) >= i12)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_WISDOM, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_CONSTITUTION) >= i12)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_CONSTITUTION, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i12)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_STRENGTH, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_CHARISMA) >= i14)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_CHARISMA, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_INTELLIGENCE) >= i14)
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ABILITY_DRAIN_INTELLIGENCE, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
// And the damaging bolts/status affecting bolts.
|
||
// I really can't be bothered to add in a lot of checks for immunities.
|
||
// Might do later.
|
||
for(iCnt = SPELLABILITY_BOLT_ACID; iCnt <= SPELLABILITY_BOLT_WEB; iCnt++)
|
||
{
|
||
if(AI_ActionCastSpellRandom(iCnt, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
// Manticore spikes
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_MANTICORE_SPIKES, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
// Shifter one
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_GWILDSHAPE_SPIKES, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
// Azer blast
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_AZER_FIRE_BLAST, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
// Shadow attack
|
||
if(!GetHasSpellEffect(AI_SPELLABILITY_SHADOW_ATTACK, GlobalSpellTarget))
|
||
{
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_SHADOW_ATTACK, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
// Petrify last.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify))
|
||
{
|
||
if(AI_ActionCastSpellRandom(SPELLABILITY_TOUCH_PETRIFY, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// Backup casting
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// Hostile spells - randomise a bit (remember, some are cast sooner still)
|
||
|
||
// Single:
|
||
// [Flame Arrow] 4d6 Reflex Fire Damage for each missile - 1/4 caster levels.
|
||
// [Hold Person] Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save.
|
||
// [Searing Light] Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead
|
||
// [Dominate Animal] Dominates an animal only. Will save.
|
||
// [Quillfire] 1 quill at 1d8 + 1-5 damage. Scorpion poison too.
|
||
// [Vampiric Touch] 1d6(caster level/2) in negative damage, heals us with temp HP.
|
||
// [Healing Sting] - 1d6 + 1/Caster evel damage, and healed for that amount. For save for none.
|
||
// [Infestation of Maggots] - 1d4 Temp constitution damage/round. (1 round/caster level)
|
||
|
||
// [Inflict Serious Wounds] - Touch attack, hit means 3d8 + 1-15 damage.
|
||
|
||
// Is it best to target with single-target spells first?
|
||
// Flame arrow, searing light and hold person are quite effective.
|
||
// 60-70% if favourable.
|
||
// - We don't cast inflict serious wounds here. GetHasSpell bodges with spontaeous spells.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i3)
|
||
{
|
||
// Flame arrow - not THE best spell, but well worth it. Also it is long range.
|
||
if(RangeLongValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i3))
|
||
{
|
||
// Flame Arrow. Level 3 (Mage) 4d6 Reflex Fire Damage for each missile - 1/4 caster levels.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLAME_ARROW, SpellHostRanged, i60, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// All others are in medium range or smaller
|
||
if(RangeMediumValid)
|
||
{
|
||
// Hold Person - must be playable race, of course. Still quite powerful - as it paralyzes.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3))
|
||
{
|
||
// Hold Person. Level 3 (Mage) 2 (Cleric/Bard) Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i40, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Searing light does good type of damage - divine, and is alright damage amount (up to 5d8) and more importantly, no save!
|
||
// Searing Light. Level 3 (Cleric). Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead
|
||
if(AI_ActionCastSpellRandom(SPELL_SEARING_LIGHT, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
// Dominate Animal. Must be an animal, duh! Dominates an animal only. Will save.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3))
|
||
{
|
||
// Dominate Animal. Level 3 (Druid) Dominates an animal only. Will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_DOMINATE_ANIMAL, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeShortValid)
|
||
{
|
||
// Quillfire. Poison isn't bad, and a little damage :-)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3))
|
||
{
|
||
// Quillfire. Level 3 (Mage) 1 quill at 1d8 + 1-5 damage. Scorpion poison too.
|
||
if(AI_ActionCastSpellRandom(SPELL_QUILLFIRE, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeTouchValid)
|
||
{
|
||
// These 3 are necromantic
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy))
|
||
{
|
||
// Infestation of maggots - lots of CON damage over time. :-)
|
||
if(!GetHasSpellEffect(SPELL_INFESTATION_OF_MAGGOTS, GlobalSpellTarget))
|
||
{
|
||
// Infestation of Maggots. Level 3 (Druid) 1d4 Temp constitution damage/round. (1 round/caster level)
|
||
if(AI_ActionCastSpellRandom(SPELL_INFESTATION_OF_MAGGOTS, SpellHostTouch, i40, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// These 2 are negative energy
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Healing sting isn't too bad. At least no immunities
|
||
// and levels up OK. Negative energy damage, however.
|
||
if(!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i3))
|
||
{
|
||
// Healing Sting. Level 3 (Druid). 1d6 + 1/Caster evel damage, and healed for that amount. Fort save for none.
|
||
if(AI_ActionCastSpellRandom(SPELL_HEALING_STING, SpellHostTouch, i40, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Vampiric touch - good spell and scales nicely, and
|
||
// heals :-D
|
||
// Vampiric Touch. Level 3 (Mage) 1d6(caster level/2) in negative damage, heals us with temp HP.
|
||
if(AI_ActionCastSpellRandom(SPELL_VAMPIRIC_TOUCH, SpellHostTouch, i30, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
// AOE spells
|
||
// [Fireball] - Up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...)
|
||
// [Call Lightning] - Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball
|
||
// [Lightning Bolt] An "oh, other spell" to fireball. Does a different damage type. can be more useful - different shape! Can't hit caster! 10d6 reflex electrical
|
||
// [Slow] -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies.
|
||
// [Negative Energy Burst] 1d8 + 1-20 (caster level) negative damage, and -1 STR/4 caster levels. Heals undead.
|
||
// [Stinking Cloud] - Dazes those in the AOE, if fail VS will.
|
||
// [Spike Growth] - Damage to those in the AOE, and slow for 24 Hours!
|
||
// [Gust of Wind] Knockdown enemies VS reflex. Main thing is Dispeling AOE's, and 1 is always kept in reserve.
|
||
// [Mestals Acid Breath] - A cone of up to 10d6 (acid) damage. Reflex save.
|
||
// [Scintillating Sphere] - Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save)
|
||
// [Glyph of Warding] - AOE, if entered, does up to 1d6/2 caster levels (to 5d6) damage.
|
||
|
||
// Fireball is fun fun fun! :-D Shadow version first - doesn't hit allies.
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_SHADES_FIREBALL) || ItemHostAreaInd == SPELL_SHADES_FIREBALL))
|
||
{
|
||
// Huge, long ranged, reflex based save. Shades == No enemies hit.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX);
|
||
// Is it valid? 100% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// (Shades) Fireball. Level 3 (Mage) Up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...)
|
||
if(AI_ActionCastSubSpell(SPELL_SHADES_FIREBALL, SpellHostAreaInd, oAOE, i17, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Real fireball is hot! (Ban pun there...)
|
||
// Scintillating Sphere is also fire-ball like, but electrical damage *shrugs*
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_FIREBALL) || ItemHostAreaInd == SPELL_FIREBALL ||
|
||
GetHasSpell(SPELL_SCINTILLATING_SPHERE) || ItemHostAreaInd == SPELL_SCINTILLATING_SPHERE))
|
||
{
|
||
// Huge, long ranged, reflex based save. Normal = reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Fireball. Level 3 (Mage) Up to 10d6 reflex fire damage, in a large AOE hit area. Allies are hit too! (as we all know...)
|
||
if(AI_ActionCastSpellRandom(SPELL_FIREBALL, SpellHostAreaInd, i60, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
|
||
// Scintillating Sphere. Level 3 (Mage) Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save)
|
||
if(AI_ActionCastSpellRandom(SPELL_SCINTILLATING_SPHERE, SpellHostAreaInd, i60, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Call Lightning - Never hits allies, so excelent to cast :-)
|
||
if(SpellHostAreaDis && RangeLongValid &&
|
||
(GetHasSpell(SPELL_CALL_LIGHTNING) || ItemHostAreaDis == SPELL_CALL_LIGHTNING))
|
||
{
|
||
// Huge, long ranged, reflex based save. No enemies hit!
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Call Lightning. Level 3 (Druid) Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball
|
||
if(AI_ActionCastSpellRandom(SPELL_CALL_LIGHTNING, SpellHostAreaDis, i60, oAOE, i13, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Mestils acid breath is on the standard 10d6 max damage. Acid + cone
|
||
if(SpellHostAreaDis && RangeShortValid &&
|
||
(GetHasSpell(SPELL_MESTILS_ACID_BREATH) || ItemHostAreaDis == SPELL_MESTILS_ACID_BREATH))
|
||
{
|
||
// Short, cone based.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, f11, i3, SAVING_THROW_REFLEX, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Mestals Acid Breath. Level 3 (Mage) A cone of up to 10d6 (acid) damage. Reflex save.
|
||
if(AI_ActionCastSpellRandom(SPELL_MESTILS_ACID_BREATH, SpellHostAreaInd, i50, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Lightning Bolt. Basically, a fireball in a line. :-)
|
||
// Requires a target object to hit.
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_LIGHTNING_BOLT) || ItemHostAreaInd == SPELL_LIGHTNING_BOLT))
|
||
{
|
||
// Requries an object. Hits allies. 30 spell cylinder range.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, f30, i3, SAVING_THROW_REFLEX, SHAPE_SPELLCYLINDER, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting - only if seen
|
||
if(GetIsObjectValid(oAOE) && GetObjectSeen(oAOE))
|
||
{
|
||
// Lightning Bolt. Level 3 (Mage) An "oh, other spell" to fireball. Does a different damage type. can be more useful - different shape! Can't hit caster! 10d6 reflex electrical
|
||
if(AI_ActionCastSpellRandom(SPELL_LIGHTNING_BOLT, SpellHostAreaInd, i40, oAOE, i13, FALSE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Slow is a good AOE - hits enemies, and slows them (Will save)
|
||
if(SpellHostAreaDis && RangeShortValid &&
|
||
(GetHasSpell(SPELL_SLOW) || ItemHostAreaDis == SPELL_SLOW))
|
||
{
|
||
// Slow - doesn't hit allies. Colossal range.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_COLOSSAL, i3, SAVING_THROW_WILL);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Slow. Level 3 (Mage/bard) -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies.
|
||
if(AI_ActionCastSpellRandom(SPELL_SLOW, SpellHostAreaDis, i40, oAOE, i13, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Negative Energy Burst. Negative damage (to non-undead) and strength loss.
|
||
if(SpellHostAreaDis && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_NEGATIVE_ENERGY_BURST) || ItemHostAreaDis == SPELL_NEGATIVE_ENERGY_BURST))
|
||
{
|
||
// Necromantic spell, hits allies.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i3, FALSE, SHAPE_SPHERE, GlobalFriendlyFireFriendly, FALSE, TRUE);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Negative Energy Burst. Level 3 (Mage) 1d8 + 1-20 (caster level) negative damage, and -1 STR/4 caster levels. Heals undead.
|
||
if(AI_ActionCastSpellRandom(SPELL_NEGATIVE_ENERGY_BURST, SpellHostAreaDis, i40, oAOE, i13, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Stinking Cloud. Daze, lots of daze (will based)
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_STINKING_CLOUD) || ItemHostAreaInd == SPELL_STINKING_CLOUD))
|
||
{
|
||
// Allies are hit, will save to be immune.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Stinking Cloud. Level 3 (Mage) Dazes those in the AOE, if fail VS will.
|
||
if(AI_ActionCastSpellRandom(SPELL_STINKING_CLOUD, SpellHostAreaInd, i40, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Spike Growth. Alright AOE - 30% speed decrease for 24HRS is the best bit.
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_SPIKE_GROWTH) || ItemHostAreaInd == SPELL_SPIKE_GROWTH))
|
||
{
|
||
// Allies are hit, will save to be immune.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Spike Growth. Level 3 (Druid) AOE - 30% speed decrease for 24HRS + 1d4 piercing damage/round.
|
||
if(AI_ActionCastSpellRandom(SPELL_SPIKE_GROWTH, SpellHostAreaInd, i40, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Gust of Wind. Always need 2 of these (1 to dispel AOE's)
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
GetHasSpell(SPELL_GUST_OF_WIND) >= i2)
|
||
{
|
||
// Allies are hit, will save to be immune.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 50% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Gust of Wind. Level 3 (Bard/Mage) Dispels all AOE's in radius, and save VS fort for 3 round knockdown.
|
||
if(AI_ActionCastSpellRandom(SPELL_GUST_OF_WIND, SpellHostAreaInd, i40, oAOE, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Glyph of Warding is cast last, lowest %, and just at spell target.
|
||
// This is because it is good for setting up traps, but not that good
|
||
// in combat, apart from the damage type. 30%
|
||
if(GlobalSeenSpell && RangeShortValid)
|
||
{
|
||
// Glyph of Warding. Level 3 (Mage) AOE, if entered, does up to 1d6/2 caster levels (to 5d6) damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_GLYPH_OF_WARDING, SpellHostAreaInd, i20, GlobalSpellTarget, i13, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Level 3 summons
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i3 &&
|
||
(GlobalOurHitDice <= i10 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Pale master
|
||
if(AI_ActionCastSummonSpell(AI_FEAT_PM_ANIMATE_DEAD, iM1, i3)) return TRUE;
|
||
|
||
// Animate Dead. Level 3 (Mage). Skeleton or zomie summon. Tough DR, long lasting, but hard to heal.
|
||
if(AI_ActionCastSummonSpell(SPELL_ANIMATE_DEAD, i13, i3)) return TRUE;
|
||
|
||
// Summon Monster III (3). Level 3 (Most classes) Summons a Dire Wolf.
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_III, i13, i3)) return TRUE;
|
||
}
|
||
|
||
// Single target spells.
|
||
// - We don't cast inflict serious wounds. GetHasSpell bodges with spontaeous spells.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i3)
|
||
{
|
||
// Flame arrow - not THE best spell, but well worth it. Also it is long range.
|
||
if(RangeLongValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i3))
|
||
{
|
||
// Flame Arrow. Level 3 (Mage) 4d6 Reflex Fire Damage for each missile - 1/4 caster levels.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLAME_ARROW, SpellHostRanged, i30, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// All others are in medium range or smaller
|
||
if(RangeMediumValid)
|
||
{
|
||
// Hold Person - must be playable race, of course. Still quite powerful - as it paralyzes.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3))
|
||
{
|
||
// Hold Person. Level 3 (Mage) 2 (Cleric/Bard) Paralyze's 1 humanoid target (Playable Race/humanoid), if they fail a will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i20, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Searing light does good type of damage - divine, and is alright damage amount (up to 5d8) and more importantly, no save!
|
||
// Searing Light. Level 3 (Cleric). Maxs of 10d6 to undead, 5d8 to others, 5d6 to constructs. Divine damage. Done above (way above) VS undead
|
||
if(AI_ActionCastSpellRandom(SPELL_SEARING_LIGHT, SpellHostRanged, i10, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
// Dominate Animal. Must be an animal, duh! Dominates an animal only. Will save.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3))
|
||
{
|
||
// Dominate Animal. Level 3 (Druid) Dominates an animal only. Will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_DOMINATE_ANIMAL, SpellHostRanged, i10, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeShortValid)
|
||
{
|
||
// Quillfire. Poison isn't bad, and a little damage :-)
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i3))
|
||
{
|
||
// Quillfire. Level 3 (Mage) 1 quill at 1d8 + 1-5 damage. Scorpion poison too.
|
||
if(AI_ActionCastSpellRandom(SPELL_QUILLFIRE, SpellHostRanged, i10, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeTouchValid)
|
||
{
|
||
// These 3 are necromantic
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy))
|
||
{
|
||
// Infestation of maggots - lots of CON damage over time. :-)
|
||
if(!GetHasSpellEffect(SPELL_INFESTATION_OF_MAGGOTS, GlobalSpellTarget))
|
||
{
|
||
// Infestation of Maggots. Level 3 (Druid) 1d4 Temp constitution damage/round. (1 round/caster level)
|
||
if(AI_ActionCastSpellRandom(SPELL_INFESTATION_OF_MAGGOTS, SpellHostTouch, i20, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// These 2 are negative energy
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Healing sting isn't too bad. At least no immunities
|
||
// and levels up OK. Negative energy damage, however.
|
||
if(!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i3))
|
||
{
|
||
// Healing Sting. Level 3 (Druid). 1d6 + 1/Caster evel damage, and healed for that amount. Fort save for none.
|
||
if(AI_ActionCastSpellRandom(SPELL_HEALING_STING, SpellHostTouch, i20, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Vampiric touch - good spell and scales nicely, and
|
||
// heals :-D
|
||
// Vampiric Touch. Level 3 (Mage) 1d6(caster level/2) in negative damage, heals us with temp HP.
|
||
if(AI_ActionCastSpellRandom(SPELL_VAMPIRIC_TOUCH, SpellHostTouch, i10, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Backup spell
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Serious wounds - damage at a touch attack.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i3 && RangeTouchValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Blackguard ability
|
||
if(AI_ActionUseFeatOnObject(FEAT_INFLICT_SERIOUS_WOUNDS, GlobalMeleeTarget)) return TRUE;
|
||
|
||
// Inflict Serious Wounds. Level 3 (Cleric) Touch attack, hit means 3d8 + 1-15 negative damage.
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_SERIOUS_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
/*::2222222222222222222222222222222222222222222222222222222222222222222222222222
|
||
Level 2 Spells.
|
||
//::2222222222222222222222222222222222222222222222222222222222222222222222222222
|
||
Thoughts - Lacking in AOE spells, as it is lower levels, it really
|
||
lacks a lot of spells anyway. A few decent damage spells, but many of the
|
||
spells are special-case healing, or cast elsewhere. Dispels all level 1
|
||
protections here.
|
||
|
||
AOE:
|
||
S [Balagarn's Iron Horn] Need to beat (Enemy STR + d20) with 20 + d20. If so, 6 second knockdown
|
||
[Web] - Reflex saves or entangles and stuck in the web. AOE, quite large, persistant.
|
||
[Sound Burst] - 1d8 sonic damage + Stun unless save VS will.
|
||
[Silence] - Silence AOE that moves with target. Applie silence to those in the AOE.
|
||
H [Cloud of Bewilderment] - Enemies only are stunned and blinded in the AOE. Fort save.
|
||
H [Gedlee's Electric Loop] - All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun.
|
||
|
||
[Lesser Dispel] - Can be cast in silence (Stomatic only). Dispel up to +5 check.
|
||
|
||
Single:
|
||
[Blindness/Deafness] - Blindness/deafness on Fort save failure.
|
||
[Ghoul Touch] - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby.
|
||
[Melf's Acid Arrow] - 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that
|
||
S [Tasha's Hideous Laughter] Knockdown for 1d3 rounds, if
|
||
[Charm Person or Animal] Charms an animal, or humanoid, if fail mind will save.
|
||
S [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save.
|
||
[Hold Animal] Paralyze's an amimal race target, if they fail a will save.
|
||
S [Inflict Moderate Wounds] 2d8 + 1-10 damage on a touch attack
|
||
H [Continual Flame] 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage.
|
||
|
||
Defender:
|
||
X [Darkness]- Darkness. Supposedly caster can see. Basically an area of blindness countered with ultravision
|
||
[Ghostly Visage] - 5/+1 DR. 0/1 spell immunity. 10% consealment.
|
||
X [Invisibility] - Invisbile (not inaudible) until a hostile action.
|
||
X [Resist Elements] - Resists 20/- elemental damage, until 30 damage is done. Cast above
|
||
[Barkskin] - 1-6 = +3, 7-12 = +4. 13+ = +5 natural armor AC bonus.
|
||
H [Death Armor] - 1d4 + 1/2 caster levels (to +5) damage shield (Magical damage!)
|
||
H [Stone Bones] - An undead target gets +3 natural AC.
|
||
|
||
Summon:
|
||
[Summon Creature II] - Summons a Dire Boar
|
||
|
||
Other:
|
||
X [Bull's Strength] +Strength stat. Cast before melee normally.
|
||
X [Cat's Grace] *bulls strength*
|
||
X [Continual Flame] - Light for ever.
|
||
X [Eagle's Splendor] *bulls strength*
|
||
X [Endurance] *bulls strength*
|
||
X [Fox's Cunning] *bulls strength*
|
||
X [Knock] - Unlocks doors. Done OnBlocked.
|
||
X [Owl's Wisdom] *bulls strength*
|
||
X [See Invisibility] Cast in special cases. Pierces Invisiblity.
|
||
X [Ultravision] Lets you see fully in Darkness AOE's.
|
||
S X [Blood Frenzy] Rage - +3STR, CON - 2 AC.
|
||
S X [One with the Land] +4 Hide, Move Silently, Set Traps and Animal Empthy. not cast in this AI
|
||
X [Aid] +1d8 HP bonus, +1 attack/damage.
|
||
X [Cure Moderate Wounds] Cures 2d8 Damage + 1-10.
|
||
X [Lesser Restoration] Removes some bad effects.
|
||
X [Remove Paralysis] Removes paralysis!
|
||
H X [Flame Weapon] - 1d4 + 1/caster level (to +10) fire damage to a weapon.
|
||
H X [Aura of Glory] - +4 Char, and allies get +4 VS Fear effects.
|
||
|
||
//::22222222222222222222222222222222222222222222222222222222222222222222222222*/
|
||
|
||
// Jump out if we don't want to cast level 2 spells.
|
||
if(iLowestSpellLevel > i2) return FALSE;
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Barkskin (if not already)
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasNaturalACSpell))
|
||
{
|
||
if(GlobalOurRace == RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Stone bones - cast if we do not have a natural armor AC spell.
|
||
if(AI_ActionCastSpell(SPELL_STONE_BONES, SpellProSinTar, OBJECT_SELF, i12, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
// Barkskin. Level 2 (Druid). 1-6 = +3, 7-12 = +4. 13+ = +5 natural armor AC bonus.
|
||
if(AI_ActionCastSpell(SPELL_BARKSKIN, SpellProSinTar, OBJECT_SELF, i12, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
// See AC notes in level 1 spells.
|
||
|
||
// Cast ally buff spells if we are a buffer
|
||
if(GlobalWeAreBuffer)
|
||
{
|
||
// Some AC protections
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_BARKSKIN, SPELL_MAGE_ARMOR)) return TRUE;
|
||
}
|
||
|
||
// All visage spells here.
|
||
if(AI_SpellWrapperVisageProtections(iLowestSpellLevel)) return TRUE;
|
||
|
||
// Eyeball rays. Low stuff, but hey, whatever, eh?
|
||
// Random cast these. 3 random ones.
|
||
if(RangeMediumValid)
|
||
{
|
||
// Random cast. Each one has 30-50 % chance. Doesn't matter which we use!
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_EYEBALL_RAY_0, SpellHostRanged, i40, GlobalSpellTarget)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_EYEBALL_RAY_1, SpellHostRanged, i40, GlobalSpellTarget)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_EYEBALL_RAY_2, SpellHostRanged, i40, GlobalSpellTarget)) return TRUE;
|
||
|
||
// Backup cast
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Dispel level 1 protections
|
||
if(RangeMediumValid && !GlobalInTimeStop)// All medium spells.
|
||
{
|
||
// Dispel number need to be 1 for breach
|
||
if(GlobalDispelTargetHighestBreach >= i1)
|
||
{
|
||
// Wrapers Greater and Lesser Breach.
|
||
if(AI_ActionCastBreach()) return TRUE;
|
||
}
|
||
// Dispel >= 1
|
||
if(GlobalDispelTargetHighestDispel >= i1)
|
||
{
|
||
// Wrappers the dispel spells
|
||
if(AI_ActionCastDispel()) return TRUE;
|
||
}
|
||
}
|
||
|
||
// BAB check
|
||
// - BAB checks check our BASE attack bonus, no modifiers. Basically, as
|
||
// normally even mages have a +X to attack, this provides a good indicator
|
||
// if we are going to easy, or very easily, hit the enemy.
|
||
// - Clerics, Druids and Bards must be able to hit even better then normal.
|
||
if(IsFirstRunThrough && !SRA &&
|
||
GlobalOurChosenClass != CLASS_TYPE_WIZARD &&
|
||
GlobalOurChosenClass != CLASS_TYPE_SORCERER &&
|
||
GlobalOurChosenClass != CLASS_TYPE_FEY)
|
||
{
|
||
// If a druid, cleric, and so on, have to be able to hit better then
|
||
// more then normal
|
||
if(GlobalOurChosenClass == CLASS_TYPE_CLERIC ||
|
||
GlobalOurChosenClass == CLASS_TYPE_DRUID ||
|
||
GlobalOurChosenClass == CLASS_TYPE_BARD)
|
||
{
|
||
// BAB check for level 3 spells.
|
||
// Must be able to hit them 75% of the time.
|
||
if(GlobalOurBaseAttackBonus + i5 >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
// Demons, fighters, anything else really.
|
||
else
|
||
{
|
||
// BAB check for level 3 spells.
|
||
// 50% chance of hitting them outright.
|
||
if(GlobalOurBaseAttackBonus + i10 >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
}
|
||
|
||
// Level 2 random hostile spell
|
||
// Single:
|
||
// [Melf's Acid Arrow] - 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that
|
||
// [Hold Animal] Paralyze's an amimal race target, if they fail a will save.
|
||
// [Blindness/Deafness] - Blindness/deafness on Fort save failure.
|
||
// [Tasha's Hideous Laughter] Knockdown for 1d3 rounds, if fail will save. -4 DC if different races
|
||
// [Charm Person or Animal] Charms an animal, or humanoid, if fail mind will save.
|
||
// [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save.
|
||
// [Ghoul Touch] - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby.
|
||
// [Continual Flame] 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage.
|
||
|
||
// Is it best to target with single-target spells first?
|
||
// Acid arrow, flame lash all scale well, while blindness/deafness is a good spell too.
|
||
// 60-70% if favourable.
|
||
// - We don't cast inflict serious wounds here. GetHasSpell bodges with spontaeous spells.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i2)
|
||
{
|
||
// Ghoul Touch. Paralysis is good :-D
|
||
if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2))
|
||
{
|
||
// Ghoul Touch. Level 2 (Mage) - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby.
|
||
if(AI_ActionCastSpellRandom(SPELL_GHOUL_TOUCH, SpellHostTouch, i50, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
if(RangeLongValid)
|
||
{
|
||
// Greater shadow conjuration
|
||
if(d10() <= i6)
|
||
{
|
||
if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_ACID_ARROW, SpellHostRanged, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Acid arrow - decent enough, and persistant damage, and no save. Long range too!
|
||
// Melf's Acid Arrow. Level 2 (Mage) 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that
|
||
if(AI_ActionCastSpellRandom(SPELL_MELFS_ACID_ARROW, SpellHostRanged, i60, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Continual flame isn't a bad spell - touch spell, and does
|
||
// continual damage (reflex based)
|
||
if(RangeTouchValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2))
|
||
{
|
||
// Continual Flame. Level 2 (Mage) 3 (Cleric) 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_CONTINUAL_FLAME, SpellHostTouch, i60, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Flame Lash is an alright amount of flame damage.
|
||
if(RangeShortValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2))
|
||
{
|
||
// [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLAME_LASH, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
if(RangeMediumValid)
|
||
{
|
||
// Hold Animal - animals are paralyzed on a will save failure.
|
||
if(GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Hold Animal. Level 2 (Druid/Ranger) Paralyze's an amimal race target, if they fail a will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Blindness/Deafness is a pretty good spell.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2))
|
||
{
|
||
// Blindness/Deafness. Level 2 (Mage/Bard) 3 (Cleric) Blindness/deafness on Fort save failure.
|
||
if(AI_ActionCastSpellRandom(SPELL_BLINDNESS_AND_DEAFNESS, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Tasha's Hideous Laughter is knockdown - not too bad.
|
||
// - We don't bother with the +4/-4.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Tasha's Hideous Laughter. Level 2 (Bard/Mage) Knockdown for 1d3 rounds, if fail will save. -4 DC if different races
|
||
if(AI_ActionCastSpellRandom(SPELL_TASHAS_HIDEOUS_LAUGHTER, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeShortValid)
|
||
{
|
||
// Charm Person or Animal. Charms an animal, or humanoid, if fail mind will save.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
(GetIsPlayableRacialType(GlobalSpellTarget) || GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Charm Person or Animal. Level 2 (Druid) Charms an animal, or humanoid, if fail mind will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON_OR_ANIMAL, SpellHostRanged, i50, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// AOE:
|
||
// [Silence] - Silence AOE that moves with target. Applies silence to those in the AOE.
|
||
// [Web] - Reflex saves or entangles and stuck in the web. AOE, quite large, persistant.
|
||
// [Balagarn's Iron Horn] Need to beat (Enemy STR + d20) with 20 + d20. If so, 6 second knockdown
|
||
// [Sound Burst] - 1d8 sonic damage + Stun unless save VS will.
|
||
// [Cloud of Bewilderment] - Creatures are stunned and blinded in the AOE. Fort save.
|
||
// [Gedlee's Electric Loop] - All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun.
|
||
|
||
// Silence - must be a mage or sorceror. No talent for this, BTW. Also
|
||
// note: can't have the spell's effects already, and must be over 10M away.
|
||
if(RangeLongValid && GlobalSpellTargetRange >= f10 &&
|
||
!GetHasSpellEffect(SPELL_SILENCE, GlobalSpellTarget) &&
|
||
(GetLevelByClass(CLASS_TYPE_WIZARD, GlobalSpellTarget) ||
|
||
GetLevelByClass(CLASS_TYPE_SORCERER, GlobalSpellTarget) ||
|
||
GetLevelByClass(CLASS_TYPE_CLERIC, GlobalSpellTarget)))
|
||
{
|
||
// Silence. Level 2 (Mage/Cleric/Bard) Silence AOE that moves with target. Applies silence to those in the AOE.
|
||
if(AI_ActionCastSpellRandom(SPELL_SILENCE, SpellOtherSpell, i60, GlobalSpellTarget, i12)) return TRUE;
|
||
}
|
||
// Gedlee's Electric Loop is a good damaging AOE spell - and the only mage level 2 one.
|
||
// - Note, it is set as Discriminate, but is really Indiscriminate (ReactionType)
|
||
if(SpellHostAreaDis && RangeShortValid &&
|
||
(GetHasSpell(SPELL_GEDLEES_ELECTRIC_LOOP) || ItemHostAreaDis == SPELL_GEDLEES_ELECTRIC_LOOP))
|
||
{
|
||
// Small, short ranged, and reflex save based.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_SMALL, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Gedlee's Electric Loop. Level 2 (Mage) All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun.
|
||
if(AI_ActionCastSpellRandom(SPELL_GEDLEES_ELECTRIC_LOOP, SpellHostAreaDis, i60, oAOE, i12, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
}
|
||
// Web - sticky stuff. Illusion version first :-D
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
(GetHasSpell(SPELL_WEB) || ItemHostAreaInd == SPELL_WEB ||
|
||
GetHasSpell(SPELL_GREATER_SHADOW_CONJURATION_WEB) || ItemHostAreaInd == SPELL_GREATER_SHADOW_CONJURATION_WEB))
|
||
{
|
||
// large AOE, medium ranged, reflex based save. Reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Shades vesion - shouldn't affect allies.
|
||
if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_WEB, SpellHostAreaInd, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
|
||
// Beblith version
|
||
if(AI_ActionCastSpellRandom(AI_SPELLABILITY_BEBELITH_WEB, SpellHostAreaInd, i60, oAOE)) return TRUE;
|
||
|
||
// Web. Level 2 (Mage) Entangles on reflex sve.
|
||
if(AI_ActionCastSpellRandom(SPELL_WEB, SpellHostAreaInd, i60, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Cloud of Bewilderment is an alright AOE - stun and blindness :-)
|
||
// Note - Only casts if the target is not immune to stun nor blindness!
|
||
if(SpellHostAreaInd && RangeMediumValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
(GetHasSpell(SPELL_CLOUD_OF_BEWILDERMENT) || ItemHostAreaInd == SPELL_CLOUD_OF_BEWILDERMENT))
|
||
{
|
||
// 5M radius (large) AOE, short ranged, fort based save. Reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Cloud of Bewilderment. Level 2 (Mage/Bard) Creatures are stunned and blinded in the AOE. Fort save.
|
||
// Web. Level 2 (Mage) Entangles on reflex sve.
|
||
if(AI_ActionCastSpellRandom(SPELL_CLOUD_OF_BEWILDERMENT, SpellHostAreaInd, i50, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Balagarn's Iron Horn is only a 6 second knockdown, not too bad.
|
||
// Mainly, it has no save. Caster gets +20 on d20, enemy gets strength bonus.
|
||
// It is a personal, affects us spell :-)
|
||
if(GlobalSpellTargetRange <= f5)
|
||
{
|
||
// Balagarn's Iron Horn. Level 2 (Mage) Need to beat (Enemy STR + d20) with 20 + d20. If so, 6 second knockdown
|
||
if(AI_ActionCastSpellRandom(SPELL_BALAGARNSIRONHORN, SpellHostAreaDis, i60, OBJECT_SELF, i12, TRUE, ItemHostAreaDis)) return TRUE;
|
||
}
|
||
// Sound burst - best part is the stun! Trust me! Long range too!
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_SOUND_BURST) || ItemHostAreaInd == SPELL_SOUND_BURST))
|
||
{
|
||
// Medium AOE, medium ranged, will based save. Reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i2, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Sound burst. Level 2 (cleric). 1d8 Sonic damage, and if fail will save, stunned for 2 rounds.
|
||
if(AI_ActionCastSpellRandom(SPELL_SOUND_BURST, SpellHostAreaInd, i50, oAOE, i12, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Level 2 summons
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i2 &&
|
||
(GlobalOurHitDice <= i8 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Summon Monster II (2). Level 2 (Most classes) Summons a Dire Boar.
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_II, i12, i2)) return TRUE;
|
||
}
|
||
|
||
// Single spells, lower %'s
|
||
// - We don't cast inflict serious wounds. GetHasSpell bodges with spontaeous spells.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i2)
|
||
{
|
||
// Ghoul Touch. Paralysis is good :-D
|
||
if(RangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2))
|
||
{
|
||
// Ghoul Touch. Level 2 (Mage) - Fort save VS paralysis and an AOE on target which gives -2 attack/damage to enemies nearby.
|
||
if(AI_ActionCastSpellRandom(SPELL_GHOUL_TOUCH, SpellHostTouch, i30, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
if(RangeLongValid)
|
||
{
|
||
if(d10() <= i4)
|
||
{
|
||
// Greater shadow conjuration
|
||
if(AI_ActionCastSubSpell(SPELL_GREATER_SHADOW_CONJURATION_ACID_ARROW, SpellHostRanged, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Acid arrow - decent enough, and persistant damage, and no save. Long range too!
|
||
// Melf's Acid Arrow. Level 2 (Mage) 3d6 Impact and 1d6 damage for up to 7 rounds (caster level/3) after that
|
||
if(AI_ActionCastSpellRandom(SPELL_MELFS_ACID_ARROW, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Continual flame isn't a bad spell - touch spell, and does
|
||
// continual damage (reflex based)
|
||
if(RangeTouchValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2))
|
||
{
|
||
// Continual Flame. Level 2 (Mage) 3 (Cleric) 2d6 + 1 Caster Level (to +10) Fire dam. Reflex save. Every round after, reflex (until sucess) at 1d6 damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_CONTINUAL_FLAME, SpellHostTouch, i30, GlobalSpellTarget, i12, FALSE, ItemHostTouch)) return TRUE;
|
||
}
|
||
// Flame Lash is an alright amount of flame damage.
|
||
if(RangeShortValid && !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i2))
|
||
{
|
||
// [Flame Lash] 2d6 + 1d6/3 caster levels of fire damage. Reflex save.
|
||
if(AI_ActionCastSpellRandom(SPELL_FLAME_LASH, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
if(RangeMediumValid)
|
||
{
|
||
// Hold Animal - animals are paralyzed on a will save failure.
|
||
if(GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityStun) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Hold Animal. Level 2 (Druid/Ranger) Paralyze's an amimal race target, if they fail a will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_HOLD_PERSON, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Blindness/Deafness is a pretty good spell.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i2))
|
||
{
|
||
// Blindness/Deafness. Level 2 (Mage/Bard) 3 (Cleric) Blindness/deafness on Fort save failure.
|
||
if(AI_ActionCastSpellRandom(SPELL_BLINDNESS_AND_DEAFNESS, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Tasha's Hideous Laughter is knockdown - not too bad.
|
||
// - We don't bother with the +4/-4.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Tasha's Hideous Laughter. Level 2 (Bard/Mage) Knockdown for 1d3 rounds, if fail will save. -4 DC if different races
|
||
if(AI_ActionCastSpellRandom(SPELL_TASHAS_HIDEOUS_LAUGHTER, SpellHostRanged, i30, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeShortValid)
|
||
{
|
||
// Charm Person or Animal. Charms an animal, or humanoid, if fail mind will save.
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
(GetIsPlayableRacialType(GlobalSpellTarget) || GlobalSpellTargetRace == RACIAL_TYPE_ANIMAL) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Charm Person or Animal. Level 2 (Druid) Charms an animal, or humanoid, if fail mind will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON_OR_ANIMAL, SpellHostRanged, i20, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Backup casting
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Moderate wounds - damage at a touch attack.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i2 && RangeTouchValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Blackguard ability
|
||
if(AI_ActionUseFeatOnObject(FEAT_INFLICT_MODERATE_WOUNDS, GlobalMeleeTarget)) return TRUE;
|
||
|
||
// Inflict Moderate Wounds. Level 2 (Cleric) Touch attack, hit means 2d8 + 1-10 negative damage.
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MODERATE_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
/*::1111111111111111111111111111111111111111111111111111111111111111111111111111
|
||
Level 1 Spells.
|
||
//::1111111111111111111111111111111111111111111111111111111111111111111111111111
|
||
Thoughts - Not bad AOE spells, better at lower levels or with more people.
|
||
Magic missile is a decent spell for level 1, as it goes up to level 9 :-) and
|
||
is a classic. Others are simple enough, doing small amounts of damage or
|
||
doing some ability daamge, or sleep for limited hit dice creatures. Useful
|
||
AC enhancing spells at level 1 though!
|
||
|
||
AOE:
|
||
[Burning Hands] - 1d4/level to 5d4 Reflex Fire damage to a cone AOE.
|
||
[Color Spray] - effect based on HD - Sleep Stun and Blindness if fail will save
|
||
[Grease] - Reflex save or knockdown, and slow in AOE.
|
||
[Sleep] - 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4.
|
||
[Entangle] - Entangles creatures in the AOE so they cannot move, reflex save, every round.
|
||
S [Bane] - -1 to all saves VS fear, attack rolls, damage. Opposite of Bless.
|
||
|
||
Single:
|
||
[Charm Person] - Charms one humanoid if they fail a mind will save.
|
||
[Magic Missile] - 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9.
|
||
[Negative Energy Ray] 1d6(CasterLevel/2) to 5d6 negative damage.
|
||
[Doom] -2 Saves, Damage, to hit at a mind will save.
|
||
[Ray of Enfeeblement] - d6 + (Caster level/2) strength damage to d6 + 10. Fort save.
|
||
[Scare] - 1d4 round of fear, saving throw VS will, for 5HD or less creature
|
||
H [Horzilkaul's Boom] - 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness.
|
||
H [Ice Dagger] - Reflex-based, 1d4/Level (to 5d4) ice damage.
|
||
|
||
S [Inflict Light Wounds] 1d8 + 1-5 negative damage on touch attack.
|
||
|
||
Defender:
|
||
X [Endure Elements] 10/- Elemental resistance until 20 damage has been done
|
||
[Mage Armor] +1 Dodge/Armor/Deflection/Natural AC bonuses. (total +4)
|
||
[Protection From Alignment] +2 AC, mind immunity from alignment
|
||
S [Shield] +4 Armor AC and immunity to magic missile.
|
||
S [Shield of Faith] +2 deflection AC bonus, +1 every 6 levels (max +5)
|
||
S [Entropic Shield] 20% Consealment VS ranged attacks.
|
||
H X [Iron Guts] +4 Saves VS poison. (cast as level 0 spell...this is specilised!)
|
||
|
||
Summon:
|
||
[Summon Creature I] - Summons a dire badger
|
||
H [Shelgarn's Persistant Blade] - Summons a dagger for the caster.
|
||
|
||
Other:
|
||
SXXX[Amiplify] +20 to listen checks.
|
||
S X [Expeditious Retreat] - 150% movement speed. Cast before melee.
|
||
XXXX[Identify] - Extra lore. Not cast
|
||
S X [True Strike] +9 attack for a single attack.
|
||
X [Cure Light Wounds] 1d8 + 1-5 healing damage
|
||
X [Bless] +1 to attack rolls, damage for AOE, +2 VS fear saves.
|
||
S X [Divine Favor] +1/3 caster levels attack bonus, to +5.
|
||
X [Remove Fear] Removes fear!
|
||
X [Sanctuary] Will saving throw for enemies to spot you. Kinda a bad invisiblity.
|
||
SXXX[Camouflage] +10 hide for self.
|
||
S [Magic Fang] + 1 attack/damage to animal companion statistics.
|
||
H X [Magic Weapon] +1 enchantment to 1 weapon.
|
||
H X [Bless Weapon] +1 enchantment and +2d6 dam VS Undead to 1 weapon.
|
||
H X [Deafening Clang] +1 enchantment. +3 sonic damage. On Hit: Deafness, on a weapon.
|
||
|
||
//::11111111111111111111111111111111111111111111111111111111111111111111111111*/
|
||
|
||
// Jump out if we don't want to cast level 1 spells.
|
||
if(iLowestSpellLevel > i1) return FALSE;
|
||
|
||
// Ok, AC increasing/defensive spells first.
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Entropic shield. 20% VS ranged attackers - we need some ranged attackers
|
||
// OR people far away (After other AC spells). Also no normal consealment spells (they don't stack)
|
||
if(GlobalRangedAttackers &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasRangedConsealment) &&
|
||
!AI_GetAIHaveEffect(GlobalEffectInvisible))
|
||
{
|
||
// Entropic Shield. Level 1 (Cleric) 20% Consealment VS ranged attacks.
|
||
if(AI_ActionCastSpell(SPELL_ENTROPIC_SHIELD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
|
||
// We don't cast AC spells which will not do well over other AC inducing spells
|
||
// - Shield + Divine Shield == Deflection. Never cast together
|
||
// - Barkskin, Stone Bones == Natural
|
||
// - Mage armor (+Epic) == Narual, Deflection, Dodge, Armor. Never cast with both Barkskin and Shield.
|
||
|
||
// Out of the deflection spells, Shield first then Shield of Faith.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasDeflectionACSpell))
|
||
{
|
||
// Shield. Level 1 (Mage) +4 Armor AC and immunity to magic missile.
|
||
if(AI_ActionCastSpell(SPELL_SHIELD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
// Shield of Faith. Level 1 (Cleric) +2 deflection AC bonus, +1 every 6 levels (max +5)
|
||
if(AI_ActionCastSpell(SPELL_SHIELD_OF_FAITH, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
// If we have not got natural or other AC, we cast mage armor.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasNaturalACSpell) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasOtherACSpell))
|
||
{
|
||
// Shadow conjuration version
|
||
if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_MAGE_ARMOR, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
// Mage Armor. Level 1 (MagE) +1 Dodge/Armor/Deflection/Natural AC bonuses. (total +4)
|
||
if(AI_ActionCastSpell(SPELL_MAGE_ARMOR, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
|
||
// Cast Entropic shield if nearest enemy is over 4 M away.
|
||
if(!GlobalEnemiesIn4Meters &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasRangedConsealment) &&
|
||
!AI_GetAIHaveEffect(GlobalEffectInvisible))
|
||
{
|
||
// Entropic Shield. Level 1 (Cleric) 20% Consealment VS ranged attacks.
|
||
if(AI_ActionCastSpell(SPELL_ENTROPIC_SHIELD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
|
||
// Cheat and use protection froms. Have to rely upon talents :-/
|
||
// as cannot specify using ActionCast - it plain don't wanna work.
|
||
if(iEnemyAlignment == ALIGNMENT_GOOD &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasProtectionGoodSpell))
|
||
{
|
||
// Protection From Alignment. Level 1 (Bard/Cleric/Paladin/Mage). +2 AC, mind immunity from alignment
|
||
if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_GOOD, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
else if(iEnemyAlignment == ALIGNMENT_EVIL &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell))
|
||
{
|
||
if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_EVIL, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) return TRUE;
|
||
}
|
||
|
||
// Ally buff spells if buffer
|
||
if(GlobalWeAreBuffer)
|
||
{
|
||
// Bless, Aid
|
||
if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_AID, SPELL_BLESS)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// BAB check
|
||
// - BAB checks check our BASE attack bonus, no modifiers. Basically, as
|
||
// normally even mages have a +X to attack, this provides a good indicator
|
||
// if we are going to easy, or very easily, hit the enemy.
|
||
// - Clerics, Druids and Bards must be able to hit even better then normal.
|
||
if(IsFirstRunThrough && !SRA &&
|
||
GlobalOurChosenClass != CLASS_TYPE_WIZARD &&
|
||
GlobalOurChosenClass != CLASS_TYPE_SORCERER &&
|
||
GlobalOurChosenClass != CLASS_TYPE_FEY)
|
||
{
|
||
// If a druid, cleric, and so on, have to be able to hit better then
|
||
// more then normal
|
||
if(GlobalOurChosenClass == CLASS_TYPE_CLERIC ||
|
||
GlobalOurChosenClass == CLASS_TYPE_DRUID ||
|
||
GlobalOurChosenClass == CLASS_TYPE_BARD)
|
||
{
|
||
// BAB check for level 4 spells. 50%
|
||
if(GlobalOurBaseAttackBonus+ i10 >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
// Demons, fighters, anything else really.
|
||
else
|
||
{
|
||
// BAB check for level 4 spells. 25%
|
||
if(GlobalOurBaseAttackBonus + i15 >= GlobalMeleeTargetAC) return FALSE;
|
||
}
|
||
}
|
||
|
||
// Try grenades - we always throw these. They are about level 1 standard of DC's
|
||
// and effects. Not too bad, when NPC's get a chance to use them! :-)
|
||
if(RangeMediumValid)
|
||
{
|
||
// - Note, these are also always thrown before melee, if the person has <5 HD.
|
||
// - Reasons for not casting are really the BAB checks.
|
||
if(AI_AttemptGrenadeThrowing(GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// Random hostile spell
|
||
// [Magic Missile] - 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9.
|
||
// [Negative Energy Ray] 1d6(CasterLevel/2) to 5d6 negative damage.
|
||
// [Scare] - 1d4 round of fear, saving throw VS will, for 5HD or less creature
|
||
// [Ray of Enfeeblement] - d6 + (Caster level/2) strength damage to d6 + 10. Fort save.
|
||
// [Doom] -2 Saves, Damage, to hit at a mind will save.
|
||
// [Charm Person] - Charms one humanoid if they fail a mind will save.
|
||
// [Horzilkaul's Boom] - 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness.
|
||
// [Ice Dagger] - Reflex-based, 1d4/Level (to 5d4) ice damage.
|
||
|
||
// Is it best to target with single-target spells first?
|
||
// Magic missile, negative energy ray and flame lash are better at damaging
|
||
// one target, if there is one target, then the AOE spells usually.
|
||
// 60-70% if favourable.
|
||
// - We don't cast inflict light wounds. GetHasSpell bodges with spontaeous spells.
|
||
if(SingleSpellsFirst && GlobalNormalSpellsNoEffectLevel < i1)
|
||
{
|
||
// Magic Missile - Is a nice long range spell. Can do damage to almost anything.
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_SHIELD, GlobalSpellTarget))
|
||
{
|
||
if(d10() <= i6)
|
||
{
|
||
// Shad. conjuration
|
||
if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_MAGIC_MISSILE, SpellHostRanged, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Magic Missile. Level 1 (Mage) 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9.
|
||
if(AI_ActionCastSpellRandom(SPELL_MAGIC_MISSILE, SpellHostRanged, i60, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Horizikaul's boom is sonic damage - no save! Also, goes also to 5d4 (at level 10 anyway). Comparable to MM!
|
||
if(RangeShortValid)
|
||
{
|
||
// Horzilkaul's Boom. Level 1 (Mage) 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness.
|
||
if(AI_ActionCastSpellRandom(SPELL_HORIZIKAULS_BOOM, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
if(RangeMediumValid)
|
||
{
|
||
// Negative energy ray is similar to Magic Missile, but heals undead. can do more dmage then MM, but more random - d6 compared to d4 + 1.
|
||
if(GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD &&
|
||
GlobalSpellTargetRace != RACIAL_TYPE_CONSTRUCT &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Negative Energy Ray. Level 1 (Mage) 2 (Cleric) 1d6(CasterLevel/2) to 5d6 negative damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_NEGATIVE_ENERGY_RAY, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Doom does some...stuff. The damage -2 is best. Will save negates.
|
||
if(!GetHasSpellEffect(SPELL_DOOM, GlobalSpellTarget) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1))
|
||
{
|
||
// Doom. Level 1 (Mage) -2 Saves, Damage, to hit at a mind will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_DOOM, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeShortValid)
|
||
{
|
||
// Damage with Ice Dagger is not bad - it is cirtinly comparable to MM! Saves though...
|
||
if(!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i1))
|
||
{
|
||
// Ice Dagger. Level 1 (Mage) Reflex-based, 1d4/Level (to 5d4) ice damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_ICE_DAGGER, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Scare - needs 5HD or under HD
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityFear) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1))
|
||
{
|
||
// Scare. Level 1 (Mage) 1d4 round of fear, saving throw VS will, for 5HD or less creature
|
||
if(AI_ActionCastSpellRandom(SPELL_SCARE, SpellHostRanged, i50, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Ray of Enfeeblement pities high strength enemies. d6 + 1-10 str. Loss
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i14 &&
|
||
!GetHasSpellEffect(SPELL_RAY_OF_ENFEEBLEMENT, GlobalSpellTarget) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i1))
|
||
{
|
||
// Ray of Enfeeblement. Level 1 (Mage) d6 + (Caster level/2) strength damage to d6 + 10. Fort save.
|
||
if(AI_ActionCastSpellRandom(SPELL_RAY_OF_ENFEEBLEMENT, SpellHostRanged, i40, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Charm Person charms them. Might as well... (Bit bad this spell)
|
||
if(GetIsPlayableRacialType(GlobalSpellTarget) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Charm Person. Level 1 (Mage/Bard) Charms one humanoid if they fail a mind will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
// Single spell override backup casting
|
||
if(SingleSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
|
||
// Random AOE spell.
|
||
// [Burning Hands] - 1d4/level to 5d4 Reflex Fire damage to a cone AOE.
|
||
// [Color Spray] - effect based on HD - Sleep Stun and Blindness if fail will save
|
||
// [Grease] - Reflex save or knockdown, and slow in AOE.
|
||
// [Sleep] - 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4.
|
||
// [Bane] - -1 to all saves VS fear, attack rolls, damage. Opposite of Bless.
|
||
// [Entangle] - Entangles creatures in the AOE so they cannot move, reflex save, every round.
|
||
|
||
// Burning hands - fire reflex damage to a cone
|
||
if(SpellHostAreaInd && RangeShortValid &&
|
||
(GetHasSpell(SPELL_BURNING_HANDS) || ItemHostAreaInd == SPELL_BURNING_HANDS))
|
||
{
|
||
// Cone AOE, short ranged, reflex based save. Reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, f10, i1, SAVING_THROW_REFLEX, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Burning Hands. Level 1 (Mage) 1d4/level to 5d4 Reflex Fire damage to a cone AOE.
|
||
if(AI_ActionCastSpellRandom(SPELL_BURNING_HANDS, SpellHostAreaInd, i60, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Color Spray - Does at least blind higher levels. Will save, however, negates
|
||
if(SpellHostAreaInd && RangeShortValid &&
|
||
(GetHasSpell(SPELL_COLOR_SPRAY) || ItemHostAreaInd == SPELL_COLOR_SPRAY))
|
||
{
|
||
// Cone AOE, short ranged, reflex based save. Reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fShortRange, f10, i1, SAVING_THROW_WILL, SHAPE_SPELLCONE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 70% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Color Spray. Level 1 (Mage) - effect based on HD - Sleep Stun and Blindness if fail will save
|
||
if(AI_ActionCastSpellRandom(SPELL_COLOR_SPRAY, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Grease means knockdown - good if they are not immune to knockdown :-P (reflex save)
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_GREASE) || ItemHostAreaInd == SPELL_GREASE))
|
||
{
|
||
// Take as Medium AOE, long ranged, reflex based save. Reaction friendly.
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_MEDIUM, i1, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Grease. Level 1 (Mage) Reflex save or knockdown, and slow in AOE.
|
||
if(AI_ActionCastSpellRandom(SPELL_GREASE, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Sleep - 8HD or less average HD for enemy to target this.
|
||
if(SpellHostAreaInd && RangeMediumValid && GlobalAverageEnemyHD <= i8 &&
|
||
(GetHasSpell(SPELL_SLEEP) || GetHasFeat(FEAT_HARPER_SLEEP) || ItemHostAreaInd == SPELL_SLEEP))
|
||
{
|
||
// Huge AOE, medium ranged, will based save. Reaction type enemy
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i1, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFireHostile);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// The harper sleep
|
||
if(AI_ActionUseFeatOnObject(FEAT_HARPER_SLEEP, oAOE)) return TRUE;
|
||
|
||
// Sleep. Level 1 (Mage) 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4.
|
||
if(AI_ActionCastSpellRandom(SPELL_SLEEP, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
// Bane is...alright, I guess. Will save, however. It doesn't affect allies though!
|
||
if(SpellEnhSinTar && RangeLongValid &&
|
||
(GetHasSpell(SPELL_BANE) || ItemEnhSinTar == SPELL_BANE))
|
||
{
|
||
// collosal AOE, long ranged, will based save. No allies
|
||
oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_COLOSSAL, i1, SAVING_THROW_WILL);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Sleep. Level 1 (Mage) 8HD or under creatures, will save VS sleep. Can do more then 1, if total HD affected under d4 + 4.
|
||
if(AI_ActionCastSpellRandom(SPELL_BANE, SpellEnhSinTar, i50, oAOE, i11, TRUE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
}
|
||
// Entangle stops them moving - not a bad spell to say the least, I guess
|
||
if(SpellHostAreaInd && RangeLongValid &&
|
||
(GetHasSpell(SPELL_ENTANGLE) || ItemHostAreaInd == SPELL_ENTANGLE))
|
||
{
|
||
// Huge AOE, Long ranged, reflex based save. Reaction type friendly
|
||
oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i1, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFireFriendly);
|
||
// Is it valid? 60% chance of casting.
|
||
if(GetIsObjectValid(oAOE))
|
||
{
|
||
// Entangle. Level 1 (Druid/Ranger) Entangles creatures in the AOE so they cannot move, reflex save, every round.
|
||
if(AI_ActionCastSpellRandom(SPELL_ENTANGLE, SpellHostAreaInd, i50, oAOE, i11, TRUE, ItemHostAreaInd)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Multi spell override backup casting
|
||
if(MultiSpellOverride) if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Level 1 summons
|
||
if(IsFirstRunThrough && GlobalCanSummonSimilarLevel <= i1 && GlobalOurHitDice <= i10 &&
|
||
(GlobalOurHitDice <= i6 || GlobalMeleeAttackers <= i2))
|
||
{
|
||
// Shelgarn's Persistant Blade. Level 1 (Mage only) Summons a dagger for the caster.
|
||
if(AI_ActionCastSummonSpell(SPELL_SHELGARNS_PERSISTENT_BLADE, i11, i1)) return TRUE;
|
||
// Summon Monster I (1). Level 1 (Most classes) Summons a Dire Badger.
|
||
if(AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_I, i11, i1)) return TRUE;
|
||
}
|
||
|
||
// Single spells again
|
||
// Similar %'s as it is level 1 spells.
|
||
// - We don't cast inflict light wounds. GetHasSpell bodges with spontaeous spells.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i1)
|
||
{
|
||
// Magic Missile - Is a nice long range spell. Can do damage to almost anything.
|
||
if(RangeLongValid && !GetHasSpellEffect(SPELL_SHIELD, GlobalSpellTarget))
|
||
{
|
||
// Shad. conjuration
|
||
if(d10() <= i4)
|
||
{
|
||
if(AI_ActionCastSubSpell(SPELL_SHADOW_CONJURATION_MAGIC_MISSILE, SpellHostRanged, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Magic Missile. Level 1 (Mage) 1d4 + 1 damage/missile. 1 missile for 2 caster levels. Max 5 at level 9.
|
||
if(AI_ActionCastSpellRandom(SPELL_MAGIC_MISSILE, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Horizikaul's boom is sonic damage - no save! Also, goes also to 5d4 (at level 10 anyway). Comparable to MM!
|
||
if(RangeShortValid)
|
||
{
|
||
// Horzilkaul's Boom. Level 1 (Mage) 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness.
|
||
if(AI_ActionCastSpellRandom(SPELL_HORIZIKAULS_BOOM, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
if(RangeMediumValid)
|
||
{
|
||
// Negative energy ray is similar to Magic Missile, but heals undead. can do more dmage then MM, but more random - d6 compared to d4 + 1.
|
||
if(GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD &&
|
||
GlobalSpellTargetRace != RACIAL_TYPE_CONSTRUCT &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Negative Energy Ray. Level 1 (Mage) 2 (Cleric) 1d6(CasterLevel/2) to 5d6 negative damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_NEGATIVE_ENERGY_RAY, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Doom does some...stuff. The damage -2 is best. Will save negates.
|
||
if(!GetHasSpellEffect(SPELL_DOOM, GlobalSpellTarget) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1))
|
||
{
|
||
// Doom. Level 1 (Mage) -2 Saves, Damage, to hit at a mind will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_DOOM, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
if(RangeShortValid)
|
||
{
|
||
// Damage with Ice Dagger is not bad - it is cirtinly comparable to MM! Saves though...
|
||
if(!AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i1))
|
||
{
|
||
// Ice Dagger. Level 1 (Mage) Reflex-based, 1d4/Level (to 5d4) ice damage.
|
||
if(AI_ActionCastSpellRandom(SPELL_ICE_DAGGER, SpellHostRanged, i30, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Scare - needs 5HD or under HD
|
||
if(!AI_GetSpellTargetImmunity(GlobalImmunityFear) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i1))
|
||
{
|
||
// Scare. Level 1 (Mage) 1d4 round of fear, saving throw VS will, for 5HD or less creature
|
||
if(AI_ActionCastSpellRandom(SPELL_SCARE, SpellHostRanged, i20, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Ray of Enfeeblement pities high strength enemies. d6 + 1-10 str. Loss
|
||
if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i14 &&
|
||
!GetHasSpellEffect(SPELL_RAY_OF_ENFEEBLEMENT, GlobalSpellTarget) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i1))
|
||
{
|
||
// Ray of Enfeeblement. Level 1 (Mage) d6 + (Caster level/2) strength damage to d6 + 10. Fort save.
|
||
if(AI_ActionCastSpellRandom(SPELL_RAY_OF_ENFEEBLEMENT, SpellHostRanged, i20, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Charm Person charms them. Might as well... (Bit bad this spell)
|
||
if(GetIsPlayableRacialType(GlobalSpellTarget) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDomination) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i2))
|
||
{
|
||
// Charm Person. Level 1 (Mage/Bard) Charms one humanoid if they fail a mind will save.
|
||
if(AI_ActionCastSpellRandom(SPELL_CHARM_PERSON, SpellHostRanged, i10, GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
// Backup casting
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
|
||
// Light wounds - damage at a touch attack.
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i1 && RangeTouchValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Blackguard ability.
|
||
if(AI_ActionUseFeatOnObject(FEAT_INFLICT_LIGHT_WOUNDS, GlobalMeleeTarget)) return TRUE;
|
||
|
||
// Inflict Light Wounds. Level 1 (Cleric) Touch attack, hit means 1d8 + 1-5 negative damage.
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_LIGHT_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// Lastly, cheat cast spells. We cast these if we run out of all others
|
||
// - Cast ABOVE level 0 spells.
|
||
if(GetAIConstant(AI_CHEAT_CAST_SPELL + s1) >= FALSE)
|
||
{
|
||
// It is normally 6 spells. It will default to the first if one
|
||
// we pick is not valid.
|
||
int iSpell = GetAIConstant(AI_CHEAT_CAST_SPELL + IntToString(d6()));
|
||
if(iSpell <= i0)
|
||
{
|
||
iSpell = GetAIConstant(AI_CHEAT_CAST_SPELL + s1);
|
||
}
|
||
// 33: "[DCR:Casting] Cheat Spell. End of Spells. [Spell] " + IntToString(iSpell) + "[Target]" + GetName(GlobalSpellTarget)
|
||
DebugActionSpeakByInt(33, GlobalSpellTarget, iSpell);
|
||
ActionCastSpellAtObject(iSpell, GlobalSpellTarget, METAMAGIC_NONE, TRUE);
|
||
return TRUE;
|
||
}
|
||
|
||
/*::0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||
Level 0 Spells.
|
||
//::0000000000000000000000000000000000000000000000000000000000000000000000000000
|
||
Thoughts - These are backup spells, even at level 1. To be honest, ray
|
||
of frost at level 1-3 is a good spell, even 5+ it is alright, as it has
|
||
no save. (Shame it isn't like Acid Splash, as in correct D&D damage).
|
||
Daze is also useful in some situations. No AOE spells and the defensive
|
||
spells are nothing to speak of, however.
|
||
|
||
AOE:
|
||
X [None]
|
||
|
||
Single:
|
||
[Ray of frost] 1d4 + 1 cold damage, at no save.
|
||
S [Flare] Target gets -1 to attack rolls if they fail a fortitude save
|
||
[Daze] If <= 5 Hit dice, target is dazed on a failed fortitude save.
|
||
S [Acid Splash] 1d3 Acid damage, no save. Longer range then ray of frost.
|
||
S [Electric Jolt] 1d3 Electrical damage, no save. Longer range then ray of frost.
|
||
S [Inflict Minor Wounds] 1 Negative damage, on a touch attack. Heals undead.
|
||
|
||
Defender:
|
||
[Resistance] +1 to all saves for 2 turns
|
||
[Virtue] +1 HP, 1 turn/caster level.
|
||
|
||
Summon:
|
||
X [None]
|
||
|
||
Other:
|
||
X [Cure Minor Wounds] - Heals 4 hit points (meant to be only 1)
|
||
[Light] 20M of light around the target.
|
||
//::00000000000000000000000000000000000000000000000000000000000000000000000000*/
|
||
|
||
// Jump out if we don't want to cast level 0 spells.
|
||
if(iLowestSpellLevel > i0) return FALSE;
|
||
|
||
// BAB check.
|
||
if(!SRA && GlobalOurChosenClass != CLASS_TYPE_WIZARD &&
|
||
GlobalOurChosenClass != CLASS_TYPE_SORCERER &&
|
||
GlobalOurChosenClass != CLASS_TYPE_FEY)
|
||
{
|
||
// BAB check for level 0 spells.
|
||
if(GlobalOurBaseAttackBonus >= GetAC(GlobalSpellTarget)) return FALSE;
|
||
// HD check
|
||
if(GlobalOurHitDice > i8) return FALSE;
|
||
}
|
||
|
||
if(IsFirstRunThrough)
|
||
{
|
||
// Need no enemies in 4 meters.
|
||
if(!GlobalEnemiesIn4Meters)
|
||
{
|
||
// Not sure of effectiveness...so acting as a cantrip.
|
||
// Sanctuary. Level 1 (Cleric). Will save (low DC!) or cannot see target.
|
||
if(AI_ActionCastSpell(SPELL_SANCTUARY, SpellEnhSinTar, OBJECT_SELF, i11, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
// Iron guts are very specifically VS poisoners - I'd see the use if I saw
|
||
// spiders. I won't bother adding them checks, however, because frankly NPC's
|
||
// won't use this much
|
||
if(!GetHasSpellEffect(SPELL_IRONGUTS))
|
||
{
|
||
// Iron Guts. Level 1 (Mage) +4 Saves VS poison. (cast as level 0 spell...this is specilised!)
|
||
if(AI_ActionCastSpell(SPELL_IRONGUTS, SpellEnhSinTar, OBJECT_SELF, i11, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE;
|
||
}
|
||
}
|
||
|
||
// Note: Also, as these are always the worst, last things to use in combat, we
|
||
// don't care about ranges :-P
|
||
// Note 2: We have the required amount of stat to 0. We check, On Spawn,
|
||
// if we can cast spells (IE right stats) and these are the worst we can cast.
|
||
if(GlobalNormalSpellsNoEffectLevel < i1 && GlobalSeenSpell)
|
||
{
|
||
// Random cast one of the 5 hostile cantrip spells
|
||
// Daze!
|
||
if(RangeLongValid &&
|
||
GlobalSpellTargetHitDice <= i5 &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityMind) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i0))
|
||
{
|
||
// Daze. Level 0 (Mage/Bard) If <= 5 Hit dice, target is dazed on a failed fortitude save.
|
||
if(AI_ActionCastSpellRandom(SPELL_DAZE, SpellHostRanged, i50, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
if(RangeMediumValid)
|
||
{
|
||
// Minor damage. Moderate range. 60% chance of casting.
|
||
if(AI_ActionCastSpellRandom(SPELL_RAY_OF_FROST, SpellHostRanged, i50, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
if(RangeLongValid)
|
||
{
|
||
// Long range, for Acid Splash and Electric Jolt. Random cast one of them
|
||
if(AI_ActionCastSpellRandom(SPELL_ELECTRIC_JOLT, SpellHostRanged, i40, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE;
|
||
if(AI_ActionCastSpellRandom(SPELL_ACID_SPLASH, SpellHostRanged, i40, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
// Flare is OK - low 10% cast, but will backup cast at end.
|
||
if(RangeMediumValid && !GetHasSpellEffect(SPELL_FLARE, GlobalSpellTarget) &&
|
||
!AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i0))
|
||
{
|
||
// Flare. Level 0 (Bard/Mage) Target gets -1 to attack rolls if they fail a fortitude save
|
||
if(AI_ActionCastSpellRandom(SPELL_FLARE, SpellHostRanged, i0, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE;
|
||
}
|
||
|
||
// Backup casting
|
||
if(AI_ActionCastBackupRandomSpell()) return TRUE;
|
||
}
|
||
// Need decent % HP and no enemies in 4 Meters to cast these
|
||
if(IsFirstRunThrough && GlobalOurPercentHP >= i60 && !GlobalEnemiesIn4Meters)
|
||
{
|
||
if(!GetHasSpellEffect(SPELL_VIRTUE))
|
||
{
|
||
// Virtue. Level 0 (Druid/Cleric/Paladin) +1 HP, 1 turn/caster level.
|
||
if(AI_ActionCastSpell(SPELL_VIRTUE, SpellEnhSinTar, OBJECT_SELF, i10, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
if(!GetHasSpellEffect(SPELL_RESISTANCE))
|
||
{
|
||
// Resistance. Level 0 (Mage/Cleric/Bard/Druid) 1 (Paladin) +1 to all saves for 2 turns
|
||
if(AI_ActionCastSpell(SPELL_RESISTANCE, SpellEnhSinTar, OBJECT_SELF, i10, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
// Light
|
||
if(!GetHasSpellEffect(SPELL_LIGHT))
|
||
{
|
||
// Light. Level 0 (Mage/Cleric/Druid/Bard) 20M of light around the target.
|
||
if(AI_ActionCastSpell(SPELL_LIGHT, SpellOtherSpell, OBJECT_SELF, i10)) return TRUE;
|
||
}
|
||
}
|
||
// Minor wounds - damage at a touch attack.
|
||
// HARDLY worth it, but use it anyway...if we pass the BAB check that is :-)
|
||
if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i1 && RangeTouchValid &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) &&
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy))
|
||
{
|
||
// Inflict Minor Wounds. Level 0 (Cleric) Touch attack, hit means 1 negative damage.
|
||
if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_MINOR_WOUNDS, SpellOtherSpell, GlobalSpellTarget)) return TRUE;
|
||
}
|
||
|
||
// This is when it may loop back, after moving forward a bit
|
||
// Ranges: 20, 8, and 2.25. Long range spells are always cast.
|
||
if(SRA)
|
||
{
|
||
// We have integers, set to TRUE or FALSE.
|
||
// - 2 sets, one that was passed from last time, one which we
|
||
// just used.
|
||
|
||
// Just used = RangeLongValid. TRUE = CASTED
|
||
// New ones = InputRangeLongValid. TRUE = DO NOT CAST.
|
||
|
||
// So, we toggle the RangeLongValid's, and if they are TRUE, we
|
||
// put that into the next loop. If it was FALSE, we check
|
||
// the InputRangeLongValid to see if we checked it before.
|
||
|
||
// If false, set the value to the Input value.
|
||
if(RangeLongValid == FALSE)
|
||
{
|
||
RangeLongValid = InputRangeLongValid;
|
||
}
|
||
if(RangeMediumValid == FALSE)
|
||
{
|
||
RangeMediumValid = InputRangeMediumValid;
|
||
}
|
||
if(RangeShortValid == FALSE)
|
||
{
|
||
RangeShortValid = InputRangeShortValid;
|
||
}
|
||
if(RangeTouchValid == FALSE)
|
||
{
|
||
RangeTouchValid = InputRangeTouchValid;
|
||
}
|
||
// Do a new loop. Took out the ones we have already done :-)
|
||
// 34: "[DCR: All Spells] Ranged Spells. Should use closer spells/move nearer"
|
||
DebugActionSpeakByInt(34);
|
||
// We go through, reducing amount by 1.
|
||
if(AI_AttemptAllSpells(iLowestSpellLevel, iBABCheckHighestLevel, iLastCheckedRange + i1, RangeLongValid, RangeMediumValid, RangeShortValid, RangeTouchValid)) return TRUE;
|
||
}
|
||
// Return false. No spell cast.
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name ActionDragonBreath
|
||
//::///////////////////////////////////////////////
|
||
Wrapper to use dragon breath. TRUE if:
|
||
1. They don't have the spell's effects (unless iDamaging is TRUE)
|
||
2. We have it!
|
||
|
||
After it is used, iWingCounter is set, debug message, and re-set counter to 0.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//:://///////////////////////////////////////////*/
|
||
|
||
// Either the chosen class is a dragon, or the appearance type is a dragon type,
|
||
// we return TRUE.
|
||
int AI_GetIsDragon()
|
||
{
|
||
// Basic check
|
||
if(GlobalOurChosenClass == CLASS_TYPE_DRAGON)
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Appearance type (includes if we polymorph into one!)
|
||
switch(GlobalOurAppearance)
|
||
{
|
||
case APPEARANCE_TYPE_DRAGON_BLACK:
|
||
case APPEARANCE_TYPE_DRAGON_BLUE:
|
||
case APPEARANCE_TYPE_DRAGON_BRASS:
|
||
case APPEARANCE_TYPE_DRAGON_BRONZE:
|
||
case APPEARANCE_TYPE_DRAGON_COPPER:
|
||
case APPEARANCE_TYPE_DRAGON_GOLD:
|
||
case APPEARANCE_TYPE_DRAGON_GREEN:
|
||
case APPEARANCE_TYPE_DRAGON_RED:
|
||
case APPEARANCE_TYPE_DRAGON_SILVER:
|
||
case APPEARANCE_TYPE_DRAGON_WHITE:
|
||
// Sorta dragons
|
||
case APPEARANCE_TYPE_FAERIE_DRAGON:
|
||
case APPEARANCE_TYPE_PSEUDODRAGON:
|
||
// Tiny ones!
|
||
case APPEARANCE_TYPE_WYRMLING_BLACK:
|
||
case APPEARANCE_TYPE_WYRMLING_BLUE:
|
||
case APPEARANCE_TYPE_WYRMLING_BRASS:
|
||
case APPEARANCE_TYPE_WYRMLING_BRONZE:
|
||
case APPEARANCE_TYPE_WYRMLING_COPPER:
|
||
case APPEARANCE_TYPE_WYRMLING_GOLD:
|
||
case APPEARANCE_TYPE_WYRMLING_GREEN:
|
||
case APPEARANCE_TYPE_WYRMLING_RED:
|
||
case APPEARANCE_TYPE_WYRMLING_SILVER:
|
||
case APPEARANCE_TYPE_WYRMLING_WHITE:
|
||
// Hordes ones
|
||
case 425: // Dragon_Pris
|
||
case 418: // Dragon_Shadow
|
||
case 405: // Dracolich
|
||
return TRUE;
|
||
break;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// Uses tBreath if they are not immune
|
||
// - TRUE if used.
|
||
int AI_ActionUseBreath(object oTarget, talent tBreath, int iSpellID)
|
||
{
|
||
int iImmune = FALSE;// If TRUE, don't use it it
|
||
// Go through them...
|
||
switch(iSpellID)
|
||
{
|
||
case SPELLABILITY_DRAGON_BREATH_FEAR: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_FEAR); break;
|
||
case SPELLABILITY_DRAGON_BREATH_PARALYZE: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_PARALYSIS); break;
|
||
case SPELLABILITY_DRAGON_BREATH_SLEEP: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_SLEEP); break;
|
||
case SPELLABILITY_DRAGON_BREATH_SLOW: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_SLOW); break;
|
||
case SPELLABILITY_DRAGON_BREATH_WEAKEN: iImmune = GetIsImmune(oTarget, IMMUNITY_TYPE_ABILITY_DECREASE); break;
|
||
}
|
||
// Use it!
|
||
if(!iImmune)
|
||
{
|
||
// 35: "[DCR:Dragon] Breath weapon & attacking [Breath ID] " + IntToString(iSpellID) + " [Target] " + GetName(oTarget)
|
||
DebugActionSpeakByInt(35, oTarget, iSpellID);
|
||
ActionUseTalentAtLocation(tBreath, GetLocation(oTarget));
|
||
ActionAttack(oTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
int AI_ActionDragonBreath(object oTarget, int iWingCounter)
|
||
{
|
||
// Get a random breath...
|
||
talent tBreath = GetCreatureTalentRandom(TALENT_CATEGORY_DRAGONS_BREATH);
|
||
if(GetIsTalentValid(tBreath))
|
||
{
|
||
int iTypeRandom, iTypeBest;
|
||
// Check if it affects them
|
||
iTypeRandom = GetIdFromTalent(tBreath);
|
||
if(!GetHasSpellEffect(iTypeRandom, oTarget))
|
||
{
|
||
if(AI_ActionUseBreath(oTarget, tBreath, iTypeRandom)) return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Try again...best this time
|
||
tBreath = GetCreatureTalentBest(TALENT_CATEGORY_DRAGONS_BREATH, i20);
|
||
if(GetIsTalentValid(tBreath))
|
||
{
|
||
iTypeBest = GetIdFromTalent(tBreath);
|
||
if(iTypeBest != iTypeRandom && !GetHasSpellEffect(iTypeBest, oTarget))
|
||
{
|
||
if(AI_ActionUseBreath(oTarget, tBreath, iTypeBest)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
int AI_DragonBreathOrWing(object oTarget)
|
||
{
|
||
// We may re-set SpellHostBreath if invalid, and we are polymorphed
|
||
if(SpellHostBreath == FALSE && AI_GetAIHaveEffect(GlobalEffectPolymorph))
|
||
{
|
||
SpellHostBreath = GetIsTalentValid(GetCreatureTalentBest(TALENT_CATEGORY_DRAGONS_BREATH, MAXCR));
|
||
}
|
||
|
||
// Breath attack, or wing buffet. Which one?!
|
||
// Check if we can do either...and that we are huge - IE not persuado dragon!
|
||
if(SpellHostBreath || GlobalOurSize >= CREATURE_SIZE_HUGE)
|
||
{
|
||
// Adds one to all things, by default, every call. This will randomise when to use things.
|
||
int nBreath = GetAIInteger(AI_DRAGONS_BREATH);
|
||
int nWing = GetAIInteger(AI_WING_BUFFET);
|
||
int iAboveXBreath = GetBoundriedAIInteger(AI_DRAGON_FREQUENCY_OF_BUFFET, i3);
|
||
int iAboveXWing = GetBoundriedAIInteger(AI_DRAGON_FREQUENCY_OF_BREATH, i3);
|
||
// There is a small chance of actuall reducing it for one after it, or
|
||
// adding another!
|
||
if(d20() == i1 && (nWing > FALSE || nBreath > FALSE))// 5% chance of x2 the number!
|
||
{
|
||
nWing *= i2;
|
||
nBreath *= i2;
|
||
}
|
||
else if(d20() == i1)// 5% of reducing each by 1.
|
||
{
|
||
nWing--;
|
||
nBreath--;
|
||
}
|
||
else // Else we add 1 as normal
|
||
{
|
||
// Add one...
|
||
nWing++;
|
||
nBreath++;
|
||
}
|
||
// Set normal values to locals again
|
||
SetAIInteger(AI_WING_BUFFET, nWing);
|
||
SetAIInteger(AI_DRAGONS_BREATH, nBreath);
|
||
// Check 1. If breath is over 2 of wing, we may use it.
|
||
if(!GetSpawnInCondition(AI_FLAG_COMBAT_NO_WING_BUFFET, AI_COMBAT_MASTER) && SpellHostBreath &&
|
||
nBreath >= iAboveXBreath && nBreath >= (nWing + i2))
|
||
{
|
||
// We don't attack, with breath, our own dragons (IE as 3E rules, and
|
||
// the factmost dragons do have that immunity to that damage in place)
|
||
if(GetAppearanceType(oTarget) != GlobalOurAppearance)
|
||
{
|
||
if(AI_ActionDragonBreath(oTarget, nWing))
|
||
{
|
||
SetAIInteger(AI_DRAGONS_BREATH, i0);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}
|
||
// Else wing must be higher, or no breath!
|
||
// So we use wing buffet, re-set that, then try breath to end...
|
||
if(nWing >= iAboveXWing && GlobalOurSize >= CREATURE_SIZE_HUGE &&
|
||
GetCreatureSize(oTarget) < CREATURE_SIZE_HUGE)
|
||
{
|
||
// 36: "[DCR:Dragon] Wing Buffet [Target] " + GetName(oTarget)
|
||
DebugActionSpeakByInt(36, oTarget);
|
||
// Reset wing buffet counter
|
||
SetAIInteger(AI_WING_BUFFET, i0);
|
||
SetAIInteger(AI_DRAGONS_BREATH, nBreath);
|
||
// - Not action do command, just Execute Script
|
||
ExecuteScript(FILE_DRAGON_WING_BUFFET, OBJECT_SELF);
|
||
return TRUE;
|
||
}
|
||
// Breath final...
|
||
if(SpellHostBreath && nBreath >= iAboveXBreath)
|
||
{
|
||
// Breaths.
|
||
if(AI_ActionDragonBreath(oTarget, nWing))
|
||
{
|
||
SetAIInteger(AI_DRAGONS_BREATH, i0);
|
||
return TRUE;
|
||
}
|
||
}
|
||
}//End any check
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name TalentDragonCombat
|
||
//::///////////////////////////////////////////////
|
||
Main call for dragons. This will cast major spells,
|
||
use feats, wing buffet and breath weapons.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Bioware. Heavily Modified: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptDragonCombat()
|
||
{
|
||
// We will attempt all spells possible with SRA on
|
||
if(SRA)
|
||
{
|
||
if(AI_AttemptAllSpells()) return TRUE;
|
||
}
|
||
|
||
// Now, we use DRAGONS BREATH! MUHAHAHAHAH!
|
||
// OR wing buffet! Yeehaw!
|
||
// - This is done on a breath by breath basis, with GetAppearance checking!
|
||
// - Basically, these are as powerful (if not more so) then level 9 spells
|
||
if(AI_DragonBreathOrWing(GlobalMeleeTarget)) return TRUE;
|
||
|
||
// Chance each round to use best spells possible.
|
||
// We, always, love level 9 spells! just great, especially if the dragon has them!
|
||
if(AI_AttemptAllSpells(i9)) return TRUE;
|
||
|
||
// We may attack our GlobalMeleeTarget if they are very weak in the AC
|
||
// department!
|
||
// - We will always hit
|
||
// - They have no DR protections
|
||
// - They have under 50 current hit points
|
||
// - They are in HTH combat range.
|
||
if(GlobalOurBaseAttackBonus - i5 >= GlobalMeleeTargetAC &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasStoneSkinProtections, GlobalMeleeTarget) &&
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasVisageProtections, GlobalMeleeTarget) &&
|
||
GetCurrentHitPoints(GlobalMeleeTarget) < i50 && GlobalRangeToMeleeTarget < f4)
|
||
{
|
||
// We then attack with feats, or whatever :-)
|
||
// - This includes flying
|
||
AI_AttemptMeleeAttackWrapper();
|
||
return TRUE;
|
||
}
|
||
// We will use more spells if there are more enemies - as AOE spells will
|
||
// be *maybe* better to use.
|
||
int iSpellLowestMinus;
|
||
if(GlobalMeleeAttackers > i4 || GlobalTotalSeenHeardEnemies > i6)
|
||
{
|
||
// -6
|
||
iSpellLowestMinus = i6;
|
||
}
|
||
else
|
||
{
|
||
// -2d4
|
||
iSpellLowestMinus = d4(i2);
|
||
}
|
||
// We randomly choose what to use...
|
||
if(AI_AttemptAllSpells(i9 - iSpellLowestMinus, i0 + Random(i6)))
|
||
{
|
||
// We also ActionAttack the enemy, as to move closer :-)
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
|
||
// We then attack with feats, or whatever :-)
|
||
// - This includes flying
|
||
return AI_AttemptMeleeAttackWrapper();
|
||
}
|
||
// Beholder teleport attempt. Flees from combat.
|
||
int AI_ActionBeholderTeleport()
|
||
{
|
||
// Go from futhest to nearest seen allies
|
||
int iCnt = GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_ALLIES_RANGE_SEEN);
|
||
if(iCnt <= FALSE) return FALSE;
|
||
int iBreak = FALSE;
|
||
object oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
if(!GetIsObjectValid(oEnemy) || GetDistanceToObject(oEnemy) > f5) return FALSE;
|
||
// Loop futhest to nearest
|
||
object oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oAlly) && iBreak != TRUE)
|
||
{
|
||
// Check nearest enemy to the ally.
|
||
oEnemy = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, oAlly, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
if(GetDistanceToObject(oEnemy) > f5)
|
||
{
|
||
iBreak = TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Next futhest.
|
||
iCnt--;
|
||
oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
}
|
||
// If true, we run to the ally
|
||
if(iBreak)
|
||
{
|
||
// 37: "[DCR] Beholder Teleport"
|
||
DebugActionSpeakByInt(37);
|
||
effect eBeholder = EffectDisappearAppear(GetLocation(oAlly));
|
||
float fTime = f3 + (GetDistanceToObject(oAlly)/f10);
|
||
// Apply effect
|
||
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eBeholder, OBJECT_SELF, fTime);
|
||
// Determine Combat Round
|
||
DelayCommand(fTime + f1, DetermineCombatRound(GlobalMeleeTarget));
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Beholders have special rays for eyes. This is used if the setting is set
|
||
// on spawn for beholders, or if the appearance is a beholder.
|
||
int AI_AttemptBeholderCombat()
|
||
{
|
||
// We will randomly teleport away if low HP, then heal
|
||
if(GlobalOurPercentHP < GetBoundriedAIInteger(AI_HEALING_US_PERCENT, i50, i100, i1))
|
||
{
|
||
// Randomly teleport directly to an ally with no one near to them.
|
||
if(AI_ActionBeholderTeleport())
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Else attempt to heal because we are far enough away.
|
||
else if(AI_AttemptHealingSelf())
|
||
{
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
// We will attempt high-level spells first.
|
||
// - This will also make them use protections of course
|
||
// - Only level 9 and 8 spells.
|
||
if(AI_AttemptAllSpells(i8)) return TRUE;
|
||
|
||
// We attempt to fire beholder rays, or do antimagic cone.
|
||
// 736 Beholder_Special_Spell_AI - Handles Beholder rays
|
||
// 727 Beholder_Anti_Magic_Cone - Dispels all Magical effects, and...
|
||
/* Beholder anti magic cone
|
||
30m cone,
|
||
100% spell failure to all targets,
|
||
100% spellresistance to all targets
|
||
9 seconds duration
|
||
No save */
|
||
|
||
// 80% chance of rays if at range (must be seen!)
|
||
if(d100() <= i80 && GlobalSeenSpell)
|
||
{
|
||
// 38: "[DCR] Beholder Rays"
|
||
DebugActionSpeakByInt(38);
|
||
ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_ALLRAYS, GlobalSpellTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
|
||
return TRUE;
|
||
}
|
||
// Then magic or bust - no BAB checks
|
||
else if(AI_AttemptAllSpells(FALSE, FALSE))
|
||
{
|
||
return TRUE;
|
||
}
|
||
// Else there is a 20% chance of melee attack if enemy is at low HP
|
||
else if(GetCurrentHitPoints(GlobalMeleeTarget) <= i30 &&
|
||
GlobalMeleeTargetAC <= i25 &&
|
||
GlobalRangeToMeleeTarget < f5 && d100() <= i20)
|
||
{
|
||
if(AI_AttemptMeleeAttackWrapper())
|
||
{
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
// 38: "[DCR] Beholder Rays"
|
||
DebugActionSpeakByInt(38);
|
||
ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_ALLRAYS, GlobalSpellTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Then rays finally
|
||
else
|
||
{
|
||
// 38: "[DCR] Beholder Rays"
|
||
DebugActionSpeakByInt(38);
|
||
ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_ALLRAYS, GlobalSpellTarget, METAMAGIC_ANY, TRUE, 0, PROJECTILE_PATH_TYPE_DEFAULT, TRUE);
|
||
return TRUE;
|
||
}
|
||
// Should never return FALSE.
|
||
return FALSE;
|
||
}
|
||
// Taken from x2_ai_mflayer.
|
||
// Bioware's implimenation of the Mind Suck.
|
||
void MindFlayerSuck(object oTarget)
|
||
{
|
||
if(GetDistanceBetween(OBJECT_SELF, oTarget) < 1.5)
|
||
{
|
||
ActionMoveAwayFromObject(oTarget, FALSE, 1.5);
|
||
}
|
||
ActionMoveToObject(oTarget, FALSE, 1.5);
|
||
ActionDoCommand(SetFacingPoint(GetPosition(oTarget)));
|
||
ActionWait(0.5);
|
||
// normal brain suck
|
||
ActionCastSpellAtObject(AI_SPELLABILITY_SUCKBRAIN, oTarget,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT);
|
||
}
|
||
// Illithid Use special attacks.
|
||
// This is set on spawn, or by the user on spawn.
|
||
int AI_AttemptMindflayerCombat()
|
||
{
|
||
// We may attempt a brain suck as Bioware's mind flayer would.
|
||
|
||
// - Mind attack if the melee enemy is NOT polymorphed,
|
||
// but is stunned/paralyzed or Dazed + % chance
|
||
|
||
// Check polymorph
|
||
if(AI_GetAIHaveEffect(GlobalEffectPolymorph, GlobalMeleeTarget)) return FALSE;
|
||
|
||
// Stunned, held, uncommandable, it is 100%
|
||
if((AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalMeleeTarget) ||
|
||
AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalMeleeTarget)) ||
|
||
// 30% with daze only.
|
||
(AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalMeleeTarget) && d100() <= i30))
|
||
{
|
||
// Bioware function.
|
||
MindFlayerSuck(GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
// 70% chance of mind blast
|
||
else if(GetDistanceToObject(GlobalMeleeTarget) < f5 && d100() <= i70)
|
||
{
|
||
// Special mind blast
|
||
ActionCastSpellAtObject(551, OBJECT_SELF,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT);
|
||
}
|
||
// False means no special mind flayer attacks.
|
||
return FALSE;
|
||
}
|
||
|
||
|
||
// Sets a value, 1-5, for what we can Dispel. Also sets
|
||
// a 1-5 value for breach spells.
|
||
// The values are NOT, I repeat NOT what the spell level are. Generally, they
|
||
// class spell-stopping spells as a higher prioritory to Dispel!
|
||
// * 5 - Dispeled before hostile spells are cast at target
|
||
// * 4 - Dispeled just before level 7 or so spells.
|
||
// * 3 - Dispeled just before level 4 or so spells
|
||
// * 2 - Dispeled just before level 2 or so spells.
|
||
// * 1 - Lowest prioritory - Dispeled at the end.
|
||
// There are NO cantrips included (level 0 spells).
|
||
void AI_SetDispelableEnchantments()
|
||
{
|
||
// We CANNOT dispel any of the epic spells, so we DO NOT check for them
|
||
// here!!!
|
||
// Note that we do not dispel GlobalDispelTarget if they are stopped
|
||
// somehow.
|
||
|
||
// Summons dispelling
|
||
object oMaster = GetMaster(GlobalDispelTarget);
|
||
if(GetIsObjectValid(oMaster))
|
||
{
|
||
// First, is it the black blade we can dispel? (If cast aganst the master)
|
||
if(GetTag(GlobalDispelTarget) == "x2_s_bblade")
|
||
{
|
||
// Check if seen
|
||
if(GetObjectSeen(oMaster) || GetObjectHeard(oMaster))
|
||
{
|
||
// Dispel the caster
|
||
GlobalDispelTarget = oMaster;
|
||
GlobalDispelTargetHighestDispel = i5;
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// Else, not seen or heard, so we try and dispel just the sword
|
||
GlobalDispelTargetHighestDispel = i5;
|
||
return;
|
||
}
|
||
}
|
||
// Check if we can dispel the master of the summon
|
||
else if(GetObjectSeen(oMaster) || GetObjectHeard(oMaster))
|
||
{
|
||
// Dispel the caster
|
||
GlobalDispelTarget = oMaster;
|
||
GlobalDispelTargetHighestDispel = i5;
|
||
return;
|
||
}
|
||
// Else - normal dispel behaviour. I don't think targeting dispels at
|
||
// a summon will kill them!
|
||
}
|
||
// If not, we see if it is appropriate to even try and dispel them.
|
||
// - Stun, sleep, fear
|
||
if(AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalDispelTarget) ||
|
||
// - Petrify
|
||
AI_GetAIHaveEffect(GlobalEffectPetrify, GlobalDispelTarget) ||
|
||
// - Paralyze
|
||
AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalDispelTarget) ||
|
||
// - Plot flag
|
||
GetPlotFlag(GlobalDispelTarget))
|
||
{
|
||
if(GlobalDispelTarget == GlobalSpellTarget)
|
||
{
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
// We can check GlobalSpellTarget if this target is not a good one
|
||
// to target.
|
||
GlobalDispelTarget = GlobalSpellTarget;
|
||
// Invalid
|
||
if(!GetIsObjectValid(GlobalDispelTarget) ||
|
||
// - Stun, sleep, fear
|
||
AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalDispelTarget) ||
|
||
// - Petrify
|
||
AI_GetAIHaveEffect(GlobalEffectPetrify, GlobalDispelTarget) ||
|
||
// - Paralyze
|
||
AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalDispelTarget) ||
|
||
// - Plot flag
|
||
GetPlotFlag(GlobalDispelTarget))
|
||
{
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// We check the spell it has been cast from :-)
|
||
int iSpellID, iSpellCounter;
|
||
// We check all of thier effects.
|
||
effect eCheck = GetFirstEffect(GlobalDispelTarget);
|
||
// Loop around valid effects.
|
||
while(GetIsEffectValid(eCheck) &&
|
||
// Break loop if we are already at 5
|
||
GlobalDispelTargetHighestDispel < i5 &&
|
||
GlobalDispelTargetHighestBreach < i5)
|
||
{
|
||
iSpellID = GetEffectSpellId(eCheck);
|
||
// Make sure that it is equal, or over, 0 (IE acid fog)
|
||
// - Must be magical, else DispelMagic will not work.
|
||
if(iSpellID >= i0 && GetEffectSubType(eCheck) == SUBTYPE_MAGICAL)
|
||
{
|
||
// We then switch the spells (I tihnk it is easier to debug :-) )
|
||
// and set what level of benifical enchantment it is :-)
|
||
switch(iSpellID)
|
||
{
|
||
// * 5 - Dispeled before hostile spells are cast at target
|
||
// Level 5 Breach Spells - should be Dispeled before attacking.
|
||
case SPELL_GREATER_SPELL_MANTLE: // Stops all offensive spells!
|
||
case SPELL_SPELL_MANTLE: // Stops all offensive spells!
|
||
case SPELL_LESSER_SPELL_MANTLE: // Stops all offensive spells!
|
||
case SPELL_SHADOW_SHIELD: // Immunity to negative energy, death spells + Necromancy! - so Dispel
|
||
case SPELL_ENERGY_BUFFER: // 40/- Elemental reistance can stop some level 9 spells! :-)
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i5) GlobalDispelTargetHighestDispel = i5;
|
||
if(GlobalDispelTargetHighestBreach < i5) GlobalDispelTargetHighestBreach = i5;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// Level 5 other spells.
|
||
case SPELL_UNDEATHS_ETERNAL_FOE: // Stops quite a bit, and is a level 9 cleric spell.
|
||
case SPELL_HOLY_AURA: // 25 spell resistance! (and mind immunity)
|
||
case SPELL_UNHOLY_AURA:// 25 spell resistance! (and mind immunity)
|
||
case SPELL_SPELL_RESISTANCE: // 11 + Caster level in spell resistance.
|
||
case SPELL_DEATH_WARD: // Immunity to death spells - so Dispel
|
||
case SPELL_NEGATIVE_ENERGY_PROTECTION:// Immunity to negative energy, - so Dispel
|
||
case SPELL_TRUE_SEEING:// Can see anything - powerful against invis!
|
||
case SPELL_SHAPECHANGE:// VERY Powerful polymorphing.
|
||
case SPELL_PROTECTION_FROM_SPELLS:// +8 on all saves
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i5) GlobalDispelTargetHighestDispel = i5;
|
||
if(GlobalDispelTargetHighestBreach < i5) GlobalDispelTargetHighestBreach = i5;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// * 4 - Dispeled just before level 7 or so spells.
|
||
case SPELL_PREMONITION: // Damage reduction 30/+5. Do this for fighters.
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i4) GlobalDispelTargetHighestDispel = i4;
|
||
if(GlobalDispelTargetHighestBreach < i4) GlobalDispelTargetHighestBreach = i4;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// Most other decent protection spells, especially ones which will
|
||
// disrupt all castings.
|
||
case SPELL_CLARITY: // Immunity: mind spells.
|
||
case SPELL_MASS_HASTE: // Why should they be hasted! >:-D
|
||
case SPELL_HASTE: // Why should they be hasted! >:-D
|
||
case SPELL_PROTECTION_FROM_ELEMENTS: // 30/- Protection
|
||
case SPELL_REGENERATE: // +5 regen is quite powerful!
|
||
case SPELL_TENSERS_TRANSFORMATION://ANother powerful +AB ETC spell.
|
||
// High level summons
|
||
case SPELL_ELEMENTAL_SWARM: // Swarm
|
||
case SPELL_SUMMON_CREATURE_IX:// 9
|
||
case SPELL_BLACK_BLADE_OF_DISASTER: // Black blade
|
||
case SPELL_SUMMON_CREATURE_VIII:// 8
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i4) GlobalDispelTargetHighestDispel = i4;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// * 3 - Dispeled just before level 4 or so spells
|
||
case SPELL_GLOBE_OF_INVULNERABILITY: // Stops level 1-4 spells.
|
||
case SPELL_GREATER_STONESKIN: // 20/+5 DR. Help fighters half-way though spells.
|
||
case SPELL_RESIST_ELEMENTS: // 20/- Protection
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i3) GlobalDispelTargetHighestDispel = i3;
|
||
if(GlobalDispelTargetHighestBreach < i3) GlobalDispelTargetHighestBreach = i3;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// Increases in abilites, and some which stop level 4, or nearby, spells.
|
||
case SPELL_AURA_OF_VITALITY: // "All allies within the AOE gain +4 Str, Con, Dex"!!
|
||
case SPELL_FREEDOM_OF_MOVEMENT:// Freedom - web ETC may be affected (and slow!)
|
||
case SPELL_MAGIC_CIRCLE_AGAINST_EVIL:// +saves, AC, and mind immunity
|
||
case SPELL_MAGIC_CIRCLE_AGAINST_GOOD:// +saves, AC, and mind immunity
|
||
case SPELL_SEE_INVISIBILITY: // See through invisiblity
|
||
case SPELL_MESTILS_ACID_SHEATH: // 2/level + 1d6 acid damage - damage shield.
|
||
// Quite High level summons
|
||
case SPELL_SUMMON_CREATURE_VII: // 7
|
||
case SPELL_SUMMON_CREATURE_VI: // 6
|
||
case SPELL_SUMMON_CREATURE_V: // 5
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i3) GlobalDispelTargetHighestDispel = i3;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// * 2 - Dispeled just before level 2 or so spells.
|
||
case SPELL_MINOR_GLOBE_OF_INVULNERABILITY: // Immunity 1-2 level spells.
|
||
case SPELL_ETHEREAL_VISAGE: // Immunity 1-2 level spells (and some DR)
|
||
case SPELL_ENDURE_ELEMENTS: // 10/- Reduction. SPELL_GHOSTLY_VISAGE
|
||
case SPELL_GHOSTLY_VISAGE: // 0-1 level spell immunity, and 10% consealment
|
||
case SPELL_STONESKIN: // 10/+5 DR. Help fighters just before low-end spells.
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i2) GlobalDispelTargetHighestDispel = i2;
|
||
if(GlobalDispelTargetHighestBreach < i2) GlobalDispelTargetHighestBreach = i2;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// Things that stop level 2 or 1 spells, and low-end ones which
|
||
// may hinder lower-end castings, and very powreful benifical
|
||
// enhancements.
|
||
case SPELL_DISPLACEMENT: // +50% consealment. Helps fighters.
|
||
case SPELL_EXPEDITIOUS_RETREAT:// +150% speed. Dispel here.
|
||
case SPELL_GREATER_SHADOW_CONJURATION_MINOR_GLOBE: // Immunity 1-2 level spells.
|
||
case SPELL_PROTECTION_FROM_EVIL:// Mind immunity mostly.
|
||
case SPELL_PROTECTION_FROM_GOOD:// Mind immunity mostly.
|
||
case SPELL_SHIELD: // +AC + Immunity magic missile
|
||
// Enhancements
|
||
case SPELL_DIVINE_POWER:// +Stat, HP, base attack bonus.
|
||
case SPELL_DIVINE_FAVOR:// +1-5 Attack/damage
|
||
case SPELL_ELEMENTAL_SHIELD: // +Damage Sheild (gives HTH attackers damage)
|
||
// Moderate level summons
|
||
case SPELL_SUMMON_CREATURE_IV: // 4
|
||
case SPELL_SUMMON_CREATURE_III: // 3
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i2) GlobalDispelTargetHighestDispel = i2;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
// * 1 - Lowest prioritory - Dispeled at the end.
|
||
// The rest - mostly low-end AC increases, bless and so forth.
|
||
// Before we attack, we Dispel them (and therefore help allies!)
|
||
case SPELL_MAGE_ARMOR: // +AC
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i1) GlobalDispelTargetHighestDispel = i1;
|
||
if(GlobalDispelTargetHighestBreach < i1) GlobalDispelTargetHighestBreach = i1;
|
||
iSpellCounter++;
|
||
}
|
||
// Non-breach
|
||
case SPELL_AID: // +Some HP and attack
|
||
case SPELL_AMPLIFY: // +20 listen
|
||
case SPELL_BARKSKIN: // +AC
|
||
case SPELL_BLESS: // +Attack, damage (AOE)
|
||
case SPELL_BLOOD_FRENZY:// +Attack, damage (Rage-like)
|
||
case SPELL_BULLS_STRENGTH:// +Stat
|
||
case SPELL_CATS_GRACE: // +Stat
|
||
case SPELL_EAGLE_SPLEDOR:// +Stat
|
||
case SPELL_ENTROPIC_SHIELD: // +20% consealment VS ranged.
|
||
case SPELL_OWLS_WISDOM: // +Stat
|
||
case AI_SPELL_OWLS_INSIGHT: // +Stat (Owls insight)
|
||
case SPELL_SANCTUARY: // Invisiblity-like.
|
||
case SPELL_SHIELD_OF_FAITH:// +2-5 AC
|
||
case SPELL_WAR_CRY: // +Attack, damage
|
||
case SPELL_WOUNDING_WHISPERS:// +Damage Sheild (gives HTH attackers damage)
|
||
case SPELL_STONE_BONES: // +3 AC to undead.
|
||
case SPELL_BATTLETIDE: // +2 Saves, attack, damage, also a negative AOE.
|
||
// Low level summons
|
||
case SPELL_SUMMON_CREATURE_II: // 2
|
||
case SPELL_SUMMON_CREATURE_I: // 1
|
||
{
|
||
if(GlobalDispelTargetHighestDispel < i1) GlobalDispelTargetHighestDispel = i1;
|
||
iSpellCounter++;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
eCheck = GetNextEffect(GlobalDispelTarget);
|
||
}
|
||
// We might dispel anything.
|
||
if(!GetSpawnInCondition(AI_FLAG_COMBAT_DISPEL_IN_ORDER, AI_COMBAT_MASTER))
|
||
{
|
||
if(GlobalDispelTargetHighestBreach) GlobalDispelTargetHighestBreach = i5;
|
||
if(GlobalDispelTargetHighestDispel) GlobalDispelTargetHighestDispel = i5;
|
||
}
|
||
// Do we have a ton of spells? We add 1 to the prioritory for every 10 spells
|
||
// applied.
|
||
if(iSpellCounter > i5) GlobalDispelTargetHighestDispel += iSpellCounter / i10;
|
||
if(GlobalDispelTargetHighestDispel > i5) GlobalDispelTargetHighestDispel = i5;
|
||
}
|
||
|
||
// Just sorts out sOriginalArrayName to sNewArrayName based on range only.
|
||
void AI_TargetingArrayDistanceStore(string sOriginalArrayName, string sNewArrayName)
|
||
{
|
||
int bFilterPC;
|
||
if(sOriginalArrayName == ARRAY_TEMP_ENEMIES &&
|
||
GetSpawnInCondition(AI_FLAG_TARGETING_FILTER_FOR_PC_TARGETS, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
// Check for the nearest seen, enemy PC.
|
||
bFilterPC = GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF, 1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN));
|
||
}
|
||
// Make sure sNewArrayName is cleared
|
||
DeleteLocalInt(OBJECT_SELF, MAXINT_ + sNewArrayName);
|
||
int iCnt = i1;
|
||
float fSetUpRange;
|
||
// Now, we check for things like if to do melee or ranged, or whatever :-)
|
||
object oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
while(GetIsObjectValid(oTarget))
|
||
{
|
||
if(!bFilterPC || GetIsPC(oTarget))
|
||
{
|
||
fSetUpRange = GetDistanceToObject(oTarget);
|
||
// We set it to sNewArrayName - highest to lowest.
|
||
SetArrayFloatValue(sNewArrayName, oTarget, fSetUpRange);
|
||
}
|
||
// Next one
|
||
iCnt++;
|
||
oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
}
|
||
// If we have no valid targets, and PC's are on, we try again.
|
||
if(bFilterPC && !GetIsObjectValid(GetLocalObject(OBJECT_SELF, sNewArrayName + s1)))
|
||
{
|
||
// Re-run again.
|
||
iCnt = i1;
|
||
oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
while(GetIsObjectValid(oTarget))
|
||
{
|
||
fSetUpRange = GetDistanceToObject(oTarget);
|
||
// We set it to sNewArrayName - highest to lowest.
|
||
SetArrayFloatValue(sNewArrayName, oTarget, fSetUpRange);
|
||
// Next one
|
||
iCnt++;
|
||
oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
}
|
||
}
|
||
// delete old ones.
|
||
int iCheck;
|
||
for(iCheck = i1; iCheck <= iCnt; iCheck++)
|
||
{
|
||
// Delete the old one
|
||
DeleteLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
}
|
||
}
|
||
|
||
// Just sorts out sOriginalArrayName to sNewArrayName based on iType.
|
||
// iType 1 = AC, iType 2 = Total Saves, iType 3 = Phisical Protections,
|
||
// iType 4 = BAB, iType 5 = Hit Dice, iType 6 = Percent HP, iType 7 = Current HP,
|
||
// iType 8 = Maximum HP. 9 = Attacking us or not.
|
||
void AI_TargetingArrayIntegerStore(int iType, string sOriginalArrayName)
|
||
{
|
||
int iCnt = i1;
|
||
int iValue;
|
||
// Now, we check for things like if to do melee or ranged, or whatever :-)
|
||
object oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
while(GetIsObjectValid(oTarget))
|
||
{
|
||
if(iType == i1)
|
||
{
|
||
// AC
|
||
iValue = GetAC(oTarget);
|
||
}
|
||
else if(iType == i2)
|
||
{
|
||
// Total saving throws.
|
||
iValue = GetFortitudeSavingThrow(oTarget) +
|
||
GetReflexSavingThrow(oTarget) +
|
||
GetReflexSavingThrow(oTarget);
|
||
}
|
||
else if(iType == i3)
|
||
{
|
||
// Damage reduction
|
||
// 30/+5
|
||
if(GetHasSpellEffect(SPELL_PREMONITION, oTarget))
|
||
{
|
||
iValue = i30;
|
||
}
|
||
// 20/+5, 20/+3
|
||
else if(GetHasSpellEffect(SPELL_GREATER_STONESKIN, oTarget) ||
|
||
GetHasSpellEffect(SPELL_ETHEREAL_VISAGE, oTarget))
|
||
{
|
||
iValue = i20;
|
||
}
|
||
// 10/+5 10/+3
|
||
else if(GetHasSpellEffect(SPELL_SHADOW_SHIELD, oTarget) ||
|
||
GetHasSpellEffect(SPELL_STONESKIN, oTarget))
|
||
{
|
||
iValue = i10;
|
||
}
|
||
// 5/+1
|
||
else if(GetHasSpellEffect(SPELL_GHOSTLY_VISAGE, oTarget))
|
||
{
|
||
iValue = i5;
|
||
}
|
||
else
|
||
{
|
||
iValue = i0;
|
||
}
|
||
}
|
||
else if(iType == i4)
|
||
{
|
||
// BAB
|
||
iValue = GetBaseAttackBonus(oTarget);
|
||
}
|
||
else if(iType == i5)
|
||
{
|
||
// Hit dice
|
||
iValue = GetHitDice(oTarget);
|
||
}
|
||
else if(iType == i6)
|
||
{
|
||
// %HP
|
||
iValue = AI_GetPercentOf(GetCurrentHitPoints(oTarget), GetMaxHitPoints(oTarget));
|
||
}
|
||
else if(iType == i7)
|
||
{
|
||
// Current
|
||
iValue = GetCurrentHitPoints(oTarget);
|
||
}
|
||
else if(iType == i8)
|
||
{
|
||
// Max
|
||
iValue = GetMaxHitPoints(oTarget);
|
||
}
|
||
else if(iType == i9)
|
||
{
|
||
// Sneak attack.
|
||
iValue = FALSE;
|
||
if(GetAttackTarget(oTarget) != OBJECT_SELF &&
|
||
!GetIsImmune(oTarget, IMMUNITY_TYPE_SNEAK_ATTACK))
|
||
{
|
||
iValue = TRUE;
|
||
}
|
||
}
|
||
// We set it to the new array name - highest to lowest.
|
||
SetArrayIntegerValue(ARRAY_TEMP_ARRAY, oTarget, iValue);
|
||
// Delete the old one
|
||
if(iType != i9)// Sneak attack, if not a valid sneak target, we target normally so don't delete
|
||
{
|
||
DeleteLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
}
|
||
// Next target
|
||
iCnt++;
|
||
oTarget = GetLocalObject(OBJECT_SELF, sOriginalArrayName + IntToString(iCnt));
|
||
}
|
||
}
|
||
// Deletes all FLoats, Integers and Objects set to sArray for valid
|
||
// objects got by GetLocalObject to sArray.
|
||
void AI_TargetingArrayDelete(string sArray)
|
||
{
|
||
int iCnt = i1;
|
||
string sCnt = IntToString(iCnt);
|
||
object oLocal = GetLocalObject(OBJECT_SELF, sArray + sCnt);
|
||
while(GetIsObjectValid(oLocal))
|
||
{
|
||
// Delete all
|
||
DeleteLocalFloat(OBJECT_SELF, sArray + sCnt);
|
||
DeleteLocalInt(OBJECT_SELF, sArray + sCnt);
|
||
DeleteLocalObject(OBJECT_SELF, sArray + sCnt);
|
||
// Get next object
|
||
iCnt++;
|
||
sCnt = IntToString(iCnt);
|
||
oLocal = GetLocalObject(OBJECT_SELF, sArray + sCnt);
|
||
}
|
||
DeleteLocalInt(OBJECT_SELF, MAXINT_ + sArray);
|
||
}
|
||
|
||
// This sets ARRAY_TEMP_ARRAY of integer values to sNewArrayName.
|
||
// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER.
|
||
// - We work until iMinimum is filled, or we get to iMinimum and we get to
|
||
// a target with value > iImputMinimum. (20 - 25 > X?)
|
||
int AI_TargetingArrayLimitTargets(string sNewArrayName, int iTypeOfTarget, int iImputMinLimit, int iMinLoop, int iMaxLoop)
|
||
{
|
||
int iAddEachTime, iValue, iCnt, iCnt2, iCnt3, iBreak, iLowestHighestValue;
|
||
string sCnt;
|
||
object oSetUpTarget;
|
||
// Is it lowest to highest, or highest to lowest?
|
||
// - 1. Lowest to highest
|
||
if(iTypeOfTarget == TARGET_LOWER)
|
||
{
|
||
iAddEachTime = i1;
|
||
iCnt = i1;
|
||
}
|
||
else // if(iTypeOfTarget == TARGET_HIGHER)
|
||
{
|
||
// Change to start at top value, and work down.
|
||
iAddEachTime = iM1;
|
||
iCnt = GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY);
|
||
}
|
||
|
||
// Start loop from array based on AC. Overrides exsisting targets
|
||
// - Use temp enemy array. ARRAY_TEMP_ARRAY
|
||
iCnt2 = i0;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE)
|
||
{
|
||
// We check AC or whatever...
|
||
iValue = GetLocalInt(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
// Delete objects
|
||
DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
DeleteLocalInt(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
// Check values.
|
||
// we check if we have under minimum OR it is under/over X.
|
||
// As it is in value order, once we get to a point to stop, we
|
||
// break the loop.
|
||
if((iCnt2 < iMaxLoop) &&
|
||
((iCnt2 < iMinLoop) /*Default, get minimum targets*/
|
||
|| (iValue - iLowestHighestValue < iImputMinLimit && iTypeOfTarget == TARGET_LOWER)// Lowest to highest (20 - 25 < X?)
|
||
|| (iLowestHighestValue - iValue < iImputMinLimit && iTypeOfTarget == TARGET_HIGHER)))// Highest to lowest (25 - 20 < X?)
|
||
{
|
||
// Set this as the newest highest/lowest value
|
||
iLowestHighestValue = iValue;
|
||
// Add it to array.
|
||
iCnt2++;
|
||
sCnt = IntToString(iCnt2);
|
||
SetLocalObject(OBJECT_SELF, sNewArrayName + sCnt, oSetUpTarget);
|
||
SetLocalInt(OBJECT_SELF, sNewArrayName + sCnt, iValue);
|
||
}
|
||
else // else break out. (If got to max targets, got to a target we don't want to target)
|
||
{
|
||
for(iCnt3 = iCnt; iBreak != TRUE; iCnt3 += iAddEachTime)
|
||
{
|
||
// Remove all other values in the loop.
|
||
sCnt = IntToString(iCnt3);
|
||
if(GetIsObjectValid(GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt)))
|
||
{
|
||
DeleteLocalInt(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
}
|
||
else
|
||
{
|
||
iBreak = TRUE;
|
||
}
|
||
}
|
||
iBreak = TRUE;
|
||
}
|
||
// Get next AC target - we add X, which is either +1 or -1, to
|
||
// continue a loop going up or down.
|
||
iCnt += iAddEachTime;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
}
|
||
// Start resetting temp array used in the rest.
|
||
DeleteLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY);
|
||
// Returns the amount of targets stored.
|
||
return iCnt2;
|
||
}
|
||
// This sets ARRAY_TEMP_ARRAY of float values to sNewArrayName.
|
||
// - iTypeOfTarget, used TARGET_LOWER, TARGET_HIGHER.
|
||
// - We work until iMinimum is filled, or we get to iMinimum and we get to
|
||
// a target with value > iImputMinimum. (20.0 - 25.0 > X?)
|
||
// Returns the amount of targets set in sNewArrayName.
|
||
int AI_TargetingArrayLimitTargetsFloat(string sNewArrayName, int iTypeOfTarget, float fImputMinLimit, int iMinLoop, int iMaxLoop)
|
||
{
|
||
int iAddEachTime, iCnt, iCnt2, iCnt3, iBreak;
|
||
float fValue, fLowestHighestValue;
|
||
string sCnt;
|
||
object oSetUpTarget;
|
||
// Is it lowest to highest, or highest to lowest?
|
||
// - 1. Lowest to highest
|
||
if(iTypeOfTarget == TARGET_LOWER)
|
||
{
|
||
iAddEachTime = i1;
|
||
iCnt = i1;
|
||
}
|
||
else // if(iTypeOfTarget == TARGET_HIGHER)
|
||
{
|
||
// Change to start at top value, and work down.
|
||
iAddEachTime = iM1;
|
||
iCnt = GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY);
|
||
}
|
||
|
||
// Start loop from array based on AC. Overrides exsisting targets
|
||
// - Use temp enemy (AC) array.
|
||
iCnt2 = FALSE;// Reset counter
|
||
iCnt = i1;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE)
|
||
{
|
||
// We check range normally...
|
||
fValue = GetLocalFloat(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
// Check values.
|
||
// we check if we have under minimum OR it is under/over X.
|
||
// As it is in value order, once we get to a point to stop, we
|
||
// break the loop.
|
||
if((iCnt2 < iMaxLoop) &&
|
||
((iCnt2 < iMinLoop) /*Default, get minimum targets*/
|
||
|| (fValue - fLowestHighestValue < fImputMinLimit && iTypeOfTarget == TARGET_LOWER)// Lowest to highest (20 - 25 < X?)
|
||
|| (fLowestHighestValue - fValue < fImputMinLimit && iTypeOfTarget == TARGET_HIGHER)))// Highest to lowest (25 - 20 < X?)
|
||
{
|
||
// Set fLowestHighestValue
|
||
fLowestHighestValue = fValue;
|
||
// Add it to array.
|
||
iCnt2++;
|
||
sCnt = IntToString(iCnt2);
|
||
SetLocalObject(OBJECT_SELF, sNewArrayName + sCnt, oSetUpTarget);
|
||
SetLocalFloat(OBJECT_SELF, sNewArrayName + sCnt, fValue);
|
||
}
|
||
else // else break out. (If got to max targets, got to a target we don't want to target)
|
||
{
|
||
for(iCnt3 = iCnt; iBreak != TRUE; iCnt3 += iAddEachTime)
|
||
{
|
||
// Remove all other values in the loop.
|
||
sCnt = IntToString(iCnt3);
|
||
if(GetIsObjectValid(GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt)))
|
||
{
|
||
DeleteLocalFloat(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
}
|
||
else
|
||
{
|
||
iBreak = TRUE;
|
||
}
|
||
}
|
||
iBreak = TRUE;
|
||
}
|
||
// Delete objects
|
||
DeleteLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
DeleteLocalFloat(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
// Get next AC target - we add X, which is either +1 or -1, to
|
||
// continue a loop going up or down.
|
||
iCnt += iAddEachTime;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + sCnt);
|
||
}
|
||
// Start resetting temp array used in the rest.
|
||
DeleteLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_TEMP_ARRAY);
|
||
// Returns the amount of targets stored.
|
||
return iCnt2;
|
||
}
|
||
|
||
// Makes sure oTarget isn't:
|
||
// - Dead
|
||
// - Petrified
|
||
// - AI Ignore ON
|
||
// - DM
|
||
// Must be: Seen or heard
|
||
// Returns: TRUE if any of these are true.
|
||
int AI_GetTargetSanityCheck(object oTarget)
|
||
{
|
||
if(!GetIsObjectValid(oTarget) || // Isn't valid
|
||
GetIsDead(oTarget) || // Is dead
|
||
GetIsDM(oTarget) || // Is DM
|
||
GetIgnore(oTarget) || // Is ignored
|
||
AI_GetAIHaveEffect(GlobalEffectPetrify, oTarget) || // Is petrified
|
||
(!GetObjectSeen(oTarget) && !GetObjectHeard(oTarget))) // Is not seen nor heard.
|
||
{
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
// We set up targets to Global* variables, GlobalSpellTarget, GlobalRangeTarget,
|
||
// and GlobalMeleeTarget. Counts enemies, and so on.
|
||
// - Uses oIntruder (to attack or move near) if anything.
|
||
// - We return TRUE if it ActionAttack's, or moves to an enemy - basically
|
||
// that we cannot do an action, but shouldn't search. False if normal.
|
||
int AI_SetUpAllObjects(object oImputBackup)
|
||
{
|
||
// Delete past arrays
|
||
AI_TargetingArrayDelete(ARRAY_ENEMY_RANGE);
|
||
AI_TargetingArrayDelete(ARRAY_ENEMY_RANGE_SEEN);
|
||
AI_TargetingArrayDelete(ARRAY_ENEMY_RANGE_HEARD);
|
||
AI_TargetingArrayDelete(ARRAY_ALLIES_RANGE);
|
||
AI_TargetingArrayDelete(ARRAY_ALLIES_RANGE_SEEN);
|
||
AI_TargetingArrayDelete(ARRAY_ALLIES_RANGE_SEEN_BUFF);
|
||
|
||
// We always use range as at least 1 of the ways to get best target.
|
||
float fCurrentRange, fSetMaxWeCanGoTo;
|
||
location lSelf = GetLocation(OBJECT_SELF);
|
||
int iCnt, iCnt2, iCnt3, iBreak;//Counter loop.
|
||
int iValue, iMaxTurnsAttackingX, iMaximum, iMinimum, iRemainingTargets, iTypeOfTarget;
|
||
string sCnt; // IntToString(iCnt) :-)
|
||
object oTempLoopObject, oSetUpTarget, oLastHostile, oLastTarget, oImputBackUpToAttack;
|
||
|
||
// Note: Here we check if oIntruder is a sane thing to attack.
|
||
if(GetIsObjectValid(oImputBackup) && !GetIgnoreNoFriend(oImputBackup))
|
||
{
|
||
oImputBackUpToAttack = oImputBackup;
|
||
}
|
||
// Note: oIntruder is still used to search near if anything.
|
||
|
||
// SRA - Spell Ranged Attacking uses the nearest seen, or the nearest heard,
|
||
// for the spell target (not ranged or melee, however)
|
||
if(SRA)
|
||
{
|
||
// Checked below for validness ETC.
|
||
GlobalSpellTarget = GlobalNearestEnemySeen;
|
||
}
|
||
|
||
// We set up 2 other targets...for testing against ETC.
|
||
oLastHostile = GetLastHostileActor();
|
||
iMaxTurnsAttackingX = GetBoundriedAIInteger(AI_MAX_TURNS_TO_ATTACK_ONE_TARGET, i6, i40, i1);
|
||
|
||
// Start...
|
||
// Gets all CREATURES within 50M andin LOS. THIS IS THE MAJOR OBJECT LOOP OF THE AI!
|
||
GlobalTotalPeople = FALSE;// Reset
|
||
oSetUpTarget = GetFirstObjectInShape(SHAPE_SPHERE, f50, lSelf, TRUE);
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// We totally ignore DM's, and AI_IGNORE people
|
||
// - We don't use us as a target
|
||
// - We do dead people later, special with GetNearestCreature
|
||
if(!GetIgnore(oSetUpTarget) && !GetIsDM(oSetUpTarget) &&
|
||
!GetIsDead(oSetUpTarget) && oSetUpTarget != OBJECT_SELF)
|
||
{
|
||
// We count +1 more person in our LOS
|
||
GlobalTotalPeople++;
|
||
// If the target is a friend, we add 1 to targets, and set in array.
|
||
if(GetIsFriend(oSetUpTarget) || GetFactionEqual(oSetUpTarget))
|
||
{
|
||
GlobalTotalAllies++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_TEMP_ALLIES + IntToString(GlobalTotalAllies), oSetUpTarget);
|
||
}
|
||
// Enemy...add to enemy array, even if not seen nor heard.
|
||
else if(GetIsEnemy(oSetUpTarget))
|
||
{
|
||
//SpeakString("Enemy (no dead) in LOS:" + GetName(oSetUpTarget));
|
||
// We set up a "Seen or heard" enemies counter below
|
||
iCnt++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_TEMP_ENEMIES + IntToString(iCnt), oSetUpTarget);
|
||
}
|
||
}
|
||
oSetUpTarget = GetNextObjectInShape(SHAPE_SPHERE, f50, lSelf, TRUE);
|
||
}
|
||
// The first simple one is therefore done :-)
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Special case:
|
||
|
||
If we have NO nearest seen/heard enemies (GetNearest* calls) we:
|
||
- Check allies for thier targets
|
||
|
||
Nothing else for now.
|
||
//::////////////////////////////////////////////*/
|
||
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Enemies
|
||
|
||
Set up finalish arrays of enemies, and lots of counting numbers.
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// Next, we loop on range. Allies and Enemies
|
||
AI_TargetingArrayDistanceStore(ARRAY_TEMP_ENEMIES, ARRAY_ENEMY_RANGE);
|
||
|
||
// Before we start re-setting targets, we do set up an extra 2 arrays based
|
||
// on seen and heard (one OR the other) arrays for the enemy. These are objects in our LOS.
|
||
iCnt = i1;
|
||
iCnt2 = FALSE;// Counter for seen
|
||
iCnt3 = FALSE;// Counter for heard
|
||
iValue = FALSE;// Counter for BAB
|
||
iBreak = FALSE;// Counter for HD
|
||
GlobalEnemiesIn4Meters = FALSE;// Make sure at 0
|
||
GlobalMeleeAttackers = FALSE;// Make sure at 0
|
||
GlobalRangedAttackers = FALSE;// Make sure at 0
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
fCurrentRange = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE + IntToString(iCnt));
|
||
// Well, we have enemies in range. We check if we need to re-set constants
|
||
// based on a timer. If done, set a timer.
|
||
if(!GetLocalInt(oSetUpTarget, AI_JASPERRES_EFFECT_SET) &&
|
||
!GetLocalInt(oSetUpTarget, AI_TIMER_EFFECT_SET))
|
||
{
|
||
AI_SetEffectsOnTarget(oSetUpTarget);
|
||
SetLocalInt(oSetUpTarget, AI_TIMER_EFFECT_SET, TRUE);
|
||
// Just set effects every round, not every possible action (2 with haste)
|
||
DelayCommand(f5, DeleteLocalInt(oSetUpTarget, AI_TIMER_EFFECT_SET));
|
||
}
|
||
// Don't target things who are petrified.
|
||
if(!AI_GetAIHaveEffect(GlobalEffectPetrify, oSetUpTarget) && !GetIsDead(oSetUpTarget))
|
||
{
|
||
// Count ranged and melee attackers.
|
||
if(GetAttackTarget(oSetUpTarget) == OBJECT_SELF)
|
||
{
|
||
// Melee/ranged attacker?
|
||
if(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSetUpTarget)))
|
||
{
|
||
// - 1.3 beta fixed this. I didn't notice I got these mixed up :-P
|
||
GlobalRangedAttackers++;
|
||
}
|
||
else
|
||
{
|
||
GlobalMeleeAttackers++;
|
||
}
|
||
}
|
||
// Total enemies in 4M
|
||
if(fCurrentRange <= f4)
|
||
{
|
||
GlobalEnemiesIn4Meters++;
|
||
}
|
||
// It is nearest to futhest. Just set each one in one of 2 arrays.
|
||
if(GetObjectSeen(oSetUpTarget))
|
||
{
|
||
// iBreak counts average HD
|
||
iBreak += GetHitDice(oSetUpTarget);
|
||
// Value counts BAB
|
||
iValue += GetBaseAttackBonus(oSetUpTarget);
|
||
// Add to total seen/heard enemies
|
||
GlobalTotalSeenHeardEnemies++;
|
||
// Object seen.
|
||
iCnt2++;
|
||
SetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt2), fCurrentRange);
|
||
SetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt2), oSetUpTarget);
|
||
}
|
||
else if(GetObjectHeard(oSetUpTarget))
|
||
{
|
||
// iBreak counts average HD
|
||
iBreak += GetHitDice(oSetUpTarget);
|
||
// Value counts BAB
|
||
iValue += GetBaseAttackBonus(oSetUpTarget);
|
||
// Add to total seen/heard enemies
|
||
GlobalTotalSeenHeardEnemies++;
|
||
// Object heard.
|
||
iCnt3++;
|
||
SetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt3), fCurrentRange);
|
||
SetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt3), oSetUpTarget);
|
||
}
|
||
}
|
||
// Next enemy
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE + IntToString(iCnt));
|
||
}
|
||
// set GlobalRangeToFuthestEnemy.
|
||
// - Using futhest heard should be good enough.
|
||
GlobalRangeToFuthestEnemy = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt3));
|
||
|
||
// We need value just above to calcualte these, else leave at 0.
|
||
if(GlobalTotalSeenHeardEnemies >= i1 && iBreak >= i1)
|
||
{
|
||
// Average enemy HD
|
||
GlobalAverageEnemyHD = iBreak / GlobalTotalSeenHeardEnemies;
|
||
if(GlobalAverageEnemyHD < i1) GlobalAverageEnemyHD = i1;
|
||
// Average BAB
|
||
GlobalAverageEnemyBAB = iValue / GlobalTotalSeenHeardEnemies;
|
||
if(GlobalAverageEnemyBAB < i0) GlobalAverageEnemyBAB = i0;
|
||
}
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Friends
|
||
|
||
Sets up all allies things
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// Friendly (ally) targets too - for curing ETC.
|
||
AI_TargetingArrayDistanceStore(ARRAY_TEMP_ALLIES, ARRAY_ALLIES_RANGE);
|
||
|
||
// Spells which affect GetIsReactionType
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + s1);
|
||
|
||
// If not valid, use self
|
||
if(!GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Use nearest us, just for testing.
|
||
GlobalFriendlyFireHostile = GetIsReactionTypeHostile(OBJECT_SELF);
|
||
GlobalFriendlyFireFriendly = GetIsReactionTypeFriendly(OBJECT_SELF);
|
||
}
|
||
|
||
// Use nearest ally, just for testing.
|
||
GlobalFriendlyFireHostile = GetIsReactionTypeHostile(oSetUpTarget);
|
||
GlobalFriendlyFireFriendly = GetIsReactionTypeFriendly(oSetUpTarget);
|
||
GlobalNearestAlly = oSetUpTarget;
|
||
GlobalValidAlly = GetIsObjectValid(GlobalNearestAlly);
|
||
GlobalRangeToAlly = GetDistanceToObject(oSetUpTarget);
|
||
|
||
// 0 Seen/heard. We don't check enemies numbers - they could be hidden.
|
||
if(!GlobalValidNearestSeenEnemy && !GlobalValidNearestHeardEnemy)
|
||
{
|
||
iBreak = FALSE;
|
||
// Loop allies for ANY target, and move to them! (Attack in HTH, this
|
||
// lets us re-set targets On Percieve)
|
||
iCnt = iM1;
|
||
// Allys
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE)
|
||
{
|
||
// It is nearest to futhest.
|
||
oTempLoopObject = GetAttackTarget(oSetUpTarget);
|
||
if(GetIsObjectValid(oTempLoopObject))
|
||
{
|
||
// If a valid attack object, we stop!
|
||
iBreak = TRUE;
|
||
}
|
||
else
|
||
{
|
||
// Next ally
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt));
|
||
}
|
||
}
|
||
// We move to the target if we have one.
|
||
if(iBreak)
|
||
{
|
||
// Just a temp most damaging melee. Normally, it is best to
|
||
// just Move to the target
|
||
// 39: "[DCR:Targeting] No valid enemies in sight, moving to allies target's. [Target] " + GetName(oSetUpTarget)
|
||
DebugActionSpeakByInt(39, oSetUpTarget);
|
||
AI_EquipBestShield();
|
||
ActionEquipMostDamagingMelee();
|
||
ActionMoveToLocation(GetLocation(oSetUpTarget), TRUE);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// What other ally stuff?
|
||
// - Nearest leader
|
||
// - Who to heal? (Most damaged in X range of lesser damaged ones)
|
||
// - Who to heal effects from? (Not done here!)
|
||
// - Nearest ally
|
||
// - Buff ally
|
||
if(GlobalValidAlly)
|
||
{
|
||
// Set up an array of seen ranged-based allies, for healing and for
|
||
// healing effects, and for buffing.
|
||
iCnt = i1;
|
||
iCnt2 = i0;
|
||
iCnt3 = i0;
|
||
iValue = FALSE; // Just a temp for leader status set
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
iCnt3 += GetHitDice(oSetUpTarget);
|
||
if(GetObjectSeen(oSetUpTarget))
|
||
{
|
||
iCnt2++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt2), oSetUpTarget);
|
||
// Set global nearest seen leader for morale ETC.
|
||
if(GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_GROUP_LEADER, AI_OTHER_COMBAT_MASTER, oSetUpTarget)
|
||
&& iValue != TRUE)
|
||
{
|
||
GlobalNearestLeader = oSetUpTarget;
|
||
iValue = TRUE;
|
||
}
|
||
}
|
||
// Next ally
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
GlobalValidLeader = iValue;
|
||
if(iCnt >= i1 && iCnt3 >= i1)
|
||
{
|
||
GlobalAverageFriendlyHD = iCnt3 / iCnt;
|
||
}
|
||
// Set nearest seen ally.
|
||
GlobalNearestSeenAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + s1);
|
||
GlobalValidSeenAlly = GetIsObjectValid(GlobalNearestSeenAlly);
|
||
|
||
// We set up buff allies in a new array.
|
||
// - We may set up summons (or people with masters) if we have under
|
||
// <= 3 people, or it is of comparable hit dice to us.
|
||
iCnt = i1;
|
||
iCnt2 = FALSE;
|
||
fSetMaxWeCanGoTo = f20;
|
||
// we add this onto any ranges we input, so how far we'll move to.
|
||
GlobalBuffRangeAddon = 0.0;
|
||
// iValue is the limit of buff targets
|
||
// - Less if sorceror/bard (very few)
|
||
// - More if proper buffer
|
||
iValue = i5;
|
||
if(GlobalWeAreSorcerorBard)
|
||
{
|
||
iValue = i2;
|
||
}
|
||
// If we are set to buff allies, we extend the range.
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS, AI_COMBAT_MASTER))
|
||
{
|
||
iValue = i8;
|
||
fSetMaxWeCanGoTo = f40;
|
||
GlobalBuffRangeAddon = f30;
|
||
}
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
// Loop allies
|
||
// - Only up to 10.
|
||
// - Check masters as above.
|
||
while(GetIsObjectValid(oSetUpTarget) && iCnt2 <= iValue &&
|
||
GetDistanceToObject(oSetUpTarget) <= fSetMaxWeCanGoTo)
|
||
{
|
||
// No arcane spellcasters.
|
||
if(!GetLevelByClass(CLASS_TYPE_SORCERER, oSetUpTarget) &&
|
||
!GetLevelByClass(CLASS_TYPE_WIZARD, oSetUpTarget) &&
|
||
// - Master check
|
||
(GlobalTotalAllies <= i3 ||
|
||
!GetIsObjectValid(GetMaster(oSetUpTarget)) ||
|
||
GetHitDice(oSetUpTarget) >= GlobalOurHitDice - i5))
|
||
{
|
||
// Add to new array
|
||
iCnt2++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN_BUFF + IntToString(iCnt2), oSetUpTarget);
|
||
}
|
||
// Next seen ally
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
}
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Well, do we change our targets?
|
||
|
||
There is a max limit (default of 6) rounds that we attack targets, melee
|
||
ranged and spell.
|
||
|
||
There is also a random % to re-set the target, for each type.
|
||
|
||
Leaders override all 3 targets, and set it to it.
|
||
|
||
And finally, the target we had last time must be:
|
||
- Not dead
|
||
- Seen or heard (Spell targets must be seen if there is a valid seen enemy)
|
||
- Not attacking us if we have sneak attack (Ranged/Melee target)
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// Leader checking
|
||
oSetUpTarget = GetAIObject(AI_ATTACK_SPECIFIC_OBJECT);
|
||
// Always delete
|
||
DeleteAIObject(AI_ATTACK_SPECIFIC_OBJECT);
|
||
// Our specific target to attack
|
||
// - sanity check just in case
|
||
if(!AI_GetTargetSanityCheck(oSetUpTarget))
|
||
{
|
||
// 40: "[DCR:Targeting] Override Target Seen. [Name]" + GetName(oSetUpTarget)
|
||
DebugActionSpeakByInt(40, oSetUpTarget);
|
||
// Melee target, we must check if we have any valid.
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
// Lagbusting, get nearest
|
||
else if(GetSpawnInCondition(AI_FLAG_OTHER_LAG_TARGET_NEAREST_ENEMY, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
if(GetIsObjectValid(GlobalNearestEnemySeen))
|
||
{
|
||
// The validness of these are checked later anyway
|
||
GlobalMeleeTarget = GlobalNearestEnemySeen;
|
||
GlobalRangedTarget = GlobalNearestEnemySeen;
|
||
GlobalSpellTarget = GlobalNearestEnemySeen;
|
||
}
|
||
}
|
||
// We may also make it so that we target only the lowest AC and so on...
|
||
// LIKE_LOWER_HP, LIKE_LOWER_AC, LIKE_MAGE_CLASSES, LIKE_ARCHERS
|
||
else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_HP, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
// We use this check - inbuilt, automatic, and also is simple!
|
||
oSetUpTarget = GetFactionMostDamagedMember(GlobalNearestEnemySeen);
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
}
|
||
// HD
|
||
else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_HD, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
// We use this check - inbuilt, automatic, and also is simple!
|
||
oSetUpTarget = GetFactionWeakestMember(GlobalNearestEnemySeen);
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
}
|
||
// AC
|
||
else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_LOWER_AC, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
// We use this check - inbuilt, automatic, and also is simple!
|
||
oSetUpTarget = GetFactionWorstAC(GlobalNearestEnemySeen);
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Make sure the nearest seen is not in front of the worst AC.
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
}
|
||
// Ranged attackers
|
||
else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_ARCHERS, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
// Get nearest one who is attacking us.
|
||
iCnt = i1;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// have they got a ranged weapon?
|
||
if(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oSetUpTarget)))
|
||
{
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
break;
|
||
}
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
}
|
||
// Mage classes
|
||
else if(GetSpawnInCondition(AI_FLAG_TARGETING_LIKE_MAGE_CLASSES, AI_TARGETING_FLEE_MASTER))
|
||
{
|
||
// Sorceror
|
||
oSetUpTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_SORCERER, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
if(!GetIsObjectValid(oSetUpTarget) ||
|
||
(!GetObjectSeen(oSetUpTarget) && !GetObjectHeard(oSetUpTarget)))
|
||
{
|
||
// Wizard
|
||
oSetUpTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_WIZARD, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
}
|
||
// If valid, use
|
||
if(GetIsObjectValid(oSetUpTarget) &&
|
||
(GetObjectSeen(oSetUpTarget) || GetObjectHeard(oSetUpTarget)))
|
||
{
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
}
|
||
iValue = GetAIConstant(AI_FAVOURED_ENEMY_RACE);
|
||
if(iValue >= FALSE)
|
||
{
|
||
oSetUpTarget = GetNearestCreature(CREATURE_TYPE_RACIAL_TYPE, iValue, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Make sure the nearest seen is not in front of the worst AC.
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
iValue = GetAIConstant(AI_FAVOURED_ENEMY_CLASS);
|
||
if(iValue >= FALSE)
|
||
{
|
||
oSetUpTarget = GetNearestCreature(CREATURE_TYPE_CLASS, iValue, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN);
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Make sure the nearest seen is not in front of the worst AC.
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) > f4 ||
|
||
GetDistanceToObject(oSetUpTarget) < f3)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
}
|
||
GlobalRangedTarget = oSetUpTarget;
|
||
GlobalSpellTarget = oSetUpTarget;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Melee targeting
|
||
|
||
This uses some of the closer people or all within reach (determined before)
|
||
|
||
After getting the right people, we determine with AC, HP and some other
|
||
things, which are the best.
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// Do we want to change melee targets?
|
||
oLastTarget = GetAIObject(AI_LAST_MELEE_TARGET);
|
||
|
||
// iValid is the counter for attacking target X...
|
||
iBreak = GetAIInteger(AI_MELEE_TURNS_ATTACKING);
|
||
iBreak++;
|
||
SetAIInteger(AI_MELEE_TURNS_ATTACKING, iBreak);
|
||
// We set this to 0 if we our oSetUpTarget != GlobalMeleeTarget below changing.
|
||
|
||
// Check %
|
||
// If we have an override, we don't check for targets, but run Global*
|
||
// setups at the end still, but with the target being oOverride.
|
||
|
||
// - No valid override target
|
||
if(!GetIsObjectValid(GlobalMeleeTarget) &&
|
||
(AI_GetTargetSanityCheck(oLastTarget) || // Sanity check (dead, valid, ETC)
|
||
// And must pass a % roll
|
||
d100() <= GetBoundriedAIInteger(AI_MELEE_LAST_TO_NEW_TARGET_CHANCE, i20, i100) ||
|
||
// OR last target attacked for X rounds
|
||
iBreak >= iMaxTurnsAttackingX))
|
||
{
|
||
// Loop targets, ETC, and get GlobalMeleeTarget normally.
|
||
// Set up melee = ARRAY_MELEE_ENEMY
|
||
|
||
// Note: if we start getting wide gaps between group A and object B, we
|
||
// stop, because they are probably behind more enemies :-D
|
||
// Note 2: We only use seen enemies as a prioritory, else heard ones (like
|
||
// invisible ones)
|
||
|
||
// 1. Nearby SEEN enemies. Obviously nearest to furthest!
|
||
// IE the maximum and minimum targets for the range checking.
|
||
iMinimum = GetBoundriedAIInteger(TARGETING_RANGE + MINIMUM, i2, i40, i1);
|
||
iMaximum = GetBoundriedAIInteger(TARGETING_RANGE + MAXIMUM, i8, i40, i1);
|
||
|
||
// fSetMaxWeCanGoTo = Maximum range away from us (GetDistanceToObject) that
|
||
// we can go to. This is increased if we have tumble (compared to level) or
|
||
// spring attack is king :-D
|
||
fSetMaxWeCanGoTo = GlobalOurReach + f1;// Have 1 extra as well
|
||
// We add a lot for spring attack - 3d4 (3 to 12)
|
||
// OR we have 13+ (IE a very high chance of suceeding) in tumble
|
||
if(GetHasFeat(FEAT_SPRING_ATTACK) || GetSkillRank(SKILL_TUMBLE) >= i13)
|
||
{
|
||
fSetMaxWeCanGoTo += IntToFloat(d4(i3));
|
||
}
|
||
else if(GetHasSkill(SKILL_TUMBLE))
|
||
{
|
||
// Else we add some for tumble
|
||
iBreak = GetSkillRank(SKILL_TUMBLE) - GlobalOurHitDice;
|
||
if(iBreak > FALSE)
|
||
{
|
||
// * Basis of Skill Rank - Our Hit Dice. 5 tumble on a level 2 makes +3M Range.
|
||
fSetMaxWeCanGoTo += IntToFloat(iBreak);
|
||
}
|
||
}
|
||
iBreak = FALSE;
|
||
// Start loop from array based on range.
|
||
// - Use seen array!
|
||
// - Break if we have added enough targets to our array.
|
||
iRemainingTargets = FALSE;
|
||
iCnt = i1;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
while(GetIsObjectValid(oSetUpTarget) && iRemainingTargets < iMaximum)
|
||
{
|
||
// If seen, we check range...
|
||
fCurrentRange = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
// We start from the closest, so we need to cancle those out which
|
||
// are too far away.
|
||
// we check if it is in our reach. If so, we add it.
|
||
if(fCurrentRange < fSetMaxWeCanGoTo || iRemainingTargets < iMinimum)
|
||
{
|
||
iRemainingTargets++;
|
||
sCnt = IntToString(iRemainingTargets);
|
||
SetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, oSetUpTarget);
|
||
SetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, fCurrentRange);
|
||
}
|
||
// Get next nearest SEEN
|
||
iCnt++;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + sCnt);
|
||
}
|
||
// 1a. If not valid, just use nearest seen if valid
|
||
if(!iRemainingTargets && GlobalValidNearestSeenEnemy)
|
||
{
|
||
iRemainingTargets = i1;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1, GlobalNearestEnemySeen);
|
||
SetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1, GetDistanceToObject(GlobalNearestEnemySeen));
|
||
}
|
||
else
|
||
{
|
||
// 2. Make sure the nearest HEARD is NOT very close compared to the nearest
|
||
// SEEN OR we have no nearest seen.
|
||
// Check if we have no seen objects set, or the nearest heard is nearer then the futhest seen by 4M
|
||
fCurrentRange = GetDistanceToObject(GlobalNearestEnemyHeard);
|
||
if(GlobalValidNearestHeardEnemy && (!iRemainingTargets ||
|
||
// Range to nearest heard is nearer then the melee enemy
|
||
(fCurrentRange > f0 && ((fCurrentRange + f4) <
|
||
// Nearest melee range enemy we've set to seen
|
||
GetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1)))))
|
||
{
|
||
// - We half the amount we need for this range check
|
||
// Maximum
|
||
iMaximum /= i2;
|
||
if(iMaximum < i1) iMaximum = i1;
|
||
// Minimum
|
||
iMinimum /= i2;
|
||
if(iMinimum < i1) iMinimum = i1;
|
||
// Start loop from array based on range. Overrides exsisting targets
|
||
// - Use heard enemy array.
|
||
iCnt = i1;
|
||
iRemainingTargets = FALSE;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + sCnt);
|
||
while(GetIsObjectValid(oSetUpTarget) && iRemainingTargets < iMaximum)
|
||
{
|
||
// If seen, we check range...
|
||
fCurrentRange = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + sCnt);
|
||
// We start from the closest, so we need to cancle those out which
|
||
// are too far away.
|
||
if(fCurrentRange < fSetMaxWeCanGoTo || iRemainingTargets < iMinimum)
|
||
{
|
||
iRemainingTargets++;
|
||
sCnt = IntToString(iRemainingTargets);
|
||
SetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, oSetUpTarget);
|
||
SetLocalFloat(OBJECT_SELF, ARRAY_MELEE_ENEMY + sCnt, fCurrentRange);
|
||
}
|
||
// Get next HEARD
|
||
iCnt++;
|
||
sCnt = IntToString(iCnt);
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + sCnt);
|
||
}
|
||
}
|
||
}
|
||
// Now, do we have any Melee Targets?
|
||
if(!iRemainingTargets)
|
||
{
|
||
// If not, what can we use?
|
||
// - We use the imputted target!
|
||
// - We use anyone who allies are attacking!
|
||
// - We use anyone we hear!
|
||
|
||
// Imputted target (EG last attack that GetObjectSeen doesn't return
|
||
// for).
|
||
oSetUpTarget = oImputBackUpToAttack;
|
||
if(!GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Anyone we hear
|
||
oSetUpTarget = GlobalNearestEnemyHeard;
|
||
if(!GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// We get who our allies are attacking
|
||
iCnt = i1;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget) && iBreak != TRUE)
|
||
{
|
||
// Get the allies attack target. If valid, attack it!
|
||
oTempLoopObject = GetAttackTarget(oSetUpTarget);
|
||
if(GetIsObjectValid(oTempLoopObject) &&
|
||
!GetFactionEqual(oTempLoopObject) &&
|
||
!GetIsFriend(oTempLoopObject))
|
||
{
|
||
oSetUpTarget = oTempLoopObject;
|
||
iBreak = TRUE;
|
||
}
|
||
else
|
||
{
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE + IntToString(iCnt));
|
||
}
|
||
}
|
||
// Don't attack anyone we got in the loop, of course.
|
||
if(GetIsFriend(oSetUpTarget) || GetFactionEqual(oSetUpTarget))
|
||
{
|
||
oSetUpTarget = OBJECT_INVALID;
|
||
}
|
||
}
|
||
}
|
||
// Do we have a target from those backups above?
|
||
// - If so, we ActionAttack it so we move near it, as it is not in
|
||
// our LOS, or isn't in our seen range in our LOS.
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// 41: [DCR:Targeting] No seen in LOS, Attempting to MOVE to something [Target]" + GetName(oSetUpTarget)
|
||
DebugActionSpeakByInt(41, oSetUpTarget);
|
||
DeleteAIObject(AI_LAST_MELEE_TARGET);
|
||
DeleteAIObject(AI_LAST_SPELL_TARGET);
|
||
DeleteAIObject(AI_LAST_RANGED_TARGET);
|
||
AI_EquipBestShield(); // in case of an ambush, be ready
|
||
ActionMoveToLocation(GetLocation(oSetUpTarget), TRUE);
|
||
return TRUE;
|
||
}
|
||
// If it doesn't have anything we can see/move to/attack/search
|
||
// near, then we make sure have make sure we have no target
|
||
// (GlobalAnyValidTargetObject) and break the function, so we can stop.
|
||
// "else"
|
||
GlobalAnyValidTargetObject = FALSE;
|
||
return FALSE;
|
||
}
|
||
// If only one, choose it
|
||
else if(iRemainingTargets == i1)
|
||
{
|
||
GlobalMeleeTarget = GetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1);
|
||
}
|
||
else //if(iRemainingTargets > i1)
|
||
{
|
||
// If we have any set targets (heard/seen ones) then we
|
||
// will reduce the amount by AC, HP (current Percent/max/current)
|
||
// and HD and BAB
|
||
|
||
// We only use range as a default. The rest are done in order if they
|
||
// are set! :-D
|
||
|
||
// We check for PC targets
|
||
// DODODODO
|
||
|
||
// Target:
|
||
// AC - Used only for phisical attacks (TARGETING_AC)
|
||
// Phisical Protections - Used for both spells and melee (TARGETING_PHISICALS)
|
||
// Base Attack Bonus - Used for both spells and melee (TARGETING_BAB)
|
||
// Hit Dice - Used for both spells and melee (TARGETING_HITDICE)
|
||
// HP Percent - Used for both spells and melee (TARGETING_HP_PERCENT)
|
||
// HP Current - Used for both spells and melee (TARGETING_HP_CURRENT)
|
||
// HP Maximum - Used for both spells and melee (TARGETING_HP_MAXIMUM)
|
||
|
||
// 0. Sneak attack check
|
||
if(GetHasFeat(FEAT_SNEAK_ATTACK))
|
||
{
|
||
// We normally get the nearest enemy who is not attacking us, and
|
||
// actually stop!
|
||
AI_TargetingArrayIntegerStore(i9, ARRAY_MELEE_ENEMY);
|
||
|
||
// Get the closest who isn't attacking us.
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + s1);
|
||
if(GetAttackTarget(oSetUpTarget) != OBJECT_SELF)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
iRemainingTargets = FALSE;
|
||
}
|
||
// If we have trouble using that one, IE they are attacking us, we
|
||
// just use normal methods.
|
||
}
|
||
// 1. AC
|
||
iMinimum = GetAIInteger(TARGETING_AC + MINIMUM);
|
||
// Do we do AC? And over one target...
|
||
// - iCnt2 is the total target stored in the last array.
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_AC);
|
||
iMaximum = GetAIInteger(TARGETING_AC + MAXIMUM);
|
||
|
||
// We then set the array up in a new temp array. 1 = AC.
|
||
AI_TargetingArrayIntegerStore(i1, ARRAY_MELEE_ENEMY);
|
||
|
||
// We loop through all the targets in the temp array (we also
|
||
// delete the temp array targets!) and set what ones we want to target
|
||
// based on AC.
|
||
// - Continue until iMinimum is reached.
|
||
// - Never go over iMaximum
|
||
// - After iMinimum we make sure the AC differences is not a major one of
|
||
// over 5.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum);
|
||
}
|
||
// Most others are similar (and all integer values so easy to check)
|
||
// 2. Phisical protections.
|
||
iMinimum = GetAIInteger(TARGETING_PHISICALS + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_PHISICALS);
|
||
iMaximum = GetAIInteger(TARGETING_PHISICALS + MAXIMUM);
|
||
// We then set the array up in a new temp array. 3 = phisicals.
|
||
AI_TargetingArrayIntegerStore(i3, ARRAY_MELEE_ENEMY);
|
||
// We loop as AC, basically. As it is stored based on the amount
|
||
// of DR offered, we set the limit to 6, so if everyone had
|
||
// 0 DR, and 1 had 5, it'd add the 5 for the hell of it. If everyone
|
||
// had 20, and one had 25 it'd take the 25 too, but not a 30.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i6, iMinimum, iMaximum);
|
||
}
|
||
// 4. BAB
|
||
iMinimum = GetAIInteger(TARGETING_BAB + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_BAB);
|
||
iMaximum = GetAIInteger(TARGETING_BAB + MAXIMUM);
|
||
// We then set the array up in a new temp array. 4 = BAB
|
||
AI_TargetingArrayIntegerStore(i4, ARRAY_MELEE_ENEMY);
|
||
// We loop as AC, basically. As it is BAB, IE how much chance
|
||
// they'll hit us (they might all hit on a 20, but it also shows
|
||
// who are the best fighters!). Set to 5, like AC.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum);
|
||
}
|
||
// 5. Hit Dice
|
||
iMinimum = GetAIInteger(TARGETING_HITDICE + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HITDICE);
|
||
iMaximum = GetAIInteger(TARGETING_HITDICE + MAXIMUM);
|
||
// We then set the array up in a new temp array. 5 = Hit dice
|
||
AI_TargetingArrayIntegerStore(i5, ARRAY_MELEE_ENEMY);
|
||
// We loop as AC. Hit Dice is even easier. We set the limit to
|
||
// a max of 4.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum);
|
||
}
|
||
// 6. Percent HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_PERCENT + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_PERCENT);
|
||
iMaximum = GetAIInteger(TARGETING_HP_PERCENT + MAXIMUM);
|
||
// We then set the array up in a new temp array. 6 = %
|
||
AI_TargetingArrayIntegerStore(i6, ARRAY_MELEE_ENEMY);
|
||
// We loop as AC. Current Hit Points are easy, and are done
|
||
// by %ages. We set the % to 15 difference max.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, i15, iMinimum, iMaximum);
|
||
}
|
||
// 7. Current HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_CURRENT + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_CURRENT);
|
||
iMaximum = GetAIInteger(TARGETING_HP_CURRENT + MAXIMUM);
|
||
// We then set the array up in a new temp array. 7 = current HP
|
||
AI_TargetingArrayIntegerStore(i7, ARRAY_MELEE_ENEMY);
|
||
// We loop as AC. Current Hit points? Well, we set this limit to
|
||
// Our Hit Dice * 2.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, GlobalOurHitDice * i2, iMinimum, iMaximum);
|
||
}
|
||
// 8. Maximum HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_MAXIMUM + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_MAXIMUM);
|
||
iMaximum = GetAIInteger(TARGETING_HP_MAXIMUM + MAXIMUM);
|
||
// We then set the array up in a new temp array. 8 = maximum
|
||
AI_TargetingArrayIntegerStore(i8, ARRAY_MELEE_ENEMY);
|
||
// We loop as AC. Max hit Hit points? Well, we set this limit to
|
||
// Our Hit Dice * 3.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_MELEE_ENEMY, iTypeOfTarget, GlobalOurHitDice * i3, iMinimum, iMaximum);
|
||
}
|
||
// WHEW!
|
||
// Now we should have 1 or more chosen targets in ARRAY_MELEE_ARRAY.
|
||
// iRemaining Targets is the array size...we mearly choose a random
|
||
// one, or the first one if there is only one :-D
|
||
|
||
// If only one, choose it
|
||
if(iRemainingTargets == i1)
|
||
{
|
||
GlobalMeleeTarget = GetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + s1);
|
||
}
|
||
// Check for 2+, if 0, we haven't got one to set!
|
||
else if(iRemainingTargets >= i2)
|
||
{
|
||
// Else Roll dice
|
||
iCnt = Random(iRemainingTargets) + i1;
|
||
// Set random target
|
||
GlobalMeleeTarget = GetLocalObject(OBJECT_SELF, ARRAY_MELEE_ENEMY + IntToString(iCnt));
|
||
}
|
||
}
|
||
}
|
||
// Else, it is oLastTarget that will be our target
|
||
else
|
||
{
|
||
GlobalMeleeTarget = oLastTarget;
|
||
}
|
||
|
||
// If it is a new target, reset attacking counter to 0
|
||
if(GlobalMeleeTarget != oLastTarget)
|
||
{
|
||
DeleteAIInteger(AI_MELEE_TURNS_ATTACKING);
|
||
}
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Ranged targeting
|
||
|
||
This uses more stuff then melee targeting - for a start, range is optional!
|
||
|
||
By default, it sets up all seen targets to the array, else all heard.
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// Do we want to change melee targets?
|
||
oLastTarget = GetAIObject(AI_LAST_RANGED_TARGET);
|
||
|
||
// iValid is the counter for attacking target X...
|
||
iBreak = GetAIInteger(AI_RANGED_TURNS_ATTACKING);
|
||
iBreak++;
|
||
SetAIInteger(AI_RANGED_TURNS_ATTACKING, iBreak);
|
||
// We set this to 0 if we our oLastTarget != GlobalMeleeTarget below changing.
|
||
|
||
// Check %
|
||
// We use the same temp reset from before, because phisical attacking
|
||
// would be range or melee ;-)
|
||
// - Hey, do we even have a ranged weapon?
|
||
// - No valid override target
|
||
// - No valid override target
|
||
if(!GetIsObjectValid(GlobalRangedTarget) && GetIsObjectValid(GetAIObject(AI_WEAPON_RANGED)) &&
|
||
(AI_GetTargetSanityCheck(oLastTarget) || // Sanity check (dead, valid, ETC)
|
||
// And must pass a % roll
|
||
d100() <= GetBoundriedAIInteger(AI_RANGED_LAST_TO_NEW_TARGET_CHANCE, i20, i100) ||
|
||
// OR last target attacked for X rounds
|
||
iBreak >= iMaxTurnsAttackingX))
|
||
{
|
||
// 1. Set up all seen, else all heard, to the range array.
|
||
iCnt = i1;
|
||
iRemainingTargets = i0;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
iRemainingTargets++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + IntToString(iRemainingTargets), oSetUpTarget);
|
||
// Get Next seen
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
// If we don't have any seen, used heard.
|
||
if(!iRemainingTargets)
|
||
{
|
||
iCnt = i1;
|
||
iRemainingTargets = i0;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
iRemainingTargets++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + IntToString(iRemainingTargets), oSetUpTarget);
|
||
// Get Next seen
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt));
|
||
}
|
||
}
|
||
// If not valid, set to melee target at the end
|
||
|
||
// Do we have exactly 1 target? (iRemainingTargets == 1)
|
||
if(iRemainingTargets == i1)
|
||
{
|
||
// Then we make this our target and end it.
|
||
GlobalRangedTarget = GetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + s1);
|
||
}
|
||
// Else we set up using range ETC, if we have more then 1.
|
||
else if(iRemainingTargets)
|
||
{
|
||
// Range - they are already in range order. As it is used for
|
||
// spells, it uses the same function.
|
||
// Range - Used only for ranged phisical attacks and spell attacks (TARGETING_RANGE)
|
||
// - Melee - Range is used to see what it can reach, and the MAX and MIN are taken to account
|
||
// AC - Used only for phisical attacks (TARGETING_AC)
|
||
// Saving Throws - Used only for spell attacks (TARGETING_SAVES)
|
||
// Phisical Protections - Used for both spells and phisical attacks (TARGETING_PHISICALS)
|
||
// Base Attack Bonus - Used for both spells and phisical attacks (TARGETING_BAB)
|
||
// Hit Dice - Used for both spells and phisical attacks (TARGETING_HITDICE)
|
||
// HP Percent - Used for both spells and phisical attacks (TARGETING_HP_PERCENT)
|
||
// HP Current - Used for both spells and phisical attacks (TARGETING_HP_CURRENT)
|
||
// HP Maximum - Used for both spells and phisical attacks (TARGETING_HP_MAXIMUM)
|
||
// 0. Sneak attack check
|
||
if(GetHasFeat(FEAT_SNEAK_ATTACK))
|
||
{
|
||
// We normally get the nearest enemy who is not attacking us, and
|
||
// actually stop!
|
||
AI_TargetingArrayIntegerStore(i9, ARRAY_RANGED_ENEMY);
|
||
|
||
// Get the closest who isn't attacking us.
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_TEMP_ARRAY + s1);
|
||
if(GetAttackTarget(oSetUpTarget) != OBJECT_SELF)
|
||
{
|
||
GlobalMeleeTarget = oSetUpTarget;
|
||
iRemainingTargets = FALSE;
|
||
}
|
||
// If we have trouble using that one, IE they are attacking us, we
|
||
// just use normal methods.
|
||
|
||
// Delete temp array
|
||
AI_TargetingArrayDelete(ARRAY_TEMP_ARRAY);
|
||
}
|
||
iMinimum = GetAIInteger(TARGETING_RANGE + MINIMUM);
|
||
// 1. Do we do range?
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_RANGE);
|
||
iMaximum = GetAIInteger(TARGETING_RANGE + MAXIMUM);
|
||
|
||
// We then set the array up in a new temp array.
|
||
AI_TargetingArrayDistanceStore(ARRAY_RANGED_ENEMY, ARRAY_TEMP_ARRAY);
|
||
// We loop range. Maximum one can be from another when got to the
|
||
// minimum is 5.0 M
|
||
iRemainingTargets = AI_TargetingArrayLimitTargetsFloat(ARRAY_RANGED_ENEMY, iTypeOfTarget, f5, iMinimum, iMaximum);
|
||
}
|
||
// 2. AC
|
||
iMinimum = GetAIInteger(TARGETING_AC + MINIMUM);
|
||
// Do we do AC? And over one target...
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_AC);
|
||
iMaximum = GetAIInteger(TARGETING_AC + MAXIMUM);
|
||
|
||
// We then set the array up in a new temp array. 1 = AC.
|
||
AI_TargetingArrayIntegerStore(i1, ARRAY_RANGED_ENEMY);
|
||
|
||
// We loop through all the targets in the temp array (we also
|
||
// delete the temp array targets!) and set what ones we want to target
|
||
// based on AC.
|
||
// - Continue until iMinimum is reached.
|
||
// - Never go over iMaximum
|
||
// - After iMinimum we make sure the AC differences is not a major one of
|
||
// over 5.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum);
|
||
}
|
||
// Most others are similar (and all integer values so easy to check)
|
||
// 3. Phisical protections.
|
||
iMinimum = GetAIInteger(TARGETING_PHISICALS + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_PHISICALS);
|
||
iMaximum = GetAIInteger(TARGETING_PHISICALS + MAXIMUM);
|
||
// We then set the array up in a new temp array. 3 = phisicals.
|
||
AI_TargetingArrayIntegerStore(i3, ARRAY_RANGED_ENEMY);
|
||
// We loop as AC, basically. As it is stored based on the amount
|
||
// of DR offered, limit is 0, not 6. Can't be any different.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum);
|
||
}
|
||
// 4. BAB
|
||
iMinimum = GetAIInteger(TARGETING_BAB + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_BAB);
|
||
iMaximum = GetAIInteger(TARGETING_BAB + MAXIMUM);
|
||
// We then set the array up in a new temp array. 4 = BAB
|
||
AI_TargetingArrayIntegerStore(i4, ARRAY_RANGED_ENEMY);
|
||
// We loop as AC, basically. As it is BAB, IE how much chance
|
||
// they'll hit us (they might all hit on a 20, but it also shows
|
||
// who are the best fighters!). Set to 5, like AC.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum);
|
||
}
|
||
// 5. Hit Dice
|
||
iMinimum = GetAIInteger(TARGETING_HITDICE + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HITDICE);
|
||
iMaximum = GetAIInteger(TARGETING_HITDICE + MAXIMUM);
|
||
// We then set the array up in a new temp array. 5 = Hit dice
|
||
AI_TargetingArrayIntegerStore(i5, ARRAY_RANGED_ENEMY);
|
||
// We loop as AC. Hit Dice is even easier. We set the limit to
|
||
// a max of 4.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum);
|
||
}
|
||
// 6. Percent HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_PERCENT + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_PERCENT);
|
||
iMaximum = GetAIInteger(TARGETING_HP_PERCENT + MAXIMUM);
|
||
// We then set the array up in a new temp array. 5 = Hit dice
|
||
AI_TargetingArrayIntegerStore(i6, ARRAY_RANGED_ENEMY);
|
||
// We loop as AC. Current Hit Points are easy, and are done
|
||
// by %ages. We set the % to 15 difference max.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, i15, iMinimum, iMaximum);
|
||
}
|
||
// 7. Current HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_CURRENT + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_CURRENT);
|
||
iMaximum = GetAIInteger(TARGETING_HP_CURRENT + MAXIMUM);
|
||
// We then set the array up in a new temp array. 5 = Hit dice
|
||
AI_TargetingArrayIntegerStore(i7, ARRAY_RANGED_ENEMY);
|
||
// We loop as AC. Current Hit points? Well, we set this limit to
|
||
// Our Hit Dice * 2.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, GlobalOurHitDice * i2, iMinimum, iMaximum);
|
||
}
|
||
// 8. Maximum HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_MAXIMUM + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_MAXIMUM);
|
||
iMaximum = GetAIInteger(TARGETING_HP_MAXIMUM + MAXIMUM);
|
||
// We then set the array up in a new temp array. 5 = Hit dice
|
||
AI_TargetingArrayIntegerStore(i8, ARRAY_RANGED_ENEMY);
|
||
// We loop as AC. Max hit Hit points? Well, we set this limit to
|
||
// Our Hit Dice * 3.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_RANGED_ENEMY, iTypeOfTarget, GlobalOurHitDice * i3, iMinimum, iMaximum);
|
||
}
|
||
// End narrowing down on ARRAY_RANGED_ENEMY
|
||
|
||
// If only one, choose it
|
||
if(iRemainingTargets == i1)
|
||
{
|
||
GlobalRangedTarget = GetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + s1);
|
||
}
|
||
// Check for 2+, if 0, we haven't got one to set!
|
||
else if(iRemainingTargets >= i2)
|
||
{
|
||
// Else Roll dice
|
||
iCnt = Random(iRemainingTargets) + i1;
|
||
// Set random target
|
||
GlobalRangedTarget = GetLocalObject(OBJECT_SELF, ARRAY_RANGED_ENEMY + IntToString(iCnt));
|
||
}
|
||
}
|
||
}
|
||
// Else, it is oLastTarget that will be our target
|
||
else
|
||
{
|
||
GlobalRangedTarget = oLastTarget;
|
||
}
|
||
|
||
// If not valid, set to melee target
|
||
if(!GetIsObjectValid(GlobalRangedTarget))
|
||
{
|
||
GlobalRangedTarget = GlobalMeleeTarget;
|
||
}
|
||
// If it is a new target, reset attacking counter to 0
|
||
if(GlobalRangedTarget != oLastTarget)
|
||
{
|
||
DeleteAIInteger(AI_RANGED_TURNS_ATTACKING);
|
||
}
|
||
|
||
/*:://////////////////////////////////////////////
|
||
Spell targeting
|
||
|
||
Spell targeting is similar to ranged targeting. It is only reset if
|
||
iResetTargets is true.
|
||
|
||
We never actually set a target if they are totally immune to our spells, uses
|
||
AI_SpellResistanceImmune for the checks.
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// Do we want to change melee targets?
|
||
oLastTarget = GetAIObject(AI_LAST_SPELL_TARGET);
|
||
|
||
// iValid is the counter for attacking target X...
|
||
iBreak = GetAIInteger(AI_SPELL_TURNS_ATTACKING);
|
||
iBreak++;
|
||
SetAIInteger(AI_SPELL_TURNS_ATTACKING, iBreak);
|
||
// We set this to 0 if we our oLastTarget != GlobalMeleeTarget below changing.
|
||
|
||
// - No valid override target
|
||
if(!GetIsObjectValid(GlobalSpellTarget) &&
|
||
(AI_GetTargetSanityCheck(oLastTarget) || // Sanity check (dead, valid, ETC)
|
||
// Or the last target was immune to all our spells
|
||
GetLocalInt(oLastTarget, AI_SPELL_IMMUNE_LEVEL) >= i9 ||
|
||
// OR must pass a % roll
|
||
d100() <= GetBoundriedAIInteger(AI_SPELL_LAST_TO_NEW_TARGET_CHANCE, i20, i100) ||
|
||
// OR last target attacked for X rounds
|
||
iBreak >= iMaxTurnsAttackingX))
|
||
{
|
||
// 1. Set up all seen, else all heard, to the range array.
|
||
iCnt = i1;
|
||
iRemainingTargets = i0;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Totally immune to our spells - ignore!
|
||
if(!AI_SpellResistanceImmune(oSetUpTarget) &&
|
||
GetLocalInt(GlobalSpellTarget, AI_SPELL_IMMUNE_LEVEL) < i9)
|
||
{
|
||
iRemainingTargets++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + IntToString(iRemainingTargets), oSetUpTarget);
|
||
}
|
||
// Get Next seen
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
// If we don't have any seen, used heard.
|
||
if(!iRemainingTargets)
|
||
{
|
||
iCnt = i1;
|
||
iRemainingTargets = i0;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt));
|
||
while(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Totally immune to our spells - ignore!
|
||
if(!AI_SpellResistanceImmune(oSetUpTarget) &&
|
||
GetLocalInt(GlobalSpellTarget, AI_SPELL_IMMUNE_LEVEL) < i9)
|
||
{
|
||
iRemainingTargets++;
|
||
SetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + IntToString(iRemainingTargets), oSetUpTarget);
|
||
}
|
||
// Get Next seen
|
||
iCnt++;
|
||
oSetUpTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_HEARD + IntToString(iCnt));
|
||
}
|
||
}
|
||
// If not valid, its invalid! As this is the last thing, return iM1.
|
||
// This CAN happen!
|
||
// - If we have all targets naturally immune to spells via. spell reistance.
|
||
// we can set GlobalNormalSpellsNoEffectLevel to 10, below, to stop all spells
|
||
// - After this lot of things, we do check for validness and set to melee target if no one else
|
||
|
||
// MELEE Targets get checked for LOS, so they should always move
|
||
// to people we should attack ABOVE
|
||
// Do we have exactly 1 target? (iRemainingTargets == 1)
|
||
if(iRemainingTargets == i1)
|
||
{
|
||
// Then we make this our target and end it.
|
||
GlobalSpellTarget = GetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + s1);
|
||
}
|
||
// Else we set up using range ETC, if we have more then 1.
|
||
else if(iRemainingTargets > i1)
|
||
{
|
||
// ARRAY_SPELL_ENEMY
|
||
// Similar to Ranged attacking for range setting :-D
|
||
|
||
// Range - they are already in range order. As it is used for
|
||
// spells, it uses the same function.
|
||
|
||
// PC, Mantals.
|
||
// Range - Used only for ranged phisical attacks and spell attacks (TARGETING_RANGE)
|
||
// - Melee - Range is used to see what it can reach, and the MAX and MIN are taken to account
|
||
// AC - Used only for phisical attacks (TARGETING_AC)
|
||
// Saving Throws - Used only for spell attacks (TARGETING_SAVES)
|
||
// Phisical Protections - Used for both spells and phisical attacks (TARGETING_PHISICALS)
|
||
// Base Attack Bonus - Used for both spells and phisical attacks (TARGETING_BAB)
|
||
// Hit Dice - Used for both spells and phisical attacks (TARGETING_HITDICE)
|
||
// HP Percent - Used for both spells and phisical attacks (TARGETING_HP_PERCENT)
|
||
// HP Current - Used for both spells and phisical attacks (TARGETING_HP_CURRENT)
|
||
// HP Maximum - Used for both spells and phisical attacks (TARGETING_HP_MAXIMUM)
|
||
|
||
// -1. Is it a PC...
|
||
// - Type 10
|
||
iMinimum = GetAIInteger(TARGETING_ISPC + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_ISPC);
|
||
iMaximum = GetAIInteger(TARGETING_ISPC + MAXIMUM);
|
||
// We then set the array up in a new temp array. 10 = PC.
|
||
AI_TargetingArrayIntegerStore(i10, ARRAY_SPELL_ENEMY);
|
||
// We loop and set up, with no difference in PC status, IE we should always choose those who are PCs.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum);
|
||
}
|
||
// 0. Mantals
|
||
// - Type 10
|
||
iMinimum = GetAIInteger(TARGETING_ISPC + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_ISPC);
|
||
iMaximum = GetAIInteger(TARGETING_ISPC + MAXIMUM);
|
||
// We then set the array up in a new temp array. 10 = PC.
|
||
AI_TargetingArrayIntegerStore(i10, ARRAY_SPELL_ENEMY);
|
||
// We loop and set up, with no difference in PC status, IE we should always choose those who are PCs.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum);
|
||
}
|
||
iMinimum = GetAIInteger(TARGETING_RANGE + MINIMUM);
|
||
// 1. Do we do range?
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_RANGE);
|
||
iMaximum = GetAIInteger(TARGETING_RANGE + MAXIMUM);
|
||
|
||
// We then set the array up in a new temp array
|
||
AI_TargetingArrayDistanceStore(ARRAY_SPELL_ENEMY, ARRAY_TEMP_ARRAY);
|
||
// We loop range. Maximum one can be from another when got to the
|
||
// minimum is 5.0 M
|
||
iRemainingTargets = AI_TargetingArrayLimitTargetsFloat(ARRAY_SPELL_ENEMY, iTypeOfTarget, f5, iMinimum, iMaximum);
|
||
}
|
||
// 2. Saving Throws
|
||
iMinimum = GetAIInteger(TARGETING_SAVES + MINIMUM);
|
||
// Do we do saves? And over one target...
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_SAVES);
|
||
iMaximum = GetAIInteger(TARGETING_SAVES + MAXIMUM);
|
||
|
||
// We then set the array up in a new temp array. 2 = total saves.
|
||
AI_TargetingArrayIntegerStore(i2, ARRAY_SPELL_ENEMY);
|
||
|
||
// Saves are basically a spells' AC, or at least may hamper
|
||
// spells' power.
|
||
// - difference of 4
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum);
|
||
}
|
||
// Most others are similar (and all integer values so easy to check)
|
||
// 3. Phisical protections.
|
||
iMinimum = GetAIInteger(TARGETING_PHISICALS + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_PHISICALS);
|
||
iMaximum = GetAIInteger(TARGETING_PHISICALS + MAXIMUM);
|
||
// We then set the array up in a new temp array. 3 = phisicals.
|
||
AI_TargetingArrayIntegerStore(i3, ARRAY_SPELL_ENEMY);
|
||
// We loop as AC, basically. As it is stored based on the amount
|
||
// of DR offered, limit is 0, not 6. Can't be any different.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i0, iMinimum, iMaximum);
|
||
}
|
||
// 4. BAB
|
||
iMinimum = GetAIInteger(TARGETING_BAB + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_BAB);
|
||
iMaximum = GetAIInteger(TARGETING_BAB + MAXIMUM);
|
||
// We then set the array up in a new temp array. 4 = BAB
|
||
AI_TargetingArrayIntegerStore(i4, ARRAY_SPELL_ENEMY);
|
||
// We loop as AC, basically. As it is BAB, IE how much chance
|
||
// they'll hit us (they might all hit on a 20, but it also shows
|
||
// who are the best fighters!). Set to 5, like AC.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i5, iMinimum, iMaximum);
|
||
}
|
||
// 5. Hit Dice
|
||
iMinimum = GetAIInteger(TARGETING_HITDICE + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HITDICE);
|
||
iMaximum = GetAIInteger(TARGETING_HITDICE + MAXIMUM);
|
||
// We then set the array up in a new temp array. 5 = Hit dice
|
||
AI_TargetingArrayIntegerStore(i5, ARRAY_SPELL_ENEMY);
|
||
// We loop as AC. Hit Dice is even easier. We set the limit to
|
||
// a max of 4.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i4, iMinimum, iMaximum);
|
||
}
|
||
// 6. Percent HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_PERCENT + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_PERCENT);
|
||
iMaximum = GetAIInteger(TARGETING_HP_PERCENT + MAXIMUM);
|
||
// We then set the array up in a new temp array. 6 = % HP
|
||
AI_TargetingArrayIntegerStore(i6, ARRAY_SPELL_ENEMY);
|
||
// We loop as AC. Current Hit Points are easy, and are done
|
||
// by %ages. We set the % to 15 difference max.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, i15, iMinimum, iMaximum);
|
||
}
|
||
// 7. Current HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_CURRENT + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_CURRENT);
|
||
iMaximum = GetAIInteger(TARGETING_HP_CURRENT + MAXIMUM);
|
||
// We then set the array up in a new temp array. 7 = Current
|
||
AI_TargetingArrayIntegerStore(i7, ARRAY_SPELL_ENEMY);
|
||
// We loop as AC. Current Hit points? Well, we set this limit to
|
||
// Our Hit Dice * 2.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, GlobalOurHitDice * i2, iMinimum, iMaximum);
|
||
}
|
||
// 8. Maximum HP
|
||
iMinimum = GetAIInteger(TARGETING_HP_MAXIMUM + MINIMUM);
|
||
if(iMinimum && iRemainingTargets >= i2)
|
||
{
|
||
// If so, is it lowest or highest?
|
||
iTypeOfTarget = GetAIInteger(TARGETING_HP_MAXIMUM);
|
||
iMaximum = GetAIInteger(TARGETING_HP_MAXIMUM + MAXIMUM);
|
||
// We then set the array up in a new temp array. 8 = Maximum
|
||
AI_TargetingArrayIntegerStore(i8, ARRAY_SPELL_ENEMY);
|
||
// We loop as AC. Max hit Hit points? Well, we set this limit to
|
||
// Our Hit Dice * 3.
|
||
iRemainingTargets = AI_TargetingArrayLimitTargets(ARRAY_SPELL_ENEMY, iTypeOfTarget, GlobalOurHitDice * i3, iMinimum, iMaximum);
|
||
}
|
||
// End narrowing down on ARRAY_SPELL_ENEMY
|
||
|
||
// If only one, choose it
|
||
if(iRemainingTargets == i1)
|
||
{
|
||
GlobalSpellTarget = GetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + s1);
|
||
}
|
||
// Check for 2+, if 0, we haven't got one to set!
|
||
else if(iRemainingTargets >= i2)
|
||
{
|
||
// Else Roll dice
|
||
iCnt = Random(iRemainingTargets - i1) + i1;
|
||
// Set random target
|
||
GlobalSpellTarget = GetLocalObject(OBJECT_SELF, ARRAY_SPELL_ENEMY + IntToString(iCnt));
|
||
}
|
||
}
|
||
}
|
||
// Else, it is oLastTarget that will be our target
|
||
else
|
||
{
|
||
GlobalSpellTarget = oLastTarget;
|
||
}
|
||
|
||
// If not valid, set to melee target
|
||
if(!GetIsObjectValid(GlobalSpellTarget))
|
||
{
|
||
GlobalSpellTarget = GlobalMeleeTarget;
|
||
}
|
||
// If it is a new target, reset attacking counter to 0
|
||
if(GlobalSpellTarget != oLastTarget)
|
||
{
|
||
DeleteAIInteger(AI_SPELL_TURNS_ATTACKING);
|
||
}
|
||
|
||
// Set the final objects we chose
|
||
SetAIObject(AI_LAST_MELEE_TARGET, GlobalMeleeTarget);
|
||
SetAIObject(AI_LAST_SPELL_TARGET, GlobalSpellTarget);
|
||
SetAIObject(AI_LAST_RANGED_TARGET, GlobalRangedTarget);
|
||
|
||
// Set Global* things for melee target
|
||
GlobalRangeToMeleeTarget = GetDistanceToObject(GlobalMeleeTarget);
|
||
GlobalMeleeTargetAC = GetAC(GlobalMeleeTarget);
|
||
GlobalMeleeTargetBAB = GetBaseAttackBonus(GlobalMeleeTarget);
|
||
// Generic check.
|
||
// - Set to TRUE, we should always have GlobalMeleeTarget as valid
|
||
GlobalAnyValidTargetObject = TRUE;//GetIsObjectValid(GlobalMeleeTarget);
|
||
|
||
// Sort immunities
|
||
AI_SortSpellImmunities();
|
||
|
||
// Set Global* things for spell target
|
||
//GlobalNormalSpellsNoEffectLevel
|
||
// - The level of spells which are not affected - IE, if set to 3, then
|
||
// spell levels 0, 1, 2, 3 will NOT be cast. Abilites are still used!
|
||
|
||
// REMEMBER:
|
||
// - We still can cast summons, and friendly spells!
|
||
// - AOE spells may still affect someone in range
|
||
// - Breaches can breach natural spell resistance.
|
||
|
||
// If they are spell resistance immune, we do not cast any normal spells
|
||
// against them, except if we are a mage class
|
||
if(AI_SpellResistanceImmune(GlobalSpellTarget))
|
||
{
|
||
// Note: We set the breach level to max, 5, so that we use breaches
|
||
// to bring down the SR of an enemy!
|
||
GlobalDispelTargetHighestBreach = i5;
|
||
|
||
// Class checks. If not a mage caster, we will not use spells
|
||
if(GlobalOurChosenClass != CLASS_TYPE_WIZARD &&
|
||
GlobalOurChosenClass != CLASS_TYPE_SORCERER &&
|
||
GlobalOurChosenClass != CLASS_TYPE_FEY)
|
||
{
|
||
// 10 means we do not want to cast any normal spells
|
||
GlobalNormalSpellsNoEffectLevel = i10;
|
||
}
|
||
else
|
||
{
|
||
// We will set this to 5, level 5 spells and under stopped, if
|
||
// we are a mage.
|
||
GlobalNormalSpellsNoEffectLevel = i5;
|
||
}
|
||
}
|
||
|
||
// Set the value to 10 if we have a 90% or greater chance of failing spells
|
||
// due to any armor, or any spell failure stuff.
|
||
if(GetArcaneSpellFailure(OBJECT_SELF) >= i90 &&
|
||
GlobalNormalSpellsNoEffectLevel < i9)
|
||
{
|
||
// Always set to 10 if it is an effect
|
||
if(AI_GetAIHaveEffect(GlobalEffectSpellFailure))
|
||
{
|
||
GlobalNormalSpellsNoEffectLevel = i10;
|
||
}
|
||
// - If we have auto-still, Leave it as it is if we can still all spells automatically
|
||
else if(!GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_3) &&
|
||
!GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_2) &&
|
||
!GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_1))
|
||
{
|
||
// Else do not cast as in armor
|
||
GlobalNormalSpellsNoEffectLevel = i10;
|
||
}
|
||
}
|
||
|
||
// We may set GlobalNormalSpellsNoEffectLevel to 1-4 if the enemy has some
|
||
// extra spell immunities.
|
||
if(GlobalNormalSpellsNoEffectLevel < i9 &&
|
||
// Global setting needed
|
||
(GlobalIntelligence >= i9 ||
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SPECIFIC_SPELL_IMMUNITY, AI_COMBAT_MASTER)))
|
||
{
|
||
// This check now includes natural effect level
|
||
iValue = AI_GetSpellLevelEffect(GlobalSpellTarget);
|
||
if(GlobalNormalSpellsNoEffectLevel < iValue)
|
||
{
|
||
GlobalNormalSpellsNoEffectLevel = iValue;
|
||
}
|
||
}
|
||
|
||
GlobalSpellTargetWill = GetWillSavingThrow(GlobalSpellTarget);
|
||
GlobalSpellTargetFort = GetFortitudeSavingThrow(GlobalSpellTarget);
|
||
GlobalSpellTargetReflex = GetReflexSavingThrow(GlobalSpellTarget);
|
||
GlobalSpellTargetHitDice = GetHitDice(GlobalSpellTarget);
|
||
GlobalSpellTargetCurrentHitPoints = GetCurrentHitPoints(GlobalSpellTarget);
|
||
GlobalSeenSpell = GetObjectSeen(GlobalSpellTarget);
|
||
GlobalSpellTargetRace = GetRacialType(GlobalSpellTarget);
|
||
// Range
|
||
GlobalSpellTargetRange = GetDistanceToObject(GlobalSpellTarget);
|
||
|
||
// Set up GlobalSummonLocation to a location between targets.
|
||
// our last hostile actor, if not in 5 M, or else us
|
||
// - Summon spells have a 8M, short, range.
|
||
oSetUpTarget = OBJECT_INVALID;
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_SUMMON_TARGETING, AI_COMBAT_MASTER))
|
||
{
|
||
if(GetIsObjectValid(oLastHostile) && GetDistanceToObject(oLastHostile) <= f16)
|
||
{
|
||
oSetUpTarget = oLastHostile;
|
||
}
|
||
if(GetDistanceToObject(GlobalRangedTarget) <= f16)
|
||
{
|
||
oSetUpTarget = GlobalRangedTarget;
|
||
}
|
||
}
|
||
// Check summon target
|
||
if(GetIsObjectValid(oSetUpTarget))
|
||
{
|
||
// Taken from bioware's summon allies - half way between the targets.
|
||
// Because we get a maximum range of 16, it means the range will be 8.
|
||
vector vTarget = GetPosition(oSetUpTarget);
|
||
vector vSource = GetPosition(OBJECT_SELF);
|
||
vector vDirection = vTarget - vSource;
|
||
float fDistance = VectorMagnitude(vDirection) / f2;
|
||
vector vPoint = VectorNormalize(vDirection) * fDistance + vSource;
|
||
GlobalSummonLocation = Location(GetArea(OBJECT_SELF), vPoint, DIRECTION_NORTH);
|
||
}
|
||
// Else self
|
||
else
|
||
{
|
||
GlobalSummonLocation = GetLocation(OBJECT_SELF);
|
||
}
|
||
|
||
// Set dispel target.
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_DISPEL_MAGES_MORE, AI_COMBAT_MASTER))
|
||
{
|
||
// Sorcerors, to mages, to clerics, to druids.
|
||
GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_SORCERER, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
|
||
if(!GetIsObjectValid(GlobalDispelTarget))
|
||
{
|
||
GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_WIZARD, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
|
||
if(!GetIsObjectValid(GlobalDispelTarget))
|
||
{
|
||
GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_CLERIC, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
|
||
if(!GetIsObjectValid(GlobalDispelTarget))
|
||
{
|
||
GlobalDispelTarget = GetNearestCreature(CREATURE_TYPE_CLASS, CLASS_TYPE_DRUID, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if(!GetIsObjectValid(GlobalDispelTarget))
|
||
{
|
||
GlobalDispelTarget = GlobalSpellTarget;
|
||
}
|
||
// Set enchantments
|
||
AI_SetDispelableEnchantments();
|
||
|
||
// Delete everything
|
||
AI_TargetingArrayDelete(ARRAY_TEMP_ENEMIES);
|
||
AI_TargetingArrayDelete(ARRAY_TEMP_ALLIES);
|
||
AI_TargetingArrayDelete(ARRAY_TEMP_ARRAY);
|
||
AI_TargetingArrayDelete(ARRAY_MELEE_ENEMY);
|
||
AI_TargetingArrayDelete(ARRAY_RANGED_ENEMY);
|
||
AI_TargetingArrayDelete(ARRAY_SPELL_ENEMY);
|
||
|
||
// FALSE lets us continue with the script.
|
||
// - GlobalAnyValidTargetObject is set to TRUE if we want to attack anything.
|
||
return FALSE;
|
||
}// END ALL TARGETING
|
||
|
||
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name AttemptHostileSkills
|
||
//::///////////////////////////////////////////////
|
||
This will use empathy, taunt, and if set, pickpocketing. Most are random, and
|
||
checks are made.Heal is done in healing functions.Done against best melee target,
|
||
or closest seen/heard.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
// Uses iSkill on GlobalMeleeTarget
|
||
// - Fired from AI_AttemptHostileSkills.
|
||
void AI_ActionUseSkillOnMeleeTarget(int iSkill)
|
||
{
|
||
// 42: "[DCR:Skill] Using agressive skill (+Attack). [Skill] " + IntToString(iSkill) + " [Enemy]" + GetName(GlobalMeleeTarget)
|
||
DebugActionSpeakByInt(42, GlobalMeleeTarget, iSkill);
|
||
// We turn off hiding/searching
|
||
AI_ActionTurnOffHiding();
|
||
// Simple most damaging
|
||
// - Equip shield first
|
||
AI_EquipBestShield();
|
||
ActionEquipMostDamagingMelee(GlobalMeleeTarget);
|
||
ActionUseSkill(iSkill, GlobalMeleeTarget);
|
||
ActionWait(f2);
|
||
ActionAttack(GlobalMeleeTarget);
|
||
}
|
||
int AI_AttemptHostileSkills()
|
||
{
|
||
if(GlobalRangeToMeleeTarget < f4 &&
|
||
!GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER) &&
|
||
!SRA)// Spell ranged attacking = AI_FLAG_COMBAT_LONGER_RANGED_SPELLS_FIRST
|
||
{
|
||
// Handles it better like this...
|
||
int iEmpathyDC, iRace;
|
||
// Either: Not turned off, and intelligence >= 3, OR forced, and has it.
|
||
if((!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_PICKPOCKETING, AI_OTHER_COMBAT_MASTER) &&
|
||
GlobalIntelligence >= i3) ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_PICKPOCKETING, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
// Need appropriate level of skill, checked On Spawn, or overriden...
|
||
AI_ActionUseSkillOnMeleeTarget(SKILL_PICK_POCKET);
|
||
return TRUE;
|
||
}
|
||
// If we have 50% in taunt (a decent amount), and concentration ETC are OK...do it!
|
||
if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_TAUNTING, AI_OTHER_COMBAT_MASTER) &&
|
||
!GetLocalTimer(AI_TIMER_TAUNT))
|
||
{
|
||
if(GlobalIntelligence >= i2 ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_TAUNTING, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
// Random determine if we use it...
|
||
if(GetSkillRank(SKILL_TAUNT) + Random(i10) >=
|
||
GetSkillRank(SKILL_CONCENTRATION, GlobalMeleeTarget) + Random(i10))
|
||
{
|
||
// If randomly used, we set a timer for longer, and attack with it.
|
||
SetLocalTimer(AI_TIMER_TAUNT, f24);
|
||
SpeakArrayString(AI_TALK_ON_TAUNT);
|
||
AI_ActionUseSkillOnMeleeTarget(SKILL_TAUNT);
|
||
return TRUE;
|
||
}
|
||
else // 2 rounds until next check...
|
||
{
|
||
SetLocalTimer(AI_TIMER_TAUNT, f12);
|
||
}
|
||
}
|
||
}
|
||
// Animal empathy. Int 3
|
||
if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_EMPATHY, AI_OTHER_COMBAT_MASTER) &&
|
||
!GetLocalTimer(AI_TIMER_EMPATHY))
|
||
{
|
||
if(GlobalIntelligence >= i3 ||
|
||
GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_FORCE_EMPATHY, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
iEmpathyDC = i20;
|
||
iRace = GetRacialType(GlobalMeleeTarget);
|
||
// we add 4 (to make DC 24 + HD) if a special animal. R_T_ANIMAL is DC 20
|
||
if(iRace == RACIAL_TYPE_BEAST || iRace == RACIAL_TYPE_MAGICAL_BEAST)
|
||
{
|
||
iEmpathyDC += i4;
|
||
}
|
||
else if(iRace != RACIAL_TYPE_ANIMAL)
|
||
{
|
||
// Else, if we are not a beast, magical beast, nor animal,
|
||
// we don't use it!
|
||
SetLocalTimer(AI_TIMER_EMPATHY, f18);
|
||
// - Last skill we can use, so just return FALSE.
|
||
return FALSE;
|
||
}
|
||
// We check our skill against it...
|
||
if((GetSkillRank(SKILL_ANIMAL_EMPATHY) + i10) >= (GetHitDice(GlobalMeleeTarget) + i20))
|
||
{
|
||
// If randomly used, we set a timer for longer, and attack with it.
|
||
SetLocalTimer(AI_TIMER_EMPATHY, f24);
|
||
AI_ActionUseSkillOnMeleeTarget(SKILL_ANIMAL_EMPATHY);
|
||
return TRUE;
|
||
}
|
||
else // 2 rounds until next check...
|
||
{
|
||
SetLocalTimer(AI_TIMER_EMPATHY, f12);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name CastCombatHostileSpells
|
||
//::///////////////////////////////////////////////
|
||
This will cast all buffs needed, or wanted, before actual combat.
|
||
EG bulls strength for HTH, Cats grace for ranged and so on. Rages
|
||
here, else it may run out if we use spells, and other lower spells as well.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
int AI_AttemptFeatCombatHostile()
|
||
{
|
||
int iUseSpells = TRUE;
|
||
// Don't chance the use of broken paladin and ranger spells.
|
||
if(GlobalOurChosenClass == CLASS_TYPE_PALADIN ||
|
||
GlobalOurChosenClass == CLASS_TYPE_RANGER)
|
||
iUseSpells = FALSE;
|
||
|
||
if(iUseSpells)
|
||
{
|
||
// Of course, we use, or attempt to use, our Divine Domain Powers, if
|
||
// we have them! They are spells, of no talent (or no talent I want to check)
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasDomainSpells))
|
||
{
|
||
if(AI_ActionCastSpell(SPELLABILITY_BATTLE_MASTERY)) return TRUE; // STRENGTH domain
|
||
if(AI_ActionCastSpell(SPELLABILITY_DIVINE_STRENGTH)) return TRUE; // STRENGTH domain
|
||
if(AI_ActionCastSpell(SPELLABILITY_BATTLE_MASTERY)) return TRUE; // WAR domain
|
||
if(AI_ActionCastSpell(SPELLABILITY_DIVINE_TRICKERY)) return TRUE; // TRICKERY domain
|
||
}
|
||
// Special power. Not, I think, a domain one.
|
||
if(AI_ActionCastSpell(SPELLABILITY_ROGUES_CUNNING)) return TRUE;
|
||
|
||
// These are good-ass strength spells. They are basically
|
||
// powerful combat-orientated, strenth-inducing ones :-D
|
||
// Divine Power: Category 10, benifical Enhance self. Lvl 4.
|
||
if(AI_ActionCastSpell(SPELL_DIVINE_POWER, SpellEnhSelf, OBJECT_SELF, i14, FALSE, ItemEnhSelf, PotionPro)) return TRUE;
|
||
|
||
// We may cast cats grace, if they are 6M or over away, we have
|
||
// a ranged weapon equipped or we have lowish AC compared to HD.
|
||
if(GlobalOurAC < GlobalOurHitDice + i6 + Random(i4) ||
|
||
GlobalRangeToMeleeTarget > f6 ||
|
||
GetWeaponRanged(GlobalRightHandWeapon))
|
||
{
|
||
// Include harper cats grace.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasCatsGraceSpell))
|
||
{
|
||
// Cats Grace. Level 2 (Mage/Bard/Ranger). + d4() + 1 dexterity.
|
||
if(AI_ActionUseFeatOnObject(FEAT_HARPER_CATS_GRACE)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_GREATER_CATS_GRACE, SpellEnhSinTar)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_CATS_GRACE, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
}
|
||
// Include blackguard bulls strength.
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasBullsStrengthSpell))
|
||
{
|
||
// Bulls Strength. Level 2 (Mage/Cleric/Bard/Paladin/Druid). + d4() + 1 strength.
|
||
if(AI_ActionCastSpell(SPELL_GREATER_BULLS_STRENGTH, SpellEnhSinTar)) return TRUE;
|
||
// Blackguard version
|
||
if(AI_ActionUseFeatOnObject(FEAT_BULLS_STRENGTH)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELL_BULLS_STRENGTH, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
|
||
// Other helping spells, lower ranks. If we can't hit much, we cast this.
|
||
// - Some help more then others.
|
||
// - We ignore some if in HTH and have low HD compared to spell.
|
||
if((GlobalOurHitDice < i15 || GlobalMeleeAttackers < i1) &&
|
||
// We only add 15, as we don't apply strength ETC. at the mo.
|
||
(GlobalOurBaseAttackBonus - i15 < GlobalMeleeTargetAC) &&
|
||
(!AI_GetAIHaveSpellsEffect(GlobalHasAidingSpell)))
|
||
{
|
||
// Prayer. Level 3 (Paladin/Cleric). +1 Attack, damage, saves to all allies in 10M Including self.
|
||
if(AI_ActionCastSpell(SPELL_PRAYER, SpellEnhAre, OBJECT_SELF, i13, FALSE, ItemEnhAre)) return TRUE;
|
||
if(GlobalOurHitDice < i14)
|
||
{
|
||
// Aid. Level 2 (Cleric/Paladin) 3 (Ranger) +1 Attack Rolls, +1d8 HP
|
||
if(AI_ActionCastSpell(SPELL_AID, SpellEnhSinTar, OBJECT_SELF, i12, FALSE, ItemEnhSinTar)) return TRUE;
|
||
if(GlobalOurHitDice < i12)
|
||
{
|
||
// Bless. Level 1 (Cleric/Paladin). +1 Saves, +1 Damage, to allies in area.
|
||
if(AI_ActionCastSpell(SPELL_BLESS, SpellEnhSinTar, OBJECT_SELF, i11, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
// Attack bonus spells/feats, that directly add to our attack bonus.
|
||
|
||
// Divine might adds charisma bonus to Attack.
|
||
if(GetHasFeat(FEAT_TURN_UNDEAD))
|
||
{
|
||
// +Cha bonus to attack bonus.
|
||
if(AI_ActionUseFeatOnObject(FEAT_DIVINE_MIGHT)) return TRUE;
|
||
}
|
||
|
||
// We now cast the spells which will affect our weapon (if we have one!)
|
||
// Only cast these if we have a valid right hand weapon.
|
||
if(GetIsObjectValid(GlobalRightHandWeapon) &&
|
||
// Casts on melee weapons only, except if set to always use range
|
||
(!GetWeaponRanged(GlobalRightHandWeapon) ||
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER)) &&
|
||
// We do it VIA. the medium of one integer.
|
||
!AI_GetAIHaveSpellsEffect(GlobalHasWeaponHelpSpell))
|
||
{
|
||
// Cast best to worst, only one in affect at once, however.
|
||
|
||
// Blackstaff. Great stuff for a STAFF ONLY.
|
||
if(GetBaseItemType(GlobalRightHandWeapon) == BASE_ITEM_QUARTERSTAFF)
|
||
{
|
||
// Blackstaff. Level 8 (Mage). Adds +4 enhancement bonus, On Hit: Dispel.
|
||
if(AI_ActionCastSpell(SPELL_BLACKSTAFF, SpellEnhSinTar, GlobalRightHandWeapon, i18, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
// Greater Magical Weapon - up to +5
|
||
// Greater Magic Weapon. Level 3 (Mage/Bard/Paladin) 4 (Cleric). Grants a +1/3 caster levels (to +5).
|
||
if(AI_ActionCastSpell(SPELL_GREATER_MAGIC_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i14, FALSE, ItemEnhSinTar)) return TRUE;
|
||
|
||
// Blade Thurst - cast on any weapon. No bother to check type.
|
||
// Blade Thurst. Level 3 (Ranger). +3 Damage for a slashing weapon.
|
||
if(AI_ActionCastSpell(SPELL_BLADE_THIRST, SpellEnhSinTar, GlobalRightHandWeapon, i13, FALSE, ItemEnhSinTar)) return TRUE;
|
||
|
||
// Dark Fire. Level 3 (Cleric). 1d6 fire damage +1/level to a maximum of +10.
|
||
if(AI_ActionCastSpell(SPELL_DARKFIRE, SpellEnhSinTar, GlobalRightHandWeapon, i13, FALSE, ItemEnhSinTar)) return TRUE;
|
||
|
||
// Flame Weapon. Level 2 (Mage). 1d4 fire damage +1/level to a maximum of +10.
|
||
if(AI_ActionCastSpell(SPELL_FLAME_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i12, FALSE, ItemEnhSinTar)) return TRUE;
|
||
|
||
// Not if we are already keen :-)
|
||
if(!GetItemHasItemProperty(GlobalRightHandWeapon, ITEM_PROPERTY_KEEN))
|
||
{
|
||
// Keen Edge. Level 1 (Bard/Mage/Paladin). Adds the Keen property to a weapon
|
||
if(AI_ActionCastSpell(SPELL_BLADE_THIRST, SpellEnhSinTar, GlobalRightHandWeapon, i11, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
|
||
// Magic weapon - +1
|
||
if(!GetItemHasItemProperty(GlobalRightHandWeapon, ITEM_PROPERTY_ENHANCEMENT_BONUS))
|
||
{
|
||
// Magic Weapon. Level 1 (Bard/Cleric/Paladin/Ranger/Mage). +1 Enchantment to melee weapon.
|
||
if(AI_ActionCastSpell(SPELL_MAGIC_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i11, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
|
||
// Bless weapon
|
||
if(GetRacialType(GlobalMeleeTarget) == RACIAL_TYPE_UNDEAD)
|
||
{
|
||
// Bless weapon. Level 1 (Paladin). +1 Attack Bonus, +2d6 Damage to melee weapon VS undead
|
||
if(AI_ActionCastSpell(SPELL_MAGIC_WEAPON, SpellEnhSinTar, GlobalRightHandWeapon, i11, FALSE, ItemEnhSinTar)) return TRUE;
|
||
}
|
||
}
|
||
}
|
||
// We also will throw grenades here, if we are at a cirtain HD - 5 or under,
|
||
// because we may have ignored them in the spells part.
|
||
|
||
// Try grenades - we always throw these. They are about level 1 standard of DC's
|
||
// and effects. Not too bad, when NPC's get a chance to use them! :-)
|
||
if(GlobalOurHitDice <= i5 ||
|
||
// Will throw at range if under 10 HD
|
||
(GlobalRangeToMeleeTarget > f5 && GlobalOurHitDice < i10))
|
||
{
|
||
if(AI_AttemptGrenadeThrowing(GlobalMeleeTarget)) return TRUE;
|
||
}
|
||
|
||
// Here, we use all potions if set too...
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_USE_ALL_POTIONS, AI_COMBAT_MASTER))
|
||
{
|
||
int iUsed = FALSE;
|
||
// Check protection potion
|
||
if(PotionPro > FALSE && !GetHasSpellEffect(PotionPro))
|
||
{
|
||
iUsed = PotionPro;
|
||
ActionUseTalentOnObject(tPotionPro, OBJECT_SELF);
|
||
}
|
||
// Check enhancement potion
|
||
else if(PotionEnh > FALSE && !GetHasSpellEffect(PotionEnh))
|
||
{
|
||
iUsed = PotionEnh;
|
||
ActionUseTalentOnObject(tPotionEnh, OBJECT_SELF);
|
||
}
|
||
// Check conditional potion
|
||
else if(PotionCon > FALSE && !GetHasSpellEffect(PotionCon))
|
||
{
|
||
iUsed = PotionCon;
|
||
ActionUseTalentOnObject(tPotionCon, OBJECT_SELF);
|
||
}
|
||
if(iUsed > FALSE)
|
||
{
|
||
// 43 "[DCR:Pre-Melee Spells] All Potions Using. [Spell ID] " + IntToString(iUsed)
|
||
DebugActionSpeakByInt(43, OBJECT_INVALID, iUsed);
|
||
return TRUE;
|
||
}
|
||
}
|
||
|
||
// Rage - check effects via the set effects..
|
||
if(!AI_GetAIHaveSpellsEffect(GlobalHasRageSpells))
|
||
{
|
||
// Rage: either:
|
||
// +6, to STR, CON, will. -2 AC.
|
||
// or +4, to STR, CON, will. -2 AC.
|
||
if(AI_ActionUseFeatOnObject(FEAT_BARBARIAN_RAGE)) return TRUE;
|
||
// Blood frenzy. Level 2 (druid) as rage, for the most part. +2, to STR, CON, Will, -1 AC though.
|
||
if(AI_ActionCastSpell(SPELL_BLOOD_FRENZY, SpellOtherSpell, OBJECT_SELF, i12)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_RAGE_5)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_RAGE_4)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_FEROCITY_3)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_INTENSITY_3)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_RAGE_3)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_INTENSITY_2)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_FEROCITY_2)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_INTENSITY_1)) return TRUE;
|
||
if(AI_ActionCastSpell(SPELLABILITY_FEROCITY_1)) return TRUE;
|
||
}
|
||
// Cast expeditious retreat if we want to get into combat quicker
|
||
if(GlobalRangeToMeleeTarget >= f20 &&
|
||
!GetWeaponRanged(GlobalRightHandWeapon) &&
|
||
!AI_GetAIHaveEffect(GlobalEffectHaste) &&
|
||
!GetHasSpellEffect(SPELL_EXPEDITIOUS_RETREAT))
|
||
{
|
||
// Expeditious Retreat. Level 1 (Bard/Mage). +150% movement speed.
|
||
if(AI_ActionCastSpell(SPELL_EXPEDITIOUS_RETREAT, SpellEnhSelf, OBJECT_SELF, i11, FALSE, ItemEnhSelf, PotionEnh)) return TRUE;
|
||
}
|
||
// Cast true strike if we are RIGHT near the melee target, or are using
|
||
// a ranged weapon
|
||
if((GlobalRangeToMeleeTarget < f3 ||
|
||
GetWeaponRanged(GlobalRightHandWeapon)) &&
|
||
!GetHasSpellEffect(SPELL_TRUE_STRIKE))
|
||
{
|
||
// True Strike. Level 1 (Mage). +20 attack bonus for 9 seconds (IE about 1, or 2, attacks)
|
||
if(AI_ActionCastSpell(SPELL_TRUE_STRIKE, SpellEnhSelf, OBJECT_SELF, i11, FALSE, ItemEnhSelf, PotionEnh))
|
||
{
|
||
// 44: "[DCR:Pre-Melee Spells] True Strike Emptive attack [Target] " + GetName(GlobalMeleeTarget)
|
||
DebugActionSpeakByInt(44, GlobalMeleeTarget);
|
||
// Add attack to end of action queue. Should do this next round
|
||
// anyway
|
||
if(GlobalRangeToMeleeTarget <= f4)
|
||
{
|
||
ActionEquipMostDamagingMelee(GlobalMeleeTarget);
|
||
}
|
||
else
|
||
{
|
||
ActionEquipMostDamagingRanged(GlobalMeleeTarget);
|
||
}
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
/*::///////////////////////////////////////////////
|
||
//:: Name PolyMorph
|
||
//::///////////////////////////////////////////////
|
||
If we are not affected by polymorph, going down from
|
||
best to worst, we will polymorph. Use after combat buffs,
|
||
after spells (although because some are random, this
|
||
may fire when they still have some) and before we attack.
|
||
//::///////////////////////////////////////////////
|
||
//:: Created By: Jasperre
|
||
//::////////////////////////////////////////////*/
|
||
|
||
// This will cheat-cast iSpell at oTarget. Note that we will know if we have it
|
||
// by checking what appearance we have.
|
||
void AI_ActionCastShifterSpell(int iSpell, object oTarget = OBJECT_SELF)
|
||
{
|
||
// Cheat cast the spell. We know we must have it.
|
||
ActionCastSpellAtObject(iSpell, oTarget, METAMAGIC_NONE, TRUE);
|
||
}
|
||
// This willcast iFirst -> iFirst + iAmount polymorph spell. It will check
|
||
// if we have iMaster (Either by feat or spell, depending on iFeat).
|
||
// TRUE if we polymorph.
|
||
int AI_ActionPolymorph(int iMaster, int iFirstSpell, int iAmount, int iFeat = FALSE, int iRemove = TRUE)
|
||
{
|
||
if((iFeat && GetHasFeat(iMaster)) ||
|
||
(!iFeat && GetHasSpell(iMaster)))
|
||
{
|
||
// Randomise
|
||
// EG: Got from 300 to 303, so iAmount is 3 and we input 300 as the start one.
|
||
int iCast = iFirstSpell + Random(iAmount + i1);
|
||
|
||
// Debug
|
||
// 11: "[DCR:Casting] SubSpecialSpell. [ID] " + IntToString(iInput) + " [Target] " + GetName(oInput) + " [Location] " + sInput; break;
|
||
DebugActionSpeakByInt(11, OBJECT_SELF, iCast);
|
||
|
||
// If a spell, we concentration check it :-)
|
||
if(!iFeat)
|
||
{
|
||
AI_AttemptConcentrationCheck(GlobalSpellTarget);
|
||
}
|
||
|
||
// Cast it
|
||
ActionCastSpellAtObject(iCast, OBJECT_SELF, METAMAGIC_NONE, TRUE);
|
||
|
||
if(iRemove)
|
||
{
|
||
// Decrement one or the other
|
||
if(iFeat)
|
||
{
|
||
// Feat
|
||
DecrementRemainingFeatUses(OBJECT_SELF, iMaster);
|
||
}
|
||
else
|
||
{
|
||
// Spell
|
||
DecrementRemainingSpellUses(OBJECT_SELF, iMaster);
|
||
}
|
||
}
|
||
// Add Action Attack melee target :-D
|
||
ActionAttack(GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Will polymorph Self if not already so. Will return TRUE if it casts best on self.
|
||
int AI_AttemptPolyMorph()
|
||
{
|
||
if(!GetSpawnInCondition(AI_FLAG_OTHER_NO_POLYMORPHING, AI_OTHER_MASTER) &&
|
||
// We don't polymorph as an archer.
|
||
!GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER))
|
||
{
|
||
// Spells that are tensers transformation and polymorth here.
|
||
// Will cast 100% on all shapechanges. Need to make it check for any spells.
|
||
// Check values needed
|
||
if(!AI_GetAIHaveEffect(GlobalEffectPolymorph))
|
||
{
|
||
// Epic shapechanging feats
|
||
// - Dragon - just different breaths.
|
||
// 707 Greater_Wild_Shape_Red_dragon
|
||
// 708 Greater_Wild_Shape_Blue_dragon
|
||
// 709 Greater_Wild_Shape_Green_dragon
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_WILD_SHAPE_DRAGON, 707, 3, TRUE)) return TRUE;
|
||
// Construct feat
|
||
// 738 Construct_Shape_StoneGolem
|
||
// 739 Construct_Shape_DemonFleshGolem
|
||
// 740 Construct_Shape_IronGolem
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_CONSTRUCT_SHAPE, 738, 3, TRUE)) return TRUE;
|
||
// Outsider shape
|
||
// 733 Outsider_Shape_Azer
|
||
// 734 Outsider_Shape_Rakshasa
|
||
// 735 Outsider_Shape_DeathSlaad
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_OUTSIDER_SHAPE, 733, 3, TRUE)) return TRUE;
|
||
// Undead - any of them
|
||
// 704 Undead_Shape_risen_lord
|
||
// 705 Undead_Shape_Vampire
|
||
// 706 Undead_Shape_Spectre
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_WILD_SHAPE_UNDEAD, 704, 3, TRUE)) return TRUE;
|
||
// Shapechange
|
||
// 392 Shapechange_RED_DRAGON
|
||
// 393 Shapechange_FIRE_GIANT
|
||
// 394 Shapechange_BALOR
|
||
// 395 Shapechange_DEATH_SLAAD
|
||
// 396 Shapechange_IRON_GOLEM
|
||
if(AI_ActionPolymorph(SPELL_SHAPECHANGE, 392, 5)) return TRUE;
|
||
// Druid feat. Elements.
|
||
// 397 Elemental_Shape_FIRE
|
||
// 398 Elemental_Shape_WATER
|
||
// 399 Elemental_Shape_EARTH
|
||
// 400 Elemental_Shape_AIR
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_DRUID_INFINITE_ELEMENTAL_SHAPE, 397, 4, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(FEAT_ELEMENTAL_SHAPE, 397, 4, TRUE)) return TRUE;
|
||
// Wildshape 4 is next best
|
||
// - Infinite and normal shapes
|
||
// - Not in any order (doh!)
|
||
// 671 Greater_Wild_Shape_Beholder
|
||
// 679 Greater_Wild_Shape_Medusa
|
||
// 691 Greater_Wild_Shape_Mindflayer
|
||
// 694 Greater_Wild_Shape_DireTiger
|
||
// Random choose one
|
||
int iSpell = 671;
|
||
switch(d4())
|
||
{
|
||
case i1: iSpell = 671; break;
|
||
case i2: iSpell = 679; break;
|
||
case i3: iSpell = 691; break;
|
||
case i4: iSpell = 694; break;
|
||
}
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_4, iSpell, FALSE, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_4, iSpell, FALSE, TRUE)) return TRUE;
|
||
// Humanoid shape
|
||
// 682 Humanoid_Shape_Drow
|
||
// 683 Humanoid_Shape_Lizardfolk
|
||
// 684 Humanoid_Shape_KoboldAssa
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_HUMANOID_SHAPE, 682, 3, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(AI_FEAT_HUMANOID_SHAPE, 682, 3, TRUE)) return TRUE;
|
||
// 3 - Infinite and normal shapes
|
||
// 3 - Not in any order (doh!)
|
||
// 670 Greater_Wild_Shape_Basilisk
|
||
// 673 Greater_Wild_Shape_Drider
|
||
// 674 Greater_Wild_Shape_Manticore
|
||
// Random choose one
|
||
iSpell = 670;
|
||
switch(d3())
|
||
{
|
||
case i1: iSpell = 670; break;
|
||
case i2: iSpell = 673; break;
|
||
case i3: iSpell = 674; break;
|
||
}
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_3, iSpell, FALSE, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_3, iSpell, FALSE, TRUE)) return TRUE;
|
||
// 2 - Infinite and normal shapes
|
||
// 2 - Not in any order (doh!)
|
||
// 672 Greater_Wild_Shape_Harpy
|
||
// 678 Greater_Wild_Shape_Gargoyle
|
||
// 680 Greater_Wild_Shape_Minotaur
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_2, iSpell, FALSE, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_2, iSpell, FALSE, TRUE)) return TRUE;
|
||
// 1 - Infinite and normal shapes - In order
|
||
// 658 Greater_Wild_Shape_Wyrmling_Red
|
||
// 659 Greater_Wild_Shape_Wyrmling_Blue
|
||
// 660 Greater_Wild_Shape_Wyrmling_Black
|
||
// 661 Greater_Wild_Shape_Wyrmling_White
|
||
// 662 Greater_Wild_Shape_Wyrmling_Green
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_SHIFTER_INFINITE_WILDSHAPE_1, 658, 5, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(AI_FEAT_GREATER_WILDSHAPE_1, 658, 5, TRUE)) return TRUE;
|
||
|
||
// We can have items for this polymorph spell :-)
|
||
if(AI_ActionCastSpell(SPELL_TENSERS_TRANSFORMATION, SpellEnhSelf, OBJECT_SELF, i16, ItemEnhSelf, PotionEnh)) return TRUE;
|
||
|
||
// Animal wildshape
|
||
// 401 Wild_Shape_BROWN_BEAR
|
||
// 402 Wild_Shape_PANTHER
|
||
// 403 Wild_Shape_WOLF
|
||
// 404 Wild_Shape_BOAR
|
||
// 405 Wild_Shape_BADGER
|
||
if(AI_ActionPolymorph(AI_FEAT_EPIC_DRUID_INFINITE_WILDSHAPE, 401, 5, TRUE, FALSE)) return TRUE;
|
||
if(AI_ActionPolymorph(FEAT_WILD_SHAPE, 401, 5, TRUE)) return TRUE;
|
||
|
||
// Shapechange into - Troll, Pixie, Uber Hulk, Giant Spider, Zombie
|
||
// 387 Polymorph_GIANT_SPIDER
|
||
// 388 Polymorph_TROLL
|
||
// 389 Polymorph_UMBER_HULK
|
||
// 390 Polymorph_PIXIE
|
||
// 391 Polymorph_ZOMBIE
|
||
if(AI_ActionPolymorph(SPELL_POLYMORPH_SELF, 387, 5, TRUE)) return TRUE;
|
||
}
|
||
else /*Else we have it*/if(d10() <= i6)
|
||
{
|
||
// The special abilities VIA. Shapechanger!
|
||
// - We check our appearance and cast as appropriately.
|
||
// - 60% base chance of casting an ability or spell.
|
||
|
||
switch(GlobalOurAppearance)
|
||
{
|
||
case APPEARANCE_TYPE_ELEMENTAL_WATER:
|
||
case APPEARANCE_TYPE_ELEMENTAL_WATER_ELDER:
|
||
{
|
||
// We can cast Pulse Drown unlimited times/day.
|
||
// Only use if above 50HP, and enemy has less then 20 Fortitude
|
||
// and is within 4M
|
||
if(GlobalRangeToMeleeTarget < f4 && GlobalOurCurrentHP > i50 &&
|
||
GetFortitudeSavingThrow(GlobalMeleeTarget) < i18)
|
||
{
|
||
AI_ActionCastShifterSpell(SPELLABILITY_PULSE_DROWN);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_ELEMENTAL_AIR:
|
||
case APPEARANCE_TYPE_ELEMENTAL_AIR_ELDER:
|
||
{
|
||
// Can use Pulse: Whirlwind unlimited times/day.
|
||
// DC 14 or knockdown + some damage.
|
||
if(GlobalRangeToMeleeTarget < f4 && GetReflexSavingThrow(GlobalMeleeTarget) < i15)
|
||
{
|
||
AI_ActionCastShifterSpell(SPELLABILITY_PULSE_WHIRLWIND);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
// Wyrmling Breath Attacks are in the default dragon behaviour
|
||
// (we check for dragon appearance and check talents)
|
||
case APPEARANCE_TYPE_MANTICORE:
|
||
{
|
||
// Can use Spike Attack (Greater Wild Shape Version) at
|
||
// some reflex save or other.
|
||
if(GlobalRangeToMeleeTarget < f10 &&
|
||
GetReflexSavingThrow(GlobalMeleeTarget) < (i18+ d6()))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_GWILDSHAPE_SPIKES, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
// Drow/Drider : Don't bother with darkness.
|
||
case 491:// Harpy!
|
||
{
|
||
// Harpysong - charm enemies. Will saving throw.
|
||
if(GlobalRangeToMeleeTarget < f4 &&
|
||
GetWillSavingThrow(GlobalMeleeTarget) < (i14 + d4()))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_HARPYSONG);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_BASILISK:
|
||
case APPEARANCE_TYPE_MEDUSA:
|
||
{
|
||
// Limited petrify gaze attack
|
||
// Pretty cool.
|
||
if(GetLocalInt(OBJECT_SELF,"X2_GWILDSHP_LIMIT_" + IntToString(AI_SPELLABILITY_GWILDSHAPE_STONEGAZE)) &&
|
||
GlobalRangeToMeleeTarget < f4 &&
|
||
GetFortitudeSavingThrow(GlobalMeleeTarget) < (i14 + d4()))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_GWILDSHAPE_STONEGAZE, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case 413: // Mind Flayer
|
||
{
|
||
// Psionic Inertial Barrier ability - unlimited uses.
|
||
if(!GetHasSpellEffect(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER);
|
||
return TRUE;
|
||
}
|
||
// Else, we have Mind Blast. Limited uses, but stuns!
|
||
else if(!GetHasSpellEffect(AI_SPELLABILITY_GWILDSHAPE_MINDBLAST, GlobalMeleeTarget) &&
|
||
GetLocalInt(OBJECT_SELF,"X2_GWILDSHP_LIMIT_" + IntToString(AI_SPELLABILITY_GWILDSHAPE_MINDBLAST)) &&
|
||
GlobalRangeToMeleeTarget < f8)
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_GWILDSHAPE_MINDBLAST);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
// Dragons (ancient) checked for breath via. normal means.
|
||
case APPEARANCE_TYPE_VAMPIRE_FEMALE: // Vampires
|
||
case APPEARANCE_TYPE_VAMPIRE_MALE: // Vampires
|
||
{
|
||
// Limited Domination Gazes.
|
||
if(GetLocalInt(OBJECT_SELF, "X2_GWILDSHP_LIMIT_" + IntToString(AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE)) &&
|
||
!GetHasSpellEffect(AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE, GlobalMeleeTarget) &&
|
||
GetWillSavingThrow(GlobalMeleeTarget) < (i14 + d4()) &&
|
||
// This is a simple check for "have we got a dominated guy already"
|
||
!AI_GetSpellTargetImmunity(GlobalImmunityDomination))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_VAMPIRE_DOMINATION_GAZE, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_SPECTRE:
|
||
{
|
||
// Unlimited invisibility, and unlimited "spectre attack".
|
||
// 60% chance of using the spectre attack.
|
||
if(d10() <= i6 && GetFortitudeSavingThrow(GlobalMeleeTarget) < (i14 + d10()) &&
|
||
!GetIsImmune(GlobalMeleeTarget, IMMUNITY_TYPE_NEGATIVE_LEVEL))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_SHIFTER_SPECTRE_ATTACK, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
// Else invisiblity
|
||
if(!AI_GetAIHaveEffect(GlobalEffectInvisible))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_VAMPIRE_INVISIBILITY);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_WILL_O_WISP:
|
||
{
|
||
// Unlimited invisibility
|
||
if(!AI_GetAIHaveEffect(GlobalEffectInvisible))
|
||
{
|
||
AI_ActionCastShifterSpell(SPELL_INVISIBILITY);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case 428:// Azer man
|
||
case 429:// Azer female
|
||
{
|
||
// Unlimited fire attacks.
|
||
// Burning hands and azer blast.
|
||
// 80% chance of azer blast
|
||
if(GetReflexSavingThrow(GlobalMeleeTarget) < (i14 + d6()) &&
|
||
d10() <= i8)
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_AZER_FIRE_BLAST, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
// Else burning hands
|
||
if(GetReflexSavingThrow(GlobalMeleeTarget) < (i12 + d4()))
|
||
{
|
||
AI_ActionCastShifterSpell(SPELL_BURNING_HANDS, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_SLAAD_DEATH:
|
||
{
|
||
// Unlimited spittle attacks. Just need the base 60% chance above.
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_SLAAD_CHAOS_SPITTLE, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_RAKSHASA_TIGER_FEMALE:
|
||
case APPEARANCE_TYPE_RAKSHASA_TIGER_MALE:
|
||
{
|
||
// Unlimited spells - 3.
|
||
// - Dispel Magic
|
||
// - Ice Storm
|
||
// - Mestils Acid Breath.
|
||
// Randomise each one. Don't bother checking saves.
|
||
if(d10() <= i6)
|
||
{
|
||
AI_ActionCastShifterSpell(SPELL_DISPEL_MAGIC, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
else if(d10() <= i6)
|
||
{
|
||
AI_ActionCastShifterSpell(VFX_FNF_ICESTORM, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
else
|
||
{
|
||
AI_ActionCastShifterSpell(SPELL_MESTILS_ACID_BREATH, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_GOLEM_IRON:
|
||
{
|
||
// Unlimited spells Iron Golem Breath.
|
||
if(GetFortitudeSavingThrow(GlobalMeleeTarget) < i20)
|
||
{
|
||
AI_ActionCastShifterSpell(SPELLABILITY_GOLEM_BREATH_GAS, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case APPEARANCE_TYPE_GOLEM_STONE:
|
||
{
|
||
// Unlimited spells: Throw rocks
|
||
if(GetReflexSavingThrow(GlobalMeleeTarget) < i20 + d6())
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_GIANT_HURL_ROCK, GlobalMeleeTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
case 302:// Kobold (assassin)
|
||
{
|
||
// Unlimited invisibility
|
||
// Unlimited invisibility
|
||
if(!AI_GetAIHaveEffect(GlobalEffectInvisible))
|
||
{
|
||
AI_ActionCastShifterSpell(AI_SPELLABILITY_VAMPIRE_INVISIBILITY);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
return FALSE;
|
||
}
|
||
// Returns TRUE if we counterspell GlobalCounterspellTarget, only does it
|
||
// if we have Dispels, and if set to want to be in a group, we are in one :-)
|
||
int AI_AttemptCounterSpell()
|
||
{
|
||
// Check for 5+ allies if counter spell in group. If <= 4, return FALSE
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ONLY_IN_GROUP, AI_COMBAT_MASTER) &&
|
||
GlobalTotalAllies <= i4)
|
||
{
|
||
return FALSE;
|
||
}
|
||
// Need a dispel spell
|
||
if(!(GetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION) ||
|
||
GetHasSpell(SPELL_GREATER_DISPELLING) ||
|
||
GetHasSpell(SPELL_DISPEL_MAGIC) ||
|
||
GetHasSpell(SPELL_LESSER_DISPEL)))
|
||
{
|
||
return FALSE;
|
||
}
|
||
object oLoopTarget, oCounterspellTarget;
|
||
int iCnt, iCasterLevels, iHighestLevels;
|
||
float fDistance;
|
||
// Try and get a Arcane caster to counter
|
||
if(GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ARCANE, AI_COMBAT_MASTER))
|
||
{
|
||
// Loop seen enemies - must be within 20M and valid
|
||
iCnt = i1;
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oLoopTarget) && fDistance <= f20)
|
||
{
|
||
// Check caster levels
|
||
iCasterLevels = GetLevelByClass(CLASS_TYPE_WIZARD, oLoopTarget) +
|
||
GetLevelByClass(CLASS_TYPE_SORCERER, oLoopTarget) +
|
||
GetLevelByClass(CLASS_TYPE_BARD, oLoopTarget);
|
||
// Check if higher.
|
||
if(iCasterLevels > iHighestLevels)
|
||
{
|
||
iHighestLevels = iCasterLevels;
|
||
oCounterspellTarget = oLoopTarget;
|
||
}
|
||
// Get next target
|
||
iCnt++;
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
}
|
||
// If not valid, might check divine
|
||
if(!GetIsObjectValid(oCounterspellTarget) &&
|
||
iHighestLevels >= GlobalOurHitDice / i3 &&
|
||
GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_DIVINE, AI_COMBAT_MASTER))
|
||
{
|
||
// Loop seen enemies - must be within 20M and valid
|
||
iHighestLevels = FALSE;
|
||
iCnt = i1;
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
while(GetIsObjectValid(oLoopTarget) && fDistance <= f20)
|
||
{
|
||
// Check caster levels
|
||
iCasterLevels = GetLevelByClass(CLASS_TYPE_CLERIC, oLoopTarget) +
|
||
GetLevelByClass(CLASS_TYPE_DRUID, oLoopTarget);
|
||
// Check if higher.
|
||
if(iCasterLevels > iHighestLevels)
|
||
{
|
||
iHighestLevels = iCasterLevels;
|
||
oCounterspellTarget = oLoopTarget;
|
||
}
|
||
// Get next target
|
||
iCnt++;
|
||
oLoopTarget = GetLocalObject(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
fDistance = GetLocalFloat(OBJECT_SELF, ARRAY_ENEMY_RANGE_SEEN + IntToString(iCnt));
|
||
}
|
||
}
|
||
// Check if valid
|
||
if(GetIsObjectValid(oCounterspellTarget))
|
||
{
|
||
// 45: "[DCR:CounterSpell] Counterspelling. [Target] " + GetName(oCounterspellTarget)
|
||
DebugActionSpeakByInt(45, oCounterspellTarget);
|
||
AI_SetMeleeMode(ACTION_MODE_COUNTERSPELL);
|
||
ActionCounterSpell(oCounterspellTarget);
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
// This will, in most occasion, ClearAllActions.
|
||
// If it does NOT, it returns FALSE, if we are doing something more important,
|
||
// and we perform that action again (or carry on doing it).
|
||
int AI_StopWhatWeAreDoing()
|
||
{
|
||
// - New wrappered function
|
||
if(GetIsPerformingSpecialAction())
|
||
{
|
||
return FALSE;
|
||
}
|
||
// See if we need ClearAllActions(TRUE) for HIPS
|
||
if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT) &&
|
||
GetStealthMode(OBJECT_SELF) == STEALTH_MODE_DISABLED &&
|
||
!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_NO_HIDING, AI_OTHER_COMBAT_MASTER))
|
||
{
|
||
// Check for nearest person with trueseeing (which pierces hiding)
|
||
if(!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_HAS_SPELL_EFFECT, SPELL_TRUE_SEEING, OBJECT_SELF, i1, CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY)))
|
||
{
|
||
ClearAllActions(TRUE);
|
||
SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE);
|
||
return TRUE;
|
||
}
|
||
}
|
||
// Else we return TRUE, meaning we have cleared all actions
|
||
ClearAllActions();
|
||
return TRUE;
|
||
}
|
||
|
||
/************************ [AI_DetermineCombatRound] ****************************
|
||
This will do an action - EG: ActionAttack, against oIntruder, or against
|
||
the best (or random) target in range.
|
||
|
||
At the end of combat, it heals up and searches for more enemies before
|
||
resuming normal activities.
|
||
************************* [History] ********************************************
|
||
1.0 - Started
|
||
1.2 - Fixed minor bugs
|
||
1.3 - Global targets added, cleaned up as Global* constants are used.
|
||
************************* [Workings] *******************************************
|
||
It will start by checking the creatures effects, if they cannot move, nothing
|
||
is done. Daze, however, forces them to move away from last attacker.
|
||
|
||
We then make sure we are not in an AOE spell, and not fleeing, if we are in
|
||
either, we react accordingly.
|
||
|
||
After that, it sets up Global* targets, SpellTarget, RangedTarget, MeleeTarget,
|
||
and the integers such as GlobalAverageEnemyHD.
|
||
|
||
If none are valid, it will search, but if we know there is an enemy around,
|
||
we move to them.
|
||
|
||
If there is a valid target, at least one object Seen or Heard in our LOS, then
|
||
we perform a set of checks, and choose an action - normally using Class
|
||
abilities - such as Turn Undead, and Bard Songs, then spells, and finally
|
||
using Combat spells, and attacking the target using feats. Skills are also
|
||
used after spells.
|
||
|
||
Dragons also run Wing Buffet, as well as using Breath attacks.
|
||
************************* [Arguments] ******************************************
|
||
Arguments: oIntruder
|
||
************************* [AI_DetermineCombatRound] ***************************/
|
||
void AI_DetermineCombatRound(object oIntruder = OBJECT_INVALID)
|
||
{
|
||
// 46: "[DRC] START [Intruder]" + GetName(oIntruder)
|
||
DebugActionSpeakByInt(46, oIntruder);
|
||
|
||
// Useful in the IsUncommandable check - we don't loop effects more than once.
|
||
AI_SetUpUsEffects();
|
||
|
||
// New check - If they are commandable, and no stupid ones.
|
||
if(AI_GetAIHaveEffect(GlobalEffectUncommandable) ||
|
||
AI_GetAIHaveEffect(GlobalEffectParalyze) ||
|
||
GetHasFeatEffect(FEAT_IMPROVED_KNOCKDOWN) ||
|
||
GetHasFeatEffect(FEAT_KNOCKDOWN) ||
|
||
GetAIOff())
|
||
{
|
||
DeleteAIObject(AI_LAST_MELEE_TARGET);
|
||
DeleteAIObject(AI_LAST_SPELL_TARGET);
|
||
DeleteAIObject(AI_LAST_RANGED_TARGET);
|
||
// 47: "[DCR] [PREMITURE EXIT] Cannot Do Anything."
|
||
DebugActionSpeakByInt(47);
|
||
return;
|
||
}
|
||
// 1.30 - daze is now as 3E rules, you can move around walking, but no
|
||
// attacking, casting or anything else :-(
|
||
else if(AI_GetAIHaveEffect(GlobalEffectDazed))
|
||
{
|
||
// 48: "[DCR] [PREMITURE EXIT] Dazed move away."
|
||
DebugActionSpeakByInt(48);
|
||
// Equip best shield for most AC
|
||
AI_EquipBestShield();
|
||
// Move away from the nearest heard enemy
|
||
GlobalNearestEnemyHeard = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
ActionMoveAwayFromObject(GlobalNearestEnemyHeard);
|
||
return;
|
||
}
|
||
// Tempory integer
|
||
int iTempInt;
|
||
|
||
// Set combat AI level
|
||
iTempInt = GetAIConstant(LAG_AI_LEVEL_COMBAT);
|
||
if(iTempInt > iM1 && GetAILevel() != iTempInt)
|
||
{
|
||
SetAILevel(OBJECT_SELF, iTempInt);
|
||
}
|
||
// We stop - ClearAllActions normally
|
||
// NOTE: This returns FALSE if we don't stop actions - and want to carry on
|
||
// doing the thing before! like fleeing! (or we do it in the Stop thing).
|
||
if(!AI_StopWhatWeAreDoing())
|
||
{
|
||
// 49: "[DCR] [PREMITURE EXIT] Fleeing or otherwise"
|
||
DebugActionSpeakByInt(49);
|
||
return;
|
||
}
|
||
|
||
// Then we check all objects. we are going to perform a normal, or fleeing
|
||
// action this round, or action call.
|
||
|
||
// Sets up us!
|
||
AI_SetUpUs();
|
||
|
||
// We set up 2 other targets...for testing against ETC.
|
||
GlobalNearestEnemySeen = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
GlobalNearestEnemyHeard = GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, i1, CREATURE_TYPE_PERCEPTION, PERCEPTION_HEARD, CREATURE_TYPE_IS_ALIVE, TRUE);
|
||
// Valids?
|
||
GlobalValidNearestSeenEnemy = GetIsObjectValid(GlobalNearestEnemySeen);
|
||
GlobalValidNearestHeardEnemy = GetIsObjectValid(GlobalNearestEnemyHeard);
|
||
|
||
// Speakstring arrays
|
||
if(GlobalValidNearestHeardEnemy)
|
||
{
|
||
// Add in range checking here
|
||
if(GetDistanceToObject(GlobalNearestEnemyHeard) < GetDistanceToObject(GlobalNearestEnemySeen))
|
||
{
|
||
GlobalRangeToNearestEnemy = GetDistanceToObject(GlobalNearestEnemyHeard);
|
||
}
|
||
else
|
||
{
|
||
GlobalRangeToNearestEnemy = GetDistanceToObject(GlobalNearestEnemySeen);
|
||
}
|
||
|
||
iTempInt = GetHitDice(GlobalNearestEnemyHeard);
|
||
// THEM_OVER_US - They have 5+ levels over us.
|
||
if(iTempInt - i5 >= GlobalOurHitDice)
|
||
{
|
||
SpeakArrayString(AI_TALK_ON_COMBAT_ROUND_THEM_OVER_US, TRUE);
|
||
}
|
||
// US_OVER_THEM - We have 5+ levels over them.
|
||
else if(GlobalOurHitDice - i5 >= iTempInt)
|
||
{
|
||
SpeakArrayString(AI_TALK_ON_COMBAT_ROUND_US_OVER_THEM, TRUE);
|
||
}
|
||
// EQUAL - Thier HD is within 4HD of us (EG: Us 10, them 10)
|
||
else //if(iTempInt - i4 <= GlobalOurHitDice && iTempInt + i4 >= GlobalOurHitDice)
|
||
{
|
||
SpeakArrayString(AI_TALK_ON_COMBAT_ROUND_EQUAL, TRUE);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
GlobalRangeToNearestEnemy = f0;
|
||
}
|
||
// We may Dispel AOE's, move out of it, or ignore AOE's, EG Darkness.
|
||
// Dragons may use this (though small chance)
|
||
// Done before other things - best get out of some bad spells else they kill us!
|
||
// - Returns TRUE if we did anything that would mean we don't want to do
|
||
// another action
|
||
if(AI_AttemptSpecialChecks()){return;}
|
||
|
||
// Sets up who to attack.
|
||
// - Uses oIntruder (to attack or move near) if anything.
|
||
// - We return TRUE if it ActionAttack's, or moves to an enemy - basically
|
||
// that we cannot do an action, but shouldn't search. False if normal.
|
||
// - We do this after AOE checking and special spells.
|
||
if(AI_SetUpAllObjects(oIntruder)){return;}
|
||
|
||
// We then check if we have anyone to attack :-) This is a global integer.
|
||
if(GlobalAnyValidTargetObject)
|
||
{
|
||
// We do our auras. Quicken casted.
|
||
AI_ActionAbilityAura();
|
||
|
||
// We may flee from massive odds, or if we panic...or commoner fleeing
|
||
if(AI_AttemptMoraleFlee()){return;}
|
||
|
||
// Beholder and Mindflayer special AI
|
||
iTempInt = GetAIInteger(AI_SPECIAL_AI);
|
||
if(iTempInt == i1)
|
||
{
|
||
// Beholder attacks. Should always return TRUE. Uses eye rays,
|
||
// casts spells, and teleports/flee's.
|
||
if(AI_AttemptBeholderCombat()){return;}
|
||
}
|
||
else if(iTempInt == i2)
|
||
{
|
||
// Special mindflayer things. This can fall through.
|
||
if(AI_AttemptMindflayerCombat()){return;}
|
||
}
|
||
|
||
// We will attempt to heal ourselves first as a prioritory.
|
||
// Dragons may use this.
|
||
if(AI_AttemptHealingSelf()){return;}
|
||
// We will attempt to heal a previously set ally, with spells.
|
||
// Dragons use this, not always (and like to save spells for themselves).
|
||
if(AI_AttemptHealingAlly()){return;}
|
||
// We will cure, normally our conditions, or an allies. Things like
|
||
// blindness always, with other things later.
|
||
// Dragons use this, mostly with themselves.
|
||
if(AI_AttemptCureCondition()){return;}
|
||
|
||
// This is a good thing to use first. Dragons may use this.
|
||
if(AI_AttemptFeatBardSong()){return;}
|
||
// We may summon our monster. Always first, because its our's and personal. Dragons may use this (though small chance)
|
||
if(AI_AttemptFeatSummonFamiliar()){return;}
|
||
// Turning, any sort of unturned and non-resistant creatures.
|
||
// Used about every 3 rounds. Dragons may use this.
|
||
if(AI_AttemptFeatTurning()){return;}
|
||
|
||
// Special Dragon things now, else other monsters.
|
||
if(AI_GetIsDragon())
|
||
{
|
||
// We may attempt a high level spells, wing buffet, and
|
||
// always attack with this.
|
||
if(AI_AttemptDragonCombat()){return;}
|
||
}
|
||
else
|
||
{
|
||
// We may move back as an archer, or even always do if set to. Normally
|
||
// only if we have help do we retreat out of AOO range.
|
||
// Will, if we can, cast invisibility first, or move back if we have it.
|
||
if(AI_AttemptArcherRetreat()){return;}
|
||
|
||
// This may knockout dying PC's or coup de grace sleeping PC's nearby.
|
||
if(AI_AttemptGoForTheKill()){return;}
|
||
|
||
// Spells attempt
|
||
if(AI_AttemptAllSpells()){return;}
|
||
|
||
// We will attempt pickpocket/taunt/animal empathy after spells, before polymorph
|
||
if(AI_AttemptHostileSkills()){return;}
|
||
|
||
// Attempt to cast all the buffing spells for melee - EG: Divine power,
|
||
// magical weapons, and ability enchanters.
|
||
if(AI_AttemptFeatCombatHostile()){return;}
|
||
|
||
// We polymorph before attacking, if set so we can.
|
||
if(AI_AttemptPolyMorph()){return;}
|
||
|
||
// This is called to attack, and should always attack something really.
|
||
if(AI_AttemptMeleeAttackWrapper()){return;}
|
||
}
|
||
}
|
||
// Else behaviour - we don't have a target. Any healing, extra or anything
|
||
// here. Then searching, and finally walking waypoints.
|
||
else
|
||
{
|
||
// We will attempt to heal ourselves first as a prioritory.
|
||
// Dragons may use this.
|
||
if(AI_AttemptHealingSelf()){return;}
|
||
// We will attempt to heal a previously set ally, with spells.
|
||
// Dragons use this, not always (and like to save spells for themselves).
|
||
if(AI_AttemptHealingAlly()){return;}
|
||
// We will cure, normally our conditions, or an allies. Things like
|
||
// blindness always, with other things later.
|
||
// Dragons use this, mostly with themselves.
|
||
if(AI_AttemptCureCondition()){return;}
|
||
}
|
||
// This is a call to the function which determines which way point to go back to.
|
||
// This will only run if it cannot heal self, is not in an AOE spell, and no target.
|
||
// 50: "[DRC] END - DELETE PAST TARGETS"
|
||
DebugActionSpeakByInt(50);
|
||
// Delete any last melee targets.
|
||
DeleteAIObject(AI_LAST_MELEE_TARGET);
|
||
DeleteAIObject(AI_LAST_SPELL_TARGET);
|
||
DeleteAIObject(AI_LAST_RANGED_TARGET);
|
||
DeleteLocalObject(OBJECT_SELF, "NW_GENERIC_LAST_ATTACK_TARGET");
|
||
|
||
// New: Search. This makes them go into search mode (if not already) and
|
||
// wanders around for an amount of time. We will search for AI_SEARCH_COOLDOWN_TIME
|
||
// seconds.
|
||
// - Search around oIntruder - might be a dead person.
|
||
Search(oIntruder);
|
||
// Search should activate this after a cirtain amount of time
|
||
}
|
||
|
||
|
||
// Debug: To compile this script full, uncomment all of the below.
|
||
/* - Add two "/"'s at the start of this line
|
||
void main()
|
||
{
|
||
AI_DetermineCombatRound();
|
||
}
|
||
//*/
|