Author Topic: Alternate GetMetaMagicFeat(); systems  (Read 458 times)

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« on: June 08, 2012, 07:32:42 pm »


                 At the moment, I'm using a wrapper function for it to allow for automatic versions of the extend, maximize, and empower feats, and filter ou the item MetaMagic exploit.

//// Expanded Metamagic check that filters out the item exploit.
int FB_GetMetaMagicFeat()
{
  object oItem = GetSpellCastItem();

  if (GetIsObjectValid (oItem))
       {
         return GetLocalInt (oItem, "FB_ITEM_METAMAGIC_FEAT");
       }

  int nMetaMagic = GetMetaMagicFeat();

  // add only if not already present.
  if (GetHasFeat (FEAT_DEITY_AUTOMATIC_EMPOWER))  nMetaMagic |= METAMAGIC_EMPOWER;
  if (GetHasFeat (FEAT_DEITY_AUTOMATIC_EXTEND))   nMetaMagic |= METAMAGIC_EXTEND;
  if (GetHasFeat (FEAT_DEITY_AUTOMATIC_MAXIMIZE)) nMetaMagic |= METAMAGIC_MAXIMIZE;

  return nMetaMagic;
}


 It's not checked against with an ==, rather, using & to allow for multiple MetaMagic types in a single return. An example of that being:

int nMetaMagic = FB_GetMetaMagicFeat();
if (nMetaMagic & METAMAGIC_MAXIMIZE) {/* do maximized stuff*/ }
if (nMetaMagic & METAMAGIC_EMPOWER) {/*Empower the spell, even if already maximized*/}



  I know ShadoOow is using a version that allows for overrides and filters out the exploit as well, but I was curious if there were any other ways that people have been doing this check, or other things that they've been adding in to check for as well.
  Were my little server running epic levels, I'd likely have added in a spell level check to it, and added automatic versions of those three as epic feats for PCs to take as well, but little point in it with a 20 cap.
               
               

               


                     Modifié par Failed.Bard, 08 juin 2012 - 06:33 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« Reply #1 on: June 09, 2012, 12:38:57 am »


               PRC uses this system long time ago

all empower/maximize calls are directed via MaxmizeOrEmpower function where & is used. Extend metamagic is checked also via & though Im not sure how they are overriding the metamagic used. But they do have the metamagic itempoperty I think.

I was and still am considering this into my spell engine from community patch as you can already override metamagic (with any value you want) but current scripts doesnt allow multiple metamagic yet. So far considered scripts checking metamagic via biwtise AND confusing and harder to read.
               
               

               
            

Legacy_Alex Warren

  • Sr. Member
  • ****
  • Posts: 326
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« Reply #2 on: June 09, 2012, 07:27:48 am »


               Here's the PRC function. I tried to optimize it a bit but it's still far from being perfect. It checks for item metamagic and it's also used for sudden metamagic, divine metamagic and some class feats. Some of them stack '<img'>

