Jump to content

EquipMostDamagingMelee - how does it actually work?


jmerry

Recommended Posts

The EquipMostDamagingMelee() script action does something that should be obvious ... but the obvious is wrong.

Among other things, this is used by the "prefer melee" and "prefer ranged" options in party member scripts. If those scripts call for you to switch from ranged to melee, they will use this action to choose which melee weapon to switch to. That makes it easy to test. And it doesn't choose the most damaging weapon by any reasonable definition I can see. Two examples:

Khalid: Bow in first slot, Varscona in second slot, +1 flail in third slot, fourth slot empty. Specialized in long sword, non-proficient in flail. Always chooses the flail.

Minsc: Bow in first slot, nonmagical two-handed sword in second slot, Stupefier +1 in third slot. Specialized in two-handed swords and maces. Always chooses the sword.

So, if this is just looking at base physical damage and to-hit bonuses, this says that 1d6+2/+1 is preferred over 1d8+2/+2 and that 1d10/0 is preferred over 1d6+2/+1. I just don't get it. And I'm curious - what actually is the logic buried in the code?

Link to comment

Odd, it appears to work in the tests I just did:

  • Staff of the Ram +4 (d6+10 base) is preferred over Foebane +5 (2d4+5) or Ixil's Spike +6 (d6+6)
  • Foebane is preferred over Ixil's
  • Hindo's Doom (d10+4) is also preferred over Foebane +5

