PRC8/nwn/nwnprc/trunk/spells/sp_ai_sphereofud.nss
Jaysyn904 6ec137a24e Updated AMS marker feats
Updated AMS marker feats.  Removed arcane & divine marker feats.  Updated Dread Necromancer for epic progression. Updated weapon baseitem models.  Updated new weapons for crafting & npc equip.
 Updated prefix.  Updated release archive.
2024-02-11 14:01:05 -05:00

146 lines
5.7 KiB
Plaintext

#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));
}
}