• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Trigger to record units attack damage + bonuses?

Level 4
Joined
Apr 6, 2022
Messages
25
Having trouble finding a way to set an integer... when recording a hero's attributes there is the option to "include bonuses", however for attack damage there is not... for some reason it will only allow me to record a units "base damage" without any include bonuses option. What I'm trying to do ultimately is make a spell that's damage is based on "weapon" or "attack" damage, and I cannot simply use "base damage" because the spell is intended to be affected by items... and the trigger doesn't include item damage bonuses, only "base damage"

Any ideas?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,557
You can rely on frames in version 1.31+. We can read the text straight from the Attack Damage frame:

To get the unit's Attack Damage:
vJASS:
call BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 0))
To get the unit's Armor:
vJASS:
call BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 2))

Now keep in mind that this comes with it's own issues like needing to ensure that the unit is selected when detecting these values since the UI needs to be present at the time. That being said, it shouldn't be too hard to cache these values consistently and almost always have the correct values when referencing them in an ability for example.

 
Level 17
Joined
Apr 13, 2008
Messages
1,597
Oh, funny how I did not come up with this solution, as in the past few days I'm working on an inventory / equipment system, and literally dream about frames.
That's actually pretty brilliant.

Note:
If you screw around with frames remember that there is a bug with loading save games using frames.
Don't be like me and troubleshoot for hours, only to find out that Warcraft III has another fatal bug:

 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,557
Here's how you can put it to use:
  • Select Unit Attack
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • Custom script: set udg_Attack_Result = ConvertAttackStringToInteger()
      • -------- --------
      • Game - Display to (All players) for 30.00 seconds the text: (String(Attack_Result))
      • -------- --------
      • -------- If the unit has negative attack damage then default it to 0: --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Attack_Result Less than 0
        • Then - Actions
          • Set VariableSet Attack_Result = 0
        • Else - Actions
  • Select Unit Armor
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • Custom script: set udg_Armor_Result = ConvertArmorStringToReal()
      • -------- --------
      • Game - Display to (All players) for 30.00 seconds the text: (String(Armor_Result))
Of course you'll need the code which creates those two Convert() functions:
vJASS:
function ConvertAttackStringToInteger takes nothing returns integer
    local string inputStr = BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 0))
    local string currentChar = ""
    local boolean isSecondHyphen = false
    local integer i = 1
    local integer len = StringLength(inputStr)
    local integer minAttack = 0
    local integer maxAttack = 0
    local integer bonus = 0
    local integer maxIndex = 0

    // Loop through the characters in the input string
    loop
        exitwhen i > len

        set currentChar = SubStringBJ(inputStr, i, i)

        if currentChar == "-" and isSecondHyphen == false then
            set isSecondHyphen = true
            set minAttack = S2I(SubStringBJ(inputStr, 1, i - 1))
            set maxIndex = i + 1
        elseif currentChar == "+" then
            set maxAttack = S2I(SubStringBJ(inputStr, maxIndex, i - 1))
            set bonus = S2I(SubStringBJ(inputStr, i + 1, len))
            return minAttack + maxAttack + (bonus * 2)
        elseif currentChar == "-" and isSecondHyphen then
            set maxAttack = S2I(SubStringBJ(inputStr, maxIndex, i - 1))
            set bonus = S2I(SubStringBJ(inputStr, i + 1, len))
            set bonus = bonus * -1
            return minAttack + maxAttack + (bonus * 2)
        endif

        set i = i + 1
        if i >= len then
            // No bonus
            set maxAttack = S2I(SubStringBJ(inputStr, maxIndex, len))
            return minAttack + maxAttack
        endif
    endloop

    return 0
endfunction

