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

[vJASS] Native Abilities

This is a library that allows the user to distinguish passive-based abilities from active abilities, and adds some functionality when mimicking the "break" behavior (break in this case is defined as Silence for passive abilities, based on the "Break" ability found in Dota 2).

After tinkering with BlzUnitDisableAbility, it was found that passive abilities (especially auras) could be silenced directly, starting from versions 1.30+, which was not possible during the 1.29 versions, as the natives exposed then might have been safe functions (safe functions might have added some checks to see if the ability was an aura or not). This led to the addition of the "break" functionality to the library.

JASS:
library NativeAbilities requires /*
    ------------
    */ ListT, /*
    ------------
        - A doubly-linked list library that provides object-based list functionalities
        - Author: Bannar
        - Link: "https://www.hiveworkshop.com/threads/containers-list-t.249011/"

    ----------------------
    */ optional UnitDex /*
    ----------------------
        - A library that provides basic unit creation and removal events
          and also assigns unique ids for each unit.
        - Breaks user-based SetUnitUserData behavior
        - Optional to use, as it adds some much needed "garbage collection" behavior
          for out-of-scope passives, but may not play well with other indexers
          which may have their own index event.
        - Author: TriggerHappy
        - Link: "https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/"

    -------
      API
    -------
        - RegisterPassiveAbility(int abilId)
            Registers a given ability as a passive ability.
            This has no logical checks for when an ability
            is actually passive, so it's up to the user to
            pass the right ability.
          
        - IsAbilityPassive(int abilId) -> bool
            Returns true if the ability is marked as passive.

        - RegisterMetaAbility(int abilId)
            Registers a given ability as a meta ability.
            Meta abilities are abilities which manipulate
            the list of abilities a unit has at any given
            moment.
            This has no logical checks for when an ability
            is actually a meta ability, so it's up to the
            user to pass the right ability.
            Note that the ability must be registered
            as a passive ability beforehand.

        - IsAbilityMeta(int abilId) -> bool
            Returns true if the ability is marked as meta.
      
        - UnitSilencePassives(unit whichUnit, bool disable, bool hide)
            Disables all passive abilities found in the unit.
            Will make a list of passive abilities for the
            unit if such a list did not exist beforehand.
            Will not check for new passive abilities if
            the list already existed during that time.

        - UnitClearPassives(unit whichUnit)
            Only invoke this function when the unit is
            about to be removed.

        - UnitUpdatePassives(unit whichUnit)
            This function should be invoked sparingly, and should
            be called when the unit has a new passive ability
            that was not caught and added to the unit's list.

            Ideally, this should be handled smoothly by the
            system, but there are cases where it can't catch
            everything ability related, so this is exposed
            for the user's convenience.

    ------------------------
        Additional Notes
    ------------------------
        - This hooks UnitAddAbility and UnitRemoveAbility
          so there might be some performance degradation
          when dealing with these functions, and in some
          cases, completely halt the entire function
          (oplimit reached).

        - The flag INCLUDE_ITEM_AS_ABILITIES is set to
          false by default. Setting it to true will make
          iteration of abilities much more expensive, but
          will correctly filter out item abilities added
          to units as passive abilities.
*/

//! runtextmacro DEFINE_LIST("private", "AbilityIdList", "integer")

globals
    private TableArray passMap              = 0
    private TableArray metaAbilMap          = 0
    private Table      tempTable            = 0
    private Table   array passAbilMap
    private integer array passAbilArr

    //  Flag variables
    //  Setting this to true will also include item abilities
    //  as if they are unit abilities.
    private constant boolean INCLUDE_ITEM_AS_ABILITIES  = false
endglobals

// A defined function which returns a unique index for a specified unit
// Uses GetHandleId by default, but can be altered to use values given
// by a unit indexer
private function GetUnitIndex takes unit whichUnit returns integer
static if LIBRARY_UnitDex then
    return GetUnitId(whichUnit)
else
    return GetHandleId(whichUnit)
endif
endfunction

function RegisterPassiveAbility takes integer abilId returns nothing
    if not passMap[1].has(abilId) then
    set passAbilArr[0] = passAbilArr[0] + 1
        set passAbilArr[passAbilArr[0]] = abilId
        set passMap[3][abilId] = passAbilArr[0]
    endif
    set passMap[1][abilId] = passMap[1][abilId] + 1