int PRCGetMetaMagicFeat(object oCaster = OBJECT_SELF, int bClearFeatFlags = TRUE)
{[list]
    int nOverride = GetLocalInt(oCaster, PRC_METAMAGIC_OVERRIDE);
    if(nOverride)
    {[list]
        if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found override metamagic = "+IntToString(nOverride)+", original = "+IntToString(GetMetaMagicFeat()));
        return nOverride;
    [/list]}

    object oItem = PRCGetSpellCastItem(oCaster);

    // we assume that we are casting from an item, if the item is valid and the item belongs to oCaster
    // however, we cannot be sure because of Bioware's inadequate implementation of GetSpellCastItem
    if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster)
    {[list]
        int nFeat;

        //check item for metamagic
        itemproperty ipTest = GetFirstItemProperty(oItem);
        while(GetIsItemPropertyValid(ipTest))
        {[list]
            if(GetItemPropertyType(ipTest) == ITEM_PROPERTY_CAST_SPELL_METAMAGIC)
            {[list]
                int nSubType = GetItemPropertySubType(ipTest);
                nSubType = StringToInt(Get2DACache("iprp_spells", "SpellIndex", nSubType));
                if(nSubType == PRCGetSpellId(oCaster))
                {[list]
                    int nCostValue = GetItemPropertyCostTableValue(ipTest);
                    if(nCostValue == -1 && DEBUG)
                        DoDebug("Problem examining itemproperty");
                    switch(nCostValue)
                    {[list]
                        //bitwise "addition" equivalent to nFeat = (nFeat | nSSFeat)
                        case 1: nFeat |= METAMAGIC_QUICKEN;  break;
                        case 2: nFeat |= METAMAGIC_EMPOWER;  break;
                        case 3: nFeat |= METAMAGIC_EXTEND;   break;
                        case 4: nFeat |= METAMAGIC_MAXIMIZE; break;
                        case 5: nFeat |= METAMAGIC_SILENT;   break;
                        case 6: nFeat |= METAMAGIC_STILL;    break;
                    [/list]}
                [/list]}
            [/list]}
            ipTest = GetNextItemProperty(oItem);
        [/list]}
        if (DEBUG) DoDebug("PRCGetMetaMagicFeat: item casting with item = "+GetName(oItem)+", found metamagic = "+IntToString(nFeat));

        //Hellfire Infusion - doesn't work on scrolls and potions
        int nType = GetBaseItemType(oItem);
        if(nType != BASE_ITEM_POTIONS && nType != BASE_ITEM_ENCHANTED_POTION
        && nType != BASE_ITEM_SCROLL && nType != BASE_ITEM_ENCHANTED_SCROLL)
        {[list]
            nFeat |= GetLocalInt(oCaster, "PRC_HF_Infusion");
            if(bClearFeatFlags) DeleteLocalInt(oCaster, "PRC_HF_Infusion");
        [/list]}

        //apply metamagic adjustment (chanell spell)
        nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT);
        return nFeat;
    [/list]}

    if(GetLocalInt(oCaster, "PRC_SPELL_EVENT"))
    {[list]
        if (DEBUG) DoDebug("PRCGetMetaMagicFeat: found spell event metamagic = "+IntToString(GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC"))+", original = "+IntToString(GetMetaMagicFeat()));
        return GetLocalInt(oCaster, "PRC_SPELL_METAMAGIC");
    [/list]}

    int nFeat = GetMetaMagicFeat();
    if(nFeat == METAMAGIC_ANY)
        // work around for spontaneous casters (bard or sorcerer) having all metamagic turned on when using ActionCastSpell*
        nFeat = METAMAGIC_NONE;

    nFeat |= GetLocalInt(oCaster, PRC_METAMAGIC_ADJUSTMENT);

    int nclass = PRCGetLastSpellCastclass(oCaster);
    // Suel Archanamach's Extend spells they cast on themselves.
    // Only works for Suel Spells, and not any other caster type they might have
    // Since this is a spellscript, it assumes OBJECT_SELF is the caster
    if(nclass == class_TYPE_SUEL_ARCHANAMACH
    && GetLevelByclass(class_TYPE_SUEL_ARCHANAMACH) >= 3)
    {[list]
        // Check that they cast on themselves
        // if (oCaster == PRCGetSpellTargetObject())
        if(oCaster == PRCGetSpellTargetObject(oCaster))
        {[list]
            // Add extend to the metamagic feat using bitwise math
            nFeat |= METAMAGIC_EXTEND;
        [/list]}
    [/list]}
    // Magical Contraction, Truenaming Utterance
    if(GetHasSpellEffect(UTTER_MAGICAL_CONTRACTION_R, oCaster))
    //(GetLocalInt(oCaster, "TrueMagicalContraction"))
    {[list]
        nFeat |= METAMAGIC_EMPOWER;
    [/list]}
    // Sudden Metamagic
    int nSuddenMeta = GetLocalInt(oCaster, "SuddenMeta");
    if(nSuddenMeta)
    {[list]
        nFeat |= nSuddenMeta;
        if(bClearFeatFlags)
            DeleteLocalInt(oCaster, "SuddenMeta");
    [/list]}

    int nDivMeta = GetLocalInt(oCaster, "DivineMeta");
    if(nDivMeta)
    {[list]
        if(GetIsDivineclass(nclass, oCaster))
        {[list]
            nFeat |= nDivMeta;
            if(bClearFeatFlags)
                DeleteLocalInt(oCaster, "DivineMeta");
        [/list]}
    [/list]}

    // if (DEBUG) DoDebug("PRCGetMetaMagicFeat: returning " +IntToString(nFeat));
    return nFeat;
[/list]}

               
               

               


                     Modifié par Alex Warren, 09 juin 2012 - 06:37 .
                     
                  


            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« Reply #3 on: June 09, 2012, 09:41:29 am »


               

object oItem = PRCGetSpellCastItem(oCaster);

// we assume that we are casting from an item, if the item is valid and the item belongs to oCaster
// however, we cannot be sure because of Bioware's inadequate implementation of GetSpellCastItem
if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster)


  I hadn't thought of the extra check for the item possessor on the spell cast item.

  What conditions have been found that GetSpellCastItem()  wasn't returning properly?  While it likely wouldn't have much effect on my metamagic check, it might in my UMD check script.
               
               

               
            

