Author Topic: How to get the level of spell slot that is being consumed in the current spell casting?  (Read 560 times)

Legacy_wolfzhu

  • Newbie
  • *
  • Posts: 24
  • Karma: +0/-0


               Hello,

I'm how working on a spell casting script and is blocked by such problem. I had tried to get spell level from spells.2da and its metamagic level reference from metamagic.2da, however I still can't get to know if the caster is utilizing his auto-meta feats or his metamagic slots.

Anybody help?
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0


               // Get the metamagic type (METAMAGIC_*) of the last spell cast by the caller
// * Return value if the caster is not a valid object: -1
int GetMetaMagicFeat()

I can elaborate much more if needed. Note that standard bioware scripts flub metamagic treatment with items. It's for this reason that we check metamagic AFTER checking for a spellcast item in our central spellinfo function:


//nSpellclass is passed into the function as a parameter, which is almost always a default of -1
    if (nSpellclass >= 0)
        si.class = nSpellclass;
    else if (GetIsObjectValid(si.item))
        si.class = class_TYPE_INVALID;
    else
        si.class = GetLastSpellCastclass();


    si.school = SFGetSpellSchool(si.id);
    si.clevel = GetCasterLevel(oCaster);
    si.meta   = (si.class == class_TYPE_INVALID ? 0 : GetMetaMagicFeat());

As for the spell level check, we do that at the beginning of the section of code that determines dc, since it's usually a component:


    /* if the spell was cast as a particular class, get the DC for it */
    if (si.class != class_TYPE_INVALID) {
        if ((si.slevel = SFGetInnateSpellLevelByclass(si.class, si.id)) < 0)
            si.slevel = SFGetInnateSpellLevel(si.id);

        if (si.class == class_TYPE_CLERIC  ||
            si.class == class_TYPE_DRUID   ||
            si.class == class_TYPE_PALADIN ||
            si.class == class_TYPE_RANGER) {
            int nReq = (si.slevel * 2) - 1;
            //Domain spell checking
            if (si.class == class_TYPE_CLERIC) {
                // Only need to check for Cleric-Only spells given at a lower level
                switch (si.id) {
                    case SPELL_CURE_SERIOUS_WOUNDS: if (GetHasFeat(FEAT_HEALING_DOMAIN_POWER, oCaster)) nReq = 3; break;
                    case SPELL_HEAL: if (GetHasFeat(FEAT_HEALING_DOMAIN_POWER, oCaster)) nReq = 9; break;
                    case SPELL_TRUE_SEEING: if (GetHasFeat(FEAT_KNOWLEDGE_DOMAIN_POWER, oCaster)) nReq = 7; else if (GetHasFeat(FEAT_ANIMAL_DOMAIN_POWER, oCaster)) nReq = 5; break;
                    case SPELL_DIVINE_POWER: if (GetHasFeat(FEAT_STRENGTH_DOMAIN_POWER, oCaster)) nReq = 5; break;
                    case SPELL_SEARING_LIGHT: if (GetHasFeat(FEAT_SUN_DOMAIN_POWER, oCaster)) nReq = 3; break;
                    case SPELL_FREEDOM_OF_MOVEMENT: if (GetHasFeat(FEAT_TRAVEL_DOMAIN_POWER, oCaster)) nReq = 5; break;
                }
            }
            if (GetLevelByclass(si.class, oCaster) < nReq) {
                FloatingTextStringOnCreature("You do not have enough class levels to cast that spell!", oCaster, FALSE);

                si.id = -1;
                return si;
            }
        }

The SFGetInnateSpellLevelByclass and SFGetInnateSpellLevel functions are taken from Old Man Whistler's spell_func_inc, available on the Vault:
http://nwvault.ign.c....Detail&id=1522

We've modified it to add additional information, but you should still be able to use that version, if you like. Might be more bang than you need for this, though.

Funky
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0


               

wolfzhu wrote...

Hello,

I'm how working on a spell casting script and is blocked by such problem. I had tried to get spell level from spells.2da and its metamagic level reference from metamagic.2da, however I still can't get to know if the caster is utilizing his auto-meta feats or his metamagic slots.

Anybody help?

practically it is not possible without NWNX

The function GetHasSpell could be used as it accounts metamagic but well its not totally accurate for all metamagic types. So it wouldnt work reliably in case of automatic quickened spell.

But to make it:

1) you need to store all spells in OnRest using GetHasSpell, loop 0 to the last spell index in spells.2da, check GetHasSpell and store uses on variable on PC.