endfunction

function IsAbilityPassive takes integer abilId returns boolean
    return passMap[1][abilId] > 0
endfunction

function RegisterMetaAbility takes integer abilId returns nothing
    if not metaAbilMap[2].has(abilId) and IsAbilityPassive(abilId) then
        set metaAbilMap[1][0] = metaAbilMap[1][0] + 1
        set metaAbilMap[2][metaAbilMap[1][0]] = abilId
        set metaAbilMap[3][abilId] = metaAbilMap[1][0]
    endif
    set metaAbilMap[1][abilId] = metaAbilMap[1][abilId] + 1
endfunction

function IsAbilityMeta takes integer abilId returns boolean
    return metaAbilMap[1][abilId] > 0
endfunction

function UnitSilencePassives takes unit whichUnit, boolean disable, boolean hide returns nothing
    local integer i     = 1
    local integer id    = GetUnitIndex(whichUnit)
    if not passMap[2].has(id) then
        set passMap[2][id]               = AbilityIdList.create()
        set passAbilMap[passMap[2][id]]  = Table.create()
        loop
            exitwhen i > passAbilArr[0]
            if GetUnitAbilityLevel(whichUnit, passAbilArr[i]) != 0 then
                set passAbilMap[passMap[2][id]][i] = AbilityIdList(passMap[2][id]).push(i).last
            endif  
            call BlzUnitDisableAbility(whichUnit, passAbilArr[i], disable, hide)
            set i = i + 1
        endloop
    else
        set i = AbilityIdList(passMap[2][id]).first
        loop
            exitwhen i == 0
            call BlzUnitDisableAbility(whichUnit, passAbilArr[AbilityIdListItem(i).data], disable, hide)
            set i = AbilityIdListItem(i).next
        endloop
    endif
endfunction

// This should only be called when a unit is about to be removed
function UnitClearPassives takes unit whichUnit returns nothing
    local integer i     = 1
    local integer id    = GetUnitIndex(whichUnit)
    if passMap[2].has(id) then
        call passAbilMap[passMap[2][id]].destroy()
        call AbilityIdList(passMap[2][id]).clear()
        call AbilityIdList(passMap[2][id]).destroy()
    endif
    call passMap[2].remove(id)
endfunction

// This should be called sparingly, on such events as levelling up a skill
function UnitUpdatePassives takes unit whichUnit returns nothing
    local integer i    = 1
    local integer id   = GetUnitIndex(whichUnit)
    if passMap[2].has(id) then
        call tempTable.flush()
        set i = AbilityIdList(passMap[2][id]).first
        loop
            exitwhen i == 0
            if BlzGetUnitAbility(whichUnit, passAbilArr[AbilityIdListItem(i).data]) == null then
                call AbilityIdList(passMap[2][id]).erase(i)
            else
                set tempTable.boolean[AbilityIdListItem(i).data] = true
            endif
            set i = AbilityIdListItem(i).next
        endloop
        // Search for any abilities that might've been added
        set i = 1
        loop
            exitwhen i > passAbilArr[0]
            if (BlzGetUnitAbility(whichUnit, passAbilArr[i]) != null) then
                set passAbilMap[passMap[2][id]][i] = AbilityIdList(passMap[2][id]).push(i).last
            endif
            loop
                set i = i + 1
                exitwhen not tempTable.boolean[i]
            endloop
        endloop
    else
        call UnitSilencePassives(whichUnit, true, false)
        call UnitSilencePassives(whichUnit, false, false)
    endif
endfunction