Legacy_Alex Warren

  • Sr. Member
  • ****
  • Posts: 326
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« Reply #4 on: June 09, 2012, 01:27:10 pm »


               

Failed.Bard wrote...

object oItem = PRCGetSpellCastItem(oCaster);

// we assume that we are casting from an item, if the item is valid and the item belongs to oCaster
// however, we cannot be sure because of Bioware's inadequate implementation of GetSpellCastItem
if(GetIsObjectValid(oItem) && GetItemPossessor(oItem) == oCaster)


  I hadn't thought of the extra check for the item possessor on the spell cast item.

  What conditions have been found that GetSpellCastItem()  wasn't returning properly?  While it likely wouldn't have much effect on my metamagic check, it might in my UMD check script.



The check is there probably because of this:
* In a proper implementation GetSpellCastItem() word return OBJECT_INVALID, when called outside of an item spell script,
 * But this is not the case. GetSpellCastItem() will always return the item, from which (according to Bioware's knowledge)
 * the last item spell was cast. As long as the item still exists, the call to GetSpellCastItem() will always return a valid item,
 * even if the item spell long expired and we are casting a completely differnt spell. So GetSpellCastItem() practically
 * NEVER returns an invalid object. [We only get an invalid object, when we didn't yet cast any item spell at all]

This was important for custom PRC spellcasting system, but I'm not sure if it's still needed. I left the check 'just in case'.
               
               

               
            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« Reply #5 on: June 09, 2012, 01:50:44 pm »


               An interesting bug.  It looks like, after a quick bit of testing, that the GetSpellCastItem() works properly within spell scripts at least.
 I put a debug message into a spell script to check, and casting by scroll or wand registered, but the casting after by normal non-item spell means registered as the item being invalid.  With the scroll it would have either way, but for the wand it was still in the PCs possession, and GetSpellCastItem() still returned OBJECT_INVALID in the later "normal" casts.

 Likely instead of clearing it at the end of the spell script as claimed in the script notes, it does it at the start of the spell.
 That, or it was fixed in a later patch and not noted.
               
               

               
            

Legacy_WhiZard

  • Hero Member
  • *****
  • Posts: 2149
  • Karma: +0/-0
Alternate GetMetaMagicFeat(); systems
« Reply #6 on: June 09, 2012, 05:07:41 pm »


               

Failed.Bard wrote...
Likely instead of clearing it at the end of the spell script as claimed in the script notes, it does it at the start of the spell.


GetCasterLevel() adjusts the caster level when the spell is cast, and if cast by item  will restore the original caster level (if higher) after the script has been completed.  However GetCasterLevel() is relative to the object running the script, so there is no conflict if multiple characters cast spells at the same time.  I am not sure if many of the other functions are relative to the object running the script and thus they may confuse near simultaneous castings.

EDIT: I have looked at this using a belt to cast and the item still returned after the spell script had fired.  The return was relative to the object running the script (so the item possessor would always be the same as the object running the script command to get the spell cast item).  After looking through all the commands with "Get" and "Spell" in them, the only one that was not relative to the object running the script was GetLastSpellCastclass() and they all were relative to the object running the script.

EDIT2: GetLastSpellCastclass() had an unusual error return value of 255, and I wasn't able to distinguish it as an error value at the time. 
               
               

               


                     Modifié par WhiZard, 09 juin 2012 - 05:01 .