function ConvertArmorStringToReal takes nothing returns real
    local string inputStr = BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 2))
    local string currentChar = ""
    local integer i = 1
    local integer len = StringLength(inputStr)
    local integer armor = 0
    local integer bonus = 0
    local real armorBonus = 0.0

    // Loop through the characters in the input string
    loop
        exitwhen i > len

        set currentChar = SubStringBJ(inputStr, i, i)

        if currentChar == "-" then
            set armor = S2I(SubStringBJ(inputStr, 1, i - 1))
            set armorBonus = S2R(SubStringBJ(inputStr, i + 1, len))
            // Check for int or real bonus armor
            if armorBonus == 0.0 then
                // Bonus is an integer
                set bonus = S2I(SubStringBJ(inputStr, i + 1, len))
                return I2R(armor - bonus)
            else
                // Bonus is a real
                return I2R(armor) - armorBonus
            endif

        elseif currentChar == "+" then
            set armor = S2I(SubStringBJ(inputStr, 1, i - 1))
            set armorBonus = S2R(SubStringBJ(inputStr, i + 1, len))
            // Check for int or real bonus armor
            if armorBonus == 0.0 then
                // Bonus is an integer
                set bonus = S2I(SubStringBJ(inputStr, i + 1, len))
                return I2R(armor + bonus)
            else
                // Bonus is a real
                return I2R(armor) + armorBonus
            endif
        endif

        set i = i + 1
        if i >= len then
            // No bonus
            set armor = S2I(SubStringBJ(inputStr, 1, len))
            return I2R(armor)
        endif
    endloop

    return 0.0
endfunction
Edit: Updated the code and test map to fix some bugs and introduce Armor.
 

Attachments

  • Get Attack And Armor Values.w3m
    19.9 KB · Views: 1
Last edited:

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,557
From one uncle to another thanks very much for the timely response!!! You guys are great!
EDIT (January_27_2024):

I updated the map/code in my previous post, there were some mistakes and it was missing Armor. Now it should properly get both Attack and Armor. There could still be some bugs but from my tests it seems to work nicely (inb4 desyncs when used online).

You can now call the ConvertAttackStringToInteger() function to get the current selected unit's Attack stat, which would be it's minimum attack + it's maximum attack + any bonuses (positive or negative). So if you wanted to deal an "average" hit based on the unit's attack then you could do something like this:
  • Actions
    • Custom script: set udg_Attack_Result = ConvertAttackStringToInteger()
    • -------- --------
    • -------- If the unit has negative attack damage then default it to 0: --------
    • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      • If - Conditions
        • Attack_Result Less than 0
      • Then - Actions
        • Set VariableSet Attack_Result = 0
      • Else - Actions
      • -------- --------
    • Unit - Cause (Triggering unit) to deal (Real(Attack_Result) x 0.50) damage to (Target unit of ability being cast)...
I'm converting Attack_Result to a Real and cutting the damage in half to get the average.

//////
Edit:
//////