private function OnUnitAddPassive takes unit whichUnit, integer abilId returns nothing
    local integer i
    local integer id
    if not IsAbilityPassive(abilId) then
        return
    endif
    set id = GetUnitIndex(whichUnit)
    // Assume passMap[2][id] has a value
    if not passMap[2].has(id) then
        call UnitSilencePassives(whichUnit, true, false)
        call UnitSilencePassives(whichUnit, false, false)
    else
        if not IsAbilityMeta(abilId) then
            set i = passMap[3][abilId]
            if not passAbilMap[passMap[2][id]].has(i) then
                set passAbilMap[passMap[2][id]][i] = AbilityIdList(passMap[2][id]).push(i).last
            endif
        else
            call passAbilMap[passMap[2][id]].destroy()
            call AbilityIdList(passMap[2][id]).clear()
            call AbilityIdList(passMap[2][id]).destroy()
            call passMap[2].remove(id)

            call UnitSilencePassives(whichUnit, true, false)
            call UnitSilencePassives(whichUnit, false, false)
        endif
    endif
endfunction

private function OnUnitRemovePassive takes unit whichUnit, integer abilId returns nothing
    local integer i
    local integer id
    if not IsAbilityPassive(abilId) then
        return
    endif
    set id = GetUnitIndex(whichUnit)
    // Assume passMap[2][id] has a value
    if not passMap[2].has(id) then
        call UnitSilencePassives(whichUnit, true, false)
        call UnitSilencePassives(whichUnit, false, false)
    else
        if not IsAbilityMeta(abilId) then
            set i = passMap[3][abilId]
            if passAbilMap[passMap[2][id]].has(i) then
                call AbilityIdList(passMap[2][id]).erase(passAbilMap[passMap[2][id]][i])
            endif
        else
            call passAbilMap[passMap[2][id]].destroy()
            call AbilityIdList(passMap[2][id]).clear()
            call AbilityIdList(passMap[2][id]).destroy()
            call passMap[2].remove(id)

            call UnitSilencePassives(whichUnit, true, false)
            call UnitSilencePassives(whichUnit, false, false)
        endif
    endif
endfunction

hook UnitAddAbility OnUnitAddPassive
hook UnitRemoveAbility OnUnitRemovePassive