2) in spellhook, check if the spell is not cast from item, there is the bug Funky mentioned (unless you have my unofficial patch that solves it) and if thats the case, check current GetHasSpell uses of the GetSpellId and compare to the previous.
if the difference is 3, then the last spell cast was memorized via extend, still or silent that is level +1, if the difference is 1 you need to check GetMetaMagic() (I would presume that if PC has auto quickened he wont memorise it quickened, that would be silly, though possible)

To get spell level is simple and I think you already figured it out, right?
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0


               

ShaDoOoW wrote...
But to make it:

1) you need to store all spells in OnRest using GetHasSpell, loop 0 to the last spell index in spells.2da, check GetHasSpell and store uses on variable on PC.

2) in spellhook, check if the spell is not cast from item, there is the bug Funky mentioned (unless you have my unofficial patch that solves it) and if thats the case, check current GetHasSpell uses of the GetSpellId and compare to the previous.
if the difference is 3, then the last spell cast was memorized via extend, still or silent that is level +1, if the difference is 1 you need to check GetMetaMagic() (I would presume that if PC has auto quickened he wont memorise it quickened, that would be silly, though possible)

To get spell level is simple and I think you already figured it out, right?


Would this account for sorcerers/bards that may use other castings of the same level before casting that spell?
               
               

               
            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0


               good question - and I don't know, quite probably do not - however once this issue is proven, I think there is some workaround applicable, like recalculate the spell uses of all spells if the last cast class is bard/sorc

anyway thats why I said, its practically not possible, there are definitely other issues like spell failure from armor/damage/entangle and I dont know how to account them
               
               

               


                     Modifié par ShaDoOoW, 01 décembre 2011 - 03:49 .
                     
                  


            

Legacy_wolfzhu

  • Newbie
  • *
  • Posts: 24
  • Karma: +0/-0


               OK I see the way to implement. Actually I have nwnx_funcs from MaxRock. All I need to do is just record each slot on rest, and loop from 0 to 9 on spell casting to check which slot was decreased.

Thanks ShaDoOow, Funky and WhiZard!
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0


               I'm assuming the MaxRock funcs is the windows port of acaos' linux plugin, so I'm not sure if you have access to all the functionality of the linux version, but if you do, you definitely do not need to record slots on rest. Here's our listing mechanism from our chat command, by way of example:


