Author Topic: Dispel Magic confusion...  (Read 586 times)

Legacy_Monsieur T

  • Jr. Member
  • **
  • Posts: 62
  • Karma: +0/-0
Dispel Magic confusion...
« on: January 28, 2012, 02:54:42 pm »


               Howdy,
I am unable to get my head around how the various dispel magic spells work (or don't) in NWN1.
Spell descriptions talk about d20 checks being made against DC 11 + the caster level of the spell being tested against, but all the code in x0_i0_spells indicates that such a mechanism is being over-ridden at an Include level, (as far as AoEs are concerned).
Are the spell descriptions bogus, or not?
Or are they right for Dispels aimed at a creature or placeable, but not when aimed at an Area of Effect?
Any enlightenment would be greatly appreciated...
All I really want is to prevent high level effects (particularly AoEs) being wasted by a low-level PC who just happens to get a lucky dice roll.
Thanks
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Dispel Magic confusion...
« Reply #1 on: January 28, 2012, 04:53:58 pm »


               you are correct, dispelling AOEs doesnt use given formula all is in the spellsDispelAoE functiuon inside x0_i0_spells where you can also change it, however changes in there wont manifest until you recompile all dispell spells

tip: in 1.70, variable "X1_L_IMMUNE_TO_DISPEL" = 10 on AOE object grants undispellability
               
               

               
            

Legacy_Monsieur T

  • Jr. Member
  • **
  • Posts: 62
  • Karma: +0/-0
Dispel Magic confusion...
« Reply #2 on: January 30, 2012, 01:53:18 am »


               Thanks ShadDoOoW,
I thought that such was the case.
Thanks also for the 1.70 tip - I'll look into that.
But here's a thought.........
What would be really cool is a series of Dispel Magic spells, such as:
Dispel 1: Dispels any 1 spell of Level 1 only (irrespective of caster's level or item Caster Level)
And...  
Dispel 2: Dispels any 1 spell of Level 1 and 2 only (irrespective of caster's level or item C.L.)
And...
Dispel 3: Dispels any 1 spell of Level 1 to 3 only (irrespective of caster's level or item C.L.)
And so on, up to whatever level you like.

This would be a much more logical system to use, and would prevent low-level PCs/NPCs getting lucky (and totally unrealistic) results when trying to dispel higher level effects.
I know that NWN1 doesn't have NWN2's GetSpellLevel function, and I am not a competent scripter, but any guidance you could offer on this subject would be greatly appreciated.
It is weird how NWN has come so far and so long with such a rubbish dispel system!!!

If you are too busy to reply, please forgive me if I post these ideas up as a new thread.
Just saved your 1.70 info files and will look at them soon - seems exciting!
Cheers
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Dispel Magic confusion...
« Reply #3 on: January 30, 2012, 02:34:41 am »


               It is of course possible to (althought a bit difficult), though by default there are only three dispell spells and mordenkainen disjunction. However I think it would be even worse, the most critical buffs in NWN1 are at lvl 2-4 - mainly ability buffs, contary with NWW2 there are not mass-choices

keep in mind that classes like ranger or paladin have only fourth spell levels, with this change they would be dispell food (not sure if in NWN2 they have half caster level - in such case they are "dispell food" already but in NWN1 these classes has full CL)

I know many builders were not satisfied with NWN1 dispells (mainly with the fact that the CL check is harcoded), the.gray.fox has his own solution and PRC also changed it somehow afaik
               
               

               


                     Modifié par ShaDoOoW, 30 janvier 2012 - 02:35 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Dispel Magic confusion...
« Reply #4 on: January 30, 2012, 02:59:10 am »


               Here's our dispel code, courtesy of acaos. The first script fires for all dispel spells (modified all dispell spell scripts to it in the 2da). The second is an include. Most of the functions you need are there, though the GetSpellInfo function, and the resulting si. struct is not - it simply collects normal information like casterlevel, dc, metamagic, and the like prior to the rest of the spellscript:


#include "hg_inc"

#include "ac_spell_inc"
#include "ac_dispel_inc"

void main() {
    struct SpellInfo si = GetSpellInfo();
    if (si.id < 0)
        return;

    int nBreach = 0, nSR = 0, nBonus = 0, nVis = -1, bAoE = !GetIsObjectValid(si.target);
    int nAoEMask = OBJECT_TYPE_CREATURE|OBJECT_TYPE_PLACEABLE|OBJECT_TYPE_AREA_OF_EFFECT;
    int nAoESpellId = SPELL_HOLY_SWORD;
    float fRadius = RADIUS_SIZE_COLOSSAL;
    int nOrigClvl = si.clevel;
    switch (si.id) {
        case SPELL_LESSER_DISPEL:
            si.clevel = 1 + (si.clevel / 4);
            nBonus    = 4;
            nVis      = VFX_FNF_DISPEL;
            break;

        case SPELL_DISPEL_MAGIC:
            si.clevel = 1 + (si.clevel / 3);
            nBonus    = 6;
            nVis      = VFX_FNF_DISPEL;
            break;

        case SPELL_GREATER_DISPELLING:
            si.clevel = 1 + (si.clevel / 2);
            nBonus    = 8;
            nVis      = VFX_FNF_DISPEL_GREATER;
            break;

        case SPELL_LESSER_SPELL_BREACH:
            si.clevel = 0;
            nBreach   = 2;
            nSR       = 3;
            break;

        case SPELL_GREATER_SPELL_BREACH:
            if (si.class == class_TYPE_SHIFTER) {
                si.clevel   = 0;
                nBreach     = 1 + GetLocalInt(si.caster, "PolyFoci");
                nSR         = nBreach + 1;

                bAoE        = TRUE;
                nVis        = VFX_IMP_DISPEL;
                nAoEMask    = OBJECT_TYPE_CREATURE;
                nAoESpellId = si.id;
                fRadius     = RADIUS_SIZE_SMALL;
            } else {
                si.clevel = 0;
                nBreach   = 4;
                nSR       = 5;
            }
            break;

        case HGSPELL_GREATER_SPELL_BREACH:
            si.clevel = 0;
            nBreach   = 6;
            nSR       = 7;
            break;

        case SPELL_MORDENKAINENS_DISJUNCTION:
            if ((si.clevel = GetHitDiceIncludingLLs(si.caster)) > 60)
                si.clevel = 60;

            nBreach = (bAoE ? 2 : 6);
            nSR     = 10;
            nBonus  = 2;
            nVis    = VFX_FNF_DISPEL_DISJUNCTION;
            break;

        default:
            FloatingTextStringOnCreature("An invalid dispel spell #" + IntToString(si.id) + " was cast. Please inform a DM.", si.caster);
            break;
    }

    if (nBonus > 0 && !GetIsObjectValid(si.item)) {
        int nFocus = GetCasterSpellFocus(SPELL_SCHOOL_ABJURATION, si.caster, nOrigClvl, si.class);

        si.clevel += nFocus * nBonus;

        if (GetIsObjectValid(si.target))
            SetLocalInt(si.target, "AuraSuppressed", 1 + nFocus);
    }

    if (bAoE) {
        float fDelay;

        if (nVis < 0)
            return;
        ApplyVisualAtLocation(nVis, si.loc);

        for (si.target = GetFirstObjectInShape(SHAPE_SPHERE, fRadius, si.loc, TRUE, nAoEMask);
             GetIsObjectValid(si.target);
             si.target = GetNextObjectInShape(SHAPE_SPHERE, fRadius, si.loc, TRUE, nAoEMask)) {

            fDelay = GetSpellEffectDelay(si.loc, si.target);
            SignalEvent(si.target, EventSpellCastAt(si.caster, si.id, FALSE));

            if (GetObjectType(si.target) == OBJECT_TYPE_AREA_OF_EFFECT)
                DoIndividualAoEDispel(si.target, si.clevel);
            else
                DelayCommand(fDelay, DoBreachAndDispel(si.target, si.clevel, (si.clevel > 0 ? 1 : 0), nBreach, nSR, nAoESpellId));
        }
    } else
        DoBreachAndDispel(si.target, si.clevel, (si.clevel > 0 ? 100 : 0), nBreach, nSR, si.id);
}


Here's the ac_dispel_inc include:


// Replacements for spellsDispelMagic, spellsDispelAoE, and DoSpellBreach,
// which integrate most of the common code from those and from the dispel
// and breach spells into a single source base.

#include "spell_func_inc"
#include "fky_chat_const"

const string COLOR_LT_ORANGE = "<cþÀP>";


void DoIndividualAoEDispel (object oTarget, int nDispelLevel) {
    object oArea = GetArea(oTarget);
    object oCreator = GetAreaOfEffectCreator(oTarget);

    /* The old code used 25% for Lesser Dispel, 50% for Dispel, 75% for Greater
       Dispel, and 100% for Mordenkainen's Disjunction. Here, just use twice the
       caster level as the dispel chance. */

    /* Area-created and Hells AoEs are not dispellable */
    if (oCreator == oArea                  ||
        GetResRef(oCreator) == "h_quimath" ||
        GetLocalInt(oTarget, "NoAoEDispel"))
        nDispelLevel = 0;

    if (Random(100) < nDispelLevel * 2) {
        object oPlaceable = GetLocalObject(oTarget, "AoEPlaceable");

        if (GetIsObjectValid(oPlaceable)) {
            SetPlotFlag(oPlaceable, FALSE);
            DestroyObject(oPlaceable);
        }

        DestroyObject(oTarget);

        FloatingTextStrRefOnCreature(100929, OBJECT_SELF);  // "AoE dispelled"
    } else {
        FloatingTextStrRefOnCreature(100930, OBJECT_SELF);  // "AoE not dispelled "
    }
}

void DoBreachAndDispel (object oTarget, int nDispelLevel, int nDispels, int nBreaches, int nSR, int nSpellId=-1, object oCaster=OBJECT_SELF) {
    if (nSpellId < 0) {
        switch (nBreaches) {
            case 4:  nSpellId = SPELL_GREATER_SPELL_BREACH;   break;
            case 2:  nSpellId = SPELL_LESSER_SPELL_BREACH;    break;
            case 6:  nSpellId = HGSPELL_GREATER_SPELL_BREACH; break;
            case 3:  nSpellId = SPELL_HOLY_SWORD;             break;
            case 0:  nSpellId = SPELL_DISPEL_MAGIC;           break;
            default: nSpellId = SPELL_GREATER_SPELL_BREACH;   break;
        }
    }

    if (nSpellId >= 0) {
        if (GetIsReactionTypeFriendly(oTarget))
            SignalEvent(oTarget, EventSpellCastAt(oCaster, nSpellId, FALSE));
        else
            SignalEvent(oTarget, EventSpellCastAt(oCaster, nSpellId));

        if (GetHasSpellImmunity(nSpellId, oTarget) || GetHasSpellEffect(20127, oTarget))  //Checks for Spellguard now
            return;
    }


    int nTargetType = GetObjectType(oTarget);

    if (!(nTargetType == OBJECT_TYPE_PLACEABLE && !GetUseableFlag(oTarget)))
        ApplyVisualToObject(VFX_IMP_BREACH, oTarget);

    if (GetIsReactionTypeFriendly(oTarget, oCaster)) {
        nSR       = 0;
        nBreaches = 0;
    } else if (nSR > 0 && nTargetType == OBJECT_TYPE_CREATURE && (nSpellId < 0 || !GetHasSpellEffect(nSpellId, oTarget))) {
        effect eSR = EffectSpellResistanceDecrease(nSR);

        if (!GetIsObjectValid(oTarget) || !GetIsPC(oTarget)) {
            if (nSR >= 10)
                eSR = EffectLinkEffects(eSR, EffectVisualEffect(HGVFX_DUR_BREACH_SEPTAGON));
            else if (nSR >= 7)
                eSR = EffectLinkEffects(eSR, EffectVisualEffect(HGVFX_DUR_BREACH_PENTAGON));
            else if (nSR >= 4)
                eSR = EffectLinkEffects(eSR, EffectVisualEffect(HGVFX_DUR_BREACH_SQUARE));
            else
                eSR = EffectLinkEffects(eSR, EffectVisualEffect(HGVFX_DUR_BREACH_TRIANGLE));
        }

        eSR = EffectLinkEffects(eSR, EffectVisualEffect(VFX_DUR_CESSATE_NEGATIVE));
        eSR = ExtraordinaryEffect(eSR);

        ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eSR, oTarget, RoundsToSeconds(10));
    }

    /* Petrified creatures cannot be dispelled without causing bugs */
    if (GetHasEffectOfType(EFFECT_TYPE_PETRIFY, oTarget))
        return;


    float fDelay = 0.1 + (IntToFloat(Random(3)) / 10.0);
    int nEffectSpellId, nEffectSubType, nDC, nBreached = 0, nDispelled = 0;
    int nCasterFilter = GetPCFilter(oCaster, PCFILTER_DISPEL);
    int nTargetFilter = GetPCFilter(oTarget, PCFILTER_DISPEL);
    string sCasterName = COLOR_LT_ORANGE + GetName(oCaster) + " : ";
    string sTargetName = COLOR_LT_GREEN  + GetName(oTarget) + " : ";
    string sSpellName, sCasterMessage = "", sTargetMessage = "", sSpellsDispelled = "";


    /* cap DC at 71 */
    if ((nDC = GetHitDiceIncludingLLs(oTarget) + 11) > 71)
        nDC = 71;

    effect eEff = GetFirstEffect(oTarget);

    while (GetIsEffectValid(eEff)) {
        int bSkip = 0;

        nEffectSpellId = GetEffectSpellId(eEff);
        nEffectSubType = GetEffectSubType(eEff);

        /* Don't check against the same spell multiple times. Use a string as a horribly
         * hacky version of a list. */
        if (nEffectSpellId != -1) {
            string sSpellId = " " + IntToString(nEffectSpellId) + " ";

            if (FindSubString(sSpellsDispelled, sSpellId) != -1)
                bSkip = 1;
            else
                sSpellsDispelled += sSpellId;
        }

        /* Extraordinary and supernatural effects cannot be dispelled */
        if (nEffectSubType == SUBTYPE_EXTRAORDINARY || nEffectSubType == SUBTYPE_SUPERNATURAL)
            bSkip = 1;

        /* If there are breaches remaining, check to see if the current effect is breachable */
        if (!bSkip && nBreached < nBreaches) {
            if (nEffectSpellId != -1 && SFGetIsSpellBreachable(nEffectSpellId)) {
                DelayCommand(fDelay, RemoveEffect(oTarget, eEff));

                sSpellName = SFGetSpellName(nEffectSpellId);

                if (sSpellName != "") {
                    if (oTarget != oCaster) {
                        if (nTargetFilter < 2)
                            sTargetMessage += sCasterName + "Breach " + sSpellName + "\\n";
                        else if (nTargetFilter == 2)
                            FloatingTextStringOnCreature(sCasterName + "Breach " + sSpellName + COLOR_END, oTarget, FALSE);
                    }

                    if (nCasterFilter < 2)
                        sCasterMessage += sTargetName + "Breach " + sSpellName + "\\n";
                    else if (nCasterFilter == 2)
                        FloatingTextStringOnCreature(sTargetName + "Breach " + sSpellName + COLOR_END, oCaster, FALSE);
                }

                nBreached += 1;
                bSkip = 1;
            }
        }

        /* If there are dispels remaining, try to dispel the current effect */
        if (!bSkip && nDispelled < nDispels) {
            object oCreator = GetEffectCreator(eEff);

            if (nTargetType == OBJECT_TYPE_PLACEABLE && GetEffectType(eEff) == EFFECT_TYPE_VISUALEFFECT) {
                bSkip = 1;
            } else if (nSpellId != HGSPELL_BREAK_ENCHANTMENT || !GetIsReactionTypeFriendly(oCreator, oCaster)) {
                int nRoll = d20(1);
                string sSpellName = SFGetSpellName(nEffectSpellId);
                string sDispelRoll;

                if (sSpellName == "") {
                    string sCreatorName = GetName(oCreator);

                    sSpellName = "unknown spell (" + (sCreatorName != "" ? sCreatorName + " / " : "") +
                        GetEffectTypeName(GetEffectType(eEff)) + ")";
                }

                sDispelRoll = "(" + IntToString(nRoll) + " + " + IntToString(nDispelLevel) +
                    " = " + IntToString(nRoll + nDispelLevel) + " vs. DC: " +
                    IntToString(nDC) + ")";

                if (nDispelLevel + nRoll >= nDC) {
                    DelayCommand(fDelay, RemoveEffect(oTarget, eEff));

                    if (sSpellName != "") {
                        if (oTarget != oCaster) {
                            if (nTargetFilter < 2)
                                sTargetMessage += sCasterName + "Dispel " + sSpellName + " : *dispelled* : " + sDispelRoll + "\\n";
                            else if (nTargetFilter == 2)
                                FloatingTextStringOnCreature(sCasterName + "Dispel " + sSpellName + " : *dispelled* : " + sDispelRoll + COLOR_END, oTarget, FALSE);
                        }

                        if (nCasterFilter < 2)
                            sCasterMessage += sTargetName + "Dispel " + sSpellName + " : *dispelled* : " + sDispelRoll + "\\n";
                        else if (nCasterFilter == 2)
                            FloatingTextStringOnCreature(sTargetName + "Dispel " + sSpellName + " : *dispelled* : " + sDispelRoll + COLOR_END, oCaster, FALSE);
                    }

                    nDispelled += 1;
                    bSkip = 1;
                } else if (sSpellName != "") {
                    if (oTarget != oCaster) {
                        if (nTargetFilter < 2)
                            sTargetMessage += sCasterName + "Dispel " + sSpellName + " : *failure* : " + sDispelRoll + "\\n";
                        else if (nTargetFilter == 2)
                            FloatingTextStringOnCreature(sCasterName + "Dispel " + sSpellName + " : *failure* : " + sDispelRoll + COLOR_END, oTarget, FALSE);
                    }

                    if (nCasterFilter < 2)
                        sCasterMessage += sTargetName + "Dispel " + sSpellName + " : *failure* : " + sDispelRoll + "\\n";
                    else if (nCasterFilter == 2)
                        FloatingTextStringOnCreature(sTargetName + "Dispel " + sSpellName + " : *failure* : " + sDispelRoll + COLOR_END, oCaster, FALSE);
                }
            }
        }

        /* Stop processing this target if there are no breaches or dispels remaining */
        if (nBreached >= nBreaches && nDispelled >= nDispels)
            break;

        eEff = GetNextEffect(oTarget);
    }


    if (sCasterMessage != "") {
        sCasterMessage = GetStringLeft(sCasterMessage, GetStringLength(sCasterMessage) - 1) + COLOR_END;

        if (nCasterFilter == 1)
            SendSystemMessage(oCaster, "\\n" + sCasterMessage);
        else
            SendMessageToPC(oCaster, sCasterMessage);
    }

    if (sTargetMessage != "") {
        sTargetMessage = GetStringLeft(sTargetMessage, GetStringLength(sTargetMessage) - 1) + COLOR_END;

        if (nTargetFilter == 1)
            SendSystemMessage(oTarget, "\\n" + sTargetMessage);
        else
            SendMessageToPC(oTarget, sTargetMessage);
    }
}




Funky
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Dispel Magic confusion...
« Reply #5 on: January 30, 2012, 03:07:13 am »


               I realized that Bastion of War pvp arena server also changed the dispelling:

Dispel Magic, Lesser Dispel Magic, Greater Dispel Magic, Mordenkainen's Disjunction:
The DC check to dispel a buff is caster level + Spell Focus Abjuration
(+1, +2 & +4 for Spell Focus, Greater SF & Epic SF Abjuration
respectively) + d20 vs. target's character level + Arcane Defense
Abjuration (+2) + 11. Instead of the caster level being capped for
Lesser Dispel, Dispel Magic, Greater Dispel & Mordenkainen
Disjunction it is the full caster level minus the following adjustment:

Bards, Clerics, Druids & dispel traps: -13 / -8 / -3 / -2 (mord trap) respectively.

Sorcerers and Wizards: -16 / -11 / -6 / -2 respectively.

If the dispeller's adjusted caster level is higher than the target's
character level, it is lowered to be the same, to allow a maximum base
chance of 50% to dispel. The Spell Focus bonus then only applies if the
target's character level is less than 6 levels higher.

This solution is probably much more balanced and very easy to script as it doesnt use the caster level of effects on target. It has some flaws but if you want I can help you to script this solution.
               
               

               
            

Legacy_Monsieur T

  • Jr. Member
  • **
  • Posts: 62
  • Karma: +0/-0
Dispel Magic confusion...
« Reply #6 on: January 31, 2012, 02:38:04 am »


               Thanks so much for replying to this, guys.
As usual, I did not explain my situation fully...doh...
I have radically altered almost every aspect of NWN1 to be a re-creation of British Live-Roleplaying rules, specifically my own club "Broadsword" (which I ran for 15 years until I got too old!)
In my NWN game-world, Sorcerers (Mystics) and Bards (Wood Elves) are the only PC spell-casters allowed.
PC Mystics are allowed the use of 1st to 3rd Level Spells (almost all of which I have re-written), while Wood Elves are allowed just 6 Level 1 nature-based spells.
NPC Wizards/Bad Guys are allowed a few higher level spells (up to 5th Level), but PCs are not supposed to be able to dispel them.
No PCs are allowed to advance beyond 5th level, and I have altered the relevant 2das to allow low-level Mystics to learn and cast spells up to 3rd Level from the get-go.
This is why I need a level-based dispelling system:
Dispel 1 nails any duration-based L1 spell
Dispel 2 nails any duration-based L1 OR L2 spell
Dispel 3 nails any duration-based L1, L2 OR L3 spell.
It's mostly important for AoE stuff like L2 Darkness (Drow Darkness), L3 Glyph of Warding (Ward of Power) and also for customized buffing spells (Protection vs Magic I, II and III which are re-written Spell Mantle scripts). And also for spells applied to weapons: L3 Enchant Weapon (Magic Weapon) etc.
If all this sounds as though I can script, then I would hasten to disabuse you of the notion!
Like most folks, I can alter the work of proper scripters (sometimes!) and find help in places like this forum (thanks again!)
Maybe this is just asking too much, though I'll definitely look at all the suggestions that you guys have kindly made (though FunkySwerve's code makes my eyes water!!)

Your quote might help to save certain L4 &L5 spells from dispels, though ShaDoOoW:

"tip: in 1.70, variable "X1_L_IMMUNE_TO_DISPEL" = 10 on AOE object grants undispellability"

Do I try to write this into the impact script for a given spell (like I would for a creature's OnSpawn script), or do I need to make custom AoE spellscripts and alter the relevant 2Das and the existing Bioware dispelling scripts?
Anyway, guys, many thanks for giving me your time and advice,
All the best,
Mr T