• 🏆 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!

1.28.x era versions break back-compat with ConvertAttackType(7) and GetUnitArmorType

Status
Not open for further replies.

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
I've just discoverd this.

Here's the old thread:

[code=jass] - Hidden ATTACK_TYPE in the World Editor?

And here's the new numbers, as far as I can tell:

JASS:
unarmored 400
medium    2048
light     1
heavy     1
hero      522
fortified 522
divine    untested
"normal"  untested

Wurst code:

JASS:
/// Made for use with STL 1.
package GetUnitArmorType
import AbilityObjEditing
import HashMap

import DamageDetection2

constant ID_BONUS_HP = '!at*'
constant BONUS_HP_VALUE = 10000

public enum ArmorType
    _DUMMY // To pad the enum.
    UNKNOWN
    _MEDIUM_FORTIFIED // Old: 1 damage. New: impossible.
    UNARMORED // Old: 25 damage. New: 400 damage.
    _HERO // Old: 150 damage. New: impossible.
    _DIVINE // Old: 400 damage. New: Untested.
    _HEAVY_NORMAL // Old: 522 damage. New: Untested.
    _LIGHT // Old: 2048 damage. New: impossible.
    LIGHT_HEAVY // Old: impossible. New: 1 damage.
    HERO_FORTIFIED // Old: impossible. New: 522 damage.
    MEDIUM // Old: impossible. New: 2048 damage.

constant tab = new HashMap<int, ArmorType>

public function getUnitArmorType(unit u) returns ArmorType
    let typ = GetUnitTypeId(u)
    var ret = ArmorType.UNKNOWN

    if tab.has(typ) and tab.get(typ) != ArmorType._DUMMY
        return tab.get(typ)
    else
        let hp = GetWidgetLife(u)
        UnitAddAbility(u, ID_BONUS_HP)
        SetUnitState(u, UNIT_STATE_LIFE, BONUS_HP_VALUE.toReal())
        disableDamageDetect()
        UnitDamageTarget(u, u, 1., true, true, ConvertAttackType(7), DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
        enableDamageDetect()
        let q = BONUS_HP_VALUE - GetWidgetLife(u)
        print(q.toString())
        if q == 1
            // tab.put(typ, ArmorType.MEDIUM_FORTIFIED)
            // ret = ArmorType.MEDIUM_FORTIFIED
            tab.put(typ, ArmorType.LIGHT_HEAVY)
            ret = ArmorType.LIGHT_HEAVY
        else if q == 25
            // tab.put(typ, ArmorType.UNARMORED)
            // ret = ArmorType.UNARMORED
            debugPrint("Unexpected `25` - are you using an old wc3 patch?", 0)
        else if q == 150
            // tab.put(typ, ArmorType.HERO)
            // ret = ArmorType.HERO
            debugPrint("Unexpected `150` - are you using an old wc3 patch?", 0)
        else if q == 400
            // tab.put(typ, ArmorType.DIVINE)
            // ret = ArmorType.DIVINE
            tab.put(typ, ArmorType.UNARMORED)
            ret = ArmorType.UNARMORED
        else if q == 522
            // tab.put(typ, ArmorType.HEAVY_NORMAL)
            // ret = ArmorType.HEAVY_NORMAL
            tab.put(typ, ArmorType.HERO_FORTIFIED)
            ret = ArmorType.HERO_FORTIFIED
        else if q == 2048
            // tab.put(typ, ArmorType.LIGHT)
            // ret = ArmorType.LIGHT
            tab.put(typ, ArmorType.MEDIUM)
            ret = ArmorType.MEDIUM
        else
            tab.put(typ, ArmorType.UNKNOWN)
            debugPrint("Debug GetUnitArmorType: Unknown armor type uncovered for " + GetUnitName(u) + "; q = " +R2S(q), 0)
            ret = ArmorType.UNKNOWN

        UnitRemoveAbility(u, ID_BONUS_HP)
        SetWidgetLife(u, hp)

        return ret

@compiletime function generateAbility()
    new AbilityDefinitionMaxLifeBonusGreater(ID_BONUS_HP)
        ..setMaxLifeGained(1, BONUS_HP_VALUE)
        ..setItemAbility(false)
        ..setName("Life Bonus for GetUnitArmorType")
 
Last edited:
Level 12
Joined
Mar 13, 2012
Messages
1,121
I would not call that "break compat".
The normal ConvertAttackType(x) uses the table values for the different armor types as it should be.
ConvertAttackType(7)'s behaviour was always undefined, it accesses values of (mostly) other gameplay constants because they happen to be in a certain memory address.

Could be that in 1.28.x in these memory addresses happens to be something else.
 
Level 2
Joined
Nov 3, 2017
Messages
25
All adresses there are static and thats gameplay constants located nearby. Its reliable to use, at least on 26

SMALL, MEDIUM, LARGE, FORT, NORMAL, HERO, DIVINE, NONE
will go as
40N0IYF.png

just look at game.dll+AB4438 to see all 8 modifiers for your map (26)
28+ versions.. figure them out yourself
 
Last edited:

Cokemonkey11

Code Reviewer
Level 29
Joined
May 9, 2006
Messages
3,522
All adresses there are static and thats gameplay constants located nearby. Its reliable to use, at least on 26

SMALL, MEDIUM, LARGE, FORT, NORMAL, HERO, DIVINE, NONE
will go as
40N0IYF.png

just look at game.dll+AB4438 to see all 8 modifiers for your map (26)
28+ versions.. figure them out yourself

The values (not all) are posted above.

I would not call that "break compat".

I disagree. This knowledge has been around unchanged for at least 7 years ( Damage Type ? ), so I am surely not the only map author that used this. In much the same way as removing 1.21 era H2I, this is a compat issue.
 
Status
Not open for further replies.
Top