#include "prc_inc_sp_tch" //#include "nw_i0_generic" //#include "x0_inc_generic" #include "prc_add_spell_dc" #include "x0_inc_generic" // // Does the disintegrate logic. // void DoDisintegrate(object oCaster, object oTarget, int nSpellSaveDC, int nSR) { if (spellsIsTarget(oTarget, SPELL_TARGET_STANDARDHOSTILE, oCaster)) { // Make SR check if (!PRCDoResistSpell(oCaster, oTarget, nSR)) { // Make the touch attack. int nTouchAttack = PRCDoMeleeTouchAttack(oTarget);; if (nTouchAttack > 0) { // Generate the RTA beam. SPApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectBeam(VFX_BEAM_ODD, OBJECT_SELF, BODY_NODE_CHEST), oTarget, 1.0,FALSE); // Fort save or die time, but we implement death by doing massive damage // since disintegrate works on constructs, undead, etc. At some point EffectDie() // should be tested to see if it works on non-living targets, and if it does it should // be used instead. int nDamage = 9999; if (PRCMySavingThrow(SAVING_THROW_FORT, oTarget, nSpellSaveDC, SAVING_THROW_TYPE_SPELL, oCaster)) { if (GetHasMettle(oTarget, SAVING_THROW_FORT)) // This script does nothing if it has Mettle, bail return; nDamage = PRCGetMetaMagicDamage(DAMAGE_TYPE_MAGICAL, 1 == nTouchAttack ? 5 : 10, 6, 0, 0, 0); nDamage += SpellDamagePerDice(oCaster, 5); } // Apply damage effect and VFX impact, and if the target is dead then apply // the fancy rune circle too. if (nDamage >= GetCurrentHitPoints (oTarget)) DelayCommand(0.25, SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_2), oTarget)); DelayCommand(0.25, SPApplyEffectToObject(DURATION_TYPE_INSTANT, PRCEffectDamage(oTarget, nDamage, DAMAGE_TYPE_MAGICAL), oTarget)); DelayCommand(0.25, SPApplyEffectToObject(DURATION_TYPE_INSTANT, EffectVisualEffect(VFX_IMP_MAGBLUE), oTarget)); } } } } // // Returns TRUE if we are in our busy state. // int IsBusy() { return GetLocalInt(OBJECT_SELF, "SP_BUSY"); } // // Sets/clears the busy state. // void SetBusy(int value) { SetLocalInt(OBJECT_SELF, "SP_BUSY", value); } // // Main AI function. This is invoked by DetermineCombatRound() as it is provided // as an override to the AI in the sphere's creature template. // // Known issues: The sphere still takes AOO's, when it shouldn't. // void main() { object oCaster = GetFactionLeader(OBJECT_SELF); //SendMessageToPC(oCaster, "sp_ai_sphereofud entering"); // Get the intruder object, the NWN AI saves it in a local variable for us. object oIntruder = GetCreatureOverrideAIScriptTarget(); ClearCreatureOverrideAIScriptTarget(); // If we don't have a valid enemy then try to find one to pick on. if (!GetIsObjectValid(oIntruder) || GetIsDead(oIntruder)) oIntruder = bkAcquireTarget(); //SendMessageToPC(oCaster, "sp_ai_sphereofud ENEMY = " + GetName(oIntruder)); // If we don't have an intruder or he's dead then just exit. if (!GetIsObjectValid(oIntruder) || GetIsDead(oIntruder)) return; // Call AI finished at this point to prevent the default combat AI from running, // once we aquire a valid target our AI takes over (we just want to run around // and disintegrate things). SetCreatureOverrideAIScriptFinished(); // If we are busy then do nothing. This is expected behavior because our // disintegrate attack is not an action, and thus takes 0 time from the AI's // point of view. We put ourselves in a busy state to wait a round before // attacking again. if (IsBusy()) { //SendMessageToPC(oCaster, "sp_ai_sphereofud BUSY"); return; } // Set our busy state. SetBusy(TRUE); // We have an enemy see if we are in touch attach range. This range varies // depending on the enemie's size, currently it's a fudge number that seems to // work ok. float fDistance = GetDistanceBetween(OBJECT_SELF, oIntruder); //SendMessageToPC(oCaster, "sp_ai_sphereofud range to ENEMEY " + FloatToString(fDistance)); if (fDistance > 3.0) { // We are too far for a touch attack, close to the enemy if we aren't already. // Once we start closing it is pointless to spam the action queue with close // requests. if (ACTION_MOVETOPOINT != GetCurrentAction(OBJECT_SELF)) { //SendMessageToPC(oCaster, "sp_ai_sphereofud closing with ENEMY"); ActionForceMoveToObject(oIntruder, TRUE); } //else //SendMessageToPC(oCaster, "sp_ai_sphereofud waiting to close"); // Clear the busy state so we can handle more actions. Ideally we should // remain busy until we close, but there is no way to know when this happens. SetBusy(FALSE); } else { // Clear our action list of any other actions just in case. ClearAllActions(); // Attempty to disintegrate the current target. //SendMessageToPC(oCaster, "Disintegrating, BUSY for 1 round"); object oSphere = OBJECT_SELF; DoDisintegrate(oCaster, oIntruder, GetLocalInt(oCaster, "SP_SPHEREOFUD_DC"), GetLocalInt(oCaster, "SP_SPHEREOFUD_SR")); // Wait a round to clear our busy state which will keep our attacks // to 1 per round. DelayCommand(RoundsToSeconds(1), SetBusy(FALSE)); } }