Author Topic: Get Modified Attack Roll  (Read 409 times)

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Get Modified Attack Roll
« on: July 23, 2011, 06:33:12 pm »


               Hey all. I'm looking for some way to find out what a PC's modified attack roll is after all buffs, item bonuses, etc are taken into account (without looking at the character sheet!). Something like GetBaseAttackBonus(), but which returns the true AB they would have in combat.

Has anyone come up with a way to accurately do this?
               
               

               
            

Legacy_Failed.Bard

  • Hero Member
  • *****
  • Posts: 1409
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #1 on: July 23, 2011, 07:36:17 pm »


               If there was a script for that (and) applying weapon damage that could be accessed (Even Bioware had to hack in the later added attacks, AA's Hail of Arrows being a perfect example), there'd be quite a few complete rewrites of the whole combat engine out already.
Hopefully someone does know of one, or a way to access the stored numbers on the character, because I'd love to know about it as well.
               
               

               


                     Modifié par Failed.Bard, 24 juillet 2011 - 01:26 .
                     
                  


            

Legacy_Xardex

  • Sr. Member
  • ****
  • Posts: 414
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #2 on: July 23, 2011, 08:54:49 pm »


               BAB

+ ability modifier
check weapon type, check finesse&zen archery and use the proper modifier

+ check weapon properties
Attack Bonus, Enchantments, use higher one... And don't forget the Decreased ... properties.

+ feats
Check if weapon matches weapon focus feats, epic prowess...

The above shouldn't be too hard to shape up, but...

+ effects (buffs, debuffs)
This is more difficult... Im not sure if you can get the value of an effect like you can get its type.

You also must check weapon properties and effects against the 20 cap.
               
               

               


                     Modifié par Xardex, 23 juillet 2011 - 07:56 .
                     
                  


            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #3 on: July 24, 2011, 02:32:43 am »


               Here's our function set for List Ab. It includes a lot of custom hacks like Spell Immunity(TRUE_STRIKE), which is how we add stacking ab over 20, but it should still help illuminate the various checks you have to do. If memory serves it still doesn't calcluate monk glove ab accurately - that's been on the todo for a while now.


/*
* This method returns the size of a weaponBaseType
* nWeaponBaseType = the base type id of a weapon.
* returns WSIZE_TINY = 1 or WSIZE_SMALL = 2 or WSIZE_MEDIUM = 3 or WSIZE_LARGE = 4 or WSIZE_HUGE = 5
*/
int GetWeaponSize(int nWeapBaseType){
    return StringToInt(Get2DAString("BASEITEMS","WeaponSize",nWeapBaseType));
}

/*
* This method checks if the weapon is considered "small" for the oPC
* If this is the case, the method returns 2 , otherwise 0 is returned.
* - oPC = The toon
* - oWeaponOffHand = The weapon
*/
int GetIsOffHandWeaponSmallModif(object oPC, object oWeaponOffHand){

    int nCSize = GetCreatureSize(oPC);
    int nWeaponBaseItem = GetBaseItemType(oWeaponOffHand);
    int nWSize = GetWeaponSize(nWeaponBaseItem);
    int nRetVal = 0;
    switch (nWSize) {
            case  1:  if(nCSize > CREATURE_SIZE_TINY)   nRetVal = 2;   break;
            case  2:  if(nCSize > CREATURE_SIZE_SMALL)  nRetVal = 2;   break;
            case  3:  if(nCSize > CREATURE_SIZE_MEDIUM) nRetVal = 2;   break;
            case  4:  if(nCSize > CREATURE_SIZE_LARGE)  nRetVal = 2;   break;
            case  5:  if(nCSize > CREATURE_SIZE_HUGE)   nRetVal = 2;   break;
    }
    return nRetVal;
}
/*
* This method returns the maximul weapon enhancement that is on a weapon (including the fixed weapon enhancement)
* - oPC = The toon
* - oWeapon = The weapon
*/
int getWeaponEnhancementBonus(object oPC,object oWeapon){
    itemproperty ip = GetFirstItemProperty(oWeapon);
    int nType, nFound, nReturn;
    while (GetIsItemPropertyValid(ip)) {
        nType = GetItemPropertyType(ip);
        if (nType == ITEM_PROPERTY_ENHANCEMENT_BONUS ||
            nType == ITEM_PROPERTY_ATTACK_BONUS) {

            nFound = GetItemPropertyCostTableValue(ip);
            if (nFound > nReturn)
                nReturn = nFound;
        }
        ip = GetNextItemProperty(oWeapon);
    }
    return nReturn;
}
/*
* This method calculates the dual wielding modifier
* - oPC = The toon
* - nSlot =  INVENTORY_SLOT_RIGHTHAND or INVENTORY_SLOT_LEFTHAND
* - nOffSmall =The amount that can be substracted from the dualing penalties (Currently only should be 0 or 2 for small weapons)
*/
int GetDualWieldingModifier(object oPC,int nSlot, int nOffSmall){
      int nTotal;
      int nTwoWeaponFight, nAmbidexTerity;
        if(nSlot == INVENTORY_SLOT_RIGHTHAND){
            nTotal = -6 + nOffSmall;
        }
        else if (nSlot == INVENTORY_SLOT_LEFTHAND){
            nTotal = -10 + nOffSmall;
            if(GetHasFeat(FEAT_AMBIDEXTERITY,oPC)){
                nTotal = nTotal + 4;
            }
        }
      if(GetHasFeat(FEAT_TWO_WEAPON_FIGHTING,oPC)){
        nTotal = nTotal +2;
      }
      return nTotal;
}


// This method finds all the MAGICAL AB increases on a toon.
// oPC = The toon
// returns the amount
// Stores the "result-strings" (for the feedback in the combat log) in a Local string on the oPC with name = gol_tmp_ab
// DO NOT FORGET TO DELETE TEMPSTRING!
int GetMagicABIncrease(object oPC){
   int nAmount = 0;
   string sResultStrings;
   string sName;
   effect eEff;
    for (eEff = GetFirstEffect(oPC); GetIsEffectValid(eEff); eEff = GetNextEffect(oPC)) {
        if (GetEffectType(eEff) == EFFECT_TYPE_ATTACK_INCREASE) {
            nAmount += GetEffectInteger(eEff, 0);
            int nEffectId =  GetEffectSpellId(eEff);
            if(nEffectId == HGEFFECT_BARDSONG_AB_00 ||
               nEffectId == HGEFFECT_BARDSONG_AB_01 ||
               nEffectId == HGEFFECT_BARDSONG_AB_03 ||
               nEffectId == HGEFFECT_BARDSONG_AB_04 ||
               nEffectId == HGEFFECT_BARDSONG_AB_05 ||
               nEffectId == HGEFFECT_BARDSONG_AB_06 ||
               nEffectId == HGEFFECT_BARDSONG_AB_07){
               sName = "Bard Song";
            }
            else{
               sName = SFGetSpellName(GetEffectSpellId(eEff));
            }
            if(GetStringLength(sName) <= 0){
                    sName = "generic";
            }
            sResultStrings += (COLOR_GREEN +"         "+ sName + " : +"+IntToString(GetEffectInteger(eEff, 0))+"\\n");
        }
    }
    SetLocalString(oPC,"gol_tmp_ab",sResultStrings);
    return nAmount;
}

/*
* This method Lists the attack bonus for a weapon
* - oPC = The actual toon
* - oWeapon = The weapon
* - nSlot = INVENTORY_SLOT_RIGHTHAND or INVENTORY_SLOT_LEFTHAND
* - nDualing = If the toon is dualing melee weapons, make this 1
* - nOffSmall = The amount that can be substracted from the dualing penalties (Currently only can be 0 or 2 for small weapons)
*/
string ListAttackBonusForWeapon (object oPC, object oWeapon, int nSlot, int nDualing, int nOffSmall) {
    /* include +20 magical bonus always */
    int nBAB = GetBaseAttackBonus(oPC), nHD = GetHitDice(oPC);
    int nEBAB, nLBAB, nWF, nGWF, nEWF, nLWF, nPWF, nEP, nCW, nWM, nAA, nAbil, nDual, nSize, nMagicIncrease, nWeaponModifs ,nOther;
    string sTempIncMagicAbStrings,sTempDecMagicAbStrings;
    if(nDualing > 0){
        nDual = GetDualWieldingModifier(oPC,nSlot,nOffSmall);
    }

    if (nHD == 40) {
        nBAB -= 30;
        nEBAB = 10;
        nLBAB = GetLocalInt(oPC, "LegendaryBAB");
    } else if (nHD == 64) {
        nBAB -= 50;
        nEBAB = 50;
    } else if (nHD > 20) {
        nEBAB = (nHD - 19) / 2;
        nBAB -= nEBAB;
    }

    int nBaseItem = BASE_ITEM_GLOVES;
    if (GetIsObjectValid(oWeapon))
        nBaseItem = GetBaseItemType(oWeapon);

    nEP   = ListAttackBonusABFeat(oPC, FEAT_EPIC_PROWESS,                      1);
    nWF   = ListAttackBonusABFeat(oPC, GetWeaponFocusFeat(nBaseItem),          1);
    nGWF  = ListAttackBonusABFeat(oPC, GetWeaponGreaterFocusFeat(nBaseItem),   1);
    nEWF  = ListAttackBonusABFeat(oPC, GetWeaponEpicFocusFeat(nBaseItem),      2);
    nLWF  = ListAttackBonusABFeat(oPC, GetWeaponLegendaryFocusFeat(nBaseItem), (nEP ? 3 : 2));
    nPWF  = 0;
    nAbil = GetAttackBonusAdjustment(oPC, oWeapon, GetItemIsRangedWeapon(oWeapon)) - (nGWF + nLWF);
    nSize = GetCreatureSizeACModifier(oPC);

    if (nBaseItem != BASE_ITEM_GLOVES) {
        nCW    = GetSkillRank(SKILL_CRAFT_WEAPON, oPC) / 40;
        nAbil -= nCW;
    }

    int nWMLevel = GetLevelByclass(class_TYPE_WEAPON_MASTER, oPC);

    if (nWMLevel >= 5 && ListAttackBonusABFeat(oPC, GetWeaponOfChoiceFeat(nBaseItem), 1) > 0) {
        nWM++;

        if (nWMLevel >= 10) {
            nWM += (nWMLevel - 10) / 3;

            if (GetIsPC(oPC)) {
                nWM++;
                nAbil--;

                if (nWMLevel == 29) {
                    nAbil--;
                    nWM++;
                } else if (nWMLevel == 30) {
                    nWM += 3;
                    nAbil -= 3;
                }
            }
        }
    }

    if (GetHasSpellImmunity(SPELL_TRUE_STRIKE, oPC)) {
        int nTrue = 0;
        effect eEff;

        for (eEff = GetFirstEffect(oPC); GetIsEffectValid(eEff); eEff = GetNextEffect(oPC)) {
            if (GetEffectType(eEff) == EFFECT_TYPE_SPELL_IMMUNITY &&
                GetEffectInteger(eEff, 0) == SPELL_TRUE_STRIKE) {

                int nTrueBuff = GetEffectInteger(eEff, 1);
                if (nTrueBuff > nTrue)
                    nTrue = nTrueBuff;
            }
        }

        nTrue -= nWM;

        if (nTrue > 0) {
            nOther += nTrue;
            nAbil  -= nTrue;
        }
    }

    if (GetBaseItemType(oWeapon) == BASE_ITEM_SHORTBOW || GetBaseItemType(oWeapon) == BASE_ITEM_LONGBOW)
        nAA = (GetLevelByclass(class_TYPE_ARCANE_ARCHER, oPC) + 1) / 2;


    nMagicIncrease = GetMagicABIncrease(oPC);
        if(nMagicIncrease >0){
          sTempIncMagicAbStrings = GetLocalString(oPC,"gol_tmp_ab");
        }
        DeleteLocalString(oPC,"gol_tmp_ab");

        nWeaponModifs = getWeaponEnhancementBonus(oPC,oWeapon);

        int nMagicTotal = nMagicIncrease + nWeaponModifs;
        int nMagicMax = 20;

        if(nMagicTotal > nMagicMax)
            nMagicTotal = nMagicMax;
        if(nMagicTotal < -20)
            nMagicTotal = -20;

    int nAB = nBAB + nEBAB + nLBAB + nWF + nGWF + nEWF + nLWF + nPWF + nEP + nCW + nWM + nAA + nAbil + nDual + nSize + nOther + nMagicTotal;
    string sMessage = COLOR_WHITE;
    if(nAB>= 0){
        sMessage += "(+";
    }
    else{
        sMessage += "(";
    }
    sMessage +=  IntToString(nAB) + ")\\n";

    if (nBAB > 0)
        sMessage += COLOR_PURPLE + "    BAB: " + COLOR_LT_PURPLE + IntToString(nBAB) + "\\n";
    if (nEBAB > 0)
        sMessage += COLOR_PURPLE + "    EBAB: " + COLOR_LT_PURPLE + IntToString(nEBAB) + "\\n";
    if (nLBAB > 0)
        sMessage += COLOR_PURPLE + "    LBAB: " + COLOR_LT_PURPLE + IntToString(nLBAB) + "\\n";
    if (nAbil != 0)
        sMessage += COLOR_PURPLE + "    Ability: " + COLOR_LT_PURPLE + IntToString(nAbil) + "\\n";
    if (nWF > 0)
        sMessage += COLOR_PURPLE + "    Weapon Focus: " + COLOR_LT_PURPLE + IntToString(nWF) + "\\n";
    if (nGWF > 0)
        sMessage += COLOR_PURPLE + "    Greater Weapon Focus: " + COLOR_LT_PURPLE + IntToString(nGWF) + "\\n";
    if (nEWF > 0)
        sMessage += COLOR_PURPLE + "    Epic Weapon Focus: " + COLOR_LT_PURPLE + IntToString(nEWF) + "\\n";
    if (nLWF > 0)
        sMessage += COLOR_PURPLE + "    Legendary Weapon Focus: " + COLOR_LT_PURPLE + IntToString(nLWF) + "\\n";
    if (nPWF > 0)
        sMessage += COLOR_PURPLE + "    Paragon Weapon Focus: " + COLOR_LT_PURPLE + IntToString(nPWF) + "\\n";
    if (nEP > 0)
        sMessage += COLOR_PURPLE + "    Epic Prowess: " + COLOR_LT_PURPLE + IntToString(nEP) + "\\n";
    if (nCW > 0)
        sMessage += COLOR_PURPLE + "    Craft Weapon: " + COLOR_LT_PURPLE + IntToString(nCW) + "\\n";
    if (nWM > 0)
        sMessage += COLOR_PURPLE + "    Weapon Master: " + COLOR_LT_PURPLE + IntToString(nWM) + "\\n";
    if (nAA > 0)
        sMessage += COLOR_PURPLE + "    Arcane Archer: " + COLOR_LT_PURPLE + IntToString(nAA) + "\\n";
    if (nDual != 0)
        sMessage += COLOR_PURPLE + "    Dual Wielding: " + COLOR_LT_PURPLE + IntToString(nDual) + "\\n";
    if (nSize != 0)
        sMessage += COLOR_PURPLE + "    Size: " + COLOR_LT_PURPLE + IntToString(nSize) + "\\n";
    if (nOther != 0)
        sMessage += COLOR_PURPLE + "    Other: " + COLOR_LT_PURPLE + IntToString(nOther) + "\\n";



    if(nMagicTotal != 0){


        sMessage += COLOR_PURPLE +   "    Magic: " + COLOR_LT_PURPLE + IntToString(nMagicTotal) + COLOR_LT_PURPLE + " (Max = +"+IntToString(nMagicMax)+")\\n";

        if(nMagicIncrease > 0){
            sMessage += COLOR_BLUE + "    -General inc : +" + COLOR_BLUE + IntToString(nMagicIncrease) + "\\n";
            sMessage += sTempIncMagicAbStrings;
        }
        if(nWeaponModifs > 0){
            sMessage += COLOR_BLUE + "    -Weapon modifs : +" + COLOR_BLUE + IntToString(nWeaponModifs) + "\\n";
        }
    }
    return (sMessage + "\\n");
}

/*
* This method lists the attack bonus (!List ab command) of a toon
* -oTarget = The target we want the attack bonus for
* -oPC = The player character
*
*/
void ListAttackBonus (object oTarget, object oPC) {
    string sMessage;
    object oWeapon;
    int nSmall = 0;
    int nDual = 0;

    if (oTarget != oPC)
        sMessage = COLOR_WHITE + "Attack bonus for " + GetName(oTarget) + ":\\n";
    else
        sMessage = COLOR_WHITE + "Attack bonus:\\n";

    object oWeaponR  = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget);
    object oWeaponL  = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget);
    if( GetIsObjectValid(oWeaponR) && GetItemIsMeleeWeapon(oWeaponR) && GetIsObjectValid(oWeaponL) && GetItemIsMeleeWeapon(oWeaponL)){
        // DUAL WIELDING
        nSmall = GetIsOffHandWeaponSmallModif(oPC,oWeaponL);
        nDual = 1;
    }

    if (GetIsObjectValid(oWeapon = GetItemInSlot(INVENTORY_SLOT_RIGHTHAND, oTarget)))
        sMessage += COLOR_GOLD + "Right Hand: " + COLOR_LT_YELLOW + GetName(oWeapon) + " ";
    else
        sMessage += COLOR_GOLD + "Right Hand: " + COLOR_LT_YELLOW + "(unarmed) ";

    sMessage += COLOR_ORANGE + "(" + IntToString(GetCriticalHitRange(oTarget, FALSE)) + "-20/x" +
        IntToString(GetCriticalHitMultiplier(oTarget, FALSE)) + ") ";

    sMessage += ListAttackBonusForWeapon(oTarget, oWeapon, INVENTORY_SLOT_RIGHTHAND,nDual,nSmall);

    if (GetIsObjectValid(oWeapon = GetItemInSlot(INVENTORY_SLOT_LEFTHAND, oTarget)) &&
        GetItemIsMeleeWeapon(oWeapon)) {
        sMessage += COLOR_GOLD + "Left Hand: " + COLOR_LT_YELLOW + GetName(oWeapon) + " ";

        sMessage += COLOR_ORANGE + "(" + IntToString(GetCriticalHitRange(oTarget, TRUE)) + "-20/x" +
            IntToString(GetCriticalHitMultiplier(oTarget, TRUE)) + ") ";

        sMessage += ListAttackBonusForWeapon(oTarget, oWeapon, INVENTORY_SLOT_LEFTHAND,nDual,nSmall);
    }

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



