/************************ [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·tel·li·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: “Intelligence is nothing if not an institutionalized black market in perishable commodities” (John le Carré). 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 "nw_i0_plot" // 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; const int GlobalImmunityKnockdown = 0x00020000; // realy? /*************************** 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, GlobalIntelligenceScore, GlobalOurHitDice, GlobalOurSize, GlobalOurAC, GlobalOurBaseAttackBonus, // Used and set for melee. USes GetBaseAttackBonus, as weapons are not set. GlobalOurBestAttackBonus, GlobalMeleeTargetBestAttackBonus, GlobalOurRace, GlobalOurGoodEvil, GlobalImHasted, 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 GlobalUseSpellPercent, GlobalSelfBuffer, GlobalWeAreBuffer, // Are we a spell buffer? AI_FLAG_COMBAT_MORE_ALLY_BUFFING_SPELLS GlobalSpellFailure, GlobalIamBlocked, // i'm blocked, can't move away. GlobalIamBlockedByObject, // i'm blocked by object, enemy or friend, anyway. GlobalLocationTargetFail, // in underdark tileset, location targeting always fails. GlobalAntiMeleeWarrior, // enemy with no caster. GlobalAntiArcaneSpellCaster, // need special action for spellcaster enemy. GlobalCounterSpellTargetPercentage, // am i marked with counterspell? GlobalPreventSpellDupleUse; // for sorcerer or etc, prevent to use same spell repeatedly. // 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 GlobalAOETargetObject, GlobalCounterSpellTarget, 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, GlobalGloveInArms, GlobalLastAttacker; // 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, and is RDD(fire immune)? GlobalSpellTargetHitDice, GlobalSpellTargetCurrentHitPoints, GlobalSpellTargetRace, GlobalSpellClassRDD, // - TRUE if seen can see spell target GlobalSeenSpell, // Special Spell things - friendly, hostile for spells (!GetIsReactionType, // GetIsReactionType etc.). // hey. it's don't work. use GlobalFriendlyFire. // GlobalFriendlySelectiveHostile : hit enemy only. GlobalFriendlyFireHostile, GlobalFriendlyFireFriendly, GlobalFriendlyFire, GlobalFriendlySelectiveHostile=i2, // 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, // if there are no enemy that be seen or heard... very long ranged attack. GlobalNoneSeenHeardEnemy, // check last attacker valid GlobalLastAttackerValid; // RANGES, Ie FLOATS :-) float GlobalRangeToMeleeTarget, GlobalSpellTargetRange, GlobalRangeToNearestEnemy, GlobalRangeToAlly, GlobalOurReach, GlobalRangeToFuthestEnemy, GlobalBuffRangeAddon; // One location - GlobalSummonLocation - for summon location :-) location GlobalSummonLocation, GlobalAOETargetLocation; /* 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. int DO_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 :-) // - iMaxLoopRun : set how many check touch range. int AI_AttemptAllSpells(int iHighestSpellLevel = i10, int iLowestSpellLevel = i0, int iMaxLoopRun = i2, int iLastCheckedRange = i1, 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(); // attempt AOE spell wrapper int AI_AttemptCastAOESpell(int nSpell, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE); // attempt AOE spell wrapper + object version. int AI_AttemptCastAOESpellToObject(int nSpell, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE); // attempt AOE spell wrapper, random version. int AI_AttemptCastAOESpellRandom(int nSpell, int nRandom, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE); // attempt AOE spell wrapper, random version. + object only version. int AI_AttemptCastAOESpellToObjectRandom(int nSpell, int nRandom, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE); // attempt instant death spells, or relate ability. int AI_AttemptDeathSpells(int nPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // death spell int AI_AttemptDeathSpell(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // death ability int AI_AttemptDeathAbility(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // attempt mind affect spells, or relate ability. int AI_AttemptMindSpells(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // mind spell int AI_AttemptMindSpell(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // mind ability int AI_AttemptMindAbility(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // attempt special effect effect spells, or relate ability. // drain, petrify, bigby... etc int AI_AttemptEffectSpells(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // effect spell int AI_AttemptEffectSpell(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // effect ability int AI_AttemptEffectAbility(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // attempt damage dealing spells, or relate ability. int AI_AttemptDamageSpells(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // damage spell int AI_AttemptDamageSpell(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // damage ability int AI_AttemptDamageAbility(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE); // attempt additional buff spells. int AI_AttemptAddBuffSpells(int iPriority); // attmept ally buff spells. int AI_AttmeptAllyBuffSpells(int iPriority); // attempt summon spells, or relate ability. int AI_AttemptSummonSpells(); // Important buff spell int AI_CoreBuffSpell(); //***************************** 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(); // Get best attack roll int AI_GetBestAttackRoll(object oTarget=OBJECT_SELF); //***************************** 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(); // 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); // tactical flee and hide int AI_TacticFleeHide(); //**************************** 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(); // beware shield damage. // Get target has shield damage magic, or target is black blade of disaster(or mordenkainen's sword) // return value=1 : swords, =2 : shield effect. =3 : target has damage shield but uncommandable. int AI_GetIsMustDispelMeleeTarget(object oTarget); // 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 run through most avalible protections spells. // - TRUE if we cast any. // Used when ethereal to protect before we break the etherealness. // - We may cast many on allies too int AI_ActionCastWhenEthereal(); // cast caster ability increase effect spells - ,EAGLE_SPLENDOR, FOXS_CUNNING, OWLS_WISDOM int AI_ActionCastCasterAbilitySpell(); // 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, int iEffect = FALSE, int iSpellEffect = FALSE); // 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); // 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_GetNearestAllyWithSpell(int iSpell, int iSpell1 = iM1, int iSpell2 = iM1, int iSpell3 = iM1); // 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); // Special case - it checks the talent again, in EffectCutsceneImmobilize (if not already so) and // uses the item, if it is an equal talent. location version int AI_ActionCastItemEqualToLocation(location lLocation, int iSpellID); // 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 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_ActionCastSpellAtLocation(int iSpellID, int nTalent, location lLocation, int iRequirement = 0, int iItemTalentValue = -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); // for fake anti magic to ally void AI_DoRemoveEffects(object oTarget); //*************************** 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); // Get any Mode is turned on. int AI_GetMeleeMode(); // Get have any sneak attack int AI_GetHaveSneakAttack(object oTarget=OBJECT_SELF); // ActionMoveAwayFromObject + action in corner void AI_AntiCornerMoveAway(object oFrom,int bRun=FALSE,float fMoveAwayRange=40.0f,int bBlocked=FALSE); // check blocked or not. void AI_BlockedCheck(); // 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, int iDamageSpell = FALSE); //set GlobalAOETargetObject //set GlobalAOETargetLocation // 0=false, 1=valid object and location, 2=valid but one enemy-use object? int AI_GetBestAreaSpellTargetAndLocation(float fRange, float fSpread, int nLevel, int iSaveType = FALSE, int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = 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); // check around of caster for meteor swarm int AI_GetAreaCheckForMeteor(); // 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); // Returns TRUE if any of the checks the oGroupTarget is immune to. int AI_AOENecroDeathDrainMindCheck(object oGroupTarget, int iNecromanticSpell, int iDeathImmune, int iNegativeImmune, int iMindImmune, int iMindType); // 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); // is allys in shape? int AI_GetIsAllyInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0]); // bigby check // 1: spell is on, 2: spell and effect is on. int AI_GetIsBigbyOn(object oTarget); // enemy type is arcane spell caster? int AI_GetIsArcaneCaster(object oTarget); // enemy type is spell caster? int AI_GetIsSpellCaster(object oTarget); // target is spell castable? int AI_GetIsTargetCastable(object oTarget); // return percentage with spelltarget saving throw and DC int AI_GetPercentageDC(int iDC, int nSavingThrowType); // if cheat cast needed? int AI_CheatCastCheck(int nSpell); // is ranged attacker? : archer or caster... int AI_GetIsRangedAttacker(object oTarget); // get counterspell possibility int AI_GetCounterSpellPossibility(object oTarget,object oSource=OBJECT_SELF); // Gets the nearest AOE cast by enemy, of sTag. object GetNearestAOECastByEnemy(string sTag); // Get tag of AOE spell ID string GetTagofAOESpellID(int nSpell); /*:://///////////////////////////////////////////// //:: 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); GlobalSpellFailure = AI_GetAIHaveEffect(GlobalEffectSpellFailure); } // Gets the percent, of X vs Y, iNumber/iTotal * 100 = %. int AI_GetPercentOf(int iNumber, int iTotal) { return FloatToInt((IntToFloat(iNumber)/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; } // for fake anti magic to ally. void AI_DoRemoveEffects(object oTarget) { effect eEff = GetFirstEffect(oTarget); while (GetIsEffectValid(eEff)) { if (GetEffectSubType(eEff) == SUBTYPE_MAGICAL) { if (GetEffectType(eEff) != EFFECT_TYPE_DISAPPEARAPPEAR && GetEffectType(eEff) != EFFECT_TYPE_SPELL_FAILURE ) { RemoveEffect (oTarget, eEff); } } eEff = GetNextEffect(oTarget); } } /*:://///////////////////////////////////////////// //:: 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() { // wizard or sorcerer must not equip shield... if(AI_GetIsArcaneCaster(OBJECT_SELF) && !GetHasFeat(FEAT_EPIC_AUTOMATIC_STILL_SPELL_3) && SpellAnySpellValid) { object oShield=GetItemInSlot(INVENTORY_SLOT_LEFTHAND); if(GetIsObjectValid(oShield) && (GetBaseItemType(oShield)==BASE_ITEM_LARGESHIELD || GetBaseItemType(oShield)==BASE_ITEM_SMALLSHIELD || GetBaseItemType(oShield)==BASE_ITEM_TOWERSHIELD)) { ActionUnequipItem(oShield); } return; } 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); } } } } int AI_GetMeleeMode() { int iCnt; for(iCnt = ACTION_MODE_PARRY; iCnt <= ACTION_MODE_DIRTY_FIGHTING; iCnt++) { if(GetActionMode(OBJECT_SELF,iCnt)) return TRUE; } return FALSE; } // Get have any sneak attack int AI_GetHaveSneakAttack(object oTarget=OBJECT_SELF) { int iCnt; if(GetHasFeat(FEAT_SNEAK_ATTACK,oTarget)) return TRUE; // assasin death attack and blackguard sneak attack for(iCnt=455;iCnt<=462;iCnt++) { if(GetHasFeat(iCnt,oTarget)) return TRUE; } return FALSE; } // ActionMoveAwayFromObject + action in corner void AI_AntiCornerMoveAway(object oFrom,int bRun=FALSE,float fMoveAwayRange=40.0f,int bBlocked=FALSE) { object oArea=GetArea(OBJECT_SELF); vector vec,vecFrom; float face=GetFacing(OBJECT_SELF); int nmapx,nmapy,iTo; if(GetDistanceBetween(OBJECT_SELF,oFrom)>fMoveAwayRange) return; nmapx=GetAreaWidth(oArea)*i10; nmapy=GetAreaHeight(oArea)*i10; vec=GetPosition(OBJECT_SELF); // set last position for block check. SetLocalFloat(OBJECT_SELF,AI_LAST_X,vec.x); SetLocalFloat(OBJECT_SELF,AI_LAST_Y,vec.y); SetLocalFloat(OBJECT_SELF,AI_LAST_Z,vec.z); vecFrom=GetPosition(oFrom); // corner check // let's think about corner only. cant' make move in maze type map. vecFrom-=vec; // when blocked // maybe, the direction to the enemy is the only route.. if(bBlocked) { float randto=VectorToAngle(vecFrom); randto+=(d2()==i1)?Random(i40)+i20:-(Random(i40)+i20); ActionMoveToLocation(Location(oArea,vec+vecFrom+AngleToVector(randto)*fMoveAwayRange,face),bRun); return; } // in the corner if((vec.xnmapy-f2) || (vec.x>nmapx-f2 && vec.ynmapx-f2 && vec.y>nmapy-f2)) { if(fabs(vecFrom.x)>fabs(vecFrom.y)) { if(vec.ynmapy-f2) iTo=iM1; // iTo=(vecFrom.y>f0)?i1:iM1; ActionMoveToLocation(Location(oArea,Vector(vec.x,vec.y+(iTo*fMoveAwayRange),vec.z),face),bRun); return; } else { if(vec.xnmapx-f2) iTo=iM1; // iTo=(vecFrom.x>f0)?i1:iM1; ActionMoveToLocation(Location(oArea,Vector(vec.x+(iTo*fMoveAwayRange),vec.y,vec.z),face),bRun); return; } } // additional move // normal move is slow //move y factor if(vec.xnmapx-f2) { iTo=(vecFrom.y>f0)?iM1:i1; ActionMoveToLocation(Location(oArea,Vector(vec.x,vec.y+(iTo*fMoveAwayRange),vec.z),face),bRun); return; } // move x factor if(vec.ynmapy-f2) { iTo=(vecFrom.x>f0)?iM1:i1; ActionMoveToLocation(Location(oArea,Vector(vec.x+(iTo*fMoveAwayRange),vec.y,vec.z),face),bRun); return; } ActionMoveAwayFromObject(oFrom,bRun,fMoveAwayRange); } // check blocked or not. void AI_BlockedCheck() { float fm1=-1.0; float x,y,z,moved; vector vec=GetPosition(OBJECT_SELF); int iPblocked; x=GetLocalFloat(OBJECT_SELF,AI_LAST_X); y=GetLocalFloat(OBJECT_SELF,AI_LAST_Y); z=GetLocalFloat(OBJECT_SELF,AI_LAST_Z); GlobalIamBlockedByObject=FALSE; // i didn't be blocked. if(x==fm1 && y==fm1 && z==fm1) { GlobalIamBlocked=FALSE; SetLocalInt(OBJECT_SELF,AI_BLOCKED,GlobalIamBlocked); return; } // can't move more than 1.0..(0.5?).. we maybe blocked. GlobalIamBlocked=GetLocalInt(OBJECT_SELF,AI_BLOCKED); moved=sqrt(pow(x-vec.x,f2)+pow(y-vec.y,f2)+pow(z-vec.z,f2)); iPblocked=GetLocalInt(OBJECT_SELF,AI_POSSIBLYBLOCKED); if(moved=i2) { int iCheck=i0,iCnt=i1,iSize,iSizeMod; float fRange; object oCreature; // hey.. you are too close!!! oCreature=GetLocalObject(OBJECT_SELF,ARRAY_ENEMY_RANGE+IntToString(iCnt)); while(GetIsObjectValid(oCreature)) { iSize=GetCreatureSize(oCreature); iSizeMod=(iSize>i3)?iSize-i2:i1; fRange=GetDistanceToObject(oCreature); if(fRange<=IntToFloat(iSizeMod)+f1)iCheck+=iSizeMod; if(fRange>f4)break; oCreature=GetLocalObject(OBJECT_SELF,ARRAY_ENEMY_RANGE+IntToString(++iCnt)); } iCnt=i1; oCreature=GetLocalObject(OBJECT_SELF,ARRAY_ALLIES_RANGE+IntToString(iCnt)); while(GetIsObjectValid(oCreature)) { iSize=GetCreatureSize(oCreature); iSizeMod=(iSize>i3)?iSize-i2:i1; fRange=GetDistanceToObject(oCreature); if(fRange<=IntToFloat(iSizeMod)+f1)iCheck+=iSizeMod; if(fRange>f4)break; oCreature=GetLocalObject(OBJECT_SELF,ARRAY_ALLIES_RANGE+IntToString(++iCnt)); } // blocked by creatures?.. if(iCheck>=i1+d2()) { GlobalIamBlockedByObject=TRUE; } } return; } // 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); } } // Get best attack roll int AI_GetBestAttackRoll(object oTarget=OBJECT_SELF) { object oInven; itemproperty ip; int nAttackRoll=GetBaseAttackBonus(oTarget); int nAttackBonus=i0; int nTemp; if(!GetIsObjectValid(oTarget)) { return i0; } oInven=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oTarget); if(GetIsObjectValid(oInven)) { ip=GetFirstItemProperty(oInven); while(GetIsItemPropertyValid(ip)) { if(GetItemPropertyType(ip)==ITEM_PROPERTY_ENHANCEMENT_BONUS || GetItemPropertyType(ip)==ITEM_PROPERTY_ATTACK_BONUS) { nTemp=GetItemPropertyCostTableValue(ip); if(nTemp>nAttackBonus) nAttackBonus=nTemp; } ip=GetNextItemProperty(oInven); } } else { oInven=GetItemInSlot(INVENTORY_SLOT_ARMS,oTarget); if(GetIsObjectValid(oInven)) { ip=GetFirstItemProperty(oInven); while(GetIsItemPropertyValid(ip)) { if(GetItemPropertyType(ip)==ITEM_PROPERTY_ENHANCEMENT_BONUS || GetItemPropertyType(ip)==ITEM_PROPERTY_ATTACK_BONUS) { nTemp=GetItemPropertyCostTableValue(ip); if(nTemp>nAttackBonus) nAttackBonus=nTemp; } ip=GetNextItemProperty(oInven); } } else { oInven=GetItemInSlot(INVENTORY_SLOT_CWEAPON_R,oTarget); if(GetIsObjectValid(oInven)) { ip=GetFirstItemProperty(oInven); while(GetIsItemPropertyValid(ip)) { if(GetItemPropertyType(ip)==ITEM_PROPERTY_ENHANCEMENT_BONUS || GetItemPropertyType(ip)==ITEM_PROPERTY_ATTACK_BONUS) { nTemp=GetItemPropertyCostTableValue(ip); if(nTemp>nAttackBonus) nAttackBonus=nTemp; } ip=GetNextItemProperty(oInven); } } } } nAttackRoll+=nAttackBonus; // need more check for ability modifier oInven=GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,oTarget); if(GetWeaponRanged(oInven)) { if(GetHasFeat(FEAT_ZEN_ARCHERY,oTarget)) { nAttackRoll+=GetAbilityModifier(ABILITY_WISDOM,oTarget); } else { nAttackRoll+=GetAbilityModifier(ABILITY_DEXTERITY,oTarget); } } else { if(GetHasFeat(FEAT_WEAPON_FINESSE,oTarget) && GetWeight(oInven)<=3) { nAttackRoll+=GetAbilityModifier(ABILITY_DEXTERITY,oTarget); } else { nAttackRoll+=GetAbilityModifier(ABILITY_STRENGTH,oTarget); } } // class bonus calc nTemp=GetLevelByClass(CLASS_TYPE_ARCANE_ARCHER,oTarget); if(nTemp>i0) { nAttackRoll+=nTemp/i2; } nTemp=GetLevelByClass(CLASS_TYPE_WEAPON_MASTER,oTarget); if(nTemp>i0) { nAttackRoll+=nTemp/i5; if(nTemp>i13) { nAttackRoll+=nTemp/i3; } } return nAttackRoll+GetHitDice(oTarget)/10; } /*:://///////////////////////////////////////////// //:: 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(i10) + i1) / f10; 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); int iAttackMethod = AI_GetIsMustDispelMeleeTarget(GlobalMeleeTarget); if(iAttackMethod>i1 && GlobalOurPercentHP>i40 && GetCurrentHitPoints(GlobalMeleeTarget)<=i50) { iAttackMethod=i0; } // uncommandable enemy... just normal attack for the best attack bonus(in melee only?) int iEnemyStatus = (AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalMeleeTarget) || AI_GetAIHaveEffect(GlobalEffectPetrify, GlobalMeleeTarget) || AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalMeleeTarget)); if(iRangeFromSetup >= i1) { fRange = IntToFloat(iRangeFromSetup); } /* else if(AI_GetAIHaveEffect(GlobalEffectEntangle)) { fRange = f2; } */ // 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((GlobalValidAlly && GlobalRangeToNearestEnemy > fRange && GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ATTACKING, AI_COMBAT_MASTER)) || (d10() <= i8 && GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER)) || GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER)) { iRangedAttack = TRUE; } else { if(d10() <= i8 && !AI_GetAIHaveEffect(GlobalEffectEntangle) && GetSpawnInCondition(AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND, AI_COMBAT_MASTER)) { iRangedAttack = FALSE; } else { // Get distance from melee target to nearest ally to it. float fAllyToMelee = GetDistanceBetween(GlobalMeleeTarget, GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, GlobalMeleeTarget, i1, CREATURE_TYPE_IS_ALIVE, TRUE)); if(fAllyToMelee==f0) { fAllyToMelee = GlobalRangeToNearestEnemy; } // Check thier range to ours // - Basically, if GlobalRangeToMeleeTarget - fAllyToMelee, is // a difference of Random(4) + 4;, we move in. // + 60% chance! if(fAllyToMelee <= (IntToFloat(Random(i4) + i4)) && d10() <= i6) // if((GlobalRangeToMeleeTarget - fAllyToMelee) <= (IntToFloat(Random(i4) + i4)) && // d10() <= i6) { iRangedAttack = FALSE; } else if(!GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,GlobalMeleeTarget))&& !AI_GetIsRangedAttacker(GlobalMeleeTarget) && GlobalRangeToNearestEnemy > fRange + f5) { iRangedAttack = TRUE; } } } /* if(GlobalRangeToNearestEnemy > fRange + f5 || 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_ARCHER_ATTACKING, AI_COMBAT_MASTER) && !GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER) && !GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER)) { if(d10() <= i8 && !AI_GetAIHaveEffect(GlobalEffectEntangle) && GetSpawnInCondition(AI_FLAG_COMBAT_BETTER_AT_HAND_TO_HAND, AI_COMBAT_MASTER)) { iRangedAttack = FALSE; } // 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(fAllyToMelee <= (IntToFloat(Random(i4) + i4)) && d10() <= i6) // if((GlobalRangeToMeleeTarget - fAllyToMelee) <= (IntToFloat(Random(i4) + i4)) && // d10() <= i6) { iRangedAttack = FALSE; } } } */ // dispel summoned sword or elemental shield caster if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_DISPEL_WITH_MELEE, AI_OTHER_COMBAT_MASTER)) { // damage shield if(iAttackMethod==i2) { GlobalDispelTarget=GlobalMeleeTarget; // try dispel target if(AI_ActionCastDispel()) { return AI_NORMAL_MELEE_ATTACK; } // else { SetLocalObject(OBJECT_SELF,AI_SAVED_DISPEL_TARGET,GlobalMeleeTarget); AISpeakString(DISPEL_THIS_TARGET); } } if(iAttackMethod>=i2 && GlobalIntelligenceScore>=i8) { iRangedAttack = TRUE; } } // Declare everything object oEquipped, oMainWeapon, oMainPossessor, oPrimary, oSecondary, oTwohanded, oGloves; int iPickUpDisarmed, iRanged, iShield, iValidAmmo, iStrength, iPrimaryNum, iPrimaryValue, iSecondaryNum, iSecondaryValue, iValidPrimary, iValidSecondary, iTwoHandedNum, iTwoHandedValue, iValidTwoHanded, iPrimaryDamage, iEquippedShield, iShieldInSlotAlready, iEquippedMostDamaging, iCurRightValue, iCurLeftValue, iCurGloveValue, iGloveNum, iValidGlove, iMonkLevels, iGloveValue, iUseUnarmed, // 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); // Monk levels iMonkLevels = GetLevelByClass(CLASS_TYPE_MONK); // 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(i1, 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) else if(!iMonkLevels) { ActionEquipMostDamagingMelee(GlobalMeleeTarget, TRUE); } // Always do... iEquippedMostDamaging = TRUE; } // Else normal weapons, IE we check stored ones :-P else if(!AI_GetAIHaveEffect(GlobalEffectPolymorph)) { // Declaring // value of current equipped weapons iCurRightValue = GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND))?GetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND):FALSE; iCurLeftValue = GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_LEFTHAND))?GetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND):FALSE; if(iMonkLevels) { iCurGloveValue = GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_ARMS))?GetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_GLOVES):FALSE; } // 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)) || GetAIInteger(AI_WEAPON_RANGED_IS_UNLIMITED)); } // 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. } } } } } // Ranged attack, but has no valid weapon? else if(iRangedAttack && (!iValidAmmo || !GetIsObjectValid(oMainWeapon))) { // check weapon again. ExecuteScript(FILE_RE_SET_WEAPONS, OBJECT_SELF); } // 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! { int iCurRangedEquipped=GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); // 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)); iPrimaryValue = GetLocalInt(OBJECT_SELF, AI_WEAPON_PRIMARY + IntToString(iPrimaryNum)); iValidPrimary = (iCurRangedEquipped || iCurRightValue-i5iPrimaryValue && iGloveValue>iTwoHandedValue) { oEquipped = oGloves; ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_LEFTHAND)); if(GlobalGloveInArms != oGloves) { ActionEquipItem(oGloves, INVENTORY_SLOT_ARMS); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,FALSE); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_GLOVES,iGloveValue); } } // 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) else 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); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } if(GlobalLeftHandWeapon != oShield) { ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } else if(iValidTwoHanded) { oEquipped = oTwohanded; if(GlobalRightHandWeapon != oTwohanded) { ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iTwoHandedValue); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } else if(iValidSecondary) { if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } if(GlobalLeftHandWeapon != oSecondary) { ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,iSecondaryValue); } } else { if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } } } else if(iValidTwoHanded) { oEquipped = oTwohanded; if(GlobalRightHandWeapon != oTwohanded) { ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iTwoHandedValue); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } } // 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); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iTwoHandedValue); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } else if(iValidPrimary) { oEquipped = oPrimary; // Secondary first, rather then shield. if(iValidSecondary) { if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } if(GlobalLeftHandWeapon != oSecondary) { ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,iSecondaryValue); } } else if(iValidShield) { iEquippedShield = TRUE; if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } if(GlobalLeftHandWeapon != oShield) { ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } else if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } } } // 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); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } if(GlobalLeftHandWeapon != oSecondary) { ActionEquipItem(oSecondary, INVENTORY_SLOT_LEFTHAND); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,iSecondaryValue); } } else if(iValidShield) { iEquippedShield = TRUE; if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } if(GlobalLeftHandWeapon != oShield) { ActionEquipItem(oShield, INVENTORY_SLOT_LEFTHAND); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } else if(GlobalRightHandWeapon != oPrimary) { ActionEquipItem(oPrimary, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iPrimaryValue); } } else if(iValidTwoHanded) { oEquipped = oTwohanded; if(GlobalRightHandWeapon != oTwohanded) { ActionEquipItem(oTwohanded, INVENTORY_SLOT_RIGHTHAND); SetLocalInt (OBJECT_SELF,AI_EQUIPED_VALUE_RIGHT_HAND,iTwoHandedValue); SetLocalInt(OBJECT_SELF,AI_EQUIPED_VALUE_LEFT_HAND,FALSE); } } } }// End no valid if(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)) && (GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B)) || GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B)) || GetIsObjectValid(GetItemInSlot(INVENTORY_SLOT_CWEAPON_B)))) { ActionUnequipItem(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)); oEquipped=OBJECT_INVALID; } } }// 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(i2); // 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); iOurHit = GlobalOurBestAttackBonus + ((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)); // number of concurrency attack int iCurAtt=(iOurHit-GlobalMeleeTargetAC)/i5; if(iCurAtt<=i0)iCurAtt=i1; if(iCurAtt>i4)iCurAtt=i4; int iCnt; // 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,GlobalMeleeTarget) - 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(GlobalOurBestAttackBonus + i20 <= GlobalMeleeTargetAC) { iOurHit = GlobalOurBaseAttackBonus + i100;// Massive amount - we act as if we rolled 100! ValidFeats = TRUE; iCurAtt=i3; } // for sneak attack if(iRanged && AI_GetHaveSneakAttack() && GlobalRangeToNearestEnemy>f6 && GlobalRangeToMeleeTarget>f6) { ActionMoveToObject(GlobalMeleeTarget,TRUE,f6); // return TRUE; } // We turn off hiding/searching as we will at least do ActionAttack... AI_ActionTurnOffHiding(); // Ranged weapon? if(iRanged && !AI_GetAIHaveEffect(GlobalEffectPolymorph)) { if(ValidFeats) { // 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) && !GetActionMode(OBJECT_SELF,ACTION_MODE_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(); for(iCnt=i0;iCnt= (GlobalOurHitDice + i4)) || ((GlobalMeleeTargetBestAttackBonus + i10 >= GlobalOurAC && GetSkillRank(SKILL_PARRY) >= GlobalMeleeTargetBestAttackBonus + i15) || // 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) { // react when target has elemental shield if(!GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_DISPEL_WITH_MELEE, AI_OTHER_COMBAT_MASTER) && AI_GetIsMustDispelMeleeTarget(GlobalMeleeTarget)>=i2 && GlobalIntelligenceScore>=i8) { if(GlobalRangeToMeleeTarget>f7) { ActionMoveToObject(GlobalMeleeTarget,FALSE,f6); } else { AI_AntiCornerMoveAway(GlobalMeleeTarget,TRUE,f5); } return AI_NORMAL_MELEE_ATTACK; } // attack anyway. if(!AI_GetAIHaveEffect(GlobalEffectEntangle) && GlobalRangeToMeleeTarget>fRange) { vector vec=GetPosition(OBJECT_SELF); // set last position for block check. (to select nearest melee target) SetLocalFloat(OBJECT_SELF,AI_LAST_X,vec.x); SetLocalFloat(OBJECT_SELF,AI_LAST_Y,vec.y); SetLocalFloat(OBJECT_SELF,AI_LAST_Z,vec.z); } else { float fm1=-1.0; SetLocalFloat(OBJECT_SELF,AI_LAST_X,fm1); SetLocalFloat(OBJECT_SELF,AI_LAST_Y,fm1); SetLocalFloat(OBJECT_SELF,AI_LAST_Z,fm1); } // 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) && !GetIsInCombat(GlobalMeleeTarget) && (!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. // + check enemy status if(!ValidFeats || iEnemyStatus) { // 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; 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_BRACER || GetBaseItemType(oEquipped) == BASE_ITEM_GLOVES || GetBaseItemType(oEquipped) == BASE_ITEM_KAMA) && iTargetCreatureRace != RACIAL_TYPE_CONSTRUCT && iTargetCreatureRace != RACIAL_TYPE_UNDEAD) { iCanUseMonks = TRUE; } // Expertise, special case: If we have a high BAB verus thier AC. // Only basic for now... if(GlobalMeleeAttackers>i0 && GetHasFeat(FEAT_IMPROVED_EXPERTISE) && !GetActionMode(OBJECT_SELF,ACTION_MODE_IMPROVED_EXPERTISE)) { // Our hit is over thier AC, and thier BAB hits us all the time... // OR when there are 3+ if((GlobalOurBestAttackBonus >= GlobalMeleeTargetAC && abs(GlobalOurAC-GlobalMeleeTargetBestAttackBonus-i10)<=i10) || (GlobalOurBestAttackBonus+i20<=GlobalMeleeTargetAC && GlobalOurAC+i10>=GlobalMeleeTargetBestAttackBonus+i10) || (GlobalMeleeAttackers >= i3 && GlobalMeleeTargetBestAttackBonus<=GlobalOurAC+i10)) { AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE); // ActionAttack(GlobalMeleeTarget); // return FEAT_IMPROVED_EXPERTISE; } // if target is ploted summon, shout dispel. if(GetIsObjectValid(GetMaster(GlobalMeleeTarget)) && GetPlotFlag(GlobalMeleeTarget)) { if(!AI_GetMeleeMode()) { AI_SetMeleeMode(ACTION_MODE_IMPROVED_EXPERTISE); } SetLocalObject(OBJECT_SELF,AI_SAVED_DISPEL_TARGET,GlobalMeleeTarget); AISpeakString(DISPEL_THIS_TARGET); } } if(GlobalMeleeAttackers>i0 && GetHasFeat(FEAT_EXPERTISE) && !GetActionMode(OBJECT_SELF,ACTION_MODE_IMPROVED_EXPERTISE) && !GetActionMode(OBJECT_SELF,ACTION_MODE_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((GlobalOurBestAttackBonus +i5 >= GlobalMeleeTargetAC && abs(GlobalOurAC-GlobalMeleeTargetBestAttackBonus-i15)<=i10) || (GlobalOurBestAttackBonus+i20<=GlobalMeleeTargetAC && GlobalOurAC+i5>=GlobalMeleeTargetBestAttackBonus+i10) || GlobalMeleeAttackers >= i2 && GlobalMeleeTargetBestAttackBonus<=GlobalOurAC+i5) { AI_SetMeleeMode(ACTION_MODE_EXPERTISE); // ActionAttack(GlobalMeleeTarget); // return FEAT_EXPERTISE; } // if target is ploted summon, shout dispel. if(GetIsObjectValid(GetMaster(GlobalMeleeTarget)) && GetPlotFlag(GlobalMeleeTarget)) { if(!AI_GetMeleeMode()) { AI_SetMeleeMode(ACTION_MODE_EXPERTISE); } SetLocalObject(OBJECT_SELF,AI_SAVED_DISPEL_TARGET,GlobalMeleeTarget); AISpeakString(DISPEL_THIS_TARGET); } } // 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 <= i30) || (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; } } // Next, flurry of blows... if(//iCanUseMonks && !AI_GetMeleeMode() && (iOurHit - i2 >= GlobalMeleeTargetAC && GetHasFeat(FEAT_FLURRY_OF_BLOWS))) { AI_SetMeleeMode(ACTION_MODE_FLURRY_OF_BLOWS); // ActionAttack(GlobalMeleeTarget); // return FEAT_FLURRY_OF_BLOWS; } // -10 to hit, for +10 damage. Good, I guess, in some circumstances. // Uses base attack bonus, no additions. if(!AI_GetMeleeMode() && GetHasFeat(FEAT_IMPROVED_POWER_ATTACK) && GlobalOurBestAttackBonus >= GlobalMeleeTargetAC+i5) { 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(!AI_GetMeleeMode() && GetHasFeat(FEAT_POWER_ATTACK) && GlobalOurBaseAttackBonus >= GlobalMeleeTargetAC) { AI_SetMeleeMode(ACTION_MODE_POWER_ATTACK); // ActionAttack(GlobalMeleeTarget); // return FEAT_POWER_ATTACK; } // 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; } } // Define sizes of weapons, ETC. // Check if they are disarmable. iTargetWeaponSize = AI_GetWeaponSize(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, GlobalMeleeTarget)); // Ki damage :-) max roll of damage for weapons for Weapon Master - Hordes // - Note that this a lot of uses. Test for usefulness! // as result of test, disarm is better than ki damage. if(GetHasFeat(FEAT_KI_DAMAGE) && !((GetHasFeat(FEAT_IMPROVED_DISARM) || GetHasFeat(FEAT_DISARM)) && iTargetWeaponSize)) { // AI_SetMeleeMode(); for(iCnt=i0;iCnt:-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; } } // Next, stunning fist. // Stuns the target, making them unable to move. -4 attack. // DC (fort) of 10 + HD/2 + wis mod. if(iCanUseMonks && !GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_STUN) && !GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_MIND_SPELLS) && !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 == i1) iAddingModifier - i4; // We hit ETC. // Save is above if(iOurHit >= GlobalMeleeTargetAC && // Save (i10 + (iMonkLevels / i2) + GetAbilityModifier(ABILITY_WISDOM) >= GetFortitudeSavingThrow(GlobalMeleeTarget) + i5 + Random(i15))) { // AI_SetMeleeMode(); for(iCnt=i0;iCnt= GlobalMeleeTargetAC) { // AI_SetMeleeMode(); for(iCnt=i0;iCnti0 && d4()i1 && iOurWeaponSize==i0)iOurWeaponSize=i2; if(!iOurWeaponSize)iOurWeaponSize=i1; // 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(); for(iCnt=i0;iCnt= GlobalMeleeTargetAC) { // AI_SetMeleeMode(); for(iCnt=i0;iCnt= 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(); for(iCnt=i0;iCnt= iTargetCreatureSize) { // Same as above, but we always take 4 more. iAddingModifier = ((GlobalOurSize - iTargetCreatureSize) * i4) - (i4); // Calculate if(iAddingModifier + iOurHit >= GlobalMeleeTargetAC) { // AI_SetMeleeMode(); for(iCnt=i0;iCnt= GlobalMeleeTargetAC && GetHasFeat(FEAT_CALLED_SHOT) && !GetHasFeatEffect(FEAT_CALLED_SHOT, GlobalMeleeTarget)) { // AI_SetMeleeMode(); for(iCnt=i0;iCnt 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; } } // 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; } // Special case - it checks the talent again, in EffectCutsceneImmobilize (if not already so) and // uses the item, if it is an equal talent. location version int AI_ActionCastItemEqualToLocation(location lLocation, int iSpellID) { // 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. ActionUseTalentAtLocation(tBestOfIt, lLocation); 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) { if(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return FALSE; if(GlobalPreventSpellDupleUse && GetAIInteger(AI_LAST_CASTED_SPELL_ID)==iSpellID) return FALSE; // 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 // paladin, ranger... to many deadlock.. just cheat cast.. // cleric domain spell also cheat cast. if(AI_CheatCastCheck(iSpellID)) { ActionCastSpellAtObject(iSpellID, oTarget, METAMAGIC_ANY, TRUE); DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); } else { 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_ANY, TRUE); DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); } SetAIInteger(AI_LAST_CASTED_SPELL_ID,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)); } SetAIInteger(AI_LAST_CASTED_SPELL_ID,iSpellID); 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_ActionCastSpellAtLocation(int iSpellID, int nTalent, location lLocation, int iRequirement = 0, int iItemTalentValue = -1) { if(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return FALSE; if(GlobalPreventSpellDupleUse && GetAIInteger(AI_LAST_CASTED_SPELL_ID)==iSpellID) return FALSE; // 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)) { // Make sure it is a spell, not an ability if(iRequirement > FALSE) { // Attempt Concentration Check (Casting a spell, not an item) if(AI_AttemptConcentrationCheck(GlobalMeleeTarget)) 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(); // If location... // 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. if(AI_CheatCastCheck(iSpellID)) { ActionCastSpellAtLocation(iSpellID, lLocation, METAMAGIC_ANY,TRUE); DecrementRemainingSpellUses(OBJECT_SELF,iSpellID); } else { ActionCastSpellAtLocation(iSpellID, lLocation, METAMAGIC_ANY); } // DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); SetAIInteger(AI_LAST_CASTED_SPELL_ID,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) { if(AI_ActionCastItemEqualToLocation(lLocation, iSpellID)) { // Lasts...recheck items if(AI_GetSpellCategoryHasItem(nTalent)) { ActionDoCommand(AI_SetItemTalentValue(nTalent)); } SetAIInteger(AI_LAST_CASTED_SPELL_ID,iSpellID); 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) { if(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return FALSE; if(GlobalPreventSpellDupleUse && GetAIInteger(AI_LAST_CASTED_SPELL_ID)==iSubSpell) return FALSE; // 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); SetAIInteger(AI_LAST_CASTED_SPELL_ID,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)); } SetAIInteger(AI_LAST_CASTED_SPELL_ID,iSubSpell); 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) { if(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return FALSE; if(GlobalPreventSpellDupleUse && GetAIInteger(AI_LAST_CASTED_SPELL_ID)==iSpellID) return FALSE; // 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 // paladin, ranger... to many deadlock.. just cheat cast. if(GetLevelByClass(CLASS_TYPE_PALADIN) || GetLevelByClass(CLASS_TYPE_RANGER)) { ActionCastSpellAtObject(iSpellID, oTarget, METAMAGIC_ANY, TRUE); DecrementRemainingSpellUses(OBJECT_SELF, iSpellID); } else { 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); } SetAIInteger(AI_LAST_CASTED_SPELL_ID,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)); } SetAIInteger(AI_LAST_CASTED_SPELL_ID,iSpellID); 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(GetHasSpell(nSpell)) { // 14: "[DCR:Feat] [ID] " + IntToString(nFeat) + " [Enemy] " + GetName(oTarget) DebugActionSpeakByInt(14, oTarget, nFeat); // We turn off hiding/searching AI_ActionTurnOffHiding(); if(nSpell==AI_SPELL_EPIC_WARDING || nSpell==SPELL_EPIC_MAGE_ARMOR) { // Cheat cast the spell ActionCastSpellAtObject(nSpell, oTarget, METAMAGIC_NONE, TRUE); // Decrement casting of it. DecrementRemainingSpellUses(OBJECT_SELF, nSpell); } else { ActionCastSpellAtObject(nSpell, oTarget, METAMAGIC_NONE); } return TRUE; } 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); GlobalIntelligenceScore = GetAbilityScore(OBJECT_SELF,ABILITY_INTELLIGENCE); // 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); GlobalGloveInArms = GetItemInSlot(INVENTORY_SLOT_ARMS); // GlobalOurBaseAttackBonus = GetBaseAttackBonus(OBJECT_SELF); GlobalOurBaseAttackBonus=AI_GetBestAttackRoll(); GlobalOurBestAttackBonus=AI_GetBestAttackRoll(); // 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); GlobalSelfBuffer = GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_ONLY_SELF_BUFFING_SPELLS,AI_OTHER_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 || GlobalOurChosenClass == CLASS_TYPE_PALADIN || GlobalOurChosenClass == CLASS_TYPE_RANGER) { 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) SpellConAre = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_AREAEFFECT; if(iLocalSpellInteger & AI_VALID_TALENT_BENEFICIAL_CONDITIONAL_SINGLE) SpellConSinTar = TALENT_CATEGORY_BENEFICIAL_CONDITIONAL_SINGLE; // 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); } } // get spell/ability percentage AssignCommand(OBJECT_SELF,SetNumberOfSpellAndAbility()); if(GetLocalInt(OBJECT_SELF,AI_NUMBER_OF_SPELLS)+GetLocalInt(OBJECT_SELF,AI_NUMBER_OF_ABILITIES)) GlobalUseSpellPercent = FloatToInt(IntToFloat(GetLocalInt(OBJECT_SELF,AI_NUMBER_OF_SPELLS))/ (IntToFloat(GetLocalInt(OBJECT_SELF,AI_NUMBER_OF_SPELLS))+IntToFloat(GetLocalInt(OBJECT_SELF,AI_NUMBER_OF_ABILITIES)))*i100); } /*:://///////////////////////////////////////////// //:: 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 + i20)) 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 + i20)) return TRUE; } } else if(iSaveType == SAVING_THROW_WILL) { // Slippery mind has a re-roll. 1.3 - Ignore if(GlobalSpellTargetWill - GlobalSaveStupidity >= (GlobalSpellBaseSaveDC + iSpellLevel + i20)) { 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_GetAIHaveEffect(GlobalEffectImmunityNecromancy, oGroupTarget)) return TRUE; } if(iDeathImmune) { if(AI_GetAIHaveSpellsEffect(GlobalHasDeathWardSpell, oGroupTarget)) return TRUE; } return FALSE; } // Returns TRUE if any of the checks the oGroupTarget is immune to. int AI_AOENecroDeathDrainMindCheck(object oGroupTarget, int iNecromanticSpell, int iDeathImmune, int iNegativeImmune, int iMindImmune, int iMindType) { if(iNecromanticSpell) { if(AI_GetAIHaveEffect(GlobalEffectImmunityNecromancy, oGroupTarget)) return TRUE; } if(iDeathImmune) { if(AI_GetAIHaveSpellsEffect(GlobalHasDeathWardSpell, oGroupTarget)) return TRUE; } if(iNegativeImmune) { if(AI_GetAIHaveEffect(GlobalEffectImmunityDrain, oGroupTarget)) return TRUE; } if(iMindImmune) { if(AI_GetAIHaveSpellsEffect(GlobalHasMindResistanceProtections, oGroupTarget)) return TRUE; } if(iMindType) { if(GetIsImmune(oGroupTarget,iMindType)) return TRUE; } 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, int iDamageSpell = 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) && (iDamageSpell || !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; } //set GlobalAOETargetObject //set GlobalAOETargetLocation // 0=false, 1=valid object and location, 2=valid but one enemy-use object? int AI_GetBestAreaSpellTargetAndLocation(float fRange, float fSpread, int nLevel, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE) { GlobalAOETargetObject=OBJECT_INVALID; GlobalAOETargetLocation=GetLocation(OBJECT_SELF); // Delcare objects // (Target = Loop of nearest creatures. oResturnTarget = Who to return, // and oGroupTarget is GetFirst/Next in nShape around oTarget) object oTarget, oGroupTarget; // Delcare Integers int iCnt,iCntEnemiesOnTarget, iCntAlliesOnTarget, iNoHittingAllies, iMostOnPerson, iMaxAlliesToHit, iOurToughness, iNewShape; // Declare Booleans int bWillHitAlly, bNoHittingAllies, bCheckAlliesHP, bSelfHit; location lTarget,lGroupTarget; // Float values float fDistance, fTargetMustBeFrom, fModSpread, fCheckRange; vector vCur,vTmp,vTarget; // - 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!=i2 && !AI_GetSpellLevelEffectAOE(OBJECT_SELF, nLevel) && (iDamageSpell || !AI_SaveImmuneAOE(OBJECT_SELF, iSaveType, nLevel)) && GetGameDifficulty()>GAME_DIFFICULTY_NORMAL) { fTargetMustBeFrom = fSpread + 0.3; } else { fTargetMustBeFrom = f0; } if(nFriendlyFire==i2) { nFriendlyFire==FALSE; fTargetMustBeFrom = f0; } // 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 if(fSpread==RADIUS_SIZE_COLOSSAL || fSpread==RADIUS_SIZE_LARGE) { fModSpread=f40; } if(fSpread==RADIUS_SIZE_GARGANTUAN) { fModSpread=RADIUS_SIZE_GARGANTUAN*f5; } if(fSpread==RADIUS_SIZE_HUGE || fSpread==RADIUS_SIZE_MEDIUM || fSpread==RADIUS_SIZE_SMALL) { fModSpread=RADIUS_SIZE_SMALL*f25; } if(fModSpread==f0) { fModSpread=fSpread*((f40/fSpread)+f1); } iNewShape=nShape; if(nShape==SHAPE_SPELLCONE || nShape==SHAPE_CONE) { fTargetMustBeFrom = f0; fModSpread=fSpread; iNewShape=SHAPE_SPHERE; } vCur.x=f0; vCur.y=f0; iCntEnemiesOnTarget=i0; iCntAlliesOnTarget=i0; bSelfHit=FALSE; bWillHitAlly=FALSE; lTarget=GetLocation(OBJECT_SELF); oTarget = GetFirstObjectInShape(iNewShape, fModSpread, lTarget); while(fModSpread>=fSpread && GetIsObjectValid(oTarget)) { // Need seen/heard target if(GetObjectSeen(oTarget) || GetObjectHeard(oTarget)) { // Sanity check for checking oGroupTarget if(!GetIsDead(oTarget) && !GetPlotFlag(oTarget) && !GetIgnore(oTarget) && !GetIsDM(oTarget) && !AI_SpellResistanceImmune(oTarget)) { if(!AI_AOENecroDeathDrainMindCheck(oTarget, iNecromanticSpell, iDeathImmune, iNegativeImmune, iMindImmune, iMindType) && (iDamageSpell || !AI_SaveImmuneAOE(oTarget, iSaveType, nLevel)) && !AI_GetSpellLevelEffectAOE(oTarget, nLevel)) { if(GetIsEnemy(oTarget)) { // Only add one if the person is an enemy, // and the spell will affect them iCntEnemiesOnTarget++; vTmp=GetPosition(oTarget); vCur.x+=vTmp.x; vCur.y+=vTmp.y; } // But else if friendly fire, we will subract // similar non-allies. else if(nFriendlyFire && (GetIsFriend(oTarget) || GetFactionEqual(oTarget)) && GetHitDice(oTarget) >= 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(oTarget) < i50) { iCntAlliesOnTarget++; } } if(fTargetMustBeFrom>f0 && oTarget==OBJECT_SELF) { bSelfHit = TRUE; } } } } oTarget = GetNextObjectInShape(iNewShape, fModSpread, lTarget); if(!GetIsObjectValid(oTarget)) { // no enemy in area. if(iCntEnemiesOnTarget==i0) { return FALSE; } // set new location to search lGroupTarget=Location(GetArea(OBJECT_SELF),Vector(vCur.x/iCntEnemiesOnTarget,vCur.y/iCntEnemiesOnTarget),f0); fCheckRange=f50; oTarget = GetFirstObjectInShape(iNewShape, fModSpread, lTarget); while(GetIsObjectValid(oTarget)) { if((GetObjectSeen(oTarget) || GetObjectHeard(oTarget)) && GetIsEnemy(oTarget) && !GetIsDead(oTarget) && !GetPlotFlag(oTarget) && !GetIgnore(oTarget) && !GetIsDM(oTarget) && !AI_SpellResistanceImmune(oTarget)) { fDistance=GetDistanceBetweenLocations(lGroupTarget,GetLocation(oTarget)); if(fDistance=fSpread) { vCur.x=f0; vCur.y=f0; iCntEnemiesOnTarget=i0; iCntAlliesOnTarget=i0; bSelfHit=FALSE; bWillHitAlly=FALSE; } } } // if spell hit caster himself, move location a little. if(fTargetMustBeFrom>f0 && bSelfHit) { vTarget=GetPositionFromLocation(lGroupTarget); vTmp=VectorNormalize(vTarget-GetPosition(OBJECT_SELF))*(fTargetMustBeFrom-GetDistanceBetweenLocations(GetLocation(OBJECT_SELF),lGroupTarget)); lGroupTarget=Location(GetAreaFromLocation(lGroupTarget),GetPositionFromLocation(lGroupTarget)+vTmp,GetFacingFromLocation(lGroupTarget)); } // set global variable. GlobalAOETargetObject=oGroupTarget; GlobalAOETargetLocation=lGroupTarget; // no enemy in location or ally is more than enemy. // but, objecttarget is valid....maybe. // spell will hit more ally than enemy. if(bWillHitAlly || iCntEnemiesOnTargetfRange) return FALSE; else return i2; } // final range check. vCur.x=IntToFloat(GetAreaWidth(GetArea(OBJECT_SELF))*i10); vCur.y=IntToFloat(GetAreaHeight(GetArea(OBJECT_SELF))*i10); vTmp=GetPositionFromLocation(GlobalAOETargetLocation); if(GetDistanceBetweenLocations(GetLocation(OBJECT_SELF),GlobalAOETargetLocation)>fRange || (vTmp.xvCur.x && vTmp.y>vCur.y)) return FALSE; else return TRUE; } // check around of caster for meteor swarm int AI_GetAreaCheckForMeteor() { // Can we hit allies? int bNoHittingAllies = GetSpawnInCondition(AI_FLAG_COMBAT_NEVER_HIT_ALLIES, AI_COMBAT_MASTER); int iEnemy,iNearEnemy,iAlly; object oTarget = GetFirstObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, GetLocation(OBJECT_SELF)); while(GetIsObjectValid(oTarget)) { if(!AI_SpellResistanceImmune(oTarget)) { if(GetIsEnemy(oTarget)) { if (GetDistanceBetween(oTarget, OBJECT_SELF) > 2.0) { iEnemy++; } else { iNearEnemy++; } } else if(GlobalFriendlyFire && (GetIsFriend(oTarget) || GetFactionEqual(oTarget))) { if (GetDistanceBetween(oTarget, OBJECT_SELF) > 2.0) { iAlly++; } } } oTarget = GetNextObjectInShape(SHAPE_SPHERE, RADIUS_SIZE_COLOSSAL, GetLocation(OBJECT_SELF)); } if(iEnemy>iAlly && iEnemy>iNearEnemy) return TRUE; return FALSE; } /*:://///////////////////////////////////////////// //:: 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)); object oAOE = GetNearestAOECastByEnemy(GetTagofAOESpellID(iSpell)); location lAOElocation = GetLocation(oAOE); // 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_ActionCastSpellAtLocation(SPELL_LESSER_DISPEL, SpellHostAreaInd, lAOElocation, i12, ItemHostAreaInd)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_DISPEL_MAGIC, SpellHostTouch, lAOElocation, i13, ItemHostTouch)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_GREATER_DISPELLING, SpellHostRanged, lAOElocation, i16, ItemHostRanged)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, lAOElocation, i19, 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 + GlobalOurSize); SetCurrentAction(AI_SPECIAL_ACTIONS_MOVE_OUT_OF_AOE); ActionMoveAwayFromLocation(GetLocation(oAOE), TRUE, fRange + GlobalOurSize); 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(); // Darkness! the ENEMY OF THE ARTIFICAL INTELlIGENCE!!! MUAHAHAH! WE COMBAT IT WELL! (sorta) if(AI_GetAIHaveEffect(GlobalEffectDarkness) || // very special case : enemy cast darkness on herself with/without vision spell. (GlobalLastAttackerValid && GlobalNoneSeenHeardEnemy && GetDistanceToObject(GlobalLastAttacker)f6) { oTmpTarget=OBJECT_INVALID; } } if(GetIsObjectValid(oTmpTarget)) { location lTarget=GetLocation(oTmpTarget); lTarget=Location(GetAreaFromLocation(lTarget),GetPositionFromLocation(lTarget)+VectorNormalize(GetPosition(oTmpTarget)-GetPosition(OBJECT_SELF))/f2,GetFacingFromLocation(lTarget)); // 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_GUST_OF_WIND, SpellHostAreaInd, oTmpTarget, i13, TRUE, ItemHostAreaInd)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_LESSER_DISPEL, SpellHostAreaInd, lTarget, i12, ItemHostAreaInd)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_DISPEL_MAGIC, SpellHostTouch, lTarget, i13, ItemHostTouch)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_GREATER_DISPELLING, SpellHostRanged, lTarget, i16, ItemHostRanged)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, lTarget, i19, ItemHostAreaInd)) return TRUE; } if(!AI_GetAIHaveEffect(GlobalEffectEthereal) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells) && !AI_GetAIHaveSpellsEffect(GlobalEffectInvisible)) { if(AI_ActionCastSpell(SPELL_ETHEREALNESS, SpellOtherSpell, OBJECT_SELF, i17)) return TRUE; if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF)) return TRUE; if(AI_SpellWrapperNormalInvisibilitySpells())return TRUE; } DeleteLocalObject(OBJECT_SELF,AI_TO_ATTACK); // run away or core buff? if((GlobalOurChosenClass == CLASS_TYPE_WIZARD || GlobalOurChosenClass == CLASS_TYPE_SORCERER || GlobalOurChosenClass == CLASS_TYPE_FEY) && !GlobalIamBlockedByObject && AI_GetAIHaveEffect(GlobalEffectDarkness) && d3()=i8 && (AI_GetAIHaveEffect(GlobalEffectTrueSeeing, GlobalLastAttacker) || AI_GetAIHaveEffect(GlobalEffectUltravision, GlobalLastAttacker)) && !(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,GlobalLastAttacker)) || AI_GetIsSpellCaster(GlobalLastAttacker) || GetDistanceBetween(OBJECT_SELF,GlobalLastAttacker)>f4)) { AI_AntiCornerMoveAway(GlobalLastAttacker, TRUE, f6, GlobalIamBlocked); return TRUE; } // go to melee!! else if(GlobalLastAttackerValid && GlobalNoneSeenHeardEnemy) { int iRun=GetObjectSeen(OBJECT_SELF,GlobalLastAttacker); if(iRun) { AI_EquipBestShield(); AI_ActionTurnOffHiding(); } DeleteLocalObject(OBJECT_SELF,AI_TO_ATTACK); ActionMoveToObject(GlobalLastAttacker,iRun); return TRUE; } } // very long ranged attack... maybe else if(GlobalLastAttackerValid && GlobalNoneSeenHeardEnemy && GetDistanceToObject(GlobalLastAttacker)>f8) { int iRun=GetObjectSeen(OBJECT_SELF,GlobalLastAttacker); if(iRun) { AI_EquipBestShield(); AI_ActionTurnOffHiding(); } if(!AI_GetAIHaveEffect(GlobalEffectEthereal) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells) && !AI_GetAIHaveSpellsEffect(GlobalEffectInvisible)) { if(AI_ActionCastSpell(SPELL_ETHEREALNESS, SpellOtherSpell, OBJECT_SELF, i17)) return TRUE; if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF)) return TRUE; if(AI_SpellWrapperNormalInvisibilitySpells())return TRUE; } if(!AI_GetAIHaveEffect(GlobalEffectUltravision) && !AI_GetAIHaveEffect(GlobalEffectTrueSeeing)) { // 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; // Ultravision - Talent category 10 (Benificial enchancement Self). if(AI_ActionCastSpell(SPELL_DARKVISION, SpellEnhSelf, OBJECT_SELF, i13, FALSE, ItemEnhSelf, PotionEnh)) return TRUE; } if(GetDistanceToObject(GlobalLastAttacker)>f35) { ActionMoveToObject(GlobalLastAttacker,iRun,f30); return TRUE; } else if(GetDistanceToObject(GlobalLastAttacker)>f20) { ActionMoveToObject(GlobalLastAttacker,iRun,f18); return TRUE; } else if(GetDistanceToObject(GlobalLastAttacker)>f10) { ActionMoveToObject(GlobalLastAttacker,iRun,f8); return TRUE; } } object oInvisible = GetAIObject(AI_LAST_TO_GO_INVISIBLE); if(!GetIsObjectValid(oInvisible) || GetObjectSeen(oInvisible)) 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)) { if(!AI_GetAIHaveEffect(GlobalEffectTrueSeeing)) { // 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)) && GetAreaFromLocation(lTarget)==GetArea(OBJECT_SELF)) { /* // We will cast at thier previous location, using a temp object we actually // create there for a few seconds. oInvisible = CreateObject(OBJECT_TYPE_PLACEABLE, "", lTarget); */ // Check range float fInvisibleDistance=GetDistanceBetweenLocations(GetLocation(OBJECT_SELF),lTarget); if(//GetIsObjectValid(oInvisible) && fInvisibleDistance != f0 && fInvisibleDistance < f40) { // Best to worst if(AI_ActionCastSpellAtLocation(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, lTarget, i19, ItemHostAreaInd)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_GREATER_DISPELLING, SpellHostRanged, lTarget, i16, ItemHostRanged)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_DISPEL_MAGIC, SpellHostTouch, lTarget, i13, ItemHostTouch)) return TRUE; if(AI_ActionCastSpellAtLocation(SPELL_LESSER_DISPEL, SpellHostAreaInd, lTarget, i12, 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; int iMultiUse; // 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; } //modified magic, healing only 150 max iMultiUse=(IntToFloat(iTargetMaxHP-iTargetCurrentHP)/150.0)>1.75; // 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 < i35 || iTargetPercentHP < i40 || // 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 ClearAllActions(TRUE); AI_ActionUseTalentDebug(tPotionHealing, OBJECT_SELF); if(iMultiUse && GetLocalInt(OBJECT_SELF,RULE_VERSION)!=i3){AI_ActionUseTalentDebug(tPotionHealing, OBJECT_SELF); return i2;} return TRUE; } else if(!GlobalSpellFailure && iTouchHealingValue == SPELL_HEAL) { AI_EquipBestShield(); // Touch heal spell ClearAllActions(TRUE); AI_ActionUseTalentDebug(tTouchHealing, oTarget); return TRUE; } else if(!GlobalSpellFailure && iAOEHealingValue == SPELL_MASS_HEAL) { AI_EquipBestShield(); // Mass heal spell ClearAllActions(TRUE); AI_ActionUseTalentDebug(tAOEHealing, oTarget); return TRUE; } } // Else, another talent - IE critical wounds and under. // Are any of them valid? if(!GlobalSpellFailure && (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 < i40 || // 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(!GlobalSpellFailure && AI_ActionCastSpell(SPELL_HARM, SpellHostTouch, oTarget, i16, FALSE, ItemHostTouch)) return TRUE; } // Circle of doom: d8 + Caster level heal. Category 1. if(!GlobalSpellFailure && 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(!GlobalSpellFailure && AI_ActionCastSpell(SPELL_NEGATIVE_ENERGY_BURST, SpellHostRanged, oTarget, i13, TRUE, ItemHostRanged)) return TRUE; // Other Under things! // Inflict range...always use top 2. if(!GlobalSpellFailure && (!GlobalAnyValidTargetObject || GlobalOurHitDice <= i15)) { if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_CRITICAL_WOUNDS, SpellHostTouch, oTarget)) return TRUE; if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i12) { if(AI_ActionCastSpontaeousSpell(SPELL_INFLICT_SERIOUS_WOUNDS, SpellOtherSpell, oTarget)) return TRUE; // Lower ones ain't too good for some HD (ours!) if(!GlobalAnyValidTargetObject || GlobalOurHitDice <= i8) { 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; if(AI_GetAIHaveEffect(GlobalEffectPolymorph) || AI_GetAIHaveEffect(GlobalEffectTensersTransformation))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; // First, heal self with potions, spells or whatever. if(AI_ActionHealObject(OBJECT_SELF))return TRUE; // 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. // mindflayer use no potion? even dragon may drink a potion... maybe... int iPotions = TRUE; if(!AI_GetAIHaveEffect(GlobalEffectPolymorph) && //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; } } } 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; } // 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_GetNearestAllyWithSpell(int iSpell, int iSpell1 = iM1, int iSpell2 = iM1, int iSpell3 = iM1) { // Check self if(GetHasSpellEffect(iSpell) || GetHasSpellEffect(iSpell1) || GetHasSpellEffect(iSpell2) || GetHasSpellEffect(iSpell3)) return OBJECT_SELF; // Check leader if(GetHasSpellEffect(iSpell, GlobalNearestLeader) || GetHasSpellEffect(iSpell1, GlobalNearestLeader) || GetHasSpellEffect(iSpell2, GlobalNearestLeader) || GetHasSpellEffect(iSpell3, 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) { // Has it got the effect? // - Stop and return oAlly. if(GetHasSpellEffect(iSpell, oAlly) || GetHasSpellEffect(iSpell1, oAlly) || GetHasSpellEffect(iSpell2, oAlly) || GetHasSpellEffect(iSpell3, 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() { // deadmagic zone? if(GlobalSpellFailure)return FALSE; // 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; } // bigby! oHeal = AI_GetNearestAllyWithSpell(SPELL_BIGBYS_CRUSHING_HAND, SPELL_BIGBYS_CLENCHED_FIST, SPELL_BIGBYS_GRASPING_HAND, SPELL_BIGBYS_FORCEFUL_HAND); if(GetIsObjectValid(oHeal) && AI_GetIsBigbyOn(oHeal)==i2) { if(oHeal!=OBJECT_SELF) { // very special. antimagic cone. if(GetHasSpell(AI_SPELLABILITY_BEHOLDER_MAGIC_CONE) && GetDistanceToObject(oHeal)=i20 || GlobalOurHitDice-i10 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 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 } } } AI_AntiCornerMoveAway(oTarget, TRUE, f50, GlobalIamBlocked); } /*:://///////////////////////////////////////////// //:: 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 { // deadmagic zone? if(GlobalSpellFailure)return FALSE; // 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(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return; if(AI_GetAIHaveEffect(GlobalEffectEthereal) || GetHasSpell(SPELL_ETHEREALNESS)) return; 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); // modified. sea hag aura, unlimited use? AI_AttemptCastAuraSpell(SPELLABILITY_AURA_HORRIFICAPPEARANCE); // 1.66 added? AI_AttemptCastAuraSpell(SPELLABILITY_TROGLODYTE_STENCH); // 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. float fMoveRange=(AI_GetHaveSneakAttack())?f5:f8; //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)) && !GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,GlobalMeleeTarget)) && !GlobalIamBlockedByObject) { // 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 && GlobalValidAlly && GlobalRangeToAlly < f10) || 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)) || GetAIInteger(AI_WEAPON_RANGED_IS_UNLIMITED)) { // 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); } if(GlobalRangeToNearestEnemy= 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() { // very special. antimagic cone. if(GetHasSpell(AI_SPELLABILITY_BEHOLDER_MAGIC_CONE) && (GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_ONLY_SELF_BUFFING_SPELLS,AI_OTHER_COMBAT_MASTER) || (GlobalFriendlyFire && !AI_GetIsAllyInShape(SHAPE_SPELLCONE, f25, GetLocation(GlobalDispelTarget))))) { if(AI_ActionCastSpell(AI_SPELLABILITY_BEHOLDER_MAGIC_CONE,SpellHostAreaInd,GlobalDispelTarget)) return TRUE; } // Mordenkainens Disjunction. Level 9 (Wizard). Acts like a breach, dispel AND lowers SR, in a big area. if(!GetLocalTimer(AI_DISPEL_CASTED_TIMER) && AI_ActionCastSpell(SPELL_MORDENKAINENS_DISJUNCTION, SpellHostAreaInd, GlobalDispelTarget, i19, FALSE, ItemHostAreaInd)) { SetLocalTimer(AI_DISPEL_CASTED_TIMER,f8); return TRUE; } // Greater Dispeling. Level 5 (Bard/Innate) 6 (Cleric/Druid/Mage) General dispel up to 15 caster levels. if(!GetLocalTimer(AI_DISPEL_CASTED_TIMER) && AI_ActionCastSpell(SPELL_GREATER_DISPELLING, SpellHostRanged, GlobalDispelTarget, i16, FALSE, ItemHostRanged)) { SetLocalTimer(AI_DISPEL_CASTED_TIMER,f8); return TRUE; } // Dispel Magic. Level 3 (Bard/Cleric/Paladin/Mage/Innate) 6 (Druid) General dispel up to 10 caster levels. if(!GetLocalTimer(AI_DISPEL_CASTED_TIMER) && AI_ActionCastSpell(SPELL_DISPEL_MAGIC, SpellHostTouch, GlobalDispelTarget, i13, FALSE, ItemHostTouch)) { SetLocalTimer(AI_DISPEL_CASTED_TIMER,f8); 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(!GetLocalTimer(AI_DISPEL_CASTED_TIMER) && AI_ActionCastSpell(SPELL_LESSER_DISPEL, SpellHostAreaInd, GlobalDispelTarget, i12, FALSE, ItemHostAreaInd)) { SetLocalTimer(AI_DISPEL_CASTED_TIMER,f8); 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)) if(!AI_GetAIHaveEffect(GlobalEffectEthereal) && !GetHasSpellEffect(AI_SPELL_EPIC_WARDING)) { // 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; } if(!AI_GetAIHaveEffect(GlobalEffectEthereal) && !GetHasSpellEffect(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER)) { // Psionic Inertial Barrier if(AI_ActionCastSpell(AI_SPELLABILITY_PSIONIC_INERTIAL_BARRIER, SpellProSelf)) return TRUE; } if(iLowest <= i8) { if(!GetHasSpellEffect(SPELL_PREMONITION)) { // 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) { if(!GetHasSpellEffect(SPELL_GREATER_STONESKIN)) { // 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) { if(!GetHasSpellEffect(SPELL_STONESKIN) && !GetHasSpellEffect(SPELL_SHADES_STONESKIN)) { // 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(!GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH) && (iLowest <= i4) && // { // Elemental Shield. Level 4 (Mage). Does some damage to melee attackers (Casterlvl + d6(), Fire). +50% cold/fire resistance. AI_ActionCastSpell(SPELL_MESTILS_ACID_SHEATH, SpellProSelf, OBJECT_SELF, i14, FALSE, ItemProSelf, PotionPro)){ return TRUE;} // } else if(!GetHasSpellEffect(SPELL_ELEMENTAL_SHIELD) && (iLowest <= i3) && // { // Acid Sheath. Level 3 (Mage) Does some damage to melee attackers (2 * CasterLvl + 1d6, Acid). AI_ActionCastSpell(SPELL_ELEMENTAL_SHIELD, SpellProSelf, OBJECT_SELF, i13, FALSE, ItemProSelf, PotionPro)){ return TRUE;} // } else if(!GetHasSpellEffect(SPELL_WOUNDING_WHISPERS) && (iLowest <= i3) && // { // Wounding Whispers. Level 3 (bard) Does some damage to melee attackers (Casterlvl + d6(), Sonic). AI_ActionCastSpell(SPELL_WOUNDING_WHISPERS, SpellProSelf, OBJECT_SELF, i13, FALSE, ItemProSelf, PotionPro)){ return TRUE;} // } else if(!GetHasSpellEffect(SPELL_HOLY_AURA) && (iLowest <= i2) && GetAlignmentGoodEvil(GlobalMeleeTarget)==ALIGNMENT_EVIL && // { // Holy: Versus Evil. Level 8 (Cleric) AI_ActionCastSubSpell(SPELL_HOLY_AURA, SpellEnhSelf, OBJECT_SELF, i18, FALSE, ItemEnhSelf)) return TRUE; // } else if(!GetHasSpellEffect(SPELL_UNHOLY_AURA) && (iLowest <= i2) && GetAlignmentGoodEvil(GlobalMeleeTarget)==ALIGNMENT_GOOD && // { // Unholy Holy: Versus Good. Level 8 (Cleric) AI_ActionCastSubSpell(SPELL_UNHOLY_AURA, SpellEnhSelf, OBJECT_SELF, i18, FALSE, ItemEnhSelf)) return TRUE; // } else if(!GetHasSpellEffect(SPELL_DEATH_ARMOR) && (iLowest <= i2) && // { // Death Armor. Level 2 (Mage) Does some damage to melee attackers (Caster Level/2 (to +5) + 1d4, Magical) AI_ActionCastSpell(SPELL_DEATH_ARMOR, SpellProSelf, OBJECT_SELF, i13, FALSE, ItemProSelf, 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, int iEffect = FALSE, int iSpellEffect = FALSE) { if(GlobalSelfBuffer) return FALSE; // 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((!iEffect || !AI_GetAIHaveEffect(iEffect,oAlly)) && (!iSpellEffect || !AI_GetAIHaveSpellsEffect(iSpellEffect,oAlly)) && (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; break; } 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. // concealment 50% miss is important. if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF)) return TRUE; // Hastes! if(AI_ActionCastAllyBuffSpell(f8, i100, SPELL_HASTE, SPELL_MASS_HASTE, iM1, iM1, iM1, iM1, GlobalEffectHaste, FALSE)) return TRUE; // Haste's if(AI_SpellWrapperHasteEnchantments()) return TRUE; // Stoneskin - protection from attackers. if(AI_SpellWrapperPhisicalProtections()) 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; } // Visages if(AI_SpellWrapperVisageProtections()) return TRUE; // Mantals if(!GlobalAntiMeleeWarrior && AI_SpellWrapperMantalProtections()) return TRUE; // Shields if(AI_SpellWrapperShieldProtections()) return TRUE; // Elemental protections (fireball ETC) if(AI_SpellWrapperElementalProtections()) return TRUE; // Globes if(!GlobalAntiMeleeWarrior && AI_SpellWrapperGlobeProtections()) return TRUE; // Mind resistances if(!GlobalAntiMeleeWarrior && AI_SpellWrapperMindResistanceProtections()) return TRUE; // Epic mage armor after specials. Not the best place... // +20 AC is good, normally. if(!GetHasSpellEffect(SPELL_EPIC_MAGE_ARMOR)) { // 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; } // increase caster ability if(AI_ActionCastCasterAbilitySpell()) 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. if(!(AI_GetAIHaveEffect(GlobalEffectTrueSeeing) || AI_GetAIHaveEffect(GlobalEffectSeeInvisible))) { // True Seeing if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, 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; } // Consealment spells if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) return TRUE; // Try stoneskins as a main one if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_STONESKIN)) return TRUE; // TRUE SEEING if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_TRUE_SEEING,iM1,iM1,iM1,iM1,iM1,GlobalEffectTrueSeeing,FALSE)) return TRUE; // If we have the setting to buff allies, we carry on buffing with some // other spells. if(GlobalWeAreBuffer) { // Elemental protections if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_PROTECTION_FROM_ELEMENTS, SPELL_RESIST_ELEMENTS, SPELL_ENDURE_ELEMENTS)) return TRUE; // 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; } // 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_ActionCastWhenEthereal() { // 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. // We run through some spells. Primarily, protection then buffs. // We don't target others else we'd break the invisiblity. // Haste's. short but need? if(AI_SpellWrapperHasteEnchantments()) return TRUE; // concealment 50% miss is important. long. if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF)) return TRUE; // Visages. short? if(AI_SpellWrapperVisageProtections()) return TRUE; // Mind resistances. long; if(AI_SpellWrapperMindResistanceProtections()) return TRUE; // Stoneskin - protection from attackers. long; if(AI_SpellWrapperPhisicalProtections()) 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; } // Epic mage armor after specials. Not the best place... // +20 AC is good, normally. if(!GetHasSpellEffect(SPELL_EPIC_MAGE_ARMOR)) { // 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; } // Elemental protections (fireball ETC) long; if(AI_SpellWrapperElementalProtections()) return TRUE; if(!(AI_GetAIHaveEffect(GlobalEffectTrueSeeing) || AI_GetAIHaveEffect(GlobalEffectSeeInvisible))) { // True Seeing. long; if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, 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; } // Hastes! short but need; if(AI_ActionCastAllyBuffSpell(f20, i100, SPELL_HASTE, SPELL_MASS_HASTE, iM1, iM1, iM1, iM1, GlobalEffectHaste, FALSE)) return TRUE; // Consealment spells. long; if(AI_ActionCastAllyBuffSpell(f16, i100, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) return TRUE; // Try stoneskins as a main one. long. if(AI_ActionCastAllyBuffSpell(f16, i100, SPELL_STONESKIN)) return TRUE; // TRUE SEEING if(AI_ActionCastAllyBuffSpell(f16, i100, SPELL_TRUE_SEEING,iM1,iM1,iM1,iM1,iM1,GlobalEffectTrueSeeing,FALSE)) return TRUE; // increase caster ability if(AI_ActionCastCasterAbilitySpell()) return TRUE; // If we have the setting to buff allies, we carry on buffing with some // other spells. if(GlobalWeAreBuffer) { // Elemental protections. long. if(AI_ActionCastAllyBuffSpell(f16, i100, SPELL_PROTECTION_FROM_ELEMENTS, SPELL_RESIST_ELEMENTS, SPELL_ENDURE_ELEMENTS)) return TRUE; // Some AC protections. long; if(AI_ActionCastAllyBuffSpell(f16, i100, SPELL_MAGE_ARMOR, SPELL_BARKSKIN)) return TRUE; // Bulls Strength, Cats Grace, Endurance. long; if(AI_ActionCastAllyBuffSpell(f20, i100, SPELL_ENDURANCE, SPELL_CATS_GRACE, SPELL_BULLS_STRENGTH, SPELL_GREATER_ENDURANCE, SPELL_GREATER_BULLS_STRENGTH, SPELL_GREATER_CATS_GRACE)) return TRUE; // Bless, Aid. long; if(AI_ActionCastAllyBuffSpell(f16, i100, SPELL_AID, SPELL_BLESS)) return TRUE; } // call black blade of disaster? if(!GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_SUMMONED))) { // 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; } // Mantals. short; if(AI_SpellWrapperMantalProtections()) return TRUE; // Shields. short; if(AI_SpellWrapperShieldProtections()) return TRUE; // Globes. short? if(AI_SpellWrapperGlobeProtections()) return TRUE; // Return FALSE - nothing cast return FALSE; } // cast caster ability increase effect spells - ,EAGLE_SPLENDOR, FOXS_CUNNING, OWLS_WISDOM int AI_ActionCastCasterAbilitySpell() { if(GetLevelByClass(CLASS_TYPE_WIZARD)>i5 && GetHasSpell(SPELL_FOXS_CUNNING) && !GetHasSpellEffect(SPELL_FOXS_CUNNING)) { ActionCastSpellAtObject(SPELL_FOXS_CUNNING,OBJECT_SELF); return TRUE; } if((GetLevelByClass(CLASS_TYPE_CLERIC)>i5 || GetLevelByClass(CLASS_TYPE_DRUID)>i5 || GetLevelByClass(CLASS_TYPE_SHIFTER)>i5) && GetHasSpell(SPELL_OWLS_WISDOM) && !GetHasSpellEffect(SPELL_OWLS_WISDOM)) { ActionCastSpellAtObject(SPELL_OWLS_WISDOM,OBJECT_SELF); return TRUE; } if((GetLevelByClass(CLASS_TYPE_BARD)>i5 || GetLevelByClass(CLASS_TYPE_SORCERER)>i5) && GetHasSpell(SPELL_EAGLE_SPLEDOR) && !GetHasSpellEffect(SPELL_EAGLE_SPLEDOR)) { ActionCastSpellAtObject(SPELL_EAGLE_SPLEDOR,OBJECT_SELF); return TRUE; } if(GetLevelByClass(CLASS_TYPE_PALADIN)>i5) { if(!GetHasSpellEffect(SPELL_EAGLE_SPLEDOR) && !GetHasSpellEffect(SPELL_AURAOFGLORY)) { if(GetHasSpell(SPELL_AURAOFGLORY)) { ActionCastSpellAtObject(SPELL_AURAOFGLORY, OBJECT_SELF,METAMAGIC_ANY,TRUE); DecrementRemainingSpellUses(OBJECT_SELF,SPELL_AURAOFGLORY); return TRUE; } else if(GetHasSpell(SPELL_EAGLE_SPLEDOR)) { ActionCastSpellAtObject(SPELL_EAGLE_SPLEDOR, OBJECT_SELF,METAMAGIC_ANY,TRUE); DecrementRemainingSpellUses(OBJECT_SELF,SPELL_EAGLE_SPLEDOR); return TRUE; } } } 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,iLoop,iCheck; // 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. iCheck=FALSE; for(iLoop=INVENTORY_SLOT_HEAD;iLoop<=INVENTORY_SLOT_CARMOUR;iLoop++) { if(GetItemHasItemProperty(GetItemInSlot(iLoop, oEnemy), ITEM_PROPERTY_TRUE_SEEING)) { iCheck=TRUE; break; } } if(GetHasSpellEffect(SPELL_SEE_INVISIBILITY, oEnemy) || GetHasSpellEffect(SPELL_TRUE_SEEING, oEnemy) || // - We obviously can tell with creatures with true seeing hides. Only checking hides! -- why? lack? // GetItemHasItemProperty(GetItemInSlot(INVENTORY_SLOT_CARMOUR, oEnemy), ITEM_PROPERTY_TRUE_SEEING)) iCheck) { 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 //::////////////////////////////////////////////*/ /*:://///////////////////////////////////////////// //:: 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 iHighestSpellLevel = i10, int iLowestSpellLevel = i0, int iMaxLoopRun = i2, int iLastCheckedRange = i1, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE) { // Checks for valid numbers, ETC. if(AI_GetAIHaveEffect(GlobalEffectPolymorph) || AI_GetAIHaveEffect(GlobalEffectTensersTransformation) || (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)) { // 31: "[DCR: All Spells] Error! No casting (No spells, items, target Etc)." DebugActionSpeakByInt(31); return FALSE; } // recheck combat function if target is invalid or dead if(!GetIsObjectValid(GlobalSpellTarget) || GetIsDead(GlobalSpellTarget)) { return TRUE; } // 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 && iLastCheckedRange>i3) || iLastCheckedRange>i4) // touch range check twice.. maybe have been checked all-round. else if(InputRangeTouchValid>=iMaxLoopRun) { // 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 { switch(iLastCheckedRange) { case i1: RangeLongValid = TRUE; RangeMediumValid = FALSE; RangeShortValid = FALSE; RangeTouchValid = FALSE; break; case i2: RangeLongValid = TRUE; RangeMediumValid = TRUE; RangeShortValid = FALSE; RangeTouchValid = FALSE; break; case i3: RangeLongValid = FALSE; RangeMediumValid = TRUE; RangeShortValid = TRUE; RangeTouchValid = FALSE; break; case i4: RangeLongValid = FALSE; RangeMediumValid = FALSE; RangeShortValid = TRUE; RangeTouchValid = TRUE; break; case i5: RangeLongValid = FALSE; RangeMediumValid = FALSE; RangeShortValid = FALSE; RangeTouchValid = TRUE; break; } // Long if(InputRangeLongValid >= i2) { // No range check, RangeLongValid is of course always true. RangeLongValid = FALSE; } // At 1, we do not do anything, and ignore long range spells. // Medium if(InputRangeMediumValid >= i2) { RangeMediumValid = FALSE; } else { // 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 >= i2) { RangeShortValid = FALSE; } else { // 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 // touch range is too short to check.. need modify. if(RangeTouchValid == i0) { // Range check for touch - or >= pass 4 if(GlobalSpellTargetRange <= 3.5 || //fTouchRange || (iLastCheckedRange >= i2 && (InputRangeShortValid >= i1 || RangeShortValid))) { 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; if(AI_AttemptCounterSpell())return 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(!GlobalCounterSpellTargetPercentage && !GlobalSpellFailure && 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(!GlobalCounterSpellTargetPercentage && !GlobalSpellFailure && IsFirstRunThrough && !GlobalInTimeStop && (GlobalIntelligence >= i10) && (GetHasSpell(SPELL_TIME_STOP) >= i2) && !AI_CompareTimeStopStored(SPELL_TIME_STOP)) { GlobalPreventSpellDupleUse=TRUE; // 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)) { int iCnt; GlobalInTimeStop=TRUE; for(iCnt=i0;iCnt<=GlobalImHasted+i1;iCnt++) { AI_AttemptAllSpells(i10,i8,i1); } return TRUE; } GlobalPreventSpellDupleUse=FALSE; } // Haste - First. Good one, I suppose. Should check for close (non-hasted) // allies, to choose between them. if(!GlobalCounterSpellTargetPercentage && !GlobalSpellFailure && 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; } // Visibility Protections - going invisible! // - We only do this not in time stop // we do this at any time // - 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) if(!GlobalCounterSpellTargetPercentage && !GlobalSpellFailure && 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(AI_ActionCastWhenEthereal()) return TRUE; } // - If we have darkness, we'll protect ourselves. (Ultravision or not!) // * We can ultra vision in the override special actions part of the AI. else if(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(GlobalHasConsealmentSpells) || !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)) if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF)) { 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; } } } } } } // Anti arcane spellCaster act. no buff time(time is more than gold!!!) // time stop -> spell protection -> dispel if need -> make uncommandable(or even blind) -> fast attack // ignore in ethereal effect? if(!GlobalCounterSpellTargetPercentage && !GlobalSpellFailure && !AI_GetAIHaveEffect(GlobalEffectEthereal) && AI_GetIsTargetCastable(GlobalSpellTarget) && ((GlobalAntiArcaneSpellCaster && AI_GetIsArcaneCaster(GlobalSpellTarget)) || (AI_GetIsArcaneCaster(GlobalSpellTarget) && GlobalTotalAllies>GlobalTotalSeenHeardEnemies && GlobalAverageFriendlyHD>GlobalAverageEnemyHD))) { // time stop. rules all. if(!GlobalInTimeStop && GlobalDispelTargetHighestDispel==i5 && !AI_CompareTimeStopStored(SPELL_TIME_STOP)) { GlobalPreventSpellDupleUse=TRUE; // 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)) { int iCnt; GlobalInTimeStop=TRUE; for(iCnt=i0;iCnt<=GlobalImHasted+i1;iCnt++) { AI_AttemptAllSpells(i10,i8,i1); } return TRUE; } GlobalPreventSpellDupleUse=FALSE; } // make fast. if(AI_SpellWrapperHasteEnchantments()) return TRUE; // important spell protection. if(AI_SpellWrapperMantalProtections()) 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(AI_AttemptSummonSpells()) 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. // 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; } } /* // This is POWERFUL! if(IsFirstRunThrough && AI_GetAIHaveEffect(GlobalEffectEthereal) && !GetIsObjectValid(GetAssociate(ASSOCIATE_TYPE_SUMMONED)) && 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; } */ // check core buff spell effect // this must be complete in ethereal or invisible status, but cast normal status as well. // not in anti arcane spell caster? if(!GlobalSpellFailure && !(GlobalAntiArcaneSpellCaster && AI_GetIsArcaneCaster(GlobalSpellTarget) && AI_GetIsTargetCastable(GlobalSpellTarget))) { if(!GlobalCounterSpellTargetPercentage && AI_CoreBuffSpell()) return TRUE; // Special protection if(!GetHasSpellEffect(AI_SPELL_EPIC_WARDING)) { // 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; } // 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; } } // attempt summon spells, or relate ability. if(AI_AttemptSummonSpells()) return TRUE; int iLoop,iLowLoop,iHighLoop; iLowLoop=i5-iHighestSpellLevel/i2; iHighLoop=i5-iLowestSpellLevel/i2; for(iLoop=iLowLoop;iLoop<=iHighLoop;iLoop++) { // Dispel Good spells on the enemy. // - Dispel Level here. // Basically, level spells are mantals and spell-stoppers, or an alful // lot of lower level spells. // Dispel number need to be 5 for breach if(GlobalDispelTargetHighestBreach >= ((i5-iLoop= 5 if(GlobalDispelTargetHighestDispel >= ((i5-iLoopi1) { if(AI_ActionCastBackupRandomSpell()) return TRUE; } // for shifter or druid, polymorph will be more effecient... maybe. if(iLoop>=i0 && GlobalRangeToNearestEnemy=i1 && GlobalRangeToNearestEnemy=i1 && GlobalRangeToNearestEnemy=i0 && !AI_GetIsArcaneCaster(OBJECT_SELF) && GlobalOurBestAttackBonus>=GlobalMeleeTargetAC-iLoop*i5 && d100()<((GlobalOurBestAttackBonus>GlobalMeleeTargetAC)?i60:i30)+iLoop*i20) 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; } // 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) { // 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(RangeLongValid)InputRangeLongValid++; if(RangeMediumValid)InputRangeMediumValid++; if(RangeShortValid)InputRangeShortValid++; if(RangeTouchValid)InputRangeTouchValid++; if(AI_AttemptAllSpells(iHighestSpellLevel, iLowestSpellLevel, iMaxLoopRun, iLastCheckedRange + i1, InputRangeLongValid, InputRangeMediumValid, InputRangeShortValid, InputRangeTouchValid)) 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) && GetHasSpell(771)) { ActionCastSpellAtLocation(771, GetLocation(oTarget)); // ActionAttack(oTarget); return TRUE; } else if(GetIsTalentValid(tBreath) && GetHasSpell(771) && d2()==1) { ActionCastSpellAtLocation(771, GetLocation(oTarget)); // ActionAttack(oTarget); return TRUE; } 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) && GetHasSpell(771)) { ActionCastSpellAtLocation(771, GetLocation(oTarget)); // ActionAttack(oTarget); return TRUE; } else if(GetIsTalentValid(tBreath) && GetHasSpell(771) && d2()==1) { ActionCastSpellAtLocation(771, GetLocation(oTarget)); // ActionAttack(oTarget); return TRUE; } 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(i10,FALSE,i1)) return TRUE; } else { // attempt core buff if(AI_CoreBuffSpell()) 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(i10,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(GlobalOurBestAttackBonus - i10 >= 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(i10,i9 - iSpellLowestMinus, i1)) { if(AI_AttemptFeatCombatHostile())return TRUE; // We also ActionAttack the enemy, as to move closer :-) AI_AttemptMeleeAttackWrapper(); return TRUE; } if(AI_AttemptFeatCombatHostile())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=i0; 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) > f8) 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(GetDistanceBetween(oAlly,oEnemy) > f10) { iBreak = TRUE; } else { // Next futhest. iCnt--; oAlly = GetLocalObject(OBJECT_SELF, ARRAY_ALLIES_RANGE_SEEN + IntToString(iCnt)); } } // random jump!!! if(!iBreak) { object oArea=GetArea(OBJECT_SELF); vector vec; location lfrom,lto; float face=GetFacing(OBJECT_SELF); int nx,ny,rangex,rangey,minx,miny,nmapx,nmapy; float qx,qy; // int nMaxCnt=10; nmapx=GetAreaWidth(oArea)*i10; nmapy=GetAreaHeight(oArea)*i10; lfrom=GetLocation(OBJECT_SELF); vec=GetPosition(OBJECT_SELF); qx=nmapx/f4; qy=nmapy/f4; minx=(qxnmapx || FloatToInt(vec.x+nx)nmapy || FloatToInt(vec.y+ny)=i3) // Dispel number need to be 5 for breach { // very special. antimagic cone. ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_MAGIC_CONE, GlobalDispelTarget, METAMAGIC_ANY, TRUE); return TRUE; } // bigby check. object oBigby = AI_GetNearestAllyWithSpell(SPELL_BIGBYS_CRUSHING_HAND, SPELL_BIGBYS_CLENCHED_FIST, SPELL_BIGBYS_GRASPING_HAND, SPELL_BIGBYS_FORCEFUL_HAND); if(GetIsObjectValid(oBigby) && AI_GetIsBigbyOn(oBigby)==i2 && oBigby!=OBJECT_SELF) /*&& (!AI_GetIsArcaneCaster(oBigby) || (AI_GetIsArcaneCaster(oBigby) && !AI_GetAIHaveSpellsEffect(GlobalHasConsealmentSpells, oBigby) && !(GetHasSpellEffect(SPELL_MESTILS_ACID_SHEATH, oBigby) || GetHasSpellEffect(SPELL_ELEMENTAL_SHIELD, oBigby)))))*/ { // very special. antimagic cone. ActionCastSpellAtObject(AI_SPELLABILITY_BEHOLDER_MAGIC_CONE, oBigby, METAMAGIC_ANY, TRUE); DelayCommand(GetDistanceBetween(OBJECT_SELF, oBigby)/f20+f1, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_BREACH), oBigby)); DelayCommand(GetDistanceBetween(OBJECT_SELF, oBigby)/f20+f1, AI_DoRemoveEffects(oBigby)); return TRUE; } // keep distance!! if(GetDistanceToObject(GlobalNearestEnemySeen)i1) && d6()!=i1) { if(AI_ActionBeholderTeleport()) 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(i10, i8, i1)) 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; } // cast anti magic cone to spell caster? // remove all buff and apply spell failure short time. else if(GlobalAntiArcaneSpellCaster && AI_GetIsArcaneCaster(GlobalSpellTarget) && GetDistanceToObject(GlobalSpellTarget)i1) && d6()!=i1 && !GlobalIamBlockedByObject) { AI_AntiCornerMoveAway(GlobalNearestEnemySeen,TRUE,f5,GlobalIamBlocked); } if(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return FALSE; // cast core buff spells. if(AI_CoreBuffSpell())return TRUE; // cast powerful spells first else if(AI_AttemptAllSpells(i10, i10, i1))return TRUE; // 70% chance of mind blast(max range=15) else if(GlobalRangeToMeleeTarget < f15 && d100() <= i70) { if(!GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_MIND_SPELLS)) { // will save check int iWill=GetWillSavingThrow(GlobalMeleeTarget),iWillCheck; // Special mind blast - infinite use? if(!GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_STUN)) { iWillCheck=i100-iWill/((i10+GlobalOurHitDice/i2+GetAbilityModifier(ABILITY_WISDOM))/i10)*i5; if(GlobalEnemiesIn4Meters>i2 && d100()i20) { //dc 19 ActionCastSpellAtObject(AI_SPELLABILITY_ILLITHID_MINDBLAST, GlobalMeleeTarget,METAMAGIC_ANY,TRUE,i0,PROJECTILE_PATH_TYPE_DEFAULT); return TRUE; } // dc 17 ActionCastSpellAtObject(SPELLABILITY_MINDBLAST, GlobalMeleeTarget,METAMAGIC_ANY,TRUE,i0,PROJECTILE_PATH_TYPE_DEFAULT); return TRUE; } // ActionCastSpellAtObject(551, GlobalMeleeTarget,METAMAGIC_ANY,TRUE,0,PROJECTILE_PATH_TYPE_DEFAULT); // return TRUE; } else if(!GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_CHARM)) { iWillCheck=i100-iWill/i2*i5; if(d100()= 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! :-) case SPELL_MESTILS_ACID_SHEATH: // 2/level + 1d6 acid damage - damage shield. case SPELL_ELEMENTAL_SHIELD: // level + 1d6 fire Damage Sheild (gives HTH attackers damage) case SPELL_SPELL_RESISTANCE: // 11 + Caster level in spell resistance. case SPELL_PROTECTION_FROM_SPELLS:// +8 on all saves case SPELL_DEATH_WARD: // Immunity to death spells - so Dispel case SPELL_NEGATIVE_ENERGY_PROTECTION:// Immunity to negative energy, - so Dispel { 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_TRUE_SEEING:// Can see anything - powerful against invis! case SPELL_SHAPECHANGE:// VERY Powerful polymorphing. case SPELL_IMPROVED_INVISIBILITY:// maybe melee, for support melee allies case SPELL_MIND_BLANK: case SPELL_LESSER_MIND_BLANK: case SPELL_PROTECTION_FROM_EVIL:// Mind immunity mostly. case SPELL_PROTECTION_FROM_GOOD:// Mind immunity mostly. case SPELL_CLARITY: // Immunity: mind spells. { if(GlobalDispelTargetHighestDispel < i5) GlobalDispelTargetHighestDispel = 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_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 // 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_SHIELD: // +AC + Immunity magic missile // Enhancements case SPELL_DIVINE_POWER:// +Stat, HP, base attack bonus. case SPELL_DIVINE_FAVOR:// +1-5 Attack/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; } // beware shield damage. // Get target has shield damage magic, or target is black blade of disaster(or mordenkainen's sword) // return value=1 : swords, =2 : shield effect. =3 : has shield but uncommandable. int AI_GetIsMustDispelMeleeTarget(object oTarget) { // Summons dispelling object oMaster = GetMaster(oTarget); if(GetIsObjectValid(oMaster)) { // First, is it the black blade we can dispel? (If cast aganst the master) // or modified modenkainen's sword. if(GetPlotFlag(oTarget)) { return i1; } } // We check the spell it has been cast from :-) int iSpellID, iSpellCounter; // We check all of thier effects. effect eCheck = GetFirstEffect(oTarget); // Loop around valid effects. while(GetIsEffectValid(eCheck)) { 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) { case SPELL_MESTILS_ACID_SHEATH: // 2/level + 1d6 acid damage - damage shield. case SPELL_ELEMENTAL_SHIELD: // level + 1d6 fire Damage Sheild (gives HTH attackers damage) // don't want dispel this target. // - Stun, sleep, fear if(AI_GetAIHaveEffect(GlobalEffectUncommandable, oTarget) || // - Petrify AI_GetAIHaveEffect(GlobalEffectPetrify, oTarget) || // - Paralyze AI_GetAIHaveEffect(GlobalEffectParalyze, oTarget) || // - Plot flag GetPlotFlag(oTarget)) { return i3; } return i2; break; } } eCheck = GetNextEffect(oTarget); } return FALSE; } // 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; } // is allys in shape? int AI_GetIsAllyInShape(int nShape, float fSize, location lTarget, int bLineOfSight=FALSE, int nObjectFilter=OBJECT_TYPE_CREATURE, vector vOrigin=[0.0,0.0,0.0]) { object oTarget; oTarget = GetFirstObjectInShape(nShape, fSize, lTarget, TRUE, nObjectFilter); while(GetIsObjectValid(oTarget)) { if(oTarget!=OBJECT_SELF && !GetIsEnemy(oTarget)) { return TRUE; } //Get next target in spell area oTarget = GetNextObjectInShape(nShape, fSize, lTarget, TRUE, nObjectFilter); } return FALSE; } // bigby check // 1: spell is on, 2: spell and effect is on. int AI_GetIsBigbyOn(object oTarget) { if(GetHasSpellEffect(SPELL_BIGBYS_INTERPOSING_HAND,oTarget) || GetHasSpellEffect(SPELL_BIGBYS_FORCEFUL_HAND,oTarget) || GetHasSpellEffect(SPELL_BIGBYS_GRASPING_HAND,oTarget) || GetHasSpellEffect(SPELL_BIGBYS_CLENCHED_FIST,oTarget) || GetHasSpellEffect(SPELL_BIGBYS_CRUSHING_HAND,oTarget)) { int iMod=GetLocalInt(oTarget,"X2_BIGBY_CASTERMODIFIER"); if(GetAbilityModifier(ABILITY_STRENGTH,oTarget)=nHD/i2 || GetLevelByClass(CLASS_TYPE_SORCERER,oTarget)>=nHD/i2) { return TRUE; } return FALSE; } // enemy type is spell caster? int AI_GetIsSpellCaster(object oTarget) { int nHD=GetHitDice(oTarget); // caster's information if(GetLevelByClass(CLASS_TYPE_WIZARD,oTarget)>=nHD/i2 || GetLevelByClass(CLASS_TYPE_SORCERER,oTarget)>=nHD/i2 || GetLevelByClass(CLASS_TYPE_BARD,oTarget)>=nHD/i2 || GetLevelByClass(CLASS_TYPE_CLERIC,oTarget)>=nHD/i2 || GetLevelByClass(CLASS_TYPE_DRUID,oTarget)>=nHD/i2) { return TRUE; } return FALSE; } // target is spell castable? int AI_GetIsTargetCastable(object oTarget) { if(GetIsObjectValid(oTarget)) { if(!GetIsDead(oTarget) && !AI_GetAIHaveEffect(GlobalEffectBlindness,oTarget) && !AI_GetAIHaveEffect(GlobalEffectUncommandable,oTarget) && !AI_GetAIHaveEffect(GlobalEffectSilenced,oTarget) && !AI_GetAIHaveEffect(GlobalEffectDazed,oTarget) && !AI_GetAIHaveEffect(GlobalEffectPetrify,oTarget) && !AI_GetAIHaveEffect(GlobalEffectParalyze,oTarget) && !AI_GetAIHaveEffect(GlobalEffectPolymorph,oTarget) && !AI_GetAIHaveEffect(GlobalEffectTensersTransformation,oTarget)) { return TRUE; } else { return FALSE; } } return FALSE; } // return percentage with spelltarget saving throw and DC int AI_GetPercentageDC(int iDC, int nSavingThrowType) { int iSave,iResult; if(nSavingThrowType==SAVING_THROW_FORT) { iSave=GlobalSpellTargetFort; } if(nSavingThrowType==SAVING_THROW_REFLEX) { iSave=GlobalSpellTargetReflex; } if(nSavingThrowType==SAVING_THROW_WILL) { iSave=GlobalSpellTargetWill; } iResult=FloatToInt(IntToFloat(iDC-iSave)/f20*f100)+GlobalIntelligence*i2; if(iResult>i100)iResult=i100; if(iResultiHD/2)return TRUE; if(GetLevelByClass(CLASS_TYPE_SORCERER,oTarget)>iHD/2)return TRUE; if(GetLevelByClass(CLASS_TYPE_DRUID,oTarget)>iHD/2)return TRUE; if(GetLevelByClass(CLASS_TYPE_CLERIC,oTarget)>iHD/2)return TRUE; return FALSE; } // get counterspell possibility int AI_GetCounterSpellPossibility(object oTarget,object oSource=OBJECT_SELF) { int iCntPct=FALSE; int iTargetHD=GetHitDice(oTarget); int iSourceHD=GetHitDice(oSource); if(GetHasSpell(SPELL_MORDENKAINENS_DISJUNCTION,oSource))iCntPct=i100; else if(GetHasSpell(SPELL_GREATER_DISPELLING,oSource)) { if(iTargetHD<=i12)iCntPct=i70; else iCntPct=i40; } else if(GetHasSpell(SPELL_DISPEL_MAGIC,oSource)) { if(iTargetHD<=i6)iCntPct=i70; else if(iTargetHD<=i12)iCntPct=i30; else iCntPct=i15; } else if(GetHasSpell(SPELL_LESSER_DISPEL,oSource)) { if(iTargetHD<=i4)iCntPct=i70; else if(iTargetHD<=i8)iCntPct=i20; else iCntPct=i5; } if((GetLevelByClass(CLASS_TYPE_BARD,oTarget)+ GetLevelByClass(CLASS_TYPE_SORCERER,oTarget))+ GetLevelByClass(CLASS_TYPE_WIZARD,oTarget)>=iTargetHD/i2 && GetLevelByClass(CLASS_TYPE_SORCERER,oSource)+GetLevelByClass(CLASS_TYPE_WIZARD,oSource)>=iSourceHD/i2) { iCntPct+=i25; } if(GetLevelByClass(CLASS_TYPE_CLERIC,oTarget)+ GetLevelByClass(CLASS_TYPE_DRUID,oTarget)>=iTargetHD/i2 && GetLevelByClass(CLASS_TYPE_CLERIC,oSource)+GetLevelByClass(CLASS_TYPE_DRUID,oSource)>=iSourceHD/i2) { iCntPct+=i25; } return iCntPct; } // Gets the nearest AOE cast by enemy, of sTag. object GetNearestAOECastByEnemy(string sTag) { int iCnt = i1; object oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, iCnt); object oReturn = OBJECT_INVALID; // Loop while(GetIsObjectValid(oAOE) && !GetIsObjectValid(oReturn)) { // Check creator if(GetIsEnemy(GetAreaOfEffectCreator(oAOE))) { oReturn = oAOE; } iCnt++; oAOE = GetNearestObjectByTag(sTag, OBJECT_SELF, iCnt); } return oReturn; } // Get tag of AOE spell ID string GetTagofAOESpellID(int nSpell) { switch(nSpell) { case SPELL_EVARDS_BLACK_TENTACLES: return AI_AOE_PER_EVARDS_BLACK_TENTACLES; break; case SPELL_SPIKE_GROWTH: case SPELL_VINE_MINE_HAMPER_MOVEMENT: return AI_AOE_PER_ENTANGLE; break; case SPELL_ENTANGLE: case SPELL_VINE_MINE_ENTANGLE: return AI_AOE_PER_ENTANGLE; break; case SPELL_WEB: return AI_AOE_PER_WEB; break; case SPELL_STINKING_CLOUD: return AI_AOE_PER_FOGSTINK; break; case SPELL_CLOUD_OF_BEWILDERMENT: return AI_AOE_PER_FOGBEWILDERMENT; break; case SPELL_STONEHOLD: return AI_AOE_PER_STONEHOLD; break; case SPELL_MIND_FOG: return AI_AOE_PER_FOGMIND; break; case SPELL_GREASE: return AI_AOE_PER_GREASE; break; case SPELL_BLADE_BARRIER: // Reflex return AI_AOE_PER_WALLBLADE; break; case SPELL_INCENDIARY_CLOUD:// reflex return AI_AOE_PER_FOGFIRE; break; case SPELL_WALL_OF_FIRE:// Reflex return AI_AOE_PER_WALLFIRE; break; case SPELL_ACID_FOG: // Fort: Half. No check, always damages. return AI_AOE_PER_FOGACID; break; case SPELL_CLOUDKILL:// No save! return AI_AOE_PER_FOGKILL; break; case SPELL_CREEPING_DOOM: // No save! return AI_AOE_PER_CREEPING_DOOM; break; case SPELL_STORM_OF_VENGEANCE: // Reflex partial. No check, always damages. return AI_AOE_PER_STORM; break; } return ""; } // 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); } SetLocalInt(OBJECT_SELF, MAXINT_+ARRAY_TEMP_ALLIES, GlobalTotalAllies); SetLocalInt(OBJECT_SELF, MAXINT_+ARRAY_TEMP_ENEMIES, iCnt); // 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 GlobalCounterSpellTarget = OBJECT_INVALID; GlobalCounterSpellTargetPercentage = FALSE; 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)) { // AssignCommand(oSetUpTarget,AI_SetEffectsOnTarget(oSetUpTarget)); 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); // Need a dispel spell if(GetActionMode(oSetUpTarget,ACTION_MODE_COUNTERSPELL) && GetObjectSeen(OBJECT_SELF,oSetUpTarget) && AI_GetIsTargetCastable(oSetUpTarget) && AI_GetCounterSpellPossibility(OBJECT_SELF,oSetUpTarget)>=d100()) { GlobalCounterSpellTargetPercentage++; } int iCasterLevels,iHighestLevels; if(//fCurrentRange<=f20 && GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_ARCANE, AI_COMBAT_MASTER) && AI_GetIsTargetCastable(oSetUpTarget) && !GetActionMode(oSetUpTarget,ACTION_MODE_COUNTERSPELL)) { // Check caster levels iCasterLevels = GetLevelByClass(CLASS_TYPE_WIZARD, oSetUpTarget) + GetLevelByClass(CLASS_TYPE_SORCERER, oSetUpTarget) + GetLevelByClass(CLASS_TYPE_BARD, oSetUpTarget); // Check if higher. oTempLoopObject = GetLocalObject(oSetUpTarget,AI_COUNTERSPELL_USER); if(iCasterLevels > iHighestLevels && iCasterLevels>=GetHitDice(oSetUpTarget)/i2 && (!GetIsObjectValid(oTempLoopObject) || oTempLoopObject==OBJECT_SELF)) { iHighestLevels = iCasterLevels; GlobalCounterSpellTarget = oSetUpTarget; } } if(//fCurrentRange<=f20 && !GetIsObjectValid(GlobalCounterSpellTarget) && !GetActionMode(oSetUpTarget,ACTION_MODE_COUNTERSPELL) && AI_GetIsTargetCastable(oSetUpTarget) && iHighestLevels >= GlobalOurHitDice / i3 && GetSpawnInCondition(AI_FLAG_COMBAT_COUNTER_SPELL_DIVINE, AI_COMBAT_MASTER)) { // Check caster levels iCasterLevels = GetLevelByClass(CLASS_TYPE_CLERIC, oSetUpTarget) + GetLevelByClass(CLASS_TYPE_DRUID, oSetUpTarget); // Check if higher. oTempLoopObject = GetLocalObject(oSetUpTarget,AI_COUNTERSPELL_USER); if(iCasterLevels > iHighestLevels && iCasterLevels>=GetHitDice(oSetUpTarget)/i2 && (!GetIsObjectValid(oTempLoopObject) || oTempLoopObject==OBJECT_SELF)) { iHighestLevels = iCasterLevels; GlobalCounterSpellTarget = 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)); } SetLocalInt(OBJECT_SELF, MAXINT_+ARRAY_ENEMY_RANGE_SEEN, iCnt2); SetLocalInt(OBJECT_SELF, MAXINT_+ARRAY_ENEMY_RANGE_HEARD, iCnt3); // 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); */ if(GetGameDifficulty()>GAME_DIFFICULTY_NORMAL && GetLocalInt(GetModule(),"X2_SWITCH_ENABLE_NPC_AOE_HURT_ALLIES") == TRUE) { GlobalFriendlyFire = TRUE; // Hostile Creature and Difficulty > Normal } else { GlobalFriendlyFire = FALSE; } 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)) { if(AI_GetIsSpellCaster(oSetUpTarget) && AI_GetIsTargetCastable(oSetUpTarget)) { // tmp use of iTypeOfTarget for ally caster count iTypeOfTarget++; } 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 + IntToString(iCnt)); } SetLocalInt(OBJECT_SELF, MAXINT_+ARRAY_ALLIES_RANGE_SEEN, iCnt2); 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. // Add multi class check if(GetLevelByClass(CLASS_TYPE_SORCERER, 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)); } SetLocalInt(OBJECT_SELF, MAXINT_+ARRAY_ALLIES_RANGE_SEEN_BUFF, iCnt2); } // percentage of enemy's counterspell is targeting me GlobalCounterSpellTargetPercentage*=i100/(iTypeOfTarget+i1); if(d100()<=GlobalCounterSpellTargetPercentage) { GlobalCounterSpellTargetPercentage = TRUE; GlobalSilenceSoItems = i2; } else { GlobalCounterSpellTargetPercentage = FALSE; } /*::////////////////////////////////////////////// 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_OTHER_MASTER) || GlobalIntelligenceScore 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); // if(!GetIsObjectValid(oLastTarget)) oLastTarget = GlobalMeleeTarget; // 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) || (!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 if(!GetIsObjectValid(GlobalMeleeTarget)) { GlobalMeleeTarget = oLastTarget; } oSetUpTarget=GetMaster(GlobalMeleeTarget); if(!AI_GetTargetSanityCheck(oSetUpTarget) && (GetAssociateType(GlobalMeleeTarget)==ASSOCIATE_TYPE_SUMMONED || GetAssociateType(GlobalMeleeTarget)==ASSOCIATE_TYPE_FAMILIAR || GetAssociateType(GlobalMeleeTarget)==ASSOCIATE_TYPE_ANIMALCOMPANION) && (GetObjectSeen(oSetUpTarget) || GetObjectHeard(oSetUpTarget)) && GlobalIntelligenceScore>=i8) { GlobalMeleeTarget = oSetUpTarget; } // react when target has elemental shield if(!GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER) && !GetSpawnInCondition(AI_FLAG_OTHER_COMBAT_DISPEL_WITH_MELEE, AI_OTHER_COMBAT_MASTER) && AI_GetIsMustDispelMeleeTarget(GlobalMeleeTarget)>=i2 && GlobalIntelligenceScore>=i8) { // if damage shilded, chose other target iCnt=i1; oSetUpTarget=GetLocalObject(OBJECT_SELF,ARRAY_ENEMY_RANGE_SEEN+IntToString(iCnt)); while(GetIsObjectValid(oSetUpTarget)) { if(!AI_GetIsMustDispelMeleeTarget(oSetUpTarget)) { GlobalMeleeTarget = oSetUpTarget; break; } oSetUpTarget=GetLocalObject(OBJECT_SELF,ARRAY_ENEMY_RANGE_SEEN+IntToString(++iCnt)); } } if(!GetIsObjectValid(GlobalMeleeTarget) || !GetObjectSeen(GlobalMeleeTarget) && !GetObjectHeard(GlobalMeleeTarget)) { GlobalMeleeTarget = GlobalNearestEnemySeen; if(!GetIsObjectValid(GlobalMeleeTarget)) { GlobalMeleeTarget = GlobalNearestEnemyHeard; } } // If it is a new target, reset attacking counter to 0 if(GlobalMeleeTarget != GetAIObject(AI_LAST_MELEE_TARGET)) { 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(GetAIObject(AI_WEAPON_RANGED)) && (!GetIsObjectValid(GlobalRangedTarget) || 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); GlobalMeleeTargetBestAttackBonus = AI_GetBestAttackRoll(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(GlobalSpellFailure) { 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); GlobalSpellClassRDD = GetLevelByClass(CLASS_TYPE_DRAGONDISCIPLE, GlobalSpellTarget)>=i10; // 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_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_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_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); } } } } GlobalAntiArcaneSpellCaster=FALSE; GlobalAntiMeleeWarrior=FALSE; int nHD,nCaster=i0,nCasterHD=i0,nNonCaster=i0,nNonCasterHD=i0, nLoop=i1,nCnt=GetLocalInt(OBJECT_SELF, MAXINT_ + ARRAY_ENEMY_RANGE); object oCaster=GetLocalObject(OBJECT_SELF,ARRAY_ENEMY_RANGE+s1),oCaster2; while(GetIsObjectValid(oCaster)) { nHD=GetHitDice(oCaster); // caster's information if(AI_GetIsArcaneCaster(oCaster) && AI_GetIsTargetCastable(oCaster)) { nCaster++; nCasterHD+=nHD; } // noncaster's information. ignore summon. else if(!GetIsObjectValid(GetMaster(oCaster))) { nNonCaster++; nNonCasterHD+=nHD; } oCaster=GetLocalObject(OBJECT_SELF,ARRAY_ENEMY_RANGE+IntToString(++nLoop)); } // one or more caster, less than 2 noncaster(maybe warrior) if(nCaster>i0 && nNonCasternNonCasterHD/nNonCaster*i2))) { // check for special act. GlobalAntiArcaneSpellCaster=TRUE; } } if(nCaster==i0 && nNonCaster) { GlobalAntiMeleeWarrior=TRUE; } oSetUpTarget=GetLocalObject(OBJECT_SELF,AI_SAVED_DISPEL_TARGET); if(GetIsObjectValid(oSetUpTarget) && AI_GetIsMustDispelMeleeTarget(oSetUpTarget)!=i0) { GlobalDispelTarget = oSetUpTarget; DelayCommand(f12,DeleteLocalObject(OBJECT_SELF,AI_SAVED_DISPEL_TARGET)); } if(!GetIsObjectValid(GlobalDispelTarget) || AI_GetTargetSanityCheck(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(f3); AI_AttemptMeleeAttackWrapper(); // ActionAttack(GlobalMeleeTarget); } int AI_AttemptHostileSkills() { if(((GlobalAntiArcaneSpellCaster && AI_GetIsArcaneCaster(GlobalMeleeTarget) && AI_GetIsTargetCastable(GlobalMeleeTarget)) || AI_GetIsMustDispelMeleeTarget(GlobalMeleeTarget) || GlobalRangeToMeleeTarget < f2) && !GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_USE_BOW, AI_COMBAT_MASTER) && !GetSpawnInCondition(AI_FLAG_COMBAT_ARCHER_ALWAYS_MOVE_BACK, AI_COMBAT_MASTER)) // why SRA?. process this mean that we have no proper magic. // !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)) // to use pick pocket skill, rank is greater than 20. && GetSkillRank(SKILL_PICK_POCKET)>i20) { // 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() { // check differ spells and feat. int iUseSpells = TRUE; if(GlobalSpellFailure || AI_GetAIHaveEffect(GlobalEffectPolymorph) || AI_GetAIHaveEffect(GlobalEffectTensersTransformation))iUseSpells=FALSE; // Don't chance the use of broken paladin and ranger spells. // what? /* 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(!GetHasSpellEffect(SPELL_DIVINE_POWER) && 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. // boost cha. if(AI_ActionCastCasterAbilitySpell()) return TRUE; int iCha=GetAbilityModifier(ABILITY_CHARISMA); // Divine Shield - a feat, but a damn good one. // Up to +5 Dodge AC. if(GetHasFeat(FEAT_TURN_UNDEAD) && iCha>=i5 && GlobalOurAC+iCha>GlobalMeleeTargetBestAttackBonus) { // Divine Shield if(AI_ActionUseFeatOnObject(FEAT_DIVINE_SHIELD)) return TRUE; } // Divine might adds charisma bonus to Attack. if(GetHasFeat(FEAT_TURN_UNDEAD) && iCha>=i7 && GlobalOurBestAttackBonus+i10>=GlobalMeleeTargetAC) { // +Cha bonus to attack bonus. if(AI_ActionUseFeatOnObject(FEAT_DIVINE_MIGHT)) return TRUE; } // many bonus. if(GetHasFeat(FEAT_DIVINE_WRATH) && GlobalOurBestAttackBonus+i10>=GlobalMeleeTargetAC) { // +Cha bonus to attack bonus. if(AI_ActionUseFeatOnObject(FEAT_DIVINE_WRATH)) return TRUE; } // spell? if(!GetHasSpellEffect(SPELLABILITY_DC_DIVINE_WRATH) && GetHasSpell(SPELLABILITY_DC_DIVINE_WRATH) && GlobalOurBestAttackBonus+i10>=GlobalMeleeTargetAC) { ActionCastSpellAtObject(SPELLABILITY_DC_DIVINE_WRATH,OBJECT_SELF,METAMAGIC_ANY); } // 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; } } } // feat base ability check, for tenser's transformation else { int iCha=GetAbilityModifier(ABILITY_CHARISMA); // Divine Shield - a feat, but a damn good one. // Up to +5 Dodge AC. if(GetHasFeat(FEAT_TURN_UNDEAD) && iCha>=i5 && GlobalOurAC+iCha>GlobalMeleeTargetBestAttackBonus) { // Divine Shield if(AI_ActionUseFeatOnObject(FEAT_DIVINE_SHIELD)) return TRUE; } // Divine might adds charisma bonus to Attack. if(GetHasFeat(FEAT_TURN_UNDEAD) && iCha>=i7 && GlobalOurBestAttackBonus+i10>=GlobalMeleeTargetAC) { // +Cha bonus to attack bonus. if(AI_ActionUseFeatOnObject(FEAT_DIVINE_MIGHT)) return TRUE; } // many bonus. if(GetHasFeat(FEAT_DIVINE_WRATH) && GlobalOurBestAttackBonus+i10>=GlobalMeleeTargetAC) { // +Cha bonus to attack bonus. if(AI_ActionUseFeatOnObject(FEAT_DIVINE_WRATH)) return TRUE; } // spell? if(!GetHasSpellEffect(SPELLABILITY_DC_DIVINE_WRATH) && GetHasSpell(SPELLABILITY_DC_DIVINE_WRATH) && GlobalOurBestAttackBonus+i10>=GlobalMeleeTargetAC) { ActionCastSpellAtObject(SPELLABILITY_DC_DIVINE_WRATH,OBJECT_SELF,METAMAGIC_ANY); } } // 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(iUseSpells && 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(iUseSpells && ((GlobalRangeToMeleeTarget < f3 && !AI_GetIsMustDispelMeleeTarget(GlobalMeleeTarget)) || 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; } } // last. divine favor.. short duration but not bad. if(iUseSpells && !GetHasSpellEffect(SPELL_DIVINE_FAVOR) && GetHasSpell(SPELL_DIVINE_FAVOR)) { ActionCastSpellAtObject(SPELL_DIVINE_FAVOR,OBJECT_SELF); } 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(GetLocalInt(OBJECT_SELF,"DEADMAGIC_SPELLFAILURE")) return FALSE; int nAge,nBreath=TRUE; 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)) { // We can have items for this polymorph spell :-) // modified, tenser first. we can use shifter ability after that. // modifyed magic. tenser is not polymorph if(!GlobalSpellFailure && !AI_GetAIHaveEffect(GlobalEffectTensersTransformation)) { if(AI_ActionCastSpell(SPELL_TENSERS_TRANSFORMATION, SpellEnhSelf, OBJECT_SELF, i16, ItemEnhSelf, PotionEnh)) return TRUE; } // 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(GlobalOurAC 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; 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,GlobalMeleeTarget); 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 && GlobalDispelTargetHighestDispel>i2) { AI_ActionCastShifterSpell(SPELL_DISPEL_MAGIC, GlobalDispelTarget); return TRUE; } else if(d10() <= i6) { AI_ActionCastShifterSpell(SPELL_ICE_STORM, 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; } // tactical flee and hide // need more modify. int AI_TacticFleeHide() { // if checked at AI_StopWhatWeAreDoing. if(GetStealthMode(OBJECT_SELF)==STEALTH_MODE_ACTIVATED && !AI_GetAIHaveEffect(GlobalEffectTrueSeeing, GlobalMeleeTarget)) { if(GlobalTotalAllies==i0 && GetHasFeat(FEAT_PRESTIGE_DEATH_ATTACK_1) && GetIsInCombat(GlobalMeleeTarget) && !GlobalIamBlockedByObject && !GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_CRITICAL_HIT) && !GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_SNEAK_ATTACK) && !(AI_GetAIHaveEffect(GlobalEffectParalyze, GlobalMeleeTarget) || AI_GetAIHaveEffect(GlobalEffectPetrify, GlobalMeleeTarget) || AI_GetAIHaveEffect(GlobalEffectUncommandable, GlobalMeleeTarget))) { DeleteLocalInt(OBJECT_SELF, AI_TIMER + AI_TIMER_TURN_OFF_HIDE); if(GlobalRangeToMeleeTargeti0 && GetAttackTarget(GlobalMeleeTarget)==OBJECT_SELF && !GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_CRITICAL_HIT) && !GetIsImmune(GlobalMeleeTarget,IMMUNITY_TYPE_SNEAK_ATTACK) && !GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,GlobalMeleeTarget))) { // ally check // there a ally who is little away from enemy oAlly=GetLocalObject(OBJECT_SELF,ARRAY_ALLIES_RANGE+IntToString(iCnt)); while(GetIsObjectValid(oAlly)) { iAllyRange=GetDistanceBetween(oAlly,GlobalMeleeTarget); if(iAllyRange>GlobalRangeToMeleeTarget+f3) { oAllyEnemy=GetLocalObject(oAlly,ARRAY_ENEMY_RANGE_SEEN+IntToString(i1)); if(!GetIsObjectValid(oAllyEnemy) || (GetIsObjectValid(oAllyEnemy) && GetDistanceBetween(oAlly,oAllyEnemy)>f5)) break; } oAlly=GetLocalObject(OBJECT_SELF,ARRAY_ALLIES_RANGE+IntToString(++iCnt)); } // melee target don't have true seeing. if(GetHasFeat(FEAT_HIDE_IN_PLAIN_SIGHT) && !AI_GetAIHaveEffect(GlobalEffectTrueSeeing, GlobalMeleeTarget)) { SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, TRUE); // ranged weapon. move away. if(GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND)) && !GlobalIamBlockedByObject && GlobalRangeToMeleeTargeti10+d6()) { SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE); if(GetIsObjectValid(oAlly)) { vector vTo; vTo=GetPosition(oAlly)-GetPosition(OBJECT_SELF); vTo=VectorNormalize(vTo); vTo*=(iAllyRange+f5); //run to ally ActionMoveToLocation(Location(GetArea(OBJECT_SELF),vTo,VectorToAngle(vTo)),TRUE); } else { //run away AI_AntiCornerMoveAway(GlobalMeleeTarget,TRUE,f5,GlobalIamBlocked); } return TRUE; } } else if(AI_GetIsArcaneCaster(OBJECT_SELF) && GlobalTotalAllies>i0 && AI_GetIsMustDispelMeleeTarget(OBJECT_SELF)!=i2 && GlobalEnemiesIn4Meters && !GetWeaponRanged(GetItemInSlot(INVENTORY_SLOT_RIGHTHAND,GlobalMeleeTarget)) && !AI_GetAIHaveEffect(GlobalEffectPolymorph) && !AI_GetAIHaveEffect(GlobalEffectTensersTransformation) && GlobalMeleeTargetBestAttackBonus+i20>=GlobalOurAC) { // ally check // there a ally who is little away from enemy oAlly=GetLocalObject(OBJECT_SELF,ARRAY_ALLIES_RANGE+IntToString(iCnt)); while(GetIsObjectValid(oAlly)) { iAllyRange=GetDistanceBetween(oAlly,GlobalMeleeTarget); if(iAllyRange>GlobalRangeToMeleeTarget+f3) { oAllyEnemy=GetLocalObject(oAlly,ARRAY_ENEMY_RANGE_SEEN+IntToString(i1)); if(!GetIsObjectValid(oAllyEnemy) || (GetIsObjectValid(oAllyEnemy) && GetDistanceBetween(oAlly,oAllyEnemy)>f5)) break; } oAlly=GetLocalObject(OBJECT_SELF,ARRAY_ALLIES_RANGE+IntToString(++iCnt)); } if(!GlobalIamBlockedByObject && GlobalRangeToMeleeTargeti10+d6()) { SetActionMode(OBJECT_SELF, ACTION_MODE_STEALTH, FALSE); if(GetIsObjectValid(oAlly)) { vector vTo; vTo=GetPosition(oAlly)-GetPosition(OBJECT_SELF); vTo=VectorNormalize(vTo); vTo*=(iAllyRange+f5); //run to ally ActionMoveToLocation(Location(GetArea(OBJECT_SELF),vTo,VectorToAngle(vTo)),TRUE); } else { //run away AI_AntiCornerMoveAway(GlobalMeleeTarget,TRUE,f8,GlobalIamBlocked); } return TRUE; } } 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 || GlobalTotalSeenHeardEnemies>GlobalTotalAllies)) { return FALSE; }*/ // Check if valid if(GlobalInTimeStop || !GetIsObjectValid(GlobalCounterSpellTarget) || GetSkillRank(SKILL_SPELLCRAFT)GlobalTotalAllies+i1) { return FALSE; } if(d100()>AI_GetCounterSpellPossibility(GlobalCounterSpellTarget)) { return FALSE; } // 45: "[DCR:CounterSpell] Counterspelling. [Target] " + GetName(oCounterspellTarget) DebugActionSpeakByInt(45, GlobalCounterSpellTarget); AI_SetMeleeMode(ACTION_MODE_COUNTERSPELL); ActionCounterSpell(GlobalCounterSpellTarget); SetLocalObject(GlobalCounterSpellTarget,AI_COUNTERSPELL_USER,OBJECT_SELF); DelayCommand(AI_GetAIHaveEffect(GlobalEffectHaste)?2.5:5.5,DeleteLocalObject(GlobalCounterSpellTarget,AI_COUNTERSPELL_USER)); return TRUE; } // attempt AOE spell wrapper int AI_AttemptCastAOESpell(int nSpell, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE) { if(GlobalLocationTargetFail) { return AI_AttemptCastAOESpellToObject(nSpell, nTalent, iItemTalentValue, fRange, fSpread, nLevel, iSaveType, nShape, nFriendlyFire, iDamageSpell, iNecromanticSpell, iDeathImmune, iNegativeImmune, iMindImmune, iMindType); } else { // false in last checking loop, don't check again. if(GetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(nSpell))) return FALSE; // 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 || !nLevel || (nLevel && GlobalSpellAbilityModifier >= nLevel+i10)) && !GlobalSilenceSoItems && GetHasSpell(nSpell)) || iItemTalentValue == nSpell) { switch(AI_GetBestAreaSpellTargetAndLocation(fRange, fSpread, nLevel, iSaveType, nShape, nFriendlyFire, iDamageSpell, iNecromanticSpell, iDeathImmune, iNegativeImmune, iMindImmune, iMindType)) { case i0: SetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(nSpell),TRUE); DelayCommand(f2,DeleteLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(nSpell))); return FALSE; break; case i1: // ActionCastSpellAtLocation(nSpell,GlobalAOETargetLocation); if(AI_ActionCastSpellAtLocation(nSpell, nTalent, GlobalAOETargetLocation, (!nLevel)?i0:nLevel+i10, iItemTalentValue)) return TRUE; break; case i2: // ActionCastSpellAtObject(nSpell,GlobalAOETargetObject); if(AI_ActionCastSpell(nSpell, nTalent, GlobalAOETargetObject, (!nLevel)?i0:nLevel+i10, FALSE, iItemTalentValue)) return TRUE; break; } } } return FALSE; } // attempt AOE spell wrapper + object version. int AI_AttemptCastAOESpellToObject(int nSpell, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE) { // false in last checking loop, don't check again. if(GetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(nSpell))) return FALSE; // 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 || !nLevel || (nLevel && GlobalSpellAbilityModifier >= nLevel+i10)) && !GlobalSilenceSoItems && GetHasSpell(nSpell)) || iItemTalentValue == nSpell) { switch(AI_GetBestAreaSpellTargetAndLocation(fRange, fSpread, nLevel, iSaveType, nShape, nFriendlyFire, iDamageSpell, iNecromanticSpell, iDeathImmune, iNegativeImmune, iMindImmune, iMindType)) { case i0: SetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(nSpell),TRUE); DelayCommand(f2,DeleteLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(nSpell))); return FALSE; break; case i1: case i2: // ActionCastSpellAtObject(nSpell,GlobalAOETargetObject); if(AI_ActionCastSpell(nSpell, nTalent, GlobalAOETargetObject, (!nLevel)?i0:nLevel+i10, FALSE, iItemTalentValue)) return TRUE; break; } } return FALSE; } // attempt AOE spell wrapper, random version. int AI_AttemptCastAOESpellRandom(int nSpell, int nRandom, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE) { if(d100()<=nRandom) { return AI_AttemptCastAOESpell(nSpell, nTalent, iItemTalentValue, fRange, fSpread, nLevel, iSaveType, nShape, nFriendlyFire, iDamageSpell, iNecromanticSpell, iDeathImmune, iNegativeImmune, iMindImmune, iMindType); } return FALSE; } // attempt AOE spell wrapper, random version. + object only version. int AI_AttemptCastAOESpellToObjectRandom(int nSpell, int nRandom, int nTalent = i0, int iItemTalentValue = iM1, float fRange = fLongRange, float fSpread = RADIUS_SIZE_COLOSSAL, int nLevel = FALSE, int iSaveType = FALSE,int nShape = SHAPE_SPHERE, int nFriendlyFire = FALSE,int iDamageSpell = FALSE, int iNecromanticSpell = FALSE, int iDeathImmune = FALSE, int iNegativeImmune = FALSE, int iMindImmune = FALSE, int iMindType = FALSE) { if(d100()<=nRandom) { return AI_AttemptCastAOESpellToObject(nSpell, nTalent, iItemTalentValue, fRange, fSpread, nLevel, iSaveType, nShape, nFriendlyFire, iDamageSpell, iNecromanticSpell, iDeathImmune, iNegativeImmune, iMindImmune, iMindType); } return FALSE; } // attempt instant death spells, or relate ability. int AI_AttemptDeathSpells(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE) { // deadmagic zone? if(GlobalSpellFailure)return FALSE; // if(AI_GetSpellTargetImmunity(GlobalImmunityDeath) || GlobalImmunityDeathPercent>75) // return FALSE; if(GlobalTotalAllies<=i8) { if(GlobalNormalSpellsNoEffectLevel>i5 || d2()==i1) { if(AI_AttemptDeathAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE;; if(AI_AttemptDeathSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } else { if(AI_AttemptDeathSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; if(AI_AttemptDeathAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } } else { if(d100()= 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 && InputRangeMediumValid) { // 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i6,SAVING_THROW_WILL), TRUE, ItemHostAreaDis)) return TRUE; } } if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_WORD_OF_FAITH) || ItemHostAreaDis == SPELL_WORD_OF_FAITH) //modified magic. effect only lower HD creature. && GetLevelByClass(CLASS_TYPE_CLERIC)>=GetHitDice(GlobalSpellTarget)) { GlobalPreventSpellDupleUse=TRUE; // 20M medium range, colossal size, will save to deflect. // 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_AttemptCastAOESpellRandom(SPELL_WORD_OF_FAITH, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_COLOSSAL, i7, SAVING_THROW_ALL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile))return TRUE; GlobalPreventSpellDupleUse=FALSE; } if(InputRangeShortValid) { // 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. if(AI_AttemptCastAOESpellRandom(SPELL_IMPLOSION, i60, SpellHostAreaInd, ItemHostAreaInd, fShortRange, RADIUS_SIZE_MEDIUM, i9, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFire)) 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. // Wail of the Banshee. Level 9 (Mage/Innate). Caster cries out, kills everything that cannot save in area affected. if(AI_AttemptCastAOESpellRandom(SPELL_WAIL_OF_THE_BANSHEE, i60, SpellHostAreaDis, ItemHostAreaDis, fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, TRUE, TRUE)) 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. // Wierd. Level 9 (Wizard/Innate). 2 saves (will/fort) against death. Doesn't kill allies! (Illusion) if(AI_AttemptCastAOESpellRandom(SPELL_WEIRD, i60, SpellHostAreaDis, ItemHostAreaDis, fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_FEAR)) return TRUE; } } break; case i1: // 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 && // GetSpawnInCondition(AI_FLAG_COMBAT_IMPROVED_INSTANT_DEATH_SPELLS, AI_COMBAT_MASTER) && GlobalSeenSpell && InputRangeShortValid && !AI_CompareTimeStopStored(SPELL_DESTRUCTION, SPELL_FINGER_OF_DEATH)) { // Check low saves, IE always fails, no immunities and no mantals. if(//(GlobalSpellTargetFort + i20) <= (GlobalSpellBaseSaveDC + i7) && d100()<=AI_GetPercentageDC(GlobalSpellBaseSaveDC + i7, SAVING_THROW_FORT) && !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; } } if(InputRangeTouchValid) { // Slay living. 60% chance to cast - it is a touch spell. if(GlobalNormalSpellsNoEffectLevel < i5 && !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,AI_GetPercentageDC(GlobalSpellBaseSaveDC + i5,SAVING_THROW_FORT), GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; } } break; case i2: if(iPriority75) // return FALSE; if(GlobalTotalAllies<=i8) { if(GlobalNormalSpellsNoEffectLevel>i5 || d2()==i1) { if(AI_AttemptMindAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE;; if(AI_AttemptMindSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } else { if(AI_AttemptMindSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; if(AI_AttemptMindAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } } else { if(d100() i10 && GlobalSeenSpell && InputRangeTouchValid && !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; } if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_WORD_OF_FAITH) || ItemHostAreaDis == SPELL_WORD_OF_FAITH) //modified magic. effect only lower HD creature. && GetLevelByClass(CLASS_TYPE_CLERIC)>=GetHitDice(GlobalSpellTarget)) { GlobalPreventSpellDupleUse=TRUE; // 20M medium range, colossal size, will save to deflect. // 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_AttemptCastAOESpellRandom(SPELL_WORD_OF_FAITH, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_COLOSSAL, i7, SAVING_THROW_ALL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile))return TRUE; GlobalPreventSpellDupleUse=FALSE; } // 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 && InputRangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityMind) && !AI_GetSpellTargetImmunity(GlobalImmunityDomination) && (GlobalOurHitDice - GlobalAverageEnemyHD) <= i8) { // Will save VS mind spells. if(GlobalNormalSpellsNoEffectLevel < i9 && !AI_SaveImmuneSpellTarget(SAVING_THROW_WILL, i9)) { // Mass charm. sorcerors/bards have better things (20% casting chance) if(!GlobalWeAreSorcerorBard) { // 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_AttemptCastAOESpellRandom(SPELL_MASS_CHARM, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_LARGE, i8, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_CHARM)) return TRUE; // if(AI_ActionCastSpell(SPELL_MASS_CHARM, SpellHostAreaDis, GlobalSpellTarget, i18, TRUE, ItemHostAreaDis)) return TRUE; } } } // 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) { // 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i7,SAVING_THROW_WILL), FALSE, ItemHostRanged)) return TRUE; } } // AOE targets. Most...but not all...are short-range spells. if(InputRangeMediumValid) // 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i9,SAVING_THROW_REFLEX), OBJECT_SELF, i19, FALSE, ItemHostAreaDis)) return TRUE; } } break; case i1: // Confusion - confusion! Decent especially against PC's. if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_CONFUSION) || ItemHostAreaDis == SPELL_CONFUSION)) { if(AI_AttemptCastAOESpellRandom(SPELL_CONFUSION, i80, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_LARGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_CONFUSED)) return TRUE; } // Fear - fear! if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_FEAR) || ItemHostAreaDis == SPELL_FEAR)) { // Shpere, no friends affected, we cast even if saves VS will. :-) if(AI_AttemptCastAOESpellRandom(SPELL_FEAR, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_LARGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_CONFUSED)) return TRUE; } // [War Cry] Will save or fear (like a howl) and all allies get +2 attack/damage if(GlobalNormalSpellsNoEffectLevel < i4) { // 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, FALSE, ItemHostAreaDis)) 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 && InputRangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityDomination)) // (GlobalOurHitDice - GlobalAverageEnemyHD) <= i8) { // Will save VS mind spells. if(GlobalNormalSpellsNoEffectLevel < i9 && !AI_GetSpellTargetImmunity(GlobalImmunityMind) && !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) { // Hold monster - Paralyze's an enemy monster (any race) // Decent enough, if not stunned already - 60% if(InputRangeMediumValid && !AI_CompareTimeStopStored(SPELL_HOLD_MONSTER) && !AI_GetSpellTargetImmunity(GlobalImmunityMind) && !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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i5,SAVING_THROW_WILL), GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; } } // Dominate spells if(!GlobalInTimeStop && GlobalSeenSpell && InputRangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityMind) && !AI_GetSpellTargetImmunity(GlobalImmunityDomination)) // (GlobalOurHitDice - GlobalAverageEnemyHD) <= i8) { 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i5,SAVING_THROW_WILL), FALSE, ItemHostRanged)) return TRUE; } } } // All single target spells, even poison and so on. if(GlobalSeenSpell && GlobalNormalSpellsNoEffectLevel < i4) { // Charm Monster. Charms any monster. Will save, mind based. if(InputRangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityMind) && !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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i4,SAVING_THROW_WILL), GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; } } // Hammer of the Gods. Divine damage is good, as well as save VS daze. if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_HAMMER_OF_THE_GODS) || ItemHostAreaDis == SPELL_HAMMER_OF_THE_GODS)) { // Hammer of the Gods. Level 4 (Cleric). Divine damage d8(half caster level) to 5d8. Can Daze. Will save. // Shpere, no friends affected, we cast even if saves VS will. :-) if(AI_AttemptCastAOESpellRandom(SPELL_HAMMER_OF_THE_GODS, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_HUGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i3 && InputRangeMediumValid) { // 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i3,SAVING_THROW_WILL), GlobalSpellTarget, i13, FALSE, ItemHostRanged)) return TRUE; } } break; case i2: // if(iPriorityi5 || d2()==i1) { if(AI_AttemptEffectAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE;; if(AI_AttemptEffectSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } else { if(AI_AttemptEffectSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; if(AI_AttemptEffectAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } } else { if(d100()=GetHitDice(GlobalSpellTarget)) { GlobalPreventSpellDupleUse=TRUE; // 20M medium range, colossal size, will save to deflect. // 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_AttemptCastAOESpellRandom(SPELL_WORD_OF_FAITH, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_COLOSSAL, i7, SAVING_THROW_ALL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile))return TRUE; GlobalPreventSpellDupleUse=FALSE; } // 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. // why? blind is good effect. if(InputRangeLongValid && (GetHasSpell(SPELL_SUNBURST) || ItemHostAreaDis == SPELL_SUNBURST)) { // 40M range, lagre (5.0 across) area. Relfex saves. // 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_AttemptCastAOESpellRandom(SPELL_SUNBURST, i60, SpellHostAreaDis, ItemHostAreaDis, fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) return TRUE; } if(InputRangeLongValid && (GetHasSpell(SPELL_SUNBEAM) || ItemHostAreaDis == SPELL_SUNBEAM)) { // 40M range, lagre (5.0 across) area. Relfex saves. // Sunbeam. Level 8 (Cleric/Druid) 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size if(AI_AttemptCastAOESpellRandom(SPELL_SUNBEAM, i60, SpellHostAreaDis, ItemHostAreaDis, fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // Great Thunderclap is OK, but doesn't really do too much. If anything, the // 3 saves are cool :-P if(InputRangeMediumValid && !AI_CompareTimeStopStored(SPELL_GREAT_THUNDERCLAP) && (GetHasSpell(SPELL_GREAT_THUNDERCLAP) || ItemHostAreaInd == SPELL_GREAT_THUNDERCLAP)) { GlobalPreventSpellDupleUse=TRUE; // 20M medium range, hits a gargantuan area. // - We ignore saves for this. // - Doesn't actually hit ourselves. Won't bother checking this though. if(AI_AttemptCastAOESpellRandom(SPELL_GREAT_THUNDERCLAP, i60, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_GARGANTUAN, i7, SAVING_THROW_ALL, SHAPE_SPHERE, GlobalFriendlyFire)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } // bigby... if(InputRangeLongValid && !AI_GetIsBigbyOn(GlobalSpellTarget)) { if(GlobalNormalSpellsNoEffectLevel < i9 && !AI_CompareTimeStopStored(SPELL_BIGBYS_CRUSHING_HAND,SPELL_BIGBYS_CLENCHED_FIST,SPELL_BIGBYS_GRASPING_HAND,SPELL_BIGBYS_FORCEFUL_HAND)) { //modified magic. ignore stun. // Bigby's Crushing Hand. Level 9 (Mage). 2d6 + 12 damage/round. if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CRUSHING_HAND, SpellHostRanged, i80, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i8 && !AI_CompareTimeStopStored(SPELL_BIGBYS_CRUSHING_HAND,SPELL_BIGBYS_CLENCHED_FIST,SPELL_BIGBYS_GRASPING_HAND,SPELL_BIGBYS_FORCEFUL_HAND)) { //modified magic. // 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; } if(GlobalNormalSpellsNoEffectLevel < i7 && !AI_CompareTimeStopStored(SPELL_BIGBYS_CRUSHING_HAND,SPELL_BIGBYS_CLENCHED_FIST,SPELL_BIGBYS_GRASPING_HAND,SPELL_BIGBYS_FORCEFUL_HAND)) //modified magic. ignore stun. // !AI_GetSpellTargetImmunity(GlobalImmunityStun)) { // Bigby's Grasping Hand. Level 7 (Mage) Hold target if sucessful grapple if(AI_ActionCastSpellRandom(SPELL_BIGBYS_GRASPING_HAND, SpellHostRanged, i60, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i6 && !AI_CompareTimeStopStored(SPELL_BIGBYS_CRUSHING_HAND,SPELL_BIGBYS_CLENCHED_FIST,SPELL_BIGBYS_GRASPING_HAND,SPELL_BIGBYS_FORCEFUL_HAND)) //modified magic. ignore stun. // !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, i60, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; } } // Dirge. // - Cast always, basically. Boring :-) but its an OK spell if(!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; } if(GlobalNormalSpellsNoEffectLevel < i9) { if(InputRangeShortValid && 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! :-) //modified magic. no save, ranged touch attack if(AI_ActionCastSpellRandom(SPELL_ENERGY_DRAIN, SpellHostRanged, i40, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; } } if(InputRangeMediumValid && !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 // Prismatic Spray. Level 7 (Mage) Random damage/effects. Chance of doing double amount of effects. if(AI_AttemptCastAOESpellRandom(SPELL_PRISMATIC_SPRAY, i60, SpellHostAreaInd, ItemHostAreaInd, f11, f11, i7, FALSE, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; // if(AI_ActionCastSpellRandom(SPELL_PRISMATIC_SPRAY, SpellHostAreaInd, i40, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i6) { // Flesh to Stone is a good spell - petrify attack. // 70% chance to cast if we have it, and not immune to the save. if(InputRangeMediumValid && !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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i6,SAVING_THROW_FORT), GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; } } break; case i1: // Slow is a good AOE - hits enemies, and slows them (Will save) if(InputRangeShortValid && (GetHasSpell(SPELL_SLOW) || ItemHostAreaDis == SPELL_SLOW)) { GlobalPreventSpellDupleUse=TRUE; // Slow - doesn't hit allies. Colossal range. // Slow. Level 3 (Mage/bard) -50% speed, -1 Attack, -4 AC (I think) to those in an AOE - doesn't hit allies. if(AI_AttemptCastAOESpellRandom(SPELL_SLOW, i60, SpellHostAreaDis, ItemHostAreaDis, fShortRange, RADIUS_SIZE_COLOSSAL, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } // 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(InputRangeLongValid && 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i2,SAVING_THROW_WILL), GlobalSpellTarget, i12)) return TRUE; } // Feeblemind is Good if in medium range, and is a mage :-) if(InputRangeMediumValid && 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; } // 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(GlobalNormalSpellsNoEffectLevel < i5 && InputRangeLongValid && !AI_CompareTimeStopStored(SPELL_BIGBYS_INTERPOSING_HAND) && !AI_GetIsBigbyOn(GlobalSpellTarget) && GetBaseAttackBonus(GlobalSpellTarget) >= GlobalOurHitDice/i2) { // Bigby's Interposing Hand. Level 5 (Mage) No save, only SR. -10 attack rolls for target if(AI_ActionCastSpell(SPELL_BIGBYS_INTERPOSING_HAND, SpellHostRanged, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; } // Battletide is not bad. Level 5 (Cleric) // - Oddly classed under SpellHostAreaDis if(!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, OBJECT_SELF, i15, FALSE, ItemHostAreaDis)) return TRUE; } break; case i2: // if(iPriority= i2) { GlobalPreventSpellDupleUse=TRUE; // Allies are hit, will save to be immune. if(AI_AttemptCastAOESpellRandom(SPELL_GUST_OF_WIND, i60, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFire)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } if(InputRangeTouchValid && !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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i3,SAVING_THROW_FORT), GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; } } // Spike Growth. Alright AOE - 30% speed decrease for 24HRS is the best bit. if(InputRangeMediumValid && (GetHasSpell(SPELL_SPIKE_GROWTH) || ItemHostAreaInd == SPELL_SPIKE_GROWTH)) { GlobalPreventSpellDupleUse=TRUE; // Allies are hit, will save to be immune. // Spike Growth. Level 3 (Druid) AOE - 30% speed decrease for 24HRS + 1d4 piercing damage/round. if(AI_AttemptCastAOESpellRandom(SPELL_SPIKE_GROWTH, i60, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_LARGE, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } // Cloud of Bewilderment is an alright AOE - stun and blindness :-) // Note - Only casts if the target is not immune to stun nor blindness! if(InputRangeMediumValid && !AI_GetSpellTargetImmunity(GlobalImmunityBlindDeaf) && !AI_GetSpellTargetImmunity(GlobalImmunityStun) && (GetHasSpell(SPELL_CLOUD_OF_BEWILDERMENT) || ItemHostAreaInd == SPELL_CLOUD_OF_BEWILDERMENT)) { GlobalPreventSpellDupleUse=TRUE; // 5M radius (large) AOE, short ranged, fort based save. Reaction friendly. // 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_AttemptCastAOESpellRandom(SPELL_CLOUD_OF_BEWILDERMENT, i70, SpellHostAreaInd, ItemHostAreaInd, fShortRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_BLINDNESS)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } // Web - sticky stuff. Illusion version first :-D if(InputRangeMediumValid && (GetHasSpell(SPELL_WEB) || ItemHostAreaInd == SPELL_WEB || GetHasSpell(SPELL_GREATER_SHADOW_CONJURATION_WEB) || ItemHostAreaInd == SPELL_GREATER_SHADOW_CONJURATION_WEB)) { // false in last checking loop, don't check again. if(!GetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_GREATER_SHADOW_CONJURATION_WEB))) { object oAOE; // large AOE, medium ranged, reflex based save. Reaction friendly. oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire); // 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; // Web. Level 2 (Mage) Entangles on reflex sve. if(AI_ActionCastSpellRandom(SPELL_WEB, SpellHostAreaInd, i60, oAOE, i12, FALSE, ItemHostAreaInd)) return TRUE; } else { SetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_GREATER_SHADOW_CONJURATION_WEB),TRUE); DelayCommand(f2,DeleteLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_GREATER_SHADOW_CONJURATION_WEB))); } } } if(InputRangeMediumValid) { // 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i2,SAVING_THROW_FORT), GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; } } break; case i4: // 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, FALSE, ItemHostAreaDis)) return TRUE; } if(InputRangeMediumValid) { // Tasha's Hideous Laughter is knockdown - not too bad. // - We don't bother with the +4/-4. if(AI_GetIsTargetCastable(GlobalSpellTarget)) { // 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i2,SAVING_THROW_WILL), GlobalSpellTarget, i12, FALSE, ItemHostRanged)) return TRUE; } } if(InputRangeShortValid) { // Horzilkaul's Boom. Level 1 (Mage) 1d4/2 caster levels (to 5d4) sonic damage, + Will VS deafness. if(AI_ActionCastSpellRandom(SPELL_HORIZIKAULS_BOOM, SpellHostRanged, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i2,SAVING_THROW_WILL), GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; } break; case i5: // if(iPriority= 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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i1,SAVING_THROW_FORT), GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; } } if(InputRangeMediumValid) { // Doom does some...stuff. The damage -2 is best. Will save negates. if(!GetHasSpellEffect(SPELL_DOOM, GlobalSpellTarget) && !AI_GetSpellTargetImmunity(GlobalImmunityCurse)) // !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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i1,SAVING_THROW_WILL), GlobalSpellTarget, i11, FALSE, ItemHostRanged)) return TRUE; } } // Color Spray - Does at least blind higher levels. Will save, however, negates if(InputRangeShortValid && (GetHasSpell(SPELL_COLOR_SPRAY) || ItemHostAreaInd == SPELL_COLOR_SPRAY)) { GlobalPreventSpellDupleUse=TRUE; // Cone AOE, short ranged, reflex based save. Reaction friendly. // Color Spray. Level 1 (Mage) - effect based on HD - Sleep Stun and Blindness if fail will save if(AI_AttemptCastAOESpellRandom(SPELL_COLOR_SPRAY, i60, SpellHostAreaInd, ItemHostAreaInd, fShortRange, f10, i1, SAVING_THROW_WILL, SHAPE_SPELLCONE, GlobalFriendlyFire)) return TRUE; // if(AI_ActionCastSpellRandom(SPELL_COLOR_SPRAY, SpellHostAreaInd, i50, GlobalSpellTarget, i11, FALSE, ItemHostAreaInd)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } // Bane is...alright, I guess. Will save, however. It doesn't affect allies though! if(InputRangeLongValid && (GetHasSpell(SPELL_BANE) || ItemEnhSinTar == SPELL_BANE)) { GlobalPreventSpellDupleUse=TRUE; // collosal AOE, long ranged, will based save. No allies if(AI_AttemptCastAOESpellRandom(SPELL_BANE, i60, SpellEnhSinTar, ItemEnhSinTar, fMediumRange, RADIUS_SIZE_COLOSSAL, i1, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile)) return TRUE; GlobalPreventSpellDupleUse=FALSE; } break; } return FALSE; } // effect ability int AI_AttemptEffectAbility(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE) { object oAOE; switch(iPriority) { case i1: // Do not check spell resistance (GlobalNormalSpellsNoEffectLevel) here. // We only use pulses 80% of the time. if(InputRangeTouchValid && !GlobalInTimeStop && GlobalSpellTargetRange < fTouchRange) { // All innate, so no matter about talents really. if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel)) { // Pulse_Level_Drain - 1 Negative Level, if fail Fort Save, DC: 10 + HD. if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_LEVEL_DRAIN, SpellHostAreaInd, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; } } if(GlobalSeenSpell && InputRangeMediumValid && d10() <= i8) { // 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. if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel) && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_LEVEL_DRAIN, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(!AI_GetSpellTargetImmunity(GlobalImmunitySlow) && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_SLOW, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; } break; case i2: // Do not check spell resistance (GlobalNormalSpellsNoEffectLevel) here. // We only use pulses 80% of the time. if(InputRangeTouchValid && !GlobalInTimeStop && GlobalSpellTargetRange < fTouchRange) { // All innate, so no matter about talents really. if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel)) { // Pulse_Ability_Drain_Strength - Large. -(HD/5) STR, if fail fort save VS: 10 + HD. if(GetAbilityScore(GlobalSpellTarget, ABILITY_STRENGTH) >= i10) if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_ABILITY_DRAIN_STRENGTH, SpellHostAreaDis, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; // Pulse_Ability_Drain_Dexterity - Large. -(HD/5) DEX, if fail fort save VS: 10 + HD. if(GetAbilityScore(GlobalSpellTarget, ABILITY_DEXTERITY) >= i10) if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_ABILITY_DRAIN_DEXTERITY, SpellHostAreaDis, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; // Pulse_Ability_Drain_Constitution - Large. -(HD/5) CON, if fail fort save VS: 10 + HD. if(GetAbilityScore(GlobalSpellTarget, ABILITY_CONSTITUTION) >= i10) if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_ABILITY_DRAIN_CONSTITUTION, SpellHostAreaDis, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; // Pulse_Ability_Drain_Intelligence - Large. -(HD/5) INT, if fail fort save VS: 10 + HD. if(GetAbilityScore(GlobalSpellTarget, ABILITY_INTELLIGENCE) >= i10) if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_ABILITY_DRAIN_INTELLIGENCE, SpellHostAreaDis, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; // Pulse_Ability_Drain_Wisdom - Large. -(HD/5) WIS, if fail fort save VS: 10 + HD. if(GetAbilityScore(GlobalSpellTarget, ABILITY_WISDOM) >= i10) if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_ABILITY_DRAIN_WISDOM, SpellHostAreaDis, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; // Pulse_Ability_Drain_Charisma - Large. -(HD/5) CHA, if fail fort save VS: 10 + HD. if(GetAbilityScore(GlobalSpellTarget, ABILITY_CHARISMA) >= i10) if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_ABILITY_DRAIN_CHARISMA, SpellHostAreaDis, AI_GetPercentageDC(i10+GlobalOurHitDice,SAVING_THROW_FORT))) return TRUE; } // Pulse_Disease - Varying Save. See diseases.2da, and it is based on the if(!AI_GetSpellTargetImmunity(GlobalImmunityDisease) && AI_ActionCastSpellRandom(SPELLABILITY_PULSE_DISEASE, SpellHostAreaDis, i30)) return TRUE; // Pulse_Poison - Varying Poison instantly applied. Check nw_s1_pulspois.nss, and poison.2da files. if(!AI_GetSpellTargetImmunity(GlobalImmunityPoison) && AI_ActionCastSpellRandom(SPELLABILITY_PULSE_POISON, SpellHostAreaDis, i30)) return TRUE; } if(GlobalSeenSpell && InputRangeMediumValid && d10() <= i8) { // 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. if(!GetHasSpellEffect(SPELLABILITY_BOLT_KNOCKDOWN) && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_KNOCKDOWN, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(!AI_GetSpellTargetImmunity(GlobalImmunityEntangle) && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_WEB, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(!AI_GetSpellTargetImmunity(GlobalImmunityDisease) && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_DISEASE, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(!AI_GetSpellTargetImmunity(GlobalImmunityPoison) && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_POISON, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel)) { // 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; } } // Bolts 80% chance. Not too good, but oh well. if(GlobalSeenSpell && InputRangeTouchValid && d10() <= i8) { // Shadow attack if(!GetHasSpellEffect(AI_SPELLABILITY_SHADOW_ATTACK, GlobalSpellTarget) && !AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel)) { if(AI_ActionCastSpellRandom(AI_SPELLABILITY_SHADOW_ATTACK, SpellHostTouch, i30, GlobalSpellTarget)) return TRUE; } } // Medium range spells. if((InputRangeMediumValid || InputRangeShortValid) && GlobalSeenSpell) { // Other one 2 - this is magical damage, little powerful... need high intelligence? if(AI_ActionCastSpellRandom(AI_SPELLABILITY_PSIONIC_MASS_CONCUSSION, SpellHostAreaDis, AI_GetPercentageDC(i15+GlobalOurHitDice/i2,SAVING_THROW_REFLEX),GlobalSpellTarget)) return TRUE; } break; case i3: if(InputRangeTouchValid && !GlobalInTimeStop && GlobalSpellTargetRange < fTouchRange) { // All innate, so no matter about talents really. if(!AI_GetSpellTargetImmunity(GlobalImmunityNegativeLevel)) { // PULSE_SPORES - DISEASE_SOLDIER_SHAKES, dc24. if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_SPORES, SpellHostAreaInd, AI_GetPercentageDC(i24,SAVING_THROW_FORT))) return TRUE; } } if(InputRangeShortValid) // No GlobalNormalSpellsNoEffectLevel check { // Petrify - SoU. if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify)) { if(AI_ActionCastSpellRandom(SPELLABILITY_BREATH_PETRIFY, FALSE, AI_GetPercentageDC(i17,SAVING_THROW_FORT), GlobalSpellTarget, FALSE, FALSE)) return TRUE; } } if(InputRangeShortValid) // 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! dc 17. if(AI_ActionCastSpellRandom(SPELLABILITY_GOLEM_BREATH_GAS, FALSE, AI_GetPercentageDC(i17,SAVING_THROW_FORT), GlobalSpellTarget, FALSE, FALSE)) return TRUE; } } // Medium range spells. if((InputRangeMediumValid || InputRangeShortValid) && GlobalSeenSpell && d10() <= i8) { if(AI_ActionCastSpellRandom(AI_SPELLABILITY_BEHOLDER_ALLRAYS, SpellHostAreaInd, AI_GetPercentageDC(i15,SAVING_THROW_REFLEX),GlobalSpellTarget)) return TRUE; } // Bolts 80% chance. Not too good, but oh well. if(GlobalSeenSpell && InputRangeMediumValid) { // Petrify last. if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify)) { if(AI_ActionCastSpellRandom(SPELLABILITY_TOUCH_PETRIFY, SpellHostTouch, AI_GetPercentageDC(i15,SAVING_THROW_FORT), GlobalSpellTarget)) return TRUE; } } if(InputRangeShortValid && d10() <= i8) // No GlobalNormalSpellsNoEffectLevel check { // Petrify - SoU. if(!AI_GetSpellTargetImmunity(GlobalImmunityPetrify)) { if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_PETRIFY, FALSE, AI_GetPercentageDC(i13,SAVING_THROW_FORT), GlobalSpellTarget, FALSE, FALSE)) return TRUE; } } break; case i4: if(InputRangeTouchValid && !GlobalInTimeStop && GlobalSpellTargetRange < fTouchRange) { // Pulse_Level_Drain - 1 Negative Level, if fail Fort Save, DC: 10 + HD. if(AI_ActionCastSpellRandom(SPELLABILITY_PULSE_WHIRLWIND, SpellHostAreaInd, AI_GetPercentageDC(i14,SAVING_THROW_REFLEX))) return TRUE; } if(InputRangeShortValid && !AI_GetSpellTargetImmunity(GlobalImmunityCurse)) // No GlobalNormalSpellsNoEffectLevel check { if(AI_ActionCastSpellRandom(SPELLABILITY_GAZE_DOOM, FALSE, AI_GetPercentageDC(i10+GlobalOurHitDice/i2,SAVING_THROW_WILL), GlobalSpellTarget, FALSE, FALSE)) return TRUE; } if(InputRangeShortValid && GlobalSeenSpell && !AI_GetSpellTargetImmunity(GlobalImmunityCurse)) { // 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, AI_GetPercentageDC(i10+GlobalOurHitDice/i4,SAVING_THROW_WILL))) return TRUE; } } // Evil Blight. This is an AOE curse, but note that we cannot check // if an AOE already has it. if(InputRangeMediumValid && !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, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i5,SAVING_THROW_WILL), GlobalSpellTarget, i16, FALSE, ItemHostTouch)) return TRUE; } break; case i5: // Monster cones - these ignore the GlobalNormalSpellsNoEffectLevel toggle. if(InputRangeShortValid) { // 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. if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_DISEASE, i40, SpellHostAreaInd, iM1, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire)) return TRUE; if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_POISON, i40, SpellHostAreaInd, iM1, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire)) return TRUE; // Cones // Uses the AOE object for cone of cold. // These are the "Cones". Appropriate to put it here, don'tca think? // if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_DISEASE, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; // if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_POISON, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; } // powerful ability damage effect, but too low dc. single target? if(AI_ActionCastSpellRandom(SPELLABILITY_SEAHAG_EVILEYE, SpellHostAreaInd, AI_GetPercentageDC(i11,SAVING_THROW_FORT), GlobalSpellTarget)) return TRUE; // Web - sticky stuff. Illusion version first :-D if(InputRangeMediumValid && (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. // Beblith version if(AI_AttemptCastAOESpellRandom(AI_SPELLABILITY_BEBELITH_WEB, i60, SpellHostRanged, iM1, fMediumRange, RADIUS_SIZE_LARGE, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire)) return TRUE; } break; } return FALSE; } // attempt damage dealing spells, or relate ability. int AI_AttemptDamageSpells(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE) { // deadmagic zone? if(GlobalSpellFailure)return FALSE; if(GlobalTotalAllies<=i8) { if(GlobalNormalSpellsNoEffectLevel>i5 || d2()==i1) { if(AI_AttemptDamageAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE;; if(AI_AttemptDamageSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } else { if(AI_AttemptDamageSpell(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; if(AI_AttemptDamageAbility(iPriority, InputRangeLongValid, InputRangeMediumValid , InputRangeShortValid, InputRangeTouchValid))return TRUE; } } else { if(d100()= i30) { // Drown. level 6 (Druid) Up to 90% of current HP damage done. Fort save for none. if(AI_ActionCastSpellRandom(SPELL_DROWN, SpellHostRanged, AI_GetPercentageDC(GlobalSpellBaseSaveDC+i6,SAVING_THROW_FORT), GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; } } // Horrid Wilting // Never affects allies. Fortitude - necromancy spell too. Lots of damage. if(InputRangeMediumValid && (GetHasSpell(SPELL_HORRID_WILTING) || ItemHostAreaDis == SPELL_HORRID_WILTING)) { // Won't cast if got lots of undead. 20M range, huge radius. // Horrid Wilting. Level 8 (Mage). d8(CasterLevel) in negative energy. Fort for half. Necromancy. if(AI_AttemptCastAOESpellRandom(SPELL_HORRID_WILTING, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_HUGE, i8, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFire, TRUE, TRUE)) return TRUE; } // Fire storm - cast on self with a 10M range around caster (circle). 60% chance. if(InputRangeShortValid && 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, FALSE, ItemHostAreaDis)) return TRUE; } // Delayed Fireball Blast. Big fireball. Lots of fire damage - reflex saves. if(InputRangeMediumValid && !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) // Delayed Fireball Blast. Level 7 (Mage) Up to 20d6 fire reflex damage. Can be set up as a trap (heh, nah!) if(AI_AttemptCastAOESpellToObjectRandom(SPELL_DELAYED_BLAST_FIREBALL, i70, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_HUGE, i7, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; /* // 20M medium range, blast is RADIUS_SIZE_HUGE, lower then Fireball, but more deadly (both save + damage) object oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_HUGE, i7, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, TRUE); // 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, FALSE, ItemHostAreaInd)) 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(InputRangeLongValid && !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_AttemptCastAOESpellRandom(SPELL_CHAIN_LIGHTNING, i60, SpellHostAreaDis, ItemHostAreaDis, fLongRange, RADIUS_SIZE_COLOSSAL, i6, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) return TRUE; // if(AI_ActionCastSpellRandom(SPELL_CHAIN_LIGHTNING, SpellHostAreaDis, i50, GlobalSpellTarget, i16, FALSE, ItemHostAreaDis)) return TRUE; } // Firebrand - good, because it hits only enemies (up to 15! thats plenty) // and 1d6 damage each. Mainly, it doesn't hit allies :-D if(InputRangeMediumValid && !AI_CompareTimeStopStored(SPELL_FIREBRAND) && (GetHasSpell(SPELL_FIREBRAND) || ItemHostRanged == SPELL_FIREBRAND)) { // 20M medium range, colossal area. Reflex save - doesn't hit allies too. // Firebrand Level 5 (Mage) Missile storm - hits enemies up to caster level (max 15) for 1d6 fire reflex damage. if(AI_AttemptCastAOESpellRandom(SPELL_FIREBRAND, i60, SpellHostRanged, ItemHostRanged, fMediumRange, RADIUS_SIZE_COLOSSAL, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) return TRUE; } // Earthquake. No SR, long range, affects anyone except caster. if(InputRangeShortValid && (GetHasSpell(SPELL_EARTHQUAKE) || ItemHostAreaInd == SPELL_EARTHQUAKE)) { // 40M range, collosal area. Relfex save. // Earthquake] - 1d6(caster level) (to 10d6) in damage to AOE, except caster. if(AI_ActionCastSpellRandom(SPELL_EARTHQUAKE, SpellHostAreaInd, i40, OBJECT_SELF, i18, FALSE, ItemHostAreaInd)) return TRUE; } if(InputRangeLongValid && GlobalNormalSpellsNoEffectLevel < i6) { // need more check... worst area? // 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, FALSE, ItemHostRanged)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i3 && InputRangeLongValid && !GlobalSpellClassRDD) { // Flame arrow - not THE best spell, but well worth it. Also it is long range. // 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; } // Then harm/heal. (Needs 20 HP, and be challenging to us). if(//(GlobalAverageEnemyHD >= (GlobalOurHitDice - i5)) && GlobalSpellTargetCurrentHitPoints > i20 && InputRangeTouchValid && !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 && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) { // If we are undead, we make sure we leave at least 1 for our own healing. - cant't target self. if(GlobalNormalSpellsNoEffectLevel < i6 && GetHasSpell(SPELL_HARM)) { // 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; } } } // 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. // modified magic.... if(InputRangeMediumValid && !GlobalInTimeStop && (GetHasSpell(SPELL_CREEPING_DOOM) || ItemHostAreaDis == SPELL_CREEPING_DOOM)) { // 20M medium range, we'll say a huge size. No save can stop all damage ;-) // Creeping Doom. Level 7 (Druid) Until 1000 damage, d6 + d6/round stayed in damage in an AOE. if(AI_AttemptCastAOESpellRandom(SPELL_CREEPING_DOOM, i50, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_HUGE, i7, FALSE, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // bigby... if(InputRangeLongValid && !AI_GetIsBigbyOn(GlobalSpellTarget)) { //modified magic. ignore stun......etc // Bigby's Crushing Hand. Level 9 (Mage). 2d6 + 12 damage/round. if(GlobalNormalSpellsNoEffectLevel < i9 && !AI_CompareTimeStopStored(SPELL_BIGBYS_CRUSHING_HAND,SPELL_BIGBYS_CLENCHED_FIST,SPELL_BIGBYS_GRASPING_HAND,SPELL_BIGBYS_FORCEFUL_HAND)) { if(AI_ActionCastSpellRandom(SPELL_BIGBYS_CRUSHING_HAND, SpellHostRanged, i60, GlobalSpellTarget, i19, FALSE, ItemHostRanged)) return TRUE; } //modified magic. ignore stun.......etc if(GlobalNormalSpellsNoEffectLevel < i8 && !AI_CompareTimeStopStored(SPELL_BIGBYS_CRUSHING_HAND,SPELL_BIGBYS_CLENCHED_FIST,SPELL_BIGBYS_GRASPING_HAND,SPELL_BIGBYS_FORCEFUL_HAND)) { // 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, i50, GlobalSpellTarget, i18, FALSE, ItemHostRanged)) 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 && InputRangeMediumValid && !AI_CompareTimeStopStored(SPELL_BLADE_BARRIER) && (GetHasSpell(SPELL_BLADE_BARRIER) || SpellHostAreaInd == SPELL_BLADE_BARRIER)) { if(AI_AttemptCastAOESpellToObjectRandom(SPELL_BLADE_BARRIER, i60, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_LARGE, i6, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; /* // 20M medium range, large area we'll say. Reflex save object oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_LARGE, i6, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, TRUE); // 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, FALSE, ItemHostAreaInd)) return TRUE; }*/ } break; case i1: // Ice storm. plenty of damage :-) if(InputRangeLongValid && (GetHasSpell(SPELL_ICE_STORM) || ItemHostAreaInd == SPELL_ICE_STORM)) { // Shpere, huge and no save :-) - long range too. // Ice Storm. Level 4 (Mage) 5 (Druid) 6 (Bard) - 2d6 (Blud) + 3d6 / 3 caster levels (cold) damage. No save! if(AI_AttemptCastAOESpellRandom(SPELL_ICE_STORM, i50, SpellHostAreaInd, ItemHostAreaInd, fLongRange, RADIUS_SIZE_HUGE, i4, FALSE, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // (Shades) Cone of Cold. Shades = Never hits allies (Still same saves ETC). if(SpellHostAreaInd && InputRangeShortValid && (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; }*/ if(AI_ActionCastSubSpell(SPELL_SHADES_CONE_OF_COLD, SpellHostAreaInd, GlobalSpellTarget, i17, TRUE, ItemHostAreaInd)) return TRUE; if(AI_ActionCastSpellRandom(SPELL_CONE_OF_COLD, SpellHostAreaInd, i50, GlobalSpellTarget, i15, FALSE, ItemHostAreaInd)) return TRUE; } // Flame Strike - Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. if(SpellHostAreaInd && InputRangeMediumValid && (GetHasSpell(SPELL_FLAME_STRIKE) || ItemHostAreaInd == SPELL_FLAME_STRIKE)) { // Small-distance, cone-based spell. Reflex save // Flame Strike. Level 5 (Cleric) Up to 15d6 in Fire + Divine damage. Reflex based. Medium area. if(AI_AttemptCastAOESpellRandom(SPELL_FLAME_STRIKE, i50, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_MEDIUM, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // Ball lightning - Medium spell, and missile storm. We cast this at the target. if(InputRangeMediumValid && GlobalSeenSpell) // !AI_SaveImmuneSpellTarget(SAVING_THROW_REFLEX, i5)) { // need more code, for worst area selection. // 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, i60, GlobalSpellTarget, i15, FALSE, ItemHostAreaInd)) return TRUE; } // Bombardment. Similar to the above. Long range, relfex save, not affect allies. if(SpellHostAreaInd && InputRangeLongValid && (GetHasSpell(SPELL_BOMBARDMENT) || ItemHostAreaInd == SPELL_BOMBARDMENT)) { // 40M range, collosal area. Relfex save. // Bombardment. Level 8 (Druid) 1d8(caster level) in fire damage. Like Horrid Wilting. Reflex save/long range/collosal if(AI_AttemptCastAOESpellRandom(SPELL_BOMBARDMENT, i60, SpellHostAreaInd, ItemHostAreaInd, fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // Flame Lash is an alright amount of flame damage. if(InputRangeShortValid && !GlobalSpellClassRDD && !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, i60, GlobalSpellTarget, i12, FALSE, ItemHostRanged)) 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(InputRangeLongValid && SpellHostAreaInd && !GlobalInTimeStop && (GetHasSpell(SPELL_ACID_FOG) || SpellHostAreaInd == SPELL_ACID_FOG)) { // 40M spell range, 5M radius (large) fortitude save, but doesn't stop damage. // Acid Fog. Level 6 (Mage) Acid damage in an AOE, including slow in AOE. 1d6/round in fog. if(AI_AttemptCastAOESpellRandom(SPELL_ACID_FOG, i70, SpellHostAreaInd, ItemHostAreaInd, fLongRange, RADIUS_SIZE_LARGE, i6, SAVING_THROW_FORT, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // Cloudkill. Acid damage, and kills level 7s or below. AOE. Quite good persistant damage. if(SpellHostAreaInd && InputRangeLongValid && (GetHasSpell(SPELL_CLOUDKILL) || ItemHostAreaInd == SPELL_CLOUDKILL)) { // No save (fortitude only halfs damage) but large (5M across) AOE - and long range // Cloudkill. Level 5 (Mage) Acid damage, and kills level 7s or below. AOE. Quite good persistant damage. if(AI_AttemptCastAOESpellRandom(SPELL_CLOUDKILL, i70, SpellHostAreaInd, ItemHostAreaInd, fLongRange, RADIUS_SIZE_LARGE, i5, FALSE, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) 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 && InputRangeMediumValid && !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 // Prismatic Spray. Level 7 (Mage) Random damage/effects. Chance of doing double amount of effects. if(AI_AttemptCastAOESpellRandom(SPELL_PRISMATIC_SPRAY, i70, SpellHostAreaInd, ItemHostAreaInd, f11, f11, i7, FALSE, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; // Is it valid? 40% chance of casting. // if(AI_ActionCastSpellRandom(SPELL_PRISMATIC_SPRAY, SpellHostAreaInd, i40, GlobalSpellTarget, i17, FALSE, ItemHostAreaInd)) return TRUE; } if(GlobalSpellTargetRace == CLASS_TYPE_CONSTRUCT) { // Crumble. Level 6 (Druid) - Up to 15d6 damage to a construct. if(AI_ActionCastSpell(SPELL_CRUMBLE, SpellHostRanged, GlobalSpellTarget, i16, FALSE, ItemHostRanged)) return TRUE; } // Call Lightning - Never hits allies, so excelent to cast :-) if(SpellHostAreaDis && InputRangeLongValid && (GetHasSpell(SPELL_CALL_LIGHTNING) || ItemHostAreaDis == SPELL_CALL_LIGHTNING)) { // Huge, long ranged, reflex based save. No enemies hit! // Call Lightning. Level 3 (Druid) Lightning damage - To 10d6 reflex electrical. Never hits allies. Smaller AOE then fireball if(AI_AttemptCastAOESpellRandom(SPELL_CALL_LIGHTNING, i70, SpellHostAreaDis, ItemHostAreaDis, fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i7 && GlobalSeenSpell && InputRangeShortValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && !AI_CompareTimeStopStored(SPELL_DESTRUCTION)) { // 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_ActionCastSpellRandom(SPELL_DESTRUCTION, SpellHostRanged, i70, GlobalSpellTarget, i17)) return TRUE; } break; case i2: if(GlobalNormalSpellsNoEffectLevel < i4 && InputRangeLongValid) { // 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, FALSE, ItemHostRanged)) return TRUE; } // Lightning Bolt. Basically, a fireball in a line. :-) // Requires a target object to hit. if(SpellHostAreaInd && InputRangeMediumValid && (GetHasSpell(SPELL_LIGHTNING_BOLT) || ItemHostAreaInd == SPELL_LIGHTNING_BOLT)) { // Requries an object. Hits allies. 30 spell cylinder range. // 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_AttemptCastAOESpellRandom(SPELL_LIGHTNING_BOLT, i50, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, f30, i3, SAVING_THROW_REFLEX, SHAPE_SPELLCYLINDER, GlobalFriendlyFire, TRUE)) return TRUE; if(AI_ActionCastSpellRandom(SPELL_LIGHTNING_BOLT, SpellHostAreaInd, i60, GlobalSpellTarget, i13, FALSE, ItemHostAreaInd)) return TRUE; } // Fireball is fun fun fun! :-D Shadow version first - doesn't hit allies. if(SpellHostAreaInd && InputRangeLongValid && (GetHasSpell(SPELL_SHADES_FIREBALL) || ItemHostAreaInd == SPELL_SHADES_FIREBALL)) { // false in last checking loop, don't check again. if(!GetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_SHADES_FIREBALL))) { // Huge, long ranged, reflex based save. Shades == No enemies hit. object oAOE = AI_GetBestAreaSpellTarget(fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, TRUE); // 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; } else { SetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_SHADES_FIREBALL),TRUE); DelayCommand(f2,DeleteLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_SHADES_FIREBALL))); } } } // Real fireball is hot! (Ban pun there...) // Scintillating Sphere is also fire-ball like, but electrical damage *shrugs* if(SpellHostAreaInd && InputRangeLongValid && (GetHasSpell(SPELL_FIREBALL) || ItemHostAreaInd == SPELL_FIREBALL || GetHasSpell(SPELL_SCINTILLATING_SPHERE) || ItemHostAreaInd == SPELL_SCINTILLATING_SPHERE)) { // Huge, long ranged, reflex based save. Normal = reaction friendly. // 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_AttemptCastAOESpellRandom(SPELL_FIREBALL, i70, SpellHostAreaInd, ItemHostAreaInd, fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE))return TRUE; // Scintillating Sphere. Level 3 (Mage) Explosion of up to 10d6 Damage (Electical) - An electiric fireball (reflex save) if(AI_AttemptCastAOESpellRandom(SPELL_SCINTILLATING_SPHERE, i70, SpellHostAreaInd, ItemHostAreaInd, fLongRange, RADIUS_SIZE_HUGE, i3, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE))return TRUE; } // Mestils acid breath is on the standard 10d6 max damage. Acid + cone if(SpellHostAreaInd && InputRangeShortValid && (GetHasSpell(SPELL_MESTILS_ACID_BREATH) || ItemHostAreaDis == SPELL_MESTILS_ACID_BREATH)) { // Short, cone based. // Mestals Acid Breath. Level 3 (Mage) A cone of up to 10d6 (acid) damage. Reflex save. if(AI_AttemptCastAOESpellRandom(SPELL_MESTILS_ACID_BREATH, i60, SpellHostAreaInd, ItemHostAreaInd, fShortRange, f11, i3, SAVING_THROW_REFLEX, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; // Is it valid? 60% chance of casting. // if(AI_ActionCastSpellRandom(SPELL_MESTILS_ACID_BREATH, SpellHostAreaInd, i50, GlobalSpellTarget, i13, FALSE, ItemHostAreaInd)) return TRUE; } break; case i3: if(GlobalNormalSpellsNoEffectLevel < i3 && InputRangeTouchValid && GlobalSpellTargetRace != RACIAL_TYPE_UNDEAD && !AI_GetSpellTargetImmunity(GlobalImmunityNegativeEnergy)) { // 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, i40, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; } // Hammer of the Gods. Divine damage is good, as well as save VS daze. if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_HAMMER_OF_THE_GODS) || ItemHostAreaDis == SPELL_HAMMER_OF_THE_GODS)) { // Hammer of the Gods. Level 4 (Cleric). Divine damage d8(half caster level) to 5d8. Can Daze. Will save. // Shpere, no friends affected, we cast even if saves VS will. :-) if(AI_AttemptCastAOESpellRandom(SPELL_HAMMER_OF_THE_GODS, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_HUGE, i4, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) 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 && InputRangeShortValid) { // 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, i60, GlobalSpellTarget, i13, FALSE, ItemHostAreaInd)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i5) { // Inferno - the Druids Acid Arrow (more damage, however, each round). // ---- No save!! if(InputRangeShortValid && !GetHasSpellEffect(SPELL_INFERNO)) { // Inferno. Level 5 (Druid) 2d6 Fire damage/round like acid arrow. No save, only SR. if(AI_ActionCastSpellRandom(SPELL_INFERNO, SpellHostRanged, i60, GlobalSpellTarget, i15, FALSE, ItemHostRanged)) return TRUE; } } if(GlobalNormalSpellsNoEffectLevel < i3 && InputRangeMediumValid) { // All others are in medium range or smaller // 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, i60, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) 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. // why? blind is good effect. if(SpellHostAreaDis && InputRangeLongValid && (GetHasSpell(SPELL_SUNBURST) || ItemHostAreaDis == SPELL_SUNBURST)) { // 40M range, lagre (5.0 across) area. Relfex saves. // 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_AttemptCastAOESpellRandom(SPELL_SUNBURST, i60, SpellHostAreaDis, ItemHostAreaDis, fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlySelectiveHostile, TRUE)) return TRUE; } if(SpellHostAreaDis && InputRangeLongValid && (GetHasSpell(SPELL_SUNBEAM) || ItemHostAreaDis == SPELL_SUNBEAM)) { // 40M range, lagre (5.0 across) area. Relfex saves. // Sunbeam. Level 8 (Cleric/Druid) 3d6 to non-undead. Blindness. Limit of 20 dice for undead damage. Medium range/colosal size if(AI_AttemptCastAOESpellRandom(SPELL_SUNBEAM, i60, SpellHostAreaDis, ItemHostAreaDis, fLongRange, RADIUS_SIZE_COLOSSAL, i8, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // Evard's Black Tentacles. AC attack and damage (fort or paralysis on enter) // Casts if average HD is under 10 if(InputRangeMediumValid &&// GlobalAverageEnemyHD <= i10 && (GetHasSpell(SPELL_EVARDS_BLACK_TENTACLES) || ItemHostAreaInd == SPELL_EVARDS_BLACK_TENTACLES)) { // 5M sized AOE spell, medium range. if(AI_AttemptCastAOESpellRandom(SPELL_EVARDS_BLACK_TENTACLES, i60, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_LARGE, i4, FALSE, SHAPE_SPHERE, GlobalFriendlyFire, TRUE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_STUN)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i2 && InputRangeLongValid) { // 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; } break; case i4: if(GlobalNormalSpellsNoEffectLevel < i7 && GlobalSeenSpell && InputRangeShortValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && !AI_CompareTimeStopStored(SPELL_FINGER_OF_DEATH)) { // 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_ActionCastSpellRandom(SPELL_FINGER_OF_DEATH, SpellHostRanged, i60, GlobalSpellTarget, i17)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i5 && InputRangeTouchValid && !AI_CompareTimeStopStored(SPELL_SLAY_LIVING) && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy)) // !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i5)) { // Slay living. 60% chance to cast - it is a touch spell. // 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; } // Circle of Doom - 1d8 + 1/caster level in negative damage if(SpellHostAreaDis && InputRangeMediumValid && (GetHasSpell(SPELL_CIRCLE_OF_DOOM) || ItemHostAreaDis == SPELL_CIRCLE_OF_DOOM)) { // Shpere, medium and reflex save. // Circle of Doom. Level 5 (Cleric) 1d8 + 1/caster level in negative damage if(AI_AttemptCastAOESpellRandom(SPELL_CIRCLE_OF_DOOM, i50, SpellHostAreaDis, ItemHostAreaDis, fMediumRange, RADIUS_SIZE_MEDIUM, i5, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE, TRUE)) return TRUE; } if(//GlobalNormalSpellsNoEffectLevel < i3 && InputRangeTouchValid && !AI_GetSpellTargetImmunity(GlobalImmunityNecromancy) && !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, i50, GlobalSpellTarget, i13, FALSE, ItemHostTouch)) return TRUE; } // Wall of Fire. if(SpellHostAreaInd && InputRangeMediumValid && (GetHasSpell(SPELL_WALL_OF_FIRE) || ItemHostAreaInd == SPELL_WALL_OF_FIRE) && (GetHasSpell(SPELL_SHADES_WALL_OF_FIRE) || ItemHostAreaInd == SPELL_SHADES_WALL_OF_FIRE)) { // false in last checking loop, don't check again. if(!(GetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_WALL_OF_FIRE)) || GetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_SHADES_WALL_OF_FIRE)))) { // Ok, retangle. Take it as a medium sized sphere. object oAOE = AI_GetBestAreaSpellTarget(fMediumRange, RADIUS_SIZE_MEDIUM, i4, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, TRUE); // Is it valid? 50% chance of casting. if(GetIsObjectValid(oAOE)) { // Shades version if(AI_ActionCastSubSpell(SPELL_SHADES_WALL_OF_FIRE, SpellHostAreaInd, oAOE, i14, FALSE, 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, i60, oAOE, i14, FALSE, ItemHostAreaInd)) return TRUE; } else { SetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_WALL_OF_FIRE),TRUE); DelayCommand(f2,DeleteLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_WALL_OF_FIRE))); SetLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_SHADES_WALL_OF_FIRE),TRUE); DelayCommand(f2,DeleteLocalInt(OBJECT_SELF,AI_CHECKED_AOE_SPELL+IntToString(SPELL_SHADES_WALL_OF_FIRE))); } } } // Spike Growth. Alright AOE - 30% speed decrease for 24HRS is the best bit. if(InputRangeMediumValid && (GetHasSpell(SPELL_SPIKE_GROWTH) || ItemHostAreaInd == SPELL_SPIKE_GROWTH)) { // Allies are hit, will save to be immune. // Spike Growth. Level 3 (Druid) AOE - 30% speed decrease for 24HRS + 1d4 piercing damage/round. if(AI_AttemptCastAOESpellRandom(SPELL_SPIKE_GROWTH, i60, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_LARGE, i3, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } break; case i5: if(GlobalNormalSpellsNoEffectLevel < i1) { // Magic Missile - Is a nice long range spell. Can do damage to almost anything. if(InputRangeLongValid && !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; } } // Burning hands - fire reflex damage to a cone if(SpellHostAreaInd && InputRangeShortValid && (GetHasSpell(SPELL_BURNING_HANDS) || ItemHostAreaInd == SPELL_BURNING_HANDS)) { // Cone AOE, short ranged, reflex based save. Reaction friendly. // Burning Hands. Level 1 (Mage) 1d4/level to 5d4 Reflex Fire damage to a cone AOE. if(AI_AttemptCastAOESpellRandom(SPELL_BURNING_HANDS, i60, SpellHostAreaInd, ItemHostAreaInd, fShortRange, f10, i1, SAVING_THROW_REFLEX, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; // Is it valid? 70% chance of casting. // Burning Hands. Level 1 (Mage) 1d4/level to 5d4 Reflex Fire damage to a cone AOE. // if(AI_ActionCastSpellRandom(SPELL_BURNING_HANDS, SpellHostAreaInd, i60, GlobalSpellTarget, i11, FALSE, ItemHostAreaInd)) return TRUE; } if(InputRangeShortValid) { // 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. // Wierd. Level 9 (Wizard/Innate). 2 saves (will/fort) against death. Doesn't kill allies! (Illusion) if(AI_AttemptCastAOESpellRandom(SPELL_WEIRD, i60, SpellHostAreaDis, ItemHostAreaDis, fShortRange, RADIUS_SIZE_COLOSSAL, i9, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, FALSE, FALSE, FALSE, FALSE, TRUE, IMMUNITY_TYPE_FEAR)) return TRUE; } } if(GlobalNormalSpellsNoEffectLevel < i4) // All level 4, all fort saves // !AI_SaveImmuneSpellTarget(SAVING_THROW_FORT, i4)) { // Phantismal Killer. Will and Fortitude save VS death. :-) if(InputRangeMediumValid && !AI_CompareTimeStopStored(SPELL_PHANTASMAL_KILLER) && !AI_GetSpellTargetImmunity(GlobalImmunityFear) && !AI_GetSpellTargetImmunity(GlobalImmunityMind)) // !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, i60, GlobalSpellTarget, i14, FALSE, ItemHostRanged)) return TRUE; } } if(GlobalNormalSpellsNoEffectLevel < i3 && InputRangeShortValid) { // Quillfire. Level 3 (Mage) 1 quill at 1d8 + 1-5 damage. Scorpion poison too. if(AI_ActionCastSpellRandom(SPELL_QUILLFIRE, SpellHostRanged, i40, GlobalSpellTarget, i13, FALSE, ItemHostRanged)) 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 && InputRangeShortValid && (GetHasSpell(SPELL_GEDLEES_ELECTRIC_LOOP) || ItemHostAreaDis == SPELL_GEDLEES_ELECTRIC_LOOP)) { // Small, short ranged, and reflex save based. // Gedlee's Electric Loop. Level 2 (Mage) All in AOE have 1d6 Electric dam (to 5d6) + will save for 1 round stun. if(AI_AttemptCastAOESpellRandom(SPELL_GEDLEES_ELECTRIC_LOOP, i60, SpellHostAreaDis, ItemHostAreaDis, fShortRange, RADIUS_SIZE_SMALL, i2, SAVING_THROW_REFLEX, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } // Sound burst - best part is the stun! Trust me! Long range too! if(SpellHostAreaInd && InputRangeLongValid && (GetHasSpell(SPELL_SOUND_BURST) || ItemHostAreaInd == SPELL_SOUND_BURST)) { // Medium AOE, medium ranged, will based save. Reaction friendly. // Sound burst. Level 2 (cleric). 1d8 Sonic damage, and if fail will save, stunned for 2 rounds. if(AI_AttemptCastAOESpellRandom(SPELL_SOUND_BURST, i60, SpellHostAreaInd, ItemHostAreaInd, fMediumRange, RADIUS_SIZE_MEDIUM, i2, SAVING_THROW_WILL, SHAPE_SPHERE, GlobalFriendlyFire, TRUE)) return TRUE; } if(GlobalNormalSpellsNoEffectLevel < i1) { // Horizikaul's boom is sonic damage - no save! Also, goes also to 5d4 (at level 10 anyway). Comparable to MM! if(InputRangeShortValid) { // 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(InputRangeMediumValid) { // 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; } // Minor damage. Moderate range. 60% chance of casting. if(AI_ActionCastSpellRandom(SPELL_RAY_OF_FROST, SpellHostRanged, i50, GlobalSpellTarget, i10, FALSE, ItemHostRanged)) return TRUE; } if(InputRangeLongValid) { // 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(InputRangeMediumValid && !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; } if(InputRangeShortValid) { // 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; } } } break; } return FALSE; } // damage ability int AI_AttemptDamageAbility(int iPriority, int InputRangeLongValid = FALSE, int InputRangeMediumValid = FALSE, int InputRangeShortValid = FALSE, int InputRangeTouchValid = FALSE) { object oAOE; switch(iPriority) { case i0: // Do not check spell resistance (GlobalNormalSpellsNoEffectLevel) here. // We only use pulses 80% of the time. if(InputRangeTouchValid && !GlobalInTimeStop && GlobalSpellTargetRange < fTouchRange) { // Pulse_Cold - Huge. d6(HD) in cold damage. Reflex Save, DC: 10 + HD if(AI_ActionCastSpell(SPELLABILITY_PULSE_COLD)) return TRUE; // Pulse_Fire - Huge. d6(HD) in fire damage. Reflex Save, DC: 10 + HD if(AI_ActionCastSpell(SPELLABILITY_PULSE_FIRE)) return TRUE; // Pulse_Holy - Large. Heals (Non-undead) allies. Harms all undead. Damage/Heal: d4(HD). Reflex Save, DC: 10 + HD. if(AI_ActionCastSpell(SPELLABILITY_PULSE_HOLY)) return TRUE; // Pulse_Lightning - Huge. d6(HD) in electrical damage. Reflex Save, DC: 10 + HD if(AI_ActionCastSpell(SPELLABILITY_PULSE_LIGHTNING)) return TRUE; // Pulse_Negative - Large. Heals (Undead) allies. Harms all non-undead. Damage/Heal: d4(HD). Reflex Save, DC: 10 + HD. if(AI_ActionCastSpell(SPELLABILITY_PULSE_NEGATIVE)) return TRUE; } // Bolts 80% chance. Not too good, but oh well. if(GlobalSeenSpell && InputRangeMediumValid) { // 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. if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_ACID, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_COLD, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(!GlobalSpellClassRDD && AI_ActionCastSpellRandom(SPELLABILITY_BOLT_FIRE, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_LIGHTNING, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_BOLT_SHARDS, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; } // Medium range spells. if((InputRangeMediumValid || InputRangeShortValid) && GlobalSeenSpell) { // slaad's chaos spittle.. ranged touch, magic damage. if(AI_ActionCastSpellRandom(AI_SPELLABILITY_SLAAD_CHAOS_SPITTLE, SpellHostRanged, i30,GlobalSpellTarget)) return TRUE; // Other one 2 - this is magical damage, little powerful... need high intelligence? if(AI_ActionCastSpellRandom(AI_SPELLABILITY_PSIONIC_MASS_CONCUSSION, SpellHostAreaDis, i30,GlobalSpellTarget)) return TRUE; } break; case i1: // Do not check spell resistance (GlobalNormalSpellsNoEffectLevel) here. // We only use pulses 80% of the time. if(InputRangeTouchValid && !GlobalInTimeStop && GlobalSpellTargetRange < fTouchRange) { // Pulse_Whirlwind - Large. Reflex DC 14 check, or Knockdown (2 rounds) and d3(HD) Damage. if(AI_ActionCastSpell(SPELLABILITY_PULSE_WHIRLWIND)) return TRUE; } // Monster cones - these ignore the GlobalNormalSpellsNoEffectLevel toggle. if(InputRangeShortValid) { // 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. // Is it valid? 50% for each. /* if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_ACID, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_COLD, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_FIRE, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_LIGHTNING, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; if(AI_ActionCastSpellRandom(SPELLABILITY_CONE_SONIC, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) return TRUE; */ if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_ACID, i40, SpellHostAreaInd, FALSE, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_COLD, i40, SpellHostAreaInd, FALSE, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_FIRE, i40, SpellHostAreaInd, FALSE, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_LIGHTNING, i40, SpellHostAreaInd, FALSE, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; if(AI_AttemptCastAOESpellRandom(SPELLABILITY_CONE_SONIC, i40, SpellHostAreaInd, FALSE, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; } break; case i2: // Golem ranged slam. 80% chance of using. if(InputRangeLongValid && GlobalSeenSpell) { // 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(InputRangeMediumValid && // 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; if(GetHasFeat(FEAT_DRAGON_DIS_BREATH)) { ActionCastSpellAtObject(690,GlobalSpellTarget,METAMAGIC_ANY,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,FEAT_DRAGON_DIS_BREATH); 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(InputRangeTouchValid && GlobalSeenSpell && d10() <= i8) { // Sonic damage is powerful - 40% // Fortitude save or sonic damage d6(HD/4) :-) if(AI_ActionCastSpellRandom(SPELLABILITY_HOWL_SONIC, SpellHostAreaInd, i30)) return TRUE; } // Giant Hurl Rock if(InputRangeLongValid && 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, FALSE)) 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, FALSE)) return TRUE; } break; case i3: // Monster cones - these ignore the GlobalNormalSpellsNoEffectLevel toggle. if(InputRangeShortValid) { // 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. if(AI_AttemptCastAOESpellRandom(SPELLABILITY_HELL_HOUND_FIREBREATH, i50, SpellHostAreaInd, FALSE, fShortRange, f11, FALSE, SAVING_THROW_ALL, SHAPE_SPELLCONE, GlobalFriendlyFire, TRUE)) return TRUE; // if(AI_ActionCastSpellRandom(SPELLABILITY_HELL_HOUND_FIREBREATH, SpellHostAreaInd, i40, GlobalSpellTarget, FALSE, FALSE)) 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; } // Bolts 80% chance. Not too good, but oh well. if(GlobalSeenSpell && InputRangeMediumValid) { // 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(!GlobalSpellClassRDD && AI_ActionCastSpellRandom(AI_SPELLABILITY_AZER_FIRE_BLAST, SpellHostRanged, i30, GlobalSpellTarget)) return TRUE; } break; case i4: // Eyeball rays. Low stuff, but hey, whatever, eh? // Random cast these. 3 random ones. if(InputRangeMediumValid) { // 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; } break; } return FALSE; } // attempt additional buff spells. int AI_AttemptAddBuffSpells(int iPriority) { // deadmagic zone? if(GlobalSpellFailure)return FALSE; object oAOE; switch(iPriority) { case i0: if(!(AI_GetAIHaveEffect(GlobalEffectTrueSeeing) || AI_GetAIHaveEffect(GlobalEffectSeeInvisible))) { // True Seeing if(AI_ActionCastSpell(SPELL_TRUE_SEEING, SpellConAre, OBJECT_SELF, i16, 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; } // 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 && !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; } } } // 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; } // Divine Shield - a feat, but a damn good one. // Up to +5 Dodge AC. if(!GetHasFeatEffect(FEAT_DIVINE_SHIELD) && GetHasFeat(FEAT_TURN_UNDEAD) && GetAbilityModifier(ABILITY_CHARISMA)>=i7 && GlobalOurAC+GetAbilityModifier(ABILITY_CHARISMA)>GlobalMeleeTargetBestAttackBonus) { // Divine Shield if(AI_ActionUseFeatOnObject(FEAT_DIVINE_SHIELD)) return TRUE; } // 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(!GetHasSpellEffect(SPELL_UNDEATHS_ETERNAL_FOE) && AI_ActionCastSpell(SPELL_UNDEATHS_ETERNAL_FOE, SpellEnhSinTar, OBJECT_SELF, i19, FALSE, ItemEnhSinTar, PotionEnh)) return TRUE; // Cast regeneration + Natures Balance here if we are lacking HP. if(GlobalOurPercentHP <= i70) { // 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; } // Battletide is not bad. Level 5 (Cleric) // - Oddly classed under SpellHostAreaDis if(!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, OBJECT_SELF, i15, FALSE, ItemHostAreaDis)) 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 && !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; } break; case i2: // Cast regeneration + Natures Balance here if we are lacking HP. if(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; } } // Monsterous Regeneration if we have 70% or less HP if(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; } break; case i4: // 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; } // 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; } // 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; } // 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; } // 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; } // Cheat and use protection froms. Have to rely upon talents :-/ // as cannot specify using ActionCast - it plain don't wanna work. if(GetAlignmentGoodEvil(GlobalMeleeTarget) == 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(GetAlignmentGoodEvil(GlobalMeleeTarget) == ALIGNMENT_EVIL && !AI_GetAIHaveSpellsEffect(GlobalHasProtectionEvilSpell)) { if(AI_ActionCastSubSpell(SPELL_PROTECTION_FROM_EVIL, SpellProSinTar, OBJECT_SELF, i11, FALSE, ItemProSinTar, PotionPro)) 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; } } } //Protection from Evil / Magic Circle Against Evil // ... in preparation for Gate! if(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; } // 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; } break; case i5: // 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; } // Need decent % HP and no enemies in 4 Meters to cast these if(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; } } break; } return FALSE; } // attmept ally buff spells. int AI_AttmeptAllyBuffSpells(int iPriority) { // deadmagic zone? if(GlobalSpellFailure)return FALSE; // run only once. if(iPriority>i0) return FALSE; // Haste for allies // - 60% chance of casting. More below somewhere (near stoneskin if(AI_ActionCastAllyBuffSpell(f6, i60, SPELL_MASS_HASTE, SPELL_HASTE, iM1, iM1, iM1, iM1, GlobalEffectHaste, FALSE)) return TRUE; // Consealment spells if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_IMPROVED_INVISIBILITY, SPELL_DISPLACEMENT)) 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; if(AI_ActionCastAllyBuffSpell(f10, i60, SPELL_NEGATIVE_ENERGY_PROTECTION)) return TRUE; } // TRUE SEEING if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_TRUE_SEEING,iM1,iM1,iM1,iM1,iM1,GlobalEffectTrueSeeing,FALSE)) 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; } // Try stoneskins as a main one if(AI_ActionCastAllyBuffSpell(f6, i100, SPELL_STONESKIN)) return TRUE; return FALSE; } // attempt summon spells, or relate ability. int AI_AttemptSummonSpells() { // // if there are more allies, than do not summon(for prevent overheat) // if(GlobalTotalAllies>GlobalTotalSeenHeardEnemies) return FALSE; object oSummon = GetAssociate(ASSOCIATE_TYPE_SUMMONED); if(GetIsObjectValid(oSummon) && !(GetIsDead(oSummon) || GetHasSpellEffect(SPELL_FLESH_TO_STONE,oSummon))) return FALSE; if(!GlobalSpellFailure && 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(!GlobalSpellFailure && 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; } } //Gate //CONDITION: Protection from Evil active on self // Balors rock, literally! If not, I kill dem all!! >:-D if(!GlobalSpellFailure && 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; } // 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(!GlobalSpellFailure && 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; } // Level 8 summons. 20HD or under, or 2 melee enemy and under. if(GlobalCanSummonSimilarLevel <= i8 && (GlobalOurHitDice <= i20 || GlobalMeleeAttackers <= i2)) { // Create Greater undead - Pale Master // if(AI_ActionCastSummonSpell(SPELLABILITY_PM_SUMMON_GREATER_UNDEAD, i0, i8)) return TRUE; if(GetHasFeat(AI_FEAT_PM_CREATE_GREATER_UNDEAD)) { ActionCastSpellAtObject(SPELLABILITY_PM_SUMMON_GREATER_UNDEAD,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,AI_FEAT_PM_CREATE_GREATER_UNDEAD); return TRUE; } // Create Greater Undead. Level 8 (Cleric) Create Vampire, Doom knight, Lich, Mummy Cleric. if(!GlobalSpellFailure && AI_ActionCastSummonSpell(SPELL_CREATE_GREATER_UNDEAD, i18, i8)) return TRUE; // Summon an Greater elemental. Summon 8 - Druid/Cleric/Bard/Mage. if(!GlobalSpellFailure && 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(!GlobalSpellFailure && AI_ActionCastSummonSpell(SPELL_GREATER_PLANAR_BINDING, i18, i8)) return TRUE; } // - Shadow based on level // if(AI_ActionCastSummonSpell(SPELLABILITY_BG_FIENDISH_SERVANT, i0, i7)) return TRUE; if(GetHasFeat(AI_FEAT_BG_FIENDISH_SERVANT)) { ActionCastSpellAtObject(SPELLABILITY_BG_FIENDISH_SERVANT,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,AI_FEAT_BG_FIENDISH_SERVANT); return TRUE; } if(GlobalAverageEnemyHD>i15) return FALSE; // Level 7 summon spells. Also cast the unique class-based summons (like // a blackguards undead, a shadowdancers shadow ETC). if(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; if(GetHasFeat(FEAT_SUMMON_SHADOW)) { ActionCastSpellAtObject(SPELL_SUMMON_SHADOW,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,FEAT_SUMMON_SHADOW); return TRUE; } // - Undead warrior (EG: doomknight) based on level // if(AI_ActionCastSummonSpell(SPELLABILITY_BG_CREATEDEAD, i0, i7)) return TRUE; if(GetHasFeat(AI_FEAT_BG_CREATE_UNDEAD)) { ActionCastSpellAtObject(SPELLABILITY_BG_CREATEDEAD,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,AI_FEAT_BG_CREATE_UNDEAD); return TRUE; } // - Shadow based on level // if(AI_ActionCastSummonSpell(SPELLABILITY_BG_FIENDISH_SERVANT, i0, i7)) return TRUE; if(GetHasFeat(AI_FEAT_BG_FIENDISH_SERVANT)) { ActionCastSpellAtObject(SPELLABILITY_BG_FIENDISH_SERVANT,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,AI_FEAT_BG_FIENDISH_SERVANT); return TRUE; } // Pale master // if(AI_ActionCastSummonSpell(SPELLABILITY_PM_SUMMON_UNDEAD, i0, i7)) return TRUE; if(GetHasFeat(AI_FEAT_PM_CREATE_UNDEAD)) { ActionCastSpellAtObject(SPELLABILITY_PM_SUMMON_UNDEAD,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,AI_FEAT_PM_CREATE_UNDEAD); 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; } } // Level 6 summons if(!GlobalSpellFailure && 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_CREATE_UNDEAD, 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; } if(GlobalAverageEnemyHD>i13) return FALSE; // Level 5 summons if(!GlobalSpellFailure && 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; } // Level 4 summons if(!GlobalSpellFailure && 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; } // Level 3 summons if(GlobalCanSummonSimilarLevel <= i3 && (GlobalOurHitDice <= i10 || GlobalMeleeAttackers <= i2)) { // Pale master // if(AI_ActionCastSummonSpell(SPELLABILITY_PM_ANIMATE_DEAD, i0, i3)) return TRUE; if(GetHasFeat(AI_FEAT_PM_ANIMATE_DEAD)) { ActionCastSpellAtObject(SPELLABILITY_PM_ANIMATE_DEAD,OBJECT_SELF,METAMAGIC_NONE,TRUE); DecrementRemainingFeatUses(OBJECT_SELF,AI_FEAT_PM_ANIMATE_DEAD); return TRUE; } // Animate Dead. Level 3 (Mage). Skeleton or zomie summon. Tough DR, long lasting, but hard to heal. if(!GlobalSpellFailure && AI_ActionCastSummonSpell(SPELL_ANIMATE_DEAD, i13, i3)) return TRUE; // Summon Monster III (3). Level 3 (Most classes) Summons a Dire Wolf. if(!GlobalSpellFailure && AI_ActionCastSummonSpell(SPELL_SUMMON_CREATURE_III, i13, i3)) return TRUE; } // Level 2 summons if(!GlobalSpellFailure && 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; } // Level 1 summons if(!GlobalSpellFailure && 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; } return FALSE; } // Important buff spell cast int AI_CoreBuffSpell() { // deadmagic zone? if(GlobalSpellFailure)return FALSE; // GlobalAntiMeleeWarrior, // enemy with no caster. // GlobalAntiArcaneSpellCaster; // need special action for spellcaster enemy. if(AI_SpellWrapperConsealmentEnhancements(OBJECT_SELF)) return TRUE; // Haste's if(AI_SpellWrapperHasteEnchantments()) return TRUE; if(!GlobalAntiArcaneSpellCaster) { // Stoneskin - protection from attackers. if(AI_SpellWrapperPhisicalProtections()) return TRUE; if(!AI_GetAIHaveEffect(GlobalEffectTrueSeeing)) { // 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; } } // Visages. short? if(AI_SpellWrapperVisageProtections()) return TRUE; if(!GlobalAntiMeleeWarrior) { 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; } } if(!GlobalAntiArcaneSpellCaster) { // Shields if(AI_SpellWrapperShieldProtections()) return TRUE; } if(!GlobalAntiMeleeWarrior) { // Mantals if(AI_SpellWrapperMantalProtections()) 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] ***************************/ int DO_AI_DetermineCombatRound(object oIntruder = OBJECT_INVALID) { // 46: "[DRC] START [Intruder]" + GetName(oIntruder) DebugActionSpeakByInt(46, oIntruder); if(GetCurrentAction()==ACTION_CASTSPELL) { return i6; } // location targeting check GlobalLocationTargetFail=FALSE; if(GetAIInteger(AI_AOE_LOCATION_TARGETING_FAILED)) { GlobalLocationTargetFail=TRUE; } else { int nSpell=GetAIInteger(AI_LAST_CASTED_AOE_SPELL); int nSpellNum=GetAIInteger(AI_LAST_CASTED_AOE_SPELL_NUM); if(GetHasSpell(nSpell) && (GetHasSpell(nSpell)==nSpellNum)) { GlobalLocationTargetFail=TRUE; SetAIInteger(AI_AOE_LOCATION_TARGETING_FAILED,TRUE); } } // check position change for block check. AI_BlockedCheck(); // Useful in the IsUncommandable check - we don't loop effects more than once. AI_SetUpUsEffects(); GlobalImHasted=(!AI_GetAIHaveEffect(GlobalEffectSlowed) && AI_GetAIHaveEffect(GlobalEffectHaste))?i2:i1; // 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 TRUE; } // 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); AI_AntiCornerMoveAway(GlobalNearestEnemyHeard,FALSE,f40,GlobalIamBlocked); return TRUE; } // 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 TRUE; } // 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); // Last Attacker Set GlobalLastAttacker = GetLocalObject(OBJECT_SELF,AI_TO_ATTACK); if(GlobalValidNearestSeenEnemy) { GlobalLastAttacker = GlobalNearestEnemySeen; } else if(GlobalValidNearestHeardEnemy) { GlobalLastAttacker = GlobalNearestEnemyHeard; } else if(!GetIsObjectValid(GlobalLastAttacker) || !GetIsEnemy(GlobalLastAttacker) || GetIsDead(GlobalLastAttacker) || GetIsDM(GlobalLastAttacker) || GetIgnore(GlobalLastAttacker) || GetArea(OBJECT_SELF)!=GetArea(GlobalLastAttacker)) { GlobalLastAttacker = GetLastHostileActor(); if(!GetIsObjectValid(GlobalLastAttacker) || !GetIsEnemy(GlobalLastAttacker) || GetIsDead(GlobalLastAttacker) || GetIsDM(GlobalLastAttacker) || GetIgnore(GlobalLastAttacker) || GetArea(OBJECT_SELF)!=GetArea(GlobalLastAttacker)) { GlobalLastAttacker = OBJECT_INVALID; } } SetLocalObject(OBJECT_SELF,AI_TO_ATTACK,GlobalLastAttacker); GlobalLastAttackerValid = GetIsObjectValid(GlobalLastAttacker); GlobalNoneSeenHeardEnemy = !(GlobalValidNearestSeenEnemy || GlobalValidNearestHeardEnemy || GetObjectSeen(GlobalLastAttacker) || GetObjectHeard(GlobalLastAttacker)); // Turn of searching and hiding here, if we want to! if(GlobalValidNearestSeenEnemy && !GetHasFeat(FEAT_KEEN_SENSE) && GetDetectMode(OBJECT_SELF) == DETECT_MODE_ACTIVE) { SetActionMode(OBJECT_SELF, ACTION_MODE_DETECT, FALSE); } // 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; } // black blade of disaster && ethereal // don't attack and cast spell, just move around object oSummon=GetAssociate(ASSOCIATE_TYPE_SUMMONED); if(AI_GetAIHaveEffect(GlobalEffectEthereal) && GetIsObjectValid(oSummon) && GetLocalInt(oSummon,"X2_L_CREATURE_NEEDS_CONCENTRATION") && GlobalValidNearestSeenEnemy && !GetObjectSeen(OBJECT_SELF,GlobalNearestEnemySeen)) { if(GlobalRangeToNearestEnemy>f8) { ActionMoveToObject(GlobalNearestEnemySeen,FALSE,f7); } else { AI_AntiCornerMoveAway(GlobalNearestEnemySeen,FALSE,f6); } // need more code !! // move out of aoe or near enemy return TRUE; } // 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 TRUE;} // 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 GlobalImHasted;} // 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 TRUE;} // 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 GlobalImHasted;} } else if(iTempInt == i2) { // Special mindflayer things. This can fall through. if(AI_AttemptMindflayerCombat()){return GlobalImHasted;} } // We will attempt to heal ourselves first as a prioritory. // Dragons may use this. // we can use this twice : haste, potion, kit(multiple). // if(AI_AttemptHealingSelf()){return;} switch(AI_AttemptHealingSelf()) { case i1: return i2; case i2: return i1; } // 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 GlobalImHasted;} // We will cure, normally our conditions, or an allies. Things like // blindness always, with other things later. // Dragons use this, mostly with themselves. // dispel ally for remove bigby effect. if(AI_AttemptCureCondition()){return GlobalImHasted;} // tactical flee and hide if(AI_TacticFleeHide())return GlobalImHasted; // This is a good thing to use first. Dragons may use this. if(AI_AttemptFeatBardSong()){return GlobalImHasted;} // We may summon our monster. Always first, because its our's and personal. Dragons may use this (though small chance) if(AI_AttemptFeatSummonFamiliar()){return GlobalImHasted;} // Turning, any sort of unturned and non-resistant creatures. // Used about every 3 rounds. Dragons may use this. if(AI_AttemptFeatTurning()){return GlobalImHasted;} // Special Dragon things now, else other monsters. if(AI_GetIsDragon() && !AI_GetAIHaveEffect(GlobalEffectPolymorph)) { // We may attempt a high level spells, wing buffet, and // always attack with this. if(AI_AttemptDragonCombat()){return (GetIsObjectValid(GetSpellCastItem()))?TRUE:GlobalImHasted;} } 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 GlobalImHasted;} // This may knockout dying PC's or coup de grace sleeping PC's nearby. if(AI_AttemptGoForTheKill()){return GlobalImHasted;} // Spells attempt // overheat prevent if(GlobalTotalAllies>i8 || GlobalSilenceSoItems==i2) { int iSpellToggle=GetLocalInt(OBJECT_SELF,AI_SPELL_HIGH_LOW_TOGGLE); if(iSpellToggle) { if(AI_AttemptAllSpells(i10,i6,i1)){return (GetIsObjectValid(GetSpellCastItem()))?TRUE:GlobalImHasted;} } else { if(AI_AttemptAllSpells(i6,i0,i1)){return (GetIsObjectValid(GetSpellCastItem()))?TRUE:GlobalImHasted;} } SetLocalInt(OBJECT_SELF,AI_SPELL_HIGH_LOW_TOGGLE,!iSpellToggle); if(!GlobalIamBlockedByObject && AI_GetIsArcaneCaster(OBJECT_SELF) && (SpellAnySpellValid || GobalOtherItemsValid)) { if(GlobalRangeToNearestEnemy