string ListSpellsMemorized (object oTarget, object oPC, int nclass=-1, int nLevel=-1) {
    string sMessage = "";

    if (nclass < 0) {
        if (GetLevelByclass(class_TYPE_ASSASSIN, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_ASSASSIN, -1) + "\\n";
        if (GetLevelByclass(class_TYPE_BLACKGUARD, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_BLACKGUARD, -1) + "\\n";
        if (GetLevelByclass(class_TYPE_CLERIC, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_CLERIC, -1) + "\\n";
        if (GetLevelByclass(class_TYPE_DRUID, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_DRUID, -1) + "\\n";
        if (GetLevelByclass(class_TYPE_PALADIN, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_PALADIN, -1) + "\\n";
        if (GetLevelByclass(class_TYPE_RANGER, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_RANGER, -1) + "\\n";
        if (GetLevelByclass(class_TYPE_WIZARD, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_WIZARD, -1) + "\\n";

        if (oTarget == oPC)
            sMessage = COLOR_WHITE + "Your memorized spells:\\n" + sMessage;
        else
            sMessage = COLOR_WHITE + "Memorized spells for " + GetName(oTarget) + ":\\n" + sMessage;

        SendChatLogMessage(oPC, sMessage + COLOR_END, oPC, 5);
        return "";
    }

    if (nLevel < 0) {
        int i, nMax = GetclassSpellLevels(nclass, TRUE);

        for (i = (nMax > 4 ? 0 : 1); i <= nMax; i++)
            sMessage += ListSpellsMemorized(oTarget, oPC, nclass, i);

        return sMessage;
    }

    string sSpell, sLeft, sTotal, sServer = GetLocalString(GetModule(), "ServerNumber");
    SQLExecDirect("DELETE FROM sort_" + sServer);
    string sSQL = "INSERT INTO sort_" + sServer + " VALUES (?, ?)";

    sMessage = COLOR_ORANGE + GetclassTypeName(nclass) + " level " + IntToString(nLevel) + ":\\n";
    int i, nSpells = GetMaxSpellSlots(oTarget, nclass, nLevel);
    struct MemorizedSpellSlot mss;

    for (i = 0; i < nSpells; i++) {
        mss = GetMemorizedSpell(oTarget, nclass, nLevel, i);

        if (mss.id < 0)
            sSpell = "(empty)";
        else
            sSpell = SFGetSpellName(mss.id);

        if (mss.meta != 0) {
            switch (mss.meta) {
                case METAMAGIC_EMPOWER:  sSpell = "[Empower] "  + sSpell; break;
                case METAMAGIC_EXTEND:   sSpell = "[Extend] "   + sSpell; break;
                case METAMAGIC_MAXIMIZE: sSpell = "[Maximize] " + sSpell; break;
                case METAMAGIC_QUICKEN:  sSpell = "[Quicken] "  + sSpell; break;
                case METAMAGIC_SILENT:   sSpell = "[Silent] "   + sSpell; break;
                case METAMAGIC_STILL:    sSpell = "[Still] "    + sSpell; break;
            }
        }

        SQLExecStatement(sSQL, sSpell, IntToString(mss.ready));
    }

    SQLExecDirect("SELECT sort_key, SUM(sort_val), COUNT(*) FROM sort_" + sServer + " GROUP BY sort_key ORDER BY sort_key");

    while (SQLFetch() != SQL_ERROR) {
        sSpell = SQLDecodeSpecialChars(SQLGetData(1));
        sLeft  = SQLGetData(2);
        sTotal = SQLGetData(3);

        if (GetSubString(sSpell, 0, 1) == "[")
            sMessage += COLOR_GOLD + "    ";
        else
            sMessage += COLOR_LT_YELLOW + "    ";

        sMessage += sSpell + " " + COLOR_WHITE + " [" + sLeft + "/" + sTotal + "]\\n";
    }

    return sMessage;
}

The core functionality for looping memorized spells is in this loop:


    for (i = 0; i < nSpells; i++) {

        mss = GetMemorizedSpell(oTarget, nclass, nLevel, i);

Here's the chunk of functions the plugin in questoin has relating to spells, by way of comparison. The only one you need for this is GetMemorizedSpell.


/* Check if oCreature knows the specified spell (this will only work for arcane casters) */
int GetKnowsSpell (int nSpellId, object oCreature, int nclass=class_TYPE_INVALID);

/* Get the spell at nIndex in nSpellLevel in oCreature's spellbook from nclass. */
int GetKnownSpell (object oCreature, int nclass, int nSpellLevel, int nIndex);

/* Set the spell at nIndex in nSpellLevel in oCreature's spellbook from nclass. */
int SetKnownSpell (object oCreature, int nclass, int nSpellLevel, int nIndex, int nSpellId);

/* Get the total number of known spells for oCreature's spellbook in nclass at nSpellLevel. */
int GetTotalKnownSpells (object oCreature, int nclass, int nSpellLevel);

/* Add a new spell to oCreature's spellbook for nclass. */
int AddKnownSpell (object oCreature, int nclass, int nSpellLevel, int nSpellId);

/* Remove a spell from oCreature's spellbook for nclass. */
int RemoveKnownSpell (object oCreature, int nclass, int nSpellLevel, int nSpellId);

/* Replace a spell in oCreature's spellbook for nclass. */
int ReplaceKnownSpell (object oCreature, int nclass, int nOldSpell, int nNewSpell);

/* Get information about one of oCreature's memorized spells. */
struct MemorizedSpellSlot GetMemorizedSpell (object oCreature, int nclass, int nLevel, int nIndex);

/* Set information about oCreature's memorized spells. */
int SetMemorizedSpell (object oCreature, int nclass, int nLevel, int nIndex, struct MemorizedSpellSlot mss);

/* Clear one of oCreature's memorized spells. */
int ClearMemorizedSpell (object oCreature, int nclass, int nLevel, int nIndex);

/* Get the maximum number of oCreature's spell slots at nclass and nSpellLevel. */
int GetMaxSpellSlots (object oCreature, int nclass, int nSpellLevel);

/* Get the number of bonus spell slots oCreature has at nclass and nSpellLevel.
 * This value only includes bonus slots from equipment and effects, not from
 * having a high ability score. */
int GetBonusSpellSlots (object oCreature, int nclass, int nSpellLevel);

/* Get the number of remaining spell slots oCreature has at nclass and
 * nSpellLevel (only applies for bards and sorcerers) */
int GetRemainingSpellSlots (object oCreature, int nclass, int nSpellLevel);

/* Set the number of remaining spell slots oCreature has at nclass and
 * nSpellLevel (only applies for bards and sorcerers) */
int SetRemainingSpellSlots (object oCreature, int nclass, int nSpellLevel, int nSlots);

/* Get a string containing all remaining spell uses for oCreature. */
string GetAllMemorizedSpells (object oCreature);

/* Restore the remaining spell uses for oCreature from the given string. */
int RestoreReadySpells (object oCreature, string sSpells);

/* Gets one of oCreature's cleric domains (either 1 or 2). */
int GetClericDomain (object oCreature, int nIndex);

/* Sets one of oCreature's cleric domains. */
int SetClericDomain (object oCreature, int nIndex, int nDomain);

/* Gets whether or not oCreature has a specialist school of wizardry. */
int GetWizardSpecialization (object oCreature);

/* Sets oCreature's wizard specialist school. */
int SetWizardSpecialization (object oCreature, int nSchool);

Funky
               
               

               
            

Legacy_wolfzhu

  • Newbie
  • *
  • Posts: 24
  • Karma: +0/-0


               

FunkySwerve wrote...

I'm assuming the MaxRock funcs is the windows port of acaos' linux plugin, so I'm not sure if you have access to all the functionality of the linux version, but if you do, you definitely do not need to record slots on rest. Here's our listing mechanism from our chat command, by way of example:


string ListSpellsMemorized (object oTarget, object oPC, int nclass=-1, int nLevel=-1) {
    string sMessage = "";

    if (nclass < 0) {
        if (GetLevelByclass(class_TYPE_ASSASSIN, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_ASSASSIN, -1) + "n";
        if (GetLevelByclass(class_TYPE_BLACKGUARD, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_BLACKGUARD, -1) + "n";
        if (GetLevelByclass(class_TYPE_CLERIC, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_CLERIC, -1) + "n";
        if (GetLevelByclass(class_TYPE_DRUID, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_DRUID, -1) + "n";
        if (GetLevelByclass(class_TYPE_PALADIN, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_PALADIN, -1) + "n";
        if (GetLevelByclass(class_TYPE_RANGER, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_RANGER, -1) + "n";
        if (GetLevelByclass(class_TYPE_WIZARD, oTarget) > 0)
            sMessage += ListSpellsMemorized(oTarget, oPC, class_TYPE_WIZARD, -1) + "n";

        if (oTarget == oPC)
            sMessage = COLOR_WHITE + "Your memorized spells:n" + sMessage;
        else
            sMessage = COLOR_WHITE + "Memorized spells for " + GetName(oTarget) + ":n" + sMessage;

        SendChatLogMessage(oPC, sMessage + COLOR_END, oPC, 5);
        return "";
    }

    if (nLevel < 0) {
        int i, nMax = GetclassSpellLevels(nclass, TRUE);

        for (i = (nMax > 4 ? 0 : 1); i <= nMax; i++)
            sMessage += ListSpellsMemorized(oTarget, oPC, nclass, i);

        return sMessage;
    }

    string sSpell, sLeft, sTotal, sServer = GetLocalString(GetModule(), "ServerNumber");
    SQLExecDirect("DELETE FROM sort_" + sServer);
    string sSQL = "INSERT INTO sort_" + sServer + " VALUES (?, ?)";

    sMessage = COLOR_ORANGE + GetclassTypeName(nclass) + " level " + IntToString(nLevel) + ":n";
    int i, nSpells = GetMaxSpellSlots(oTarget, nclass, nLevel);
    struct MemorizedSpellSlot mss;

    for (i = 0; i < nSpells; i++) {
        mss = GetMemorizedSpell(oTarget, nclass, nLevel, i);

        if (mss.id < 0)
            sSpell = "(empty)";
        else
            sSpell = SFGetSpellName(mss.id);

        if (mss.meta != 0) {
            switch (mss.meta) {
                case METAMAGIC_EMPOWER:  sSpell = "[Empower] "  + sSpell; break;
                case METAMAGIC_EXTEND:   sSpell = "[Extend] "   + sSpell; break;
                case METAMAGIC_MAXIMIZE: sSpell = "[Maximize] " + sSpell; break;
                case METAMAGIC_QUICKEN:  sSpell = "[Quicken] "  + sSpell; break;
                case METAMAGIC_SILENT:   sSpell = "[Silent] "   + sSpell; break;
                case METAMAGIC_STILL:    sSpell = "[Still] "    + sSpell; break;
            }
        }

        SQLExecStatement(sSQL, sSpell, IntToString(mss.ready));
    }

    SQLExecDirect("SELECT sort_key, SUM(sort_val), COUNT(*) FROM sort_" + sServer + " GROUP BY sort_key ORDER BY sort_key");

    while (SQLFetch() != SQL_ERROR) {
        sSpell = SQLDecodeSpecialChars(SQLGetData(1));
        sLeft  = SQLGetData(2);
        sTotal = SQLGetData(3);

        if (GetSubString(sSpell, 0, 1) == "[")
            sMessage += COLOR_GOLD + "    ";
        else
            sMessage += COLOR_LT_YELLOW + "    ";

        sMessage += sSpell + " " + COLOR_WHITE + " [" + sLeft + "/" + sTotal + "]n";
    }

    return sMessage;
}

The core functionality for looping memorized spells is in this loop:


    for (i = 0; i < nSpells; i++) {

        mss = GetMemorizedSpell(oTarget, nclass, nLevel, i);

Here's the chunk of functions the plugin in questoin has relating to spells, by way of comparison. The only one you need for this is GetMemorizedSpell.


/* Check if oCreature knows the specified spell (this will only work for arcane casters) */
int GetKnowsSpell (int nSpellId, object oCreature, int nclass=class_TYPE_INVALID);

/* Get the spell at nIndex in nSpellLevel in oCreature's spellbook from nclass. */
int GetKnownSpell (object oCreature, int nclass, int nSpellLevel, int nIndex);

/* Set the spell at nIndex in nSpellLevel in oCreature's spellbook from nclass. */
int SetKnownSpell (object oCreature, int nclass, int nSpellLevel, int nIndex, int nSpellId);

/* Get the total number of known spells for oCreature's spellbook in nclass at nSpellLevel. */
int GetTotalKnownSpells (object oCreature, int nclass, int nSpellLevel);

/* Add a new spell to oCreature's spellbook for nclass. */
int AddKnownSpell (object oCreature, int nclass, int nSpellLevel, int nSpellId);

/* Remove a spell from oCreature's spellbook for nclass. */
int RemoveKnownSpell (object oCreature, int nclass, int nSpellLevel, int nSpellId);

/* Replace a spell in oCreature's spellbook for nclass. */
int ReplaceKnownSpell (object oCreature, int nclass, int nOldSpell, int nNewSpell);

/* Get information about one of oCreature's memorized spells. */
struct MemorizedSpellSlot GetMemorizedSpell (object oCreature, int nclass, int nLevel, int nIndex);

/* Set information about oCreature's memorized spells. */
int SetMemorizedSpell (object oCreature, int nclass, int nLevel, int nIndex, struct MemorizedSpellSlot mss);

/* Clear one of oCreature's memorized spells. */
int ClearMemorizedSpell (object oCreature, int nclass, int nLevel, int nIndex);

/* Get the maximum number of oCreature's spell slots at nclass and nSpellLevel. */
int GetMaxSpellSlots (object oCreature, int nclass, int nSpellLevel);

/* Get the number of bonus spell slots oCreature has at nclass and nSpellLevel.
 * This value only includes bonus slots from equipment and effects, not from
 * having a high ability score. */
int GetBonusSpellSlots (object oCreature, int nclass, int nSpellLevel);

/* Get the number of remaining spell slots oCreature has at nclass and
 * nSpellLevel (only applies for bards and sorcerers) */
int GetRemainingSpellSlots (object oCreature, int nclass, int nSpellLevel);

/* Set the number of remaining spell slots oCreature has at nclass and
 * nSpellLevel (only applies for bards and sorcerers) */
int SetRemainingSpellSlots (object oCreature, int nclass, int nSpellLevel, int nSlots);

/* Get a string containing all remaining spell uses for oCreature. */
string GetAllMemorizedSpells (object oCreature);

/* Restore the remaining spell uses for oCreature from the given string. */
int RestoreReadySpells (object oCreature, string sSpells);

/* Gets one of oCreature's cleric domains (either 1 or 2). */
int GetClericDomain (object oCreature, int nIndex);

/* Sets one of oCreature's cleric domains. */
int SetClericDomain (object oCreature, int nIndex, int nDomain);

/* Gets whether or not oCreature has a specialist school of wizardry. */
int GetWizardSpecialization (object oCreature);

/* Sets oCreature's wizard specialist school. */
int SetWizardSpecialization (object oCreature, int nSchool);

Funky


Thanks. These are the interfaces I do have. But I have another question: how to deal with the case that I'm casting a spell of sorcerer class?