Funky
               
               

               
            

Legacy_the.gray.fox

  • Full Member
  • ***
  • Posts: 214
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #4 on: July 24, 2011, 03:20:12 pm »


               

Xardex wrote...

+ effects (buffs, debuffs)
This is more difficult... Im not sure if you can get the value of an effect like you can get its type.


You are quite right.
But you ought to point out that it is _horribly_ complex to implement that.

You should take note of the properties used to create the effect, the intended duration of the effect, and also Who has created the effect. Then you much keep an eye on every usage of RemoveEffect(), ForceRest(), EffectDispelMagic*() functions, as well as canonical Resting, Clients Leaving, Players dieing and respawning... in short: every thing that can cause the premature departure of an effect is to be kept under constant vigilance.
And finally, because you can not retrieve the parameters used to create an effect... you have to compare the effect with _itself_ if you want to determine whether or not said effect is still active. Which means that you have to preemptively "save" said effect on a creature locked in some unreachable service-Area where no player can go screw up.

Seriously, it is a scary amount of work.
Unless the ability to track the Modified Attack Roll is pivotal to a full fledged module, it will not be worth the effort to go through ALL that... and just to determine the current value of 1 number anyway.

NWNX and a slick plugin would do far better -- incidentally sparing you the trouble to brutalize a module as well as a good hundred of the original bioware scripts.