Here it is, a version that gives you greater control and should be more GUI friendly:
vJASS:
library CASConvertUnitStrings

    function CAS_Get_Attack takes nothing returns nothing
        local string inputStr = BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 0))
        local string currentChar = ""
        local boolean isSecondHyphen = false
        local integer i = 1
        local integer len = StringLength(inputStr)
        local integer maxIndex = 0
 
        // Loop through the characters in the input string
        loop
            exitwhen i > len
 
            set currentChar = SubStringBJ(inputStr, i, i)
 
            if currentChar == "-" and isSecondHyphen == false then
                set udg_CAS_Attack_Min = S2I(SubStringBJ(inputStr, 1, i - 1))
                set isSecondHyphen = true
                set maxIndex = i + 1
            elseif currentChar == "+" then
                set udg_CAS_Attack_Max = S2I(SubStringBJ(inputStr, maxIndex, i - 1))
                set udg_CAS_Attack_Bonus = S2I(SubStringBJ(inputStr, i + 1, len))
                set udg_CAS_Attack = udg_CAS_Attack_Min + udg_CAS_Attack_Max + (udg_CAS_Attack_Bonus * 2)
                return
            elseif currentChar == "-" and isSecondHyphen then
                set udg_CAS_Attack_Max = S2I(SubStringBJ(inputStr, maxIndex, i - 1))
                set udg_CAS_Attack_Bonus = S2I(SubStringBJ(inputStr, i + 1, len))
                set udg_CAS_Attack_Bonus = (udg_CAS_Attack_Bonus * -1)
                set udg_CAS_Attack = udg_CAS_Attack_Min + udg_CAS_Attack_Max + (udg_CAS_Attack_Bonus * 2)
                return
            endif
 
            set i = i + 1
            if i >= len then
                // No attack bonus
                set udg_CAS_Attack_Max = S2I(SubStringBJ(inputStr, maxIndex, len))
                set udg_CAS_Attack = udg_CAS_Attack_Min + udg_CAS_Attack_Max
                set udg_CAS_Attack_Bonus = 0
                return
            endif
        endloop
 
    endfunction
 
    function CAS_Get_Armor takes nothing returns nothing
        local string inputStr = BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 2))
        local string currentChar = ""
        local integer i = 1
        local integer len = StringLength(inputStr)
        local integer armor = 0
        local integer bonus = 0
        local real armorBonus = 0.0
 
        // Loop through the characters in the input string
        loop
            exitwhen i > len
 
            set currentChar = SubStringBJ(inputStr, i, i)
 
            if currentChar == "-" then
                set armor = S2I(SubStringBJ(inputStr, 1, i - 1))
                set armorBonus = S2R(SubStringBJ(inputStr, i + 1, len))
                // Check for int or real bonus armor
                if armorBonus == 0.0 then
                    // Bonus is an integer
                    set bonus = S2I(SubStringBJ(inputStr, i + 1, len))
                    set udg_CAS_Armor = I2R(armor - bonus)
                    set udg_CAS_Armor_Bonus = I2R(bonus * -1)
                    return
                else
                    // Bonus is a real
                    set udg_CAS_Armor = I2R(armor) - armorBonus
                    set udg_CAS_Armor_Bonus = (armorBonus * -1)
                    return
                endif
 
            elseif currentChar == "+" then
                set armor = S2I(SubStringBJ(inputStr, 1, i - 1))
                set armorBonus = S2R(SubStringBJ(inputStr, i + 1, len))
                // Check for int or real bonus armor
                if armorBonus == 0.0 then
                    // Bonus is an integer
                    set bonus = S2I(SubStringBJ(inputStr, i + 1, len))
                    set udg_CAS_Armor = I2R(armor + bonus)
                    set udg_CAS_Armor_Bonus = I2R(bonus)
                    return
                else
                    // Bonus is a real
                    set udg_CAS_Armor = I2R(armor) + armorBonus
                    set udg_CAS_Armor_Bonus = armorBonus
                    return
                endif
            endif
 
            set i = i + 1
            if i >= len then
                // No armor bonus
                set armor = S2I(SubStringBJ(inputStr, 1, len))
                set udg_CAS_Armor = I2R(armor)
                set udg_CAS_Armor_Bonus = 0.0
                return
            endif
        endloop
    endfunction
 
endlibrary

How to use:
  • Select Unit Attack
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • Custom script: call CAS_Get_Attack()
      • -------- --------
      • -------- These CAS variables get set in response to calling a CAS() function: --------
      • Game - Display to (All players) for 30.00 seconds the text: (String(CAS_Attack))
      • Game - Display to (All players) for 30.00 seconds the text: (String(CAS_Attack_Min))
      • Game - Display to (All players) for 30.00 seconds the text: (String(CAS_Attack_Max))
      • Game - Display to (All players) for 30.00 seconds the text: (String(CAS_Attack_Bonus))
  • Select Unit Armor
    • Events
      • Player - Player 1 (Red) Selects a unit
    • Conditions
    • Actions
      • Custom script: call CAS_Get_Armor()
      • -------- --------
      • -------- These CAS variables get set in response to calling a CAS() function: --------
      • Game - Display to (All players) for 30.00 seconds the text: (String(CAS_Armor))
      • Game - Display to (All players) for 30.00 seconds the text: (String(CAS_Armor_Bonus))
It's now easier to call the functions and has CAS variables which you can reference afterwards. So after calling CAS_Get_Attack() the CAS_Attack, CAS_Attack_Min, CAS_Attack_Max, and CAS_Attack_Bonus variables will be set to the unit's corresponding values.
 

Attachments

  • CAS Get Attack and Armor.w3m
    20.2 KB · Views: 5
Last edited:
Top