Clearly, the selection is based on the maximal possible damage in the extended header, i.e. the dice plus bonuses but disregarding any extra damage in the feature block (which would put Foebane ahead of Hindo's doom).

Perhaps this action varies between engines.

Link to comment

You don't have to include on-hit effects to put Foebane +5 ahead of Hindo's Doom +4. Just looking at the base weapon damage, 2d4+5 is an average of 10, beating out 1d10+4 averaging to 9.5. But the order of operations matters here if that's integer division; Hindo's Doom comes out as 9, while Foebane comes out as either 9 or 10 depending on whether the division or multiplication comes first. What happens if the values that come out of the formula are the same? I think it's chosen based on slot order, with later slots preferred over earlier ones.

But at the same time ... my experience does not match yours. Or the formula. Or anything that makes sense, really.

I just tested in BG2EE, and got similar weirdness to my BGEE tests. Start a new character (I went with an unkitted Paladin) in ToB and test some of the weapons in the freebie bag. Harbinger (d10+3) is preferred over Impaler (d6+3) which is preferred over Stonefire (d8+3). Between Harbinger and Dragon's Bane (also d10+3), it depends on which slot they're in; third slot beats second slot. For another one - Mauler's Arm +2 (d6+3) is preferred over the Flail of Ages +3 (d6+4) regardless of order.

Then I tried your weapons. Ixil's Spike +6 (d6+6)  > Staff of the Ram +4 (d6+10) > Hindo's Doom +4 (d10+4)  > Foebane +5 (2d4+5) for me. Not the same order that you saw.

Um. Is there a cross-platform difference here? (My tests are in the Mac/GoG version, patch 2.6)

Link to comment
local nMaxDamageSlot = -1
local nMaxDamageAbility = -1

local nMaxDamageSeenSoFar = 0

for nCurWeaponSlot = 35, 38 do

    for nCurAbilityIndex, ability in ipairs(<every weapon ability>) do

        if ability.type == 1 then -- [+0x0], MELEE

            local damageDice      = ability.damageDice  -- [+0x16]
            local damageDiceCount = ability.diceSize    -- [+0x18]
            local damageDiceBonus = ability.damageBonus -- [+0x1A]

            if (ability.flags & 5) ~= 0 then -- [+0x26], Strength bonus on "Add strength bonus(0)" or "EE: Damage strength bonus(2)"
                damageDiceBonus = damageDiceBonus + STRMOD.2DA["DAMAGE", this.m_nSTR] + STRMODEX.2DA["DAMAGE", this.m_nSTRExtra]
            end

            for abilityEffect in <every ability effect> do

                if abilityEffect.m_effectId == 318 or abilityEffect.m_effectId == 324 then
                    break
                end

                if abilityEffect.m_effectId == 12 then
                    damageDice      = abilityEffect.m_diceSize     -- [+0x20]
                    damageDiceCount = abilityEffect.m_numDice      -- [+0x1C]
                    damageDiceBonus = abilityEffect.m_effectAmount -- [+0x4]
                    break
                end
            end

            local nTotalDamage = damageDice * damageDiceCount + damageDiceBonus
                + STYLBONU.2DA["DAMAGE_RIGHT", <resolved style prof level>]
                + WSPECIAL.2DA["DAMAGE", <resolved weapon prof level>]
                & 0xFFFF

            if nMaxDamageSeenSoFar <= nTotalDamage then
                nMaxDamageSlot = nCurWeaponSlot
                nMaxDamageAbility = nCurAbilityIndex
                nMaxDamageSeenSoFar = nTotalDamage;
            end
        end
    end
end

local nSlotToSelect
local nAbilityToSelect

if nMaxDamageSlot == -1 and nMaxDamageAbility == -1 then
    nSlotToSelect = 10 -- SLOT_FIST
    nAbilityToSelect = 0
else
    nSlotToSelect = nMaxDamageSlot
    nAbilityToSelect = nMaxDamageAbility
end

this:SelectWeaponAbility(nSlotToSelect, nAbilityToSelect, true, true)
  • The first damage effect on an item ability overrides the ability's base values.
  • Hitting either op318 or op324 stops the search for a damage effect on an item ability.
  • The strength bonus is thrown away if it finds and uses a damage effect.
  • Ties are broken by the item with the highest slot index.
Link to comment
1 hour ago, Bubb said:
  • The strength bonus is thrown away if it finds and uses a damage effect.

Not just the strength bonus. It looks like that code ignores the whole base weapon damage and just takes the effect plus proficiencies. If I give Varscona and a normal dagger to my protagonist with 18/68 strength but no proficiency in either weapon, it decides that 1d4+3 damage from the dagger is better than 1 damage from the sword. If I give those to Khalid with his 15 strength and longsword specialization, it decides that 3 damage from the sword is better than 1d4 from the dagger. Testing agrees.

That's the bug. Any weapon with an unconditional damage effect will see its normal weapon damage ignored in the calculation. Frostreaver is "worse" than a nonmagical battle axe. The Flail of Ages is "worse" than a nonmagical flail. Impaler is "worse" than a nonmagical spear if you have 18/51 or more strength. The Staff of the Ram +6 is "worse" than a nonmagical staff.

And now it finally makes sense to me.

Edited by jmerry
Link to comment
1 hour ago, Bubb said:
  • The first damage effect on an item ability overrides the ability's base values.
  • Hitting either op318 or op324 stops the search for a damage effect on an item ability.
  • The strength bonus is thrown away if it finds and uses a damage effect.

Sounds like a bug caused by trying to account for the Snow Maiden's Reaver (IWD) and the "Void" weapons (SoD), which deal no physical damage, but do display that first op12 damage effect as their base damage in the inventory damage comparison.

Link to comment

Another EE bug then, it doesn't matter much for enemy AI though, since most only have one melee weapon equipped and this action is either used to switch from their ranged weapon if an attacker gets close or to ensure that they aren't just using their fists, also very few enemies have melee weapons that inflict extra elemental damage in the feature block of their attacks, and it's almost always a higher number than base fist damage (fire salamanders/elementals, fire giants, Slayer Irenicus in hell...).

Still, if a monster had a 3d8 melee attack with 1 point of additional magic damage in the feature block, would it instead try to use the default 1d2 fist attack? Or are fists other than monk fists exempt from this action?

Edited by polytope
Link to comment

Thank you for the algorithm, Bubb!

For completeness, does it apply as well to the "hotkey" that the user can press to swap from melee to ranged? I use them often, and I could swear it pretty much chooses the less appropriate weapon all the time (but my main use case as a user is to swap from ranged to melee while casting a spell is in progress, to protect the caster, as it can be done without interruption). I sometimes have noticed that it is different depending on which slot you put the weapon, so that might make sense with what you posted.

Link to comment
11 hours ago, suy said:

For completeness, does it apply as well to the "hotkey" that the user can press to swap from melee to ranged?

Yep, the hotkey directly invokes EquipMostDamagingMelee() without going through the action system, which is also why it doesn't interrupt the current action.

Link to comment

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...