-fox
               
               

               
            

Legacy_FunkySwerve

  • Hero Member
  • *****
  • Posts: 2325
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #5 on: July 24, 2011, 06:28:43 pm »


               

the.gray.fox wrote...
And finally, because you can not retrieve the parameters used to create an effect... you have to compare the effect with _itself_ if you want to determine whether or not said effect is still active. Which means that you have to preemptively "save" said effect on a creature locked in some unreachable service-Area where no player can go screw up
....
NWNX and a slick plugin would do far better -- incidentally sparing you the trouble to brutalize a module as well as a good hundred of the original bioware scripts.


Actually, you CAN retrieve the effect params, using nwnx_structs. That's what the code I posted above does, and it makes tallying effects bonuses child's play:


if (GetEffectType(eEff) == EFFECT_TYPE_ATTACK_INCREASE) {
            nAmount += GetEffectInteger(eEff, 0);


Funky
               
               

               


                     Modifié par FunkySwerve, 24 juillet 2011 - 05:29 .
                     
                  


            

Legacy_Shadooow

  • Hero Member
  • *****
  • Posts: 7698
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #6 on: July 24, 2011, 06:46:50 pm »


               

FunkySwerve wrote...

the.gray.fox wrote...
And finally, because you can not retrieve the parameters used to create an effect... you have to compare the effect with _itself_ if you want to determine whether or not said effect is still active. Which means that you have to preemptively "save" said effect on a creature locked in some unreachable service-Area where no player can go screw up
....
NWNX and a slick plugin would do far better -- incidentally sparing you the trouble to brutalize a module as well as a good hundred of the original bioware scripts.


Actually, you CAN retrieve the effect params, using nwnx_structs. That's what the code I posted above does, and it makes tallying effects bonuses child's play:


if (GetEffectType(eEff) == EFFECT_TYPE_ATTACK_INCREASE) {
            nAmount += GetEffectInteger(eEff, 0);


Funky

But linux only. There is no global solution yet and without linux nwnx its not even worth to try, otherwise yo end up like PRC ':unsure:'
               
               

               
            

Legacy_Thayan

  • Sr. Member
  • ****
  • Posts: 435
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #7 on: July 24, 2011, 07:57:09 pm »


               Thanks for the very detailed code Funky. I was afraid it would not be easy, and since we're a Windows server I unfortunately can't use (all of) it. Hopefully someone else can though.

But yeah, it would be pretty sweet if NWNX would be able to find and pull that info. I'll keep dreaming of a NWNFuncs_GetAttackBonus in nwnx_funcs or something. Until then, at least for my purposes, it's just not worth the effort for what I would have liked it for.
               
               

               
            

Legacy_the.gray.fox

  • Full Member
  • ***
  • Posts: 214
  • Karma: +0/-0
Get Modified Attack Roll
« Reply #8 on: July 26, 2011, 01:12:25 pm »


               Hm. Funky, by "slick plugin" I meant one that would read directly the Modified Attack Roll value.
The game already computes it for you -- It makes sense to go get just that, rather than recalculate it off equipment and effects.


-fox