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

AuraSys - a flexible custom aura system

This bundle is marked as awaiting update. A staff member has requested changes to it before it can be approved.
  • Like
Reactions: Spellbound
Hello guys,

After searching for a while I couldn't find a decent custom aura system to use. Therefore I decided to just write one.

AuraSys Supports:
- Similar mechanics as the vanilla Warcraft 3
- Stacking & non-stacking auras
- Aura lingering
- Aura enumeration

Update v0.2, 2/24/2020:
- Fixed AuraSys.on_apply() called after the initial tick
- Fixed some stub function typo in demo auras.

Check out the demo map for examples!

Any advice/criticisms are welcome.
Contents

AuraSys (Map)

Reviews
MyPad
Elegantly written from the system itself to the documentation of the public functions. The demo scripts serve as fairly sufficient templates for one to use in their own maps, though they require careful tinkering in order to achieve success at the...
So how do you level up/down the BUFF_SPELL_ID to match the level of the SPELL_ID? I see StrengthAura has a this.max_level member but it appears to only affect the actual strength value and not the BUFF_SPELL_ID itself? What's more, does this system track when the SPELL_ID changes level or do I need a separate timer for this?

@quackerd

EDIT: So I'm having quite the issues getting AuraSys to work properly. Everything seems to be doing fine (aside from the level issue metioned above), but it looks like deleting the aura doesn't actually remove the buff it applies? I looked through the code and to the best of my understanding it should be doing that, but I don't know. Here's the Aura I've been trying to make - it's supposed to project an invisibility aura when a unit morphs. The events are firing as expected: "new aura" and "delete aura" are printing when the unit morphs, but after "delete aura" is printed, there are no "remove" messages that print, so I'm assuming the AuraBuff's on_remove method is not being called.

JASS:
scope Conceal initializer init

/*
    During a "udg_UnitTypeEvent", GetUnitTypeId() will return the id the unit has morphed into. If you wish to
    retrieve the previous ID, refer to the variable UnitTypeOf[UDex].
*/

globals
    private constant integer    UNIT_ID_NORMAL      = 'h00Q'
    private constant integer    UNIT_ID_MORPHED     = 'h00U'

    private constant real       AURA_RADIUS         = 750.
    private constant integer    INVIS_ABIL          = 'Apiv'
   
    private constant integer    ABIL_ID             = 'A077'
    private Table auraStorage   = 0
endglobals

private struct AuraBuff extends AuraSysBuff
    private static constant string TARGET_EFFECT = ""

    private boolean removeInvis

    // called when this buff is applied to an unit the first time
    method on_apply takes nothing returns nothing
        call BJDebugMsg("apply")
        set this.removeInvis = false
        if UnitAddAbility(this.as_target, INVIS_ABIL) then
            set this.removeInvis = true
        endif
    endmethod

    // called when the last AuraSys that inflicts this buff is removed from an unit
    method on_remove takes nothing returns nothing
        call BJDebugMsg("remove")
        if this.removeInvis then
            call UnitRemoveAbility(this.as_target, INVIS_ABIL)
        endif
    endmethod
endstruct

private struct AuraNode extends AuraSysNode
    // called everytime a new AuraSys is added to the buff
    method on_apply takes nothing returns nothing
    endmethod

    // called everytime an AuraSys is removed from the buff
    method on_remove takes nothing returns nothing
    endmethod
endstruct

private struct Aura extends AuraSys
    implement AuraSysInit

    private static constant integer BUFF_SPELL_ID = 'A0D6'
    private static constant integer BUFF_ID = 'B01F'

    method get_range takes nothing returns real
        return AURA_RADIUS
    endmethod

    method active_cond takes nothing returns boolean
        return UnitAlive(this.as_source)
    endmethod

    method filter takes unit u returns boolean
        return IsUnitAlly(u, GetOwningPlayer(this.as_source)) and (not IsUnitType(u, UNIT_TYPE_STRUCTURE)) and UnitAlive(u) and (not IsUnitType(u, UNIT_TYPE_FLYING))
    endmethod

    method linger_filter takes unit u returns boolean
        return UnitAlive(u)
    endmethod

    method new_node takes nothing returns AuraSysNode
        return AuraNode.create()
    endmethod

    method new_buff takes nothing returns AuraSysBuff
        return AuraBuff.create()
    endmethod

    method get_buff_typeid takes nothing returns integer
        return AuraBuff.typeid
    endmethod

    method on_apply takes nothing returns nothing
    endmethod

    method on_remove takes nothing returns nothing
    endmethod
endstruct

private function onMorph takes nothing returns nothing
    local unit u = udg_UDexUnits[udg_UDex]
    local integer uID = GetUnitTypeId(u)
    local Aura aura
    if uID == UNIT_ID_MORPHED then
        call BJDebugMsg("new aura")
        set aura = Aura.new(u)
        set auraStorage[GetHandleId(u)] = aura
    elseif uID == UNIT_ID_NORMAL then // alway check id in the else if because UnitType event happens for every morph.
        set aura = auraStorage[GetHandleId(u)]
        if aura != 0 then
            call BJDebugMsg("delete aura")
            call aura.delete()
            call auraStorage.remove(GetHandleId(u))
        endif
    endif
    set u = null
endfunction

private function onIndex takes nothing returns nothing
    local Aura aura = Aura.new(udg_UDexUnits[udg_UDex])
    set auraStorage[GetHandleId(udg_UDexUnits[udg_UDex])] = aura
endfunction

private function init takes nothing returns nothing
    local trigger eventListener = CreateTrigger()
    call TriggerRegisterVariableEvent(eventListener, "udg_UnitTypeEvent", EQUAL, 1.00)
    call TriggerAddCondition(eventListener, function onMorph)
   
    set auraStorage = Table.create()
    call RegisterSpellIndexEvent(ABIL_ID, function onIndex)
endfunction

endscope
 
Last edited:
Sorry for double posting but I felt this needs to be separate from the above post.

@quackerd so it turns out there's a small issue in AuraSys that bugs out the delete method. The arguments in BlzGropupAddGroupFast() should be reversed. Right now they're
JASS:
        call BlzGroupAddGroupFast(this.as_tmpg, this.as_activeg)
        call BlzGroupAddGroupFast(this.as_tmpg, this.as_lingerg)
when they should be
JASS:
        call BlzGroupAddGroupFast(this.as_activeg, this.as_tmpg)
        call BlzGroupAddGroupFast(this.as_lingerg, this.as_tmpg)
instead. It's an understandable confusion, that function is somewhat ambiguous when you look at the native declaration. At any rate, this should be updated ASAP.
 
Elegantly written from the system itself to the documentation of the public functions.
The demo scripts serve as fairly sufficient templates for one to use in their own maps,
though they require careful tinkering in order to achieve success at the first try.

Multiple times in the system, the call to GetHandleId is not stored in a variable (This also ignores the amount of
debug statements that invoke this function). Storing the result is suggested.

The global table variable is initialized in the library initialization phase, which might cause problems with
scripts using this library during the module / struct initialization phase. To avert this, it is recommended
that the global table variable is initialized during the module initialization phase.

Due to the concerns raised above, the bundle is temporarily moved to Awaiting Update
 
Top