//  Hardcoded variants
private function InitPassives takes nothing returns nothing
    call RegisterPassiveAbility('Aroc')
    call RegisterPassiveAbility('Sch4')
    call RegisterPassiveAbility('Srtt')
    call RegisterPassiveAbility('Aroc')
    call RegisterPassiveAbility('Afbt')
    call RegisterPassiveAbility('Afbk')
    call RegisterPassiveAbility('Aflk')
    call RegisterPassiveAbility('Agyb')
    call RegisterPassiveAbility('Afsh')
    call RegisterPassiveAbility('Aphx')
    call RegisterPassiveAbility('Asph')
    call RegisterPassiveAbility('Asth')
    call RegisterPassiveAbility('Ahsb')
    call RegisterPassiveAbility('Agyv')
    call RegisterPassiveAbility('AHbh')
    call RegisterPassiveAbility('AHab')
    call RegisterPassiveAbility('AHad')
    call RegisterPassiveAbility('Sbsk')
    call RegisterPassiveAbility('Abof')
    call RegisterPassiveAbility('Abun')
    call RegisterPassiveAbility('Sca6')
    call RegisterPassiveAbility('Sca1')
    call RegisterPassiveAbility('Sca4')
    call RegisterPassiveAbility('Sca5')
    call RegisterPassiveAbility('Sca2')
    call RegisterPassiveAbility('Sca3')
    call RegisterPassiveAbility('Achl')
    call RegisterPassiveAbility('Adt1')
    call RegisterPassiveAbility('Advc')
    call RegisterPassiveAbility('Aven')
    call RegisterPassiveAbility('Aliq')
    call RegisterPassiveAbility('Apak')
    call RegisterPassiveAbility('Asal')
    call RegisterPassiveAbility('Awar')
    call RegisterPassiveAbility('Arbr')
    call RegisterPassiveAbility('Aspi')
    call RegisterPassiveAbility('Aakb')
    call RegisterPassiveAbility('AOcr')
    call RegisterPassiveAbility('AOae')
    call RegisterPassiveAbility('AOr2')
    call RegisterPassiveAbility('AOre')
    call RegisterPassiveAbility('AOr3')
    call RegisterPassiveAbility('Aabr')
    call RegisterPassiveAbility('Abgl')
    call RegisterPassiveAbility('Abgs')
    call RegisterPassiveAbility('Abgm')
    call RegisterPassiveAbility('Sch2')
    call RegisterPassiveAbility('Agyd')
    call RegisterPassiveAbility('Aap1')
    call RegisterPassiveAbility('Apts')
    call RegisterPassiveAbility('Aap2')
    call RegisterPassiveAbility('Aexh')
    call RegisterPassiveAbility('Afrz')
    call RegisterPassiveAbility('Afr2')
    call RegisterPassiveAbility('Afra')
    call RegisterPassiveAbility('Afrb')
    call RegisterPassiveAbility('Agho')
    call RegisterPassiveAbility('Aeth')
    call RegisterPassiveAbility('Aspa')
    call RegisterPassiveAbility('Atru')
    call RegisterPassiveAbility('AUts')
    call RegisterPassiveAbility('AUau')
    call RegisterPassiveAbility('AUav')
    // Night Elf
    call RegisterPassiveAbility('Acor')
    call RegisterPassiveAbility('Aegm')
    call RegisterPassiveAbility('Aetl')
    call RegisterPassiveAbility('Alit')
    call RegisterPassiveAbility('Arsk')
    call RegisterPassiveAbility('Aspo')
    call RegisterPassiveAbility('Amim')
    call RegisterPassiveAbility('Asp1')
    call RegisterPassiveAbility('Asp2')
    call RegisterPassiveAbility('Asp3')
    call RegisterPassiveAbility('Asp4')
    call RegisterPassiveAbility('Asp5')
    call RegisterPassiveAbility('Asp6')
    call RegisterPassiveAbility('Ault')
    call RegisterPassiveAbility('Aimp')
    call RegisterPassiveAbility('AEev')
    call RegisterPassiveAbility('AEah')
    call RegisterPassiveAbility('AEar')
    // Neutral Hostile
    call RegisterPassiveAbility('ACbh')
    call RegisterPassiveAbility('ANbh')
    call RegisterPassiveAbility('ACba')
    call RegisterPassiveAbility('ACce')
    call RegisterPassiveAbility('ACac')
    call RegisterPassiveAbility('ACct')
    call RegisterPassiveAbility('ACav')
    call RegisterPassiveAbility('Scae')
    call RegisterPassiveAbility('ACvs')
    call RegisterPassiveAbility('ACes')
    call RegisterPassiveAbility('ACev')
    call RegisterPassiveAbility('Afbb')
    call RegisterPassiveAbility('ACnr')
    call RegisterPassiveAbility('ANre')
    call RegisterPassiveAbility('SCva')
    call RegisterPassiveAbility('ACpv')
    call RegisterPassiveAbility('ACrn')
    call RegisterPassiveAbility('ACrk')
    call RegisterPassiveAbility('ACsk')
    call RegisterPassiveAbility('Asla')
    call RegisterPassiveAbility('Aspy')
    call RegisterPassiveAbility('Aspt')
    call RegisterPassiveAbility('Asod')
    call RegisterPassiveAbility('Assp')
    call RegisterPassiveAbility('Aspd')
    call RegisterPassiveAbility('ACm2')
    call RegisterPassiveAbility('ACm3')
    call RegisterPassiveAbility('ACmi')
    call RegisterPassiveAbility('ANth')
    call RegisterPassiveAbility('ANt2')
    call RegisterPassiveAbility('ACah')
    call RegisterPassiveAbility('ACat')
    call RegisterPassiveAbility('ACua')
    call RegisterPassiveAbility('ACvp')
    // Heroes
    call RegisterPassiveAbility('Aamk')
    call RegisterPassiveAbility('ANde')
    call RegisterPassiveAbility('ANd1')
    call RegisterPassiveAbility('ANd2')
    call RegisterPassiveAbility('ANd3')
    call RegisterPassiveAbility('ANdb')
    call RegisterPassiveAbility('Acdb')
    call RegisterPassiveAbility('ANeg')
    // Passive
    call RegisterPassiveAbility('Atdg')
    call RegisterPassiveAbility('Ansk')
    call RegisterPassiveAbility('Aasl')
    call RegisterPassiveAbility('Atsp')
    call RegisterPassiveAbility('Atwa')
    // Other
    call RegisterPassiveAbility('Amnz')
    call RegisterPassiveAbility('Amnx')
    call RegisterPassiveAbility('Adda')
    call RegisterPassiveAbility('Abdl')
    call RegisterPassiveAbility('Abds')
    call RegisterPassiveAbility('Abdt')
    call RegisterPassiveAbility('Sch3')
    call RegisterPassiveAbility('Sch5')
    call RegisterPassiveAbility('Achd')
    call RegisterPassiveAbility('Agld')
    call RegisterPassiveAbility('AInv')
    call RegisterPassiveAbility('Avul')
    call RegisterPassiveAbility('Amin')
    call RegisterPassiveAbility('ANpi')
    call RegisterPassiveAbility('Apig')
    call RegisterPassiveAbility('Apiv')
    call RegisterPassiveAbility('Apmf')
    call RegisterPassiveAbility('Apoi')
    call RegisterPassiveAbility('Argd')
    call RegisterPassiveAbility('Argl')
    call RegisterPassiveAbility('Arlm')
    call RegisterPassiveAbility('Arng')
    call RegisterPassiveAbility('Arev')
    call RegisterPassiveAbility('Aawa')
    call RegisterPassiveAbility('Apit')
    call RegisterPassiveAbility('Aall')
    call RegisterPassiveAbility('Adtg')
    call RegisterPassiveAbility('ANtr')
    call RegisterPassiveAbility('Aihn')
    call RegisterPassiveAbility('Aien')
    call RegisterPassiveAbility('Aion')
    call RegisterPassiveAbility('Aiun')
    call RegisterPassiveAbility('Awan')
    call RegisterPassiveAbility('Awrp')
    // Items
static if INCLUDE_ITEM_AS_ABILITIES then
    call RegisterPassiveAbility('AIbx')
    call RegisterPassiveAbility('AIba')
    call RegisterPassiveAbility('AIcd')
    call RegisterPassiveAbility('AIcs')
    call RegisterPassiveAbility('AIad')
    call RegisterPassiveAbility('AIae')
    call RegisterPassiveAbility('AIev')
    call RegisterPassiveAbility('AIam')
    call RegisterPassiveAbility('AIgm')
    call RegisterPassiveAbility('AId1')
    call RegisterPassiveAbility('AId0')
    call RegisterPassiveAbility('AId2')
    call RegisterPassiveAbility('AId3')
    call RegisterPassiveAbility('AId5')
    call RegisterPassiveAbility('AId7')
    call RegisterPassiveAbility('AId8')
    call RegisterPassiveAbility('AIcb')
    call RegisterPassiveAbility('AIfb')
    call RegisterPassiveAbility('AIgd')
    call RegisterPassiveAbility('AIob')
    call RegisterPassiveAbility('AIf2')
    call RegisterPassiveAbility('AIlb')
    call RegisterPassiveAbility('AIll')
    call RegisterPassiveAbility('AIpb')
    call RegisterPassiveAbility('AIsb')
    call RegisterPassiveAbility('AIsx')
    call RegisterPassiveAbility('AIs2')
    call RegisterPassiveAbility('AItg')
    call RegisterPassiveAbility('AItn')
    call RegisterPassiveAbility('AItc')
    call RegisterPassiveAbility('AItf')
    call RegisterPassiveAbility('AIth')
    call RegisterPassiveAbility('AItx')
    call RegisterPassiveAbility('AIat')
    call RegisterPassiveAbility('AIti')
    call RegisterPassiveAbility('AItj')
    call RegisterPassiveAbility('AIt6')
    call RegisterPassiveAbility('AItk')
    call RegisterPassiveAbility('AItl')
    call RegisterPassiveAbility('AIt9')
    call RegisterPassiveAbility('AIzb')
    call RegisterPassiveAbility('AIx4')
    call RegisterPassiveAbility('AIx3')
    call RegisterPassiveAbility('AIa1')
    call RegisterPassiveAbility('AIx1')
    call RegisterPassiveAbility('AIi1')
    call RegisterPassiveAbility('AIs1')
    call RegisterPassiveAbility('AIaz')
    call RegisterPassiveAbility('AIx2')
    call RegisterPassiveAbility('AIa3')
    call RegisterPassiveAbility('AIi3')
    call RegisterPassiveAbility('AIs3')
    call RegisterPassiveAbility('AIa4')
    call RegisterPassiveAbility('AIi4')
    call RegisterPassiveAbility('AIs4')
    call RegisterPassiveAbility('AIx5')
    call RegisterPassiveAbility('AIa6')
    call RegisterPassiveAbility('AIi6')
    call RegisterPassiveAbility('AIs6')
    call RegisterPassiveAbility('AIcf')
    call RegisterPassiveAbility('AIl2')
    call RegisterPassiveAbility('AIlf')
    call RegisterPassiveAbility('AIl1')
    call RegisterPassiveAbility('AIlz')
    call RegisterPassiveAbility('Arel')
    call RegisterPassiveAbility('Arll')
    call RegisterPassiveAbility('AIva')
    call RegisterPassiveAbility('AImz')
    call RegisterPassiveAbility('AI2m')
    call RegisterPassiveAbility('AImv')
    call RegisterPassiveAbility('AIbm')
    call RegisterPassiveAbility('AImb')
    call RegisterPassiveAbility('AIrm')
    call RegisterPassiveAbility('AIrn')
    call RegisterPassiveAbility('AIms')
    call RegisterPassiveAbility('AIrc')
    call RegisterPassiveAbility('AIsi')
    call RegisterPassiveAbility('AIft')
    call RegisterPassiveAbility('AIfw')
    call RegisterPassiveAbility('AIfx')
    call RegisterPassiveAbility('Apo2')
    call RegisterPassiveAbility('AIgx')
    call RegisterPassiveAbility('AIsr')
    call RegisterPassiveAbility('AImx')
    call RegisterPassiveAbility('AIss')
    call RegisterPassiveAbility('AIse')
    call RegisterPassiveAbility('AIar')
    call RegisterPassiveAbility('AIuv')
    call RegisterPassiveAbility('AIau')
    call RegisterPassiveAbility('AIav')
    call RegisterPassiveAbility('AIwd')
endif
endfunction

private function InitMetaAbilities takes nothing returns nothing
    call RegisterMetaAbility('ANeg')
    call RegisterMetaAbility('Aspb')
endfunction

static if LIBRARY_UnitDex then
/**
**/
private function OnUnitLeave takes nothing returns nothing
    call UnitClearPassives(GetIndexedUnit())
endfunction

private function OnUnitEnter takes nothing returns nothing
    call UnitUpdatePassives(GetIndexedUnit())
endfunction

private function InitUnitEvents takes nothing returns nothing
    call OnUnitIndex(function OnUnitEnter)
    call OnUnitDeindex(function OnUnitLeave)
endfunction
/**
**/
endif

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerAddCondition(t, Filter(function InitPassives))
    call TriggerAddCondition(t, Filter(function InitMetaAbilities))
    static if LIBRARY_UnitDex then
        call TriggerAddCondition(t, Filter(function InitUnitEvents))
    endif
    call TriggerEvaluate(t)
    call DestroyTrigger(t)
    set t = null
endfunction


private module M
    private static method onInit takes nothing returns nothing
        // Index 1 - ability id mapping
        // Index 2 - ability list per unit id mapping
        // Index 3 - ability index mapping
        set passMap        = TableArray[4]
        set metaAbilMap    = TableArray[4]
        set tempTable      = Table.create()
        call Init()
    endmethod
endmodule

private struct S extends array
    implement M
endstruct

endlibrary


Changelog:
  • v.1.0.0.0 - Introduced.
  • v.1.0.0.1 - Removed "Asla" (Sleep Always) from the list of passive abilities, as calling UnitSilencePassives might break the ability.
 
Last edited:
Level 39
Joined
Feb 27, 2007
Messages
5,013
  • The title of this resource makes no sense. Where does the "native" come from? At the very least it should include "passive" in its name.
  • Does this break when a unit uses a morph ability, is forcibly morphed by a chaos ability or reverse bear form, or gains different abilities through engineering upgrade?
  • BlzGetUnitAbility() != null is more performance-efficient than GetUnitAbilityLevel() > 0? My question comes from the fact that the first allocates a handle (agent in this case?) and the latter a primitive.
  • Since you have the whole section that adds all the default passives, wouldn't it make sense to have a config module near the top of the script where users can easily add their own passives, then implement that module in the InitPassives function?
  • Disabling and then immediately re-enabling the passives like you're doing in a few separate places has two consequences:
    • If the unit was in the disabled state before the code is run it will be automatically enabled, possibly against the mapmaker's will
    • AFAIK this would cause any auras the unit had to fizzle for a short time as the aura update period is quite slow, and in that time they'd only affect the source unit itself.
Your documentation is, err, confusing and not well explained.
Note that the ability must be registered as a passive ability beforehand.
Why must it be registered as a passive? A morphing spell could be a meta-ability, could it not? What about a toggle? Registering these activated spells as passives would disable it when disabling the unit's passives even though they are not passive.
- UnitClearPassives(unit whichUnit)
Only invoke this function when the unit is
about to be removed
So do I have to call this or is it optional? Why shouldn't this data be automatically flushed when the unit dies?
- UnitUpdatePassives(unit whichUnit)
This function should be invoked sparingly, and should
be called when the unit has a new passive ability
that was not caught and added to the unit's list.

Ideally, this should be handled smoothly by the
system, but there are cases where it can't catch
everything ability related, so this is exposed
for the user's convenience
Again, do I have to call this or not? An explanation of the types of things the system cannot catch would be helpful.
This hooks UnitAddAbility and UnitRemoveAbility so there might be some performance degradation when dealing with these functions, and in some cases, completely halt the entire function (oplimit reached).
This seems to me to be an unacceptable side effect. Breaking Add/Remove abilities by hitting the op limit before every call would likely destroy a map's ability to be played. If it's possible to unwittingly hit the op limit by using this system... its code should be forcibly run in a new thread.
 
So this function mainly supports adding the ability to neutralize auras/on-hit effects for a period of time - would this be a good resource to also consider allowing the user to enable/disable a passive effect based on randomness?

Depending on the definition of "randomness", most likely.​

The title of this resource makes no sense. Where does the "native" come from? At the very least it should include "passive" in its name.

Passive Abilities" was the original name of "Native Abilities". It was changed to "Native Abilities" due to the inclusion of "Meta-Abilities".​

Does this break when a unit uses a morph ability, is forcibly morphed by a chaos ability or reverse bear form, or gains different abilities through engineering upgrade?

On its' own, yes, since it doesn't have access to the event when a unit would be morphing. If it does have access to the event, UnitUpdatePassives can be called by that time.​

BlzGetUnitAbility() != null is more performance-efficient than GetUnitAbilityLevel() > 0? My question comes from the fact that the first allocates a handle (agent in this case?) and the latter a primitive.
To keep consistency with the rest of the system, the former is used. Not sure about which is more performant than the other.
Since you have the whole section that adds all the default passives, wouldn't it make sense to have a config module near the top of the script where users can easily add their own passives, then implement that module in the InitPassives function?

Although it would, it was preferable to just expose the function to the user instead.​

Why must it be registered as a passive? A morphing spell could be a meta-ability, could it not? What about a toggle? Registering these activated spells as passives would disable it when disabling the unit's passives even though they are not passive.

Well, the principle behind this system is that only passive abilities are viable meta-abilities. Basically, if you can't click it, it's a passive (except Spellbook, its' a rare case).​

So do I have to call this or is it optional? Why shouldn't this data be automatically flushed when the unit dies?

Unless one has UnitDex, this will have to be called in a deindex event (yep). Flushing the data would only make sense for a unit if the unit would no longer exist in the game by that time to begin with, so it can't be flushed upon death.​

Again, do I have to call this or not? An explanation of the types of things the system cannot catch would be helpful.

Some cases unreachable by this system alone are quoted below.
when a unit uses a morph ability, is forcibly morphed by a chaos ability or reverse bear form, or gains different abilities through engineering upgrade

Detecting when a unit has transformed is a non-native event that the system can't catch alone. Gaining different abilities through engineering upgrade or spell book is the reason a "meta-ability" type exists in the first place.

Once the events can be caught, the appropriate functions can be called by then. For example, on a morph event or the levelling up of a meta-ability, UnitUpdatePassives can be called at that point.​
 
Last edited:
Top