1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still haven't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. From the gates of hell, the 5th Special Effect Contest Results have emerged.
    Dismiss Notice
  4. Rubbed the right way, the genie is out of its lamp! The 12th Concept Art Contest Results have been announced.
    Dismiss Notice
  5. Race against the odds and Reforge, Don't Refund. The 14th Techtree Contest has begun!
    Dismiss Notice
  6. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

[System] Buff

Discussion in 'Graveyard' started by Sapeur-Goblin, Feb 27, 2013.

  1. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    Hi,
    I would like to share this buff system and improve it if you have suggestions.
    I have tested it and I didn't find bug but it's possible that there are.
    (Sorry for the bad english in the code ^^)
    Code (vJASS):
    library Buff /*
    *************************************************************************************
    *
    *   Allows rapid and efficient buff development.
    *
    *************************************************************************************
    *
    *   */
    uses /*
    *
    *       -   */
    UnitIndexer /*
    *       -   */
    TimerUtils /*
    *       -   */
    DDS /*
    *       -   */
    optional DamageEvent /*
    *
    *************************************************************************************
    *
    *   First you have to define a buff type : create a struct that extends array and
    *   implement BuffStruct module.
    *
    *   struct BuffTypeA extends array
    *
    *       implement BuffStruct
    *
    *   endstruct
    *
    *   Then you can configure your buff type with those optional parameters :
    *       -   static boolean stackable = false
    *               -   When a non-stackable buff is applied on a unit that already
    *                   have this buff type, the old buff is refreshed.
    *               -   See onRefresh event response for more informations.
    *       -   static boolean permanent = false
    *               -   Permanent buffs can't be dispelled.
    *               -   See Dispel part for more informations.
    *       -   static integer dispelPriority = -1
    *               -   See Dispel part for more informations.
    *       -   static integer auraId = 0
    *               -   Displays the buff in the status bar of the unit.
    *               -   To do this, create a spell based on slow aura, and the set
    *                   targets on self.
    *               -   Then add the corresponding buff.
    *       -   static integer buffId = 0
    *               -   This is the buff added by the slow aura.
    *       -   static integer alignment = BUFF_ALIGNMENT_NEUTRAL
    *               -   Buff alignment. There are three types :
    *               -   BUFF_ALIGNMENT_POSITIVE
    *               -   BUFF_ALIGNMENT_NEGATIVE
    *               -   BUFF_ALIGNMENT_NEUTRAL
    *       -   static boolean positiveBuffImmune = false
    *               -   Immune unit against the positive buffs.
    *       -   static boolean negativeBuffImmune = false
    *               -   Immune unit against the negative buffs.
    *       -   static boolean neutralBuffImmune = false
    *               -   Immune unit against the neutral buffs.
    *       -   static boolean positiveDispellImmune = false
    *               -   Immune unit against the positive buffs dissipations.
    *       -   static boolean negativeDispellImmune = false
    *               -   Immune unit against the negative buffs dissipations.
    *       -   static boolean neutralDispellImmune = false
    *               -   Immune unit against the neutral buffs dissipations.
    *       -   static boolean keepAtDeath = false
    *               -   If true, the unit keep the buff at death.
    *       -   static real period
    *               -   Period for the periodic callback.
    *       -   static integer damagePriority = 0
    *               -   Priority for the onDamage method call.
    *       -   static integer category = 0
    *               -   Use it as you wish.
    *
    *   Once these settings done, it is possible to add event responses.
    *   There are eight responses, all optional:
    *       -   static method onApply takes Buff this returns nothing
    *               -   Called when the buff is applied.
    *       -   static method onPeriodic takes Buff this returns nothing
    *               -   Method called periodically.
    *               -   It is necessary to add a period parameter.
    *
    *       -   static method onRefresh takes Buff this returns nothing
    *               -   Called when the buff is refreshed.
    *               -   To access the settings from the old buff, use:
    *                       -   previousDuration
    *                       -   previousCaster
    *                       -   previousLevel
    *
    *       -   static method onDispel takes Buff this, unit dispeler, boolean b returns nothing
    *               -   Called when an attempt to buff dissipation occur.
    *               -   The unit argument is the unit trying to dispel the buff.
    *               -   The boolean argument is true if the dissipation was successful
    *               -   (so the buff will be destroyed), false otherwise.
    *               -   See Dispel part for more informations.
    *
    *       -   static method onExpire takes Buff this returns nothing
    *               -   Called when the buff expires (the duration is null).
    *               -   It is possible to redefine the duration so the buff is not destroyed.
    *
    *       -   static method onDeath takes Buff this returns nothing
    *               -   Called when the buff target dies.
    *
    *       -   static method onRemove takes Buff this returns nothing
    *               -   Called when the buff is destroyed.
    *               -   Redefine the duration or refresh don't prevent its destruction.
    *
    *       -   static method onDamage takes Buff this returns nothing
    *               -   Called when buff target takes damage.
    *               -   Can only be used with the library DamageEvent.
    *
    *   The module implements a method apply to apply the buff and a method type which returns
    *   the buff type.
    *
    *************************************************************************************
    *
    *   struct Buff extends array
    *
    *       -   readonly unit caster (use refresh method if you want to modify it)
    *       -   readonly unit target
    *       -   readonly integer targetId
    *       -   readonly real level (use refresh method if you want to modify it)
    *       -   readonly integer buffType
    *       -   public real duration (method operator)
    *       -   readonly real initialDuration (method operator)
    *       -   static method apply takes BuffType buffType, unit caster, unit target, real level,
    *           real duration returns thistype
    *               -   If the duration is null, the buff will never expire.
    *       -   method destroy takes nothing returns nothing
    *               -   Prefer using buff dissipation instead of destroy method.
    *       -   method refresh takes unit caster, real level, real duration returns thistype
    *               -   Refreshes the buff with the new settings.
    *       -   static method has takes unit target, BuffType buffType returns boolean
    *       -   static method count takes unit target returns integer
    *       -   static method countType takes unit target, BuffType buffType returns integer
    *       -   static method countAlignment takes unit target, integer alignment returns integer
    *       -   static method immune takes unit target, integer alignment returns boolean
    *               -   Returns true if the unit is immune to buffs of the specified alignment.
    *       -   static method dispelImmune takes unit target, integer alignment returns boolean
    *               -   Returns true if the unit is immune to dispels of the specified alignment.
    *
    *
    *************************************************************************************
    *
    *   Dispel
    *   ------
    *
    *   -   method dispel takes unit dispeler, integer priority returns boolean
    *           -   The buff will be dissipated if the priority of dispel is strictly
    *               greater than the priority of the buff dissipation.
    *
    *   -   static method dispelAlignment takes unit dispeler, unit target, integer priority, integer alignment returns integer
    *           -   Applies a dissipation on all buffs of the specified alignment to the target.
    *
    *   -   static method dispelAll takes unit dispeler, unit target, integer priority returns integer
    *           -   Applies a dissipation on all bufff of the target.
    *
    *   The permanent buffs can not be dispelled, but can be destroyed through with the destroy () method.
    *
    *   Enumerating buffs on a unit
    *   ---------------------------
    *
    *   Buffs unit are classified to enable efficient enumerations. Each buff has next and prev
    *   members, allowing access to the next and previous buffs:
    *       A1 A2 A3 C1 C2    B1 B2 B3 D1 D2    E1 E2 E3 E4 E5
    *       -->-->-->-->-- -> -->-->-->-->-- -> -->-->-->-->--
    *       Positives buffs   Negatives buffs   Neutral buffs
    *
    *   There are three ways to access the buffs a unit:
    *       -   local Buff this = Buff[unit]
    *               -   Returns the first buff of the unit (A1 in the example) :
    *               -   loop
    *                       exitwhen this == 0
    *                       set this = this.next
    *                   endloop
    *       -   local Buff this = Buff[unit][type]
    *               -   Returns the first unit buff of the specified type (A1, C1, B1, D1 or E1 in the example)
    *               -   loop
    *                       exitwhen this.type != myType
    *                       set this = this.next
    *                   endloop
    *       -   local Buff this = Buff[unit][alignment]
    *               -   Returns the first unit buff of the specified alignment (A1, B1 or E1 in the example)
    *               -   loop
    *                       exitwhen this.type.alignment != monAlignement
    *                       set this = this.next
    *                   endloop
    *
    *************************************************************************************/

    globals

        key BUFF_ALIGNMENT_POSITIVE
        key BUFF_ALIGNMENT_NEGATIVE
        key BUFF_ALIGNMENT_NEUTRAL

    endglobals

    globals

        private constant integer applyEvent = 0
        private constant integer refreshEvent = 1
        private constant integer dispelEvent = 2
        private constant integer removeEvent = 3
        private constant integer expireEvent = 4
        private constant integer deathEvent = 5

        private boolean eventBool
        private integer triggerBuff
        private boolexpr array buffEvent
        private integer eventType
        private trigger eventTrig = CreateTrigger()

    endglobals

    globals

        private real array buffTypePeriod
        private boolean array buffTypeStackable
        private integer array buffTypeAuraId
        private integer array buffTypeBuffId
        private integer array buffTypeAlignment
        private integer array buffTypeDispelPriority
        private boolean array buffTypePermanent
        private boolean array buffTypePositiveBuffImmune
        private boolean array buffTypeNegativeBuffImmune
        private boolean array buffTypeNeutralBuffImmune
        private boolean array buffTypePositiveDispelImmune
        private boolean array buffTypeNegativeDispelImmune
        private boolean array buffTypeNeutralDispelImmune
        private boolean array buffTypeKeepAtDeath
       
        private boolean array buffTypeHasApplyEvent
        private boolean array buffTypeHasDeathEvent
        private boolean array buffTypeHasRemoveEvent
        private boolean array buffTypeHasDispelEvent
        private boolean array buffTypeHasExpireEvent
        private boolean array buffTypeHasRefreshEvent
       
        private integer array buffTypeCategory
       
    endglobals

    struct BuffType extends array

        method operator stackable takes nothing returns boolean
            return buffTypeStackable[this]
        endmethod

        method operator period takes nothing returns real
            return buffTypePeriod[this]
        endmethod

        method operator auraId takes nothing returns integer
            return buffTypeAuraId[this]
        endmethod
       
        method operator buffId takes nothing returns integer
            return buffTypeBuffId[this]
        endmethod

        method operator alignment takes nothing returns integer
            return buffTypeAlignment[this]
        endmethod

        method operator dispelPriority takes nothing returns integer
            return buffTypeDispelPriority[this]
        endmethod

        method operator permanent takes nothing returns boolean
            return buffTypePermanent[this]
        endmethod

        method operator positiveBuffImmune takes nothing returns boolean
            return buffTypePositiveBuffImmune[this]
        endmethod

        method operator negativeBuffImmune takes nothing returns boolean
            return buffTypeNegativeBuffImmune[this]
        endmethod

        method operator neutralBuffImmune takes nothing returns boolean
            return buffTypeNeutralBuffImmune[this]
        endmethod

        method operator positiveDispelImmune takes nothing returns boolean
            return buffTypePositiveDispelImmune[this]
        endmethod

        method operator negativeDispelImmune takes nothing returns boolean
            return buffTypeNegativeDispelImmune[this]
        endmethod

        method operator neutralDispelImmune takes nothing returns boolean
            return buffTypeNeutralDispelImmune[this]
        endmethod

        method operator keepAtDeath takes nothing returns boolean
            return buffTypeKeepAtDeath[this]
        endmethod

        method operator hasApplyEvent takes nothing returns boolean
            return buffTypeHasApplyEvent[this]
        endmethod
       
        method operator hasDeathEvent takes nothing returns boolean
            return buffTypeHasDeathEvent[this]
        endmethod

        method operator hasRemoveEvent takes nothing returns boolean
            return buffTypeHasRemoveEvent[this]
        endmethod
       
        method operator hasDispelEvent takes nothing returns boolean
            return buffTypeHasDispelEvent[this]
        endmethod
       
        method operator hasExpireEvent takes nothing returns boolean
            return buffTypeHasExpireEvent[this]
        endmethod

        method operator hasRefreshEvent takes nothing returns boolean
            return buffTypeHasRefreshEvent[this]
        endmethod

        method operator category takes nothing returns integer
            return buffTypeCategory[this]
        endmethod

    endstruct

    globals
        // Periodic timer for onPeriodic method.
        private timer array periodicTim

    endglobals

    private module Init

        private static method onInit takes nothing returns nothing
            local trigger death = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(death, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(death, Filter(function thistype.onDeath))
            call UnitIndexer.DEINDEX.register(Filter(function thistype.onDeindex))
        endmethod

    endmodule

    struct Buff extends array

        readonly static real previousLevel = 0
        readonly static unit previousCaster = null
        readonly static real previousDuration = 0

        private static integer array buffCount
        private static thistype array firstBuff

        private static hashtable table = InitHashtable()
        private static integer instanceCount = 0
       
        private thistype recycle
        readonly thistype prev
        readonly thistype next

        readonly real level
        readonly unit caster

        readonly integer targetId
        readonly BuffType buffType
       
        private timer tim

        method operator target takes nothing returns unit
            return GetUnitById(targetId)
        endmethod

        method operator initialDuration takes nothing returns real
            return TimerGetTimeout(tim)
        endmethod

        method operator duration takes nothing returns real
            return TimerGetRemaining(tim)
        endmethod

        method operator duration= takes real dur returns nothing
            if (dur > 0) then
                if (tim == null) then
                    set tim = NewTimerEx(this)
                endif
                call TimerStart(tim, dur, false, function thistype.expire)
            else
                if (tim != null) then
                    call ReleaseTimer(tim)
                    set tim = null
                endif
            endif
        endmethod

        static method has takes unit targ, integer id returns boolean
            return HaveSavedInteger(table, GetUnitUserData(targ), id)
        endmethod

        static method count takes unit target returns integer
            return buffCount[GetUnitUserData(target)]
        endmethod

        static method countType takes unit target, integer id returns integer
            return LoadInteger(table, GetUnitUserData(target), -id)
        endmethod

        static method countAlignment takes unit target, integer align returns integer
            return LoadInteger(table, GetUnitUserData(target), -align)
        endmethod

        static method operator [] takes unit targ returns thistype
            return firstBuff[GetUnitUserData(targ)]
        endmethod

        method operator [] takes integer id returns thistype
            return LoadInteger(table, GetUnitUserData(target), id)
        endmethod

        static method immune takes unit targ, integer align returns boolean
            return LoadInteger(table, -GetUnitUserData(targ), align) > 0
        endmethod
       
        static method immuneById takes integer targetId, integer align returns boolean
            return LoadInteger(table, -targetId, align) > 0
        endmethod

        static method dispelImmune takes unit targ, integer align returns boolean
            return LoadInteger(table, -GetUnitUserData(targ), -align) > 0
        endmethod

        static method dispelImmuneById takes integer targetId, integer align returns boolean
            return LoadInteger(table, -targetId, -align) > 0
        endmethod

        // Methods for more readability
        //******************************************************************************************
       
        private static method increaseImmuneCount takes integer targetId, integer alignment returns nothing
            call SaveInteger(table, -targetId, alignment, LoadInteger(table, -targetId, alignment) + 1)
        endmethod
       
        private static method decreaseImmuneCount takes integer targetId, integer alignment returns nothing
            call SaveInteger(table, -targetId, alignment, LoadInteger(table, -targetId, alignment) - 1)
        endmethod

        private static method increaseDispelImmuneCount takes integer targetId, integer alignment returns nothing
            call SaveInteger(table, -targetId, -alignment, LoadInteger(table, -targetId, -alignment) + 1)
        endmethod
       
        private static method decreaseDispelImmuneCount takes integer targetId, integer alignment returns nothing
            call SaveInteger(table, -targetId, -alignment, LoadInteger(table, -targetId, -alignment) - 1)
        endmethod
       
        private static method increaseCount takes integer targetId, integer id returns nothing
            call SaveInteger(table, targetId, -id, LoadInteger(table, targetId, -id) + 1)
        endmethod
       
        private static method decreaseCount takes integer targetId, integer id returns nothing
            call SaveInteger(table, targetId, -id, LoadInteger(table, targetId, -id) - 1)
        endmethod

        private static method first takes integer targetId, integer id returns thistype
            return LoadInteger(table, targetId, id)
        endmethod

        private static method setFirst takes integer targetId, integer id, integer this returns nothing
            call SaveInteger(table, targetId, id, this)
        endmethod

        private static method removeCount takes integer targetId, integer id returns nothing
            call RemoveSavedInteger(table, targetId, -id)
        endmethod

        private static method removeFirst takes integer targetId, integer id returns nothing
            call RemoveSavedInteger(table, targetId, id)
        endmethod

        //********************************************************************************************

        private method fireEvent takes integer whichEvent returns nothing
            set eventType = whichEvent
            set triggerBuff = this
            call TriggerClearConditions(eventTrig)
            call TriggerAddCondition(eventTrig, buffEvent[buffType])
            call TriggerEvaluate(eventTrig)
        endmethod

        method destroy takes nothing returns nothing
            local thistype previous = first(targetId, buffType)
            if (buffType.hasRemoveEvent) then
                call fireEvent(removeEvent)
            endif
            set buffCount[targetId] = buffCount[targetId] - 1
            call decreaseCount(targetId, buffType)
            call decreaseCount(targetId, buffType.alignment)
            if (buffType.positiveBuffImmune) then
                call decreaseImmuneCount(targetId, BUFF_ALIGNMENT_POSITIVE)
            endif
            if (buffType.negativeBuffImmune) then
                call decreaseImmuneCount(targetId, BUFF_ALIGNMENT_NEGATIVE)
            endif
            if (buffType.neutralBuffImmune) then
                call decreaseImmuneCount(targetId, BUFF_ALIGNMENT_NEUTRAL)
            endif
            if (buffType.positiveDispelImmune) then
                call decreaseDispelImmuneCount(targetId, BUFF_ALIGNMENT_POSITIVE)
            endif
            if (buffType.negativeDispelImmune) then
                call decreaseDispelImmuneCount(targetId, BUFF_ALIGNMENT_NEGATIVE)
            endif
            if (buffType.neutralDispelImmune) then
                call decreaseDispelImmuneCount(targetId, BUFF_ALIGNMENT_NEUTRAL)
            endif

            if (this == previous) then
                if (buffType != next.buffType) then
                    call removeFirst(targetId, buffType)
                    call removeCount(targetId, buffType)
                    call UnitRemoveAbility(target, buffType.auraId)
                    call UnitRemoveAbility(target, buffType.buffId)
                else
                    call setFirst(targetId, buffType, next)
                endif
                set previous = first(targetId, buffType.alignment)
                if (this == previous) then
                    if (buffType.alignment != next.buffType.alignment) then
                        call removeFirst(targetId, buffType.alignment)
                        call removeCount(targetId, buffType.alignment)
                    else
                        call setFirst(targetId, buffType.alignment, next)
                    endif
                endif
            endif
            if (prev == 0) then
                set firstBuff[targetId] = next
            else
                set prev.next = next
            endif
            set next.prev = prev      

            set caster = null
            call ReleaseTimer(tim)
            set tim = null
            call ReleaseTimer(periodicTim[this])
            set periodicTim[this] = null

            set recycle = thistype(0).recycle
            set thistype(0).recycle = this
        endmethod

        method dispel takes unit cast, integer priority returns boolean
            set eventBool = priority > buffType.dispelPriority and not buffType.permanent and not dispelImmuneById(targetId, buffType.alignment)
            if (buffType.hasDispelEvent) then
                set thistype(0).caster = cast
                call fireEvent(dispelEvent)
            endif
            if (eventBool) then
                call destroy()
            endif
            return eventBool
        endmethod

        static method dispelAlignment takes unit cast, unit targ, integer priority, integer align returns integer
            local thistype this = thistype[targ][align]
            local integer c = 0
            loop
                exitwhen (buffType.alignment != align)
                set eventBool = priority > buffType.dispelPriority and not buffType.permanent and not dispelImmuneById(targetId, align)
                if (buffType.hasDispelEvent) then
                    set thistype(0).caster = cast
                    call fireEvent(dispelEvent)
                endif
                if (eventBool) then
                    set c = c + 1
                    call destroy()
                endif
                set this = next
            endloop
            return c
        endmethod

        static method dispelAll takes unit cast, unit targ, integer priority returns integer
            return dispelAlignment(cast, targ, priority, BUFF_ALIGNMENT_POSITIVE) + /*
            */
        dispelAlignment(cast, targ, priority, BUFF_ALIGNMENT_NEGATIVE) + /*
            */
        dispelAlignment(cast, targ, priority, BUFF_ALIGNMENT_NEUTRAL)
        endmethod

        private static method expire takes nothing returns nothing
            local thistype this = GetTimerData(GetExpiredTimer())
            if (buffType.hasExpireEvent) then
                call fireEvent(expireEvent)
            endif
            if (tim != null and duration == 0) then
                call destroy()
            endif
        endmethod

        method refresh takes unit cast, real lev, real dur returns thistype
            local real plev = previousLevel
            local real pdur = previousDuration
            local unit pcas = previousCaster
            set previousLevel = level
            set previousDuration = duration
            set previousCaster = caster
            set caster = cast
            set level = lev
            set duration = dur
            if (buffType.hasRefreshEvent) then
                call fireEvent(refreshEvent)
            endif
            set previousLevel = plev
            set previousDuration = pdur
            set previousCaster = pcas
            return this
        endmethod

        static method apply takes BuffType buffType, unit cast, unit targ, real lev, real duration returns thistype
            local thistype this
            local thistype previous
            local thistype align
            local integer targId = GetUnitUserData(targ)
            if (targId == 0 or immuneById(targId, buffType.alignment)) then
                return 0
            endif
            set previous = LoadInteger(table, targId, buffType)
            if (not buffType.stackable and previous != 0) then
                return previous.refresh(cast, lev, duration)
            else
                if (thistype(0).recycle != 0) then
                    set this = thistype(0).recycle
                    set thistype(0).recycle = recycle
                else
                    set instanceCount = instanceCount + 1
                    set this = instanceCount
                endif        
               
                set align = first(targId, buffType.alignment)
                if (previous == 0) then
                    if (align == 0) then
                        set previous = firstBuff[targId]
                    else
                        set previous = align
                    endif
                    call UnitAddAbility(targ, buffType.auraId)
                    call SetUnitAbilityLevel(targ, buffType.auraId, R2I(lev))
                    call UnitMakeAbilityPermanent(targ, true, buffType.auraId)
                endif

                if (previous == firstBuff[targId]) then
                    set firstBuff[targId] = this
                    call setFirst(targId, buffType.alignment, this)
                elseif (previous == align) then
                    call setFirst(targId, buffType.alignment, this)
                endif

                call setFirst(targId, buffType, this)
               
                set prev = previous.prev
                set next = previous
                set prev.next = this
                set next.prev = this            

                call increaseCount(targId, buffType)
                call increaseCount(targId, buffType.alignment)
                set buffCount[targId] = buffCount[targId] + 1

                if (buffType.positiveBuffImmune) then
                    call increaseImmuneCount(targId, BUFF_ALIGNMENT_POSITIVE)
                endif
                if (buffType.negativeBuffImmune) then
                    call increaseImmuneCount(targId, BUFF_ALIGNMENT_NEGATIVE)
                endif
                if (buffType.neutralBuffImmune) then
                    call increaseImmuneCount(targId, BUFF_ALIGNMENT_NEUTRAL)
                endif
                if (buffType.positiveDispelImmune) then
                    call increaseDispelImmuneCount(targId, BUFF_ALIGNMENT_POSITIVE)
                endif
                if (buffType.negativeDispelImmune) then
                    call increaseDispelImmuneCount(targId, BUFF_ALIGNMENT_NEGATIVE)
                endif
                if (buffType.neutralDispelImmune) then
                    call increaseDispelImmuneCount(targId, BUFF_ALIGNMENT_NEUTRAL)
                endif
       
                set caster = cast
                set targetId = targId
                set level = lev
                set this.buffType = buffType
                if (duration > 0) then
                    set tim = NewTimerEx(this)
                    call TimerStart(tim, duration, false, function thistype.expire)
                endif

                if (buffType.hasApplyEvent) then
                    call fireEvent(applyEvent)
                endif

            endif
            return this
        endmethod

        private static method onDeath takes nothing returns boolean
            local thistype this = firstBuff[GetUnitUserData(GetDyingUnit())]
            loop
                exitwhen (this == 0)
                if (buffType.hasDeathEvent) then
                    call fireEvent(deathEvent)
                endif
                if (not buffType.keepAtDeath) then
                    call destroy()
                endif
                set this = next
            endloop
            return false
        endmethod

        private static method onDeindex takes nothing returns boolean
            local integer unitId = GetIndexedUnitId()
            local thistype this = firstBuff[unitId]
            loop
                exitwhen (this == 0)
                if (buffType.hasRemoveEvent) then
                    call fireEvent(removeEvent)
                endif
                if (next == 0) then
                    set recycle = thistype(0).recycle
                else
                    set recycle = next
                endif
                set caster = null
                call ReleaseTimer(tim)
                set tim = null
                call ReleaseTimer(periodicTim[this])
                set periodicTim[this] = null
                set this = next
            endloop
            set this = firstBuff[unitId]
            set firstBuff[unitId] = 0
            set buffCount[unitId] = 0
            call FlushChildHashtable(table, unitId)
            call FlushChildHashtable(table, -unitId)
            set thistype(0).recycle = this
            return false
        endmethod

        implement Init

    endstruct

    private struct DefaultValues extends array

        static boolean stackable = false
        static boolean permanent = false
        static integer dispelPriority = -1
        static integer auraId = 0
        static integer buffId = 0
        static integer alignment = BUFF_ALIGNMENT_NEUTRAL
        static boolean positiveBuffImmune = false
        static boolean negativeBuffImmune = false
        static boolean neutralBuffImmune = false
        static boolean positiveDispelImmune = false
        static boolean negativeDispelImmune = false
        static boolean neutralDispelImmune = false
        static boolean keepAtDeath = false
       
        static integer category = 0
       
        static if (LIBRARY_PriorityEvent) then
            static integer damagePriority = 0
        endif

    endstruct

    module BuffStruct

        private static delegate DefaultValues default

        static if (LIBRARY_DamageEvent and thistype.onDamage.exists) then
            private static method damageCall takes nothing returns boolean
                static if (thistype.damageFilter.exists) then
                    local Buff this
                    if (damageFilter()) then
                        set this = Buff[DDS.target][typeid]
                        loop
                            exitwhen (this == 0)
                            call onDamage(this)
                            set this = this.next
                        endloop
                    endif
                else
                    local Buff this = Buff[DDS.target][typeid]
                    loop
                        exitwhen (this == 0)
                        call onDamage(this)
                        set this = this.next
                    endloop
                endif
                return false
            endmethod
        endif

        static if (thistype.onPeriodic.exists) then
            private static method periodicCall takes nothing returns nothing
                call onPeriodic(GetTimerData(GetExpiredTimer()))
            endmethod
        endif

        private static method onEvent takes nothing returns boolean
            if (eventType == applyEvent) then
                static if thistype.onPeriodic.exists then
                    set periodicTim[triggerBuff] = NewTimerEx(triggerBuff)
                    call TimerStart(periodicTim[triggerBuff], buffType.period, true, function thistype.periodicCall)
                endif
                static if thistype.onApply.exists then
                    call onApply(triggerBuff)
                endif
            elseif (eventType == dispelEvent) then
                static if (thistype.onDispel.exists) then
                    call onDispel(triggerBuff, Buff(0).caster, eventBool)
                endif
            elseif (eventType == refreshEvent) then
                static if (thistype.onRefresh.exists) then
                    call onRefresh(triggerBuff)
                endif
            elseif (eventType == removeEvent) then
                static if (thistype.onRemove.exists) then
                    call onRemove(triggerBuff)
                endif
            elseif (eventType == expireEvent) then
                static if thistype.onExpire.exists then
                    call onExpire(triggerBuff)
                endif
            elseif (eventType == deathEvent) then
                static if (thistype.onDeath.exists) then
                    call onDeath(triggerBuff)
                endif
            endif
            return false
        endmethod

        private static method onInit takes nothing returns nothing
            set buffEvent[typeid] = Filter(function thistype.onEvent)
            set buffTypeStackable[typeid] = stackable
            static if thistype.onPeriodic.exists then
                set buffTypePeriod[typeid] = period
            endif
            set buffTypeAuraId[typeid] = auraId
            set buffTypeBuffId[typeid] = buffId
            set buffTypeAlignment[typeid] = alignment
            set buffTypeDispelPriority[typeid] = dispelPriority
            set buffTypePermanent[typeid] = permanent
            set buffTypePositiveBuffImmune[typeid] = positiveBuffImmune
            set buffTypeNegativeBuffImmune[typeid] = negativeBuffImmune
            set buffTypeNeutralBuffImmune[typeid] = neutralBuffImmune
            set buffTypePositiveDispelImmune[typeid] = positiveDispelImmune
            set buffTypeNegativeDispelImmune[typeid] = negativeDispelImmune
            set buffTypeNeutralDispelImmune[typeid] = neutralDispelImmune
            set buffTypeKeepAtDeath[typeid] = keepAtDeath
            set buffTypeCategory[typeid] = category
            static if (LIBRARY_DamageEvent and thistype.onDamage.exists) then
                static if LIBRARY_PriorityEvent then
                    call DDS.ANY.register(Filter(function thistype.damageCall), damagePriority)
                else
                    call DDS.ANY.register(Filter(function thistype.damageCall))
                endif
            endif
            static if (thistype.onApply.exists) then
                set buffTypeHasApplyEvent[typeid] = true
            elseif (thistype.onPeriodic.exists) then
                set buffTypeHasApplyEvent[typeid] = true
            endif
            static if (thistype.onRefresh.exists) then
                set buffTypeHasRefreshEvent[typeid] = true
            endif
            static if (thistype.onRemove.exists) then
                set buffTypeHasRemoveEvent[typeid] = true
            endif
            static if (thistype.onExpire.exists) then
                set buffTypeHasExpireEvent[typeid] = true
            endif
            static if (thistype.onDeath.exists) then
                set buffTypeHasDeathEvent[typeid] = true
            endif
            static if (thistype.onDispel.exists) then
                set buffTypeHasDispelEvent[typeid] = true
            endif
        endmethod

        static method apply takes unit caster, unit target, real level, real duration returns Buff
            return Buff.apply(typeid, caster, target, level, duration)
        endmethod

        static method operator buffType takes nothing returns BuffType
            return typeid
        endmethod

        static method operator previousCaster takes nothing returns unit
            return Buff.previousCaster
        endmethod
       
        static method operator previousDuration takes nothing returns real
            return Buff.previousDuration
        endmethod
       
        static method operator previousLevel takes nothing returns real
            return Buff.previousLevel
        endmethod

    endmodule

    endlibrary


    Example :
    Code (vJASS):
    scope MagicShield initializer onInit
    //Very original name :P

    struct MagicShield extends array

        private static constant integer auraId = 'A000' //Spell based on slow aura
        private static constant integer buffId = 'B000' //The buff added by the slow aura
        private static constant boolean negativeBuffImmune = true

    /*
        private static method onApply takes Buff this returns nothing
            call UnitAddArmor(this.target, R2I(this.level))
        endmethod
       
        private static method onRefresh takes Buff this returns nothing
            call UnitRemoveArmor(this.target, R2I(previousLevel))
            call UnitAddArmor(this.target, R2S(this.level))
        endmethod
       
        private static method onRemove takes Buff this returns nothing
            call UnitRemoveArmor(this.target, R2I(this.level))
        endmethod
    */

        private static method onDamage takes Buff this returns nothing
            call SetUnitState(this.target, UNIT_STATE_LIFE, GetUnitState(this.target, UNIT_STATE_LIFE) + DamageEvent.amount / 2)
        endmethod

        implement BuffStruct

    endstruct

    private function onInit takes nothing returns nothing
        local unit caster = CreateUnit(Player(0), 'Hpal', 0, 0, 0)
        local unit target = CreateUnit(Player(0), 'hfoo', 200, 0, 0)
        local unit attacker = CreateUnit(Player(1), 'hfoo', 300, 0, 180)
        call MagicShield.apply(caster, target, 3, 15)
        set caster = null
        set target = null
        set attacker = null
    endfunction

    endscope
     
    Last edited: Jun 29, 2013
  2. Ender

    Ender

    Joined:
    Jan 28, 2012
    Messages:
    208
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    Code (vJASS):
    *               •   BUFF_ALIGNMENT_POSITIVE
    *               •   BUFF_ALIGNMENT_NEUTRAL
    *               •   BUFF_ALIGNMENT_NEUTRAL

    ->
    Code (vJASS):
    *               •   BUFF_ALIGNMENT_POSITIVE
    *               •   BUFF_ALIGNMENT_NEUTRAL
    *               •   BUFF_ALIGNMENT_HOSTILE

    we all make little mistakes:)
    looks pretty nice, could you please attach an example?


    I think you can change this, so it doesn't generate all of those elseifs if the buff doesn't have that method
    Code (vJASS):
    if (EventType == 0) then // Apply & Periodic initialization
                static if thistype.onPeriodic.exists then
                    set periodicTim[TriggerBuff] = NewTimerEx(TriggerBuff)
                    call TimerStart(periodicTim[TriggerBuff], BuffTypePeriod[typeid], true, function thistype.periodicCall)
                endif
                static if thistype.onApply.exists then
                    call onApply(TriggerBuff)
                endif
            elseif (EventType == 1) then // dispel
                static if (thistype.onDispel.exists) then
                    call onDispel(TriggerBuff, UnitEvent, BoolEvent)
                endif
            elseif (EventType == 2) then // Refresh
                static if (thistype.onRefresh.exists) then
                    call onRefresh(TriggerBuff)
                endif
            elseif (EventType == 3) then // Remove
                static if (thistype.onRemove.exists) then
                    call onRemove(TriggerBuff)
                endif
            elseif (EventType == 4) then // Expire
                static if thistype.onExpire.exists then
                    call onExpire(TriggerBuff)
                endif
            elseif (EventType == 5) then // Death
                static if (thistype.onDeath.exists) then
                    call onDeath(TriggerBuff)
                endif
            endif


    Edit: adding a couple of constant integers, EventOnDeath, EventOnApply, EventOnRefresh, would help people understand what is what in your code.
     
  3. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    I thought that elseifs were faster than simple ifs.
    The method onEvent is currently always called even if the event method doesn't exist. How can I improve it?

    I am improving it for more readability, but I have one question. Should I use RemoveSavedInteger when the buff count reachs 0 (this makes the code more complex but safer I think), or should I just decrease the buff count (and flush the hashtable when the unit is removed)?

    I'll add an example today.
     
  4. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Could you use dashes (-) or asterisks (*) instead of those unicode characters? :/
    We have some pretty bad history with unicode on this site. I don't want to see people's resources going crazy some very unfortunate day :p

    readonly BuffType type

    My compiler doesn't like this very much :p
    I think it would be a safer bet to call it buffType. type_p works too.

    One other thing, the accepted JASS convention currently states that global variables are to be writtenLikeThis (camel-cased just like your struct members)
     
  5. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    Updated.

    I didn't know that :eek: (strange convention ^^).
     
  6. Ender

    Ender

    Joined:
    Jan 28, 2012
    Messages:
    208
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    when ever you call an event you could do a boolean check, something like bufftype.has(EVENT), or maybe it would be better just to do bufftype.hasOnDispel, bufftype.hasOnDeath etc..

    then you could add to the setup module for bufftypes,
    Code (vJASS):
     static if onApply.exists
                    set typeid.hasOnApply=true
              endif
              //etc... this would reduce the number of TriggerEvaluates


    as for getting rid of the unnecessary elifs you can do
    Code (vJASS):
     if EventType == 0 then
                     //regular actions her then add

                    static if thistype.onDispel.exists then
                        elseif  EventType == 1 then
                            call onDispel()
                     endif// end of static if

                     static if thistype.onWhatEvs.exists then
                         elseif EventType == whoKnows then
                             call doSomeMethod
                      endif//end of static if

                     //etc..
                endif


    why aren't you using table?
     
  7. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    To avoid 8 boolean array, I could also match a prime number to each event, and use an integer x for each buff type whose value is the product of each event. Then to verify if a buff type has an event type, it suffices to verify that x / eventType == R2I (x / eventType).
    But I think it's a horrible technique : /.

    It doesn't work with my compiler : /.


    I can't since I use negative parent keys. And it's not necessary.
     
  8. Ender

    Ender

    Joined:
    Jan 28, 2012
    Messages:
    208
    Resources:
    2
    Spells:
    1
    Tutorials:
    1
    Resources:
    2
    you should do something like that, it would be more efficient then doing a trigger Eval

    meh, just realized you would have to do this
    Code (vJASS):
    private static method onEvent takes nothing returns boolean
            local integer eT = eventType

            if (eT == applyEvent) then
                static if thistype.onPeriodic.exists then
                    set periodicTim[triggerBuff] = NewTimerEx(triggerBuff)
                    call TimerStart(periodicTim[triggerBuff], buffType.period, true, function thistype.periodicCall)
                endif
                static if thistype.onApply.exists then
                    call onApply(triggerBuff)
                endif
            static if (thistype.onDispel.exists) then
                //! novjass
                elseif (eT == dispelEvent) then
                //! endnovjass
                    call onDispel(triggerBuff, eventUnit, eventBool)
            endif
            static if (thistype.onRefresh.exists) then
                //! novjass
                elseif (eT == refreshEvent) then
                //! endnovjass
                        call onRefresh(triggerBuff)
            endif
            static if (thistype.onRemove.exists) then
                //! novjass
                elseif (eT == removeEvent) then
                //! endnovjass
                    call onRemove(triggerBuff)
            endif
           
            static if thistype.onExpire.exists then
                //! novjass
                elseif (eT == expireEvent) then
                //! endnovjass
                    call onExpire(triggerBuff)
            endif
            static if (thistype.onDeath.exists) then
                //! novjass
                elseif (eT == deathEvent) then
                //! endnovjass
                    call onDeath(triggerBuff)
            endif
            endif
            return false
        endmethod

    and you would also need to replace deathEvent etc... in the code with , with either their generated name (you would need to switch them to public), or a local var. on the plus side it would reduce generated code size dramatically

    you would be able to change it over, it would be a lot of hassle, but it might be better as it would reduce a maps hashtable usage. but its really not that important.

    anyways cool system keep it up.
     
  9. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,823
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    Awesome stuff!
    Too bad this hasn't been made more early. There was a good buffstruct available on TheHelper, but it had a bug with multiple instances of the same buff that was never fixed (and I was too lazy to do it on my own), so I hardcoded almost all of my buff spells.
    ... and now it's way too many of them to make it worth a switch. :/
     
  10. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    Thank you for advices and comments =).

    I will update it soon but I haven't much time currently : /.
     
  11. Zwiebelchen

    Zwiebelchen

    Joined:
    Sep 17, 2009
    Messages:
    6,823
    Resources:
    12
    Models:
    5
    Maps:
    1
    Spells:
    1
    Tutorials:
    1
    JASS:
    4
    Resources:
    12
    Btw, do you use levelable abilities to apply the buff for stacking buffs? I'm asking because I don't know if Tornado Aura (which is the most common practice for adding dummybuffs) allows multiple levels to be displayed.
    If not, I really suggest doing it or adding a feature for it, because the WC3 UI will display the level of an aura ability with levels in the buff tooltip, which is useful for the player (ingame, I know you can get the number of stacks by your system) to find out how many stacks are on the target.
     
  12. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    Code (vJASS):
        method operator duration takes nothing returns real
            return TimerGetRemaining(tim)
        endmethod

        method operator duration= takes real duration returns nothing
            if (duration > 0) then
                if (tim == null) then
                    set tim = NewTimerEx(this)
                endif
                call TimerStart(tim, duration, false, function thistype.expire)
            else
                if (tim != null) then
                    call ReleaseTimer(tim)
                    set tim = null
                endif
            endif
        endmethod


    I think think this brings up compiler dependence.
    It would be better to have the parameter named dur so that JassHelper will /never/ use the duration method operator inside the duration= method operator. Who knows, an update might give rise to some bugs we most likely won't be aware of :p
     
  13. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    This system has a lot of potential, and I'm willing to approve it immediately after updates are done.

    I'll give it a [Needs work] thread title and leave it in Submissions for now.
     
  14. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    Updated :ogre_haosis:.

    Sorry for the long wait I had exams.

    I maybe should change the requirement "DamageEvent" since it has been graveyarded?
     
  15. Magtheridon96

    Magtheridon96

    Joined:
    Dec 12, 2008
    Messages:
    6,006
    Resources:
    26
    Maps:
    1
    Spells:
    8
    Tutorials:
    7
    JASS:
    10
    Resources:
    26
    You can make this use DDS instead :p (with the DamageEvent plugin)

    The API would probably remain the same.
     
  16. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,843
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    I agree with Mag.
    I have also seen the Buff of TheHelper and it uses J4L's Damage.
     
  17. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,087
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    J4L's Damage I seen have the buff of TheHelper also.
    Mag I agree with.
     
  18. Sapeur-Goblin

    Sapeur-Goblin

    Joined:
    Jun 25, 2011
    Messages:
    35
    Resources:
    0
    Resources:
    0
    Hm I don't understand this new Damage Event library :eek:.
    There are only modules, no structs.
     
  19. Bannar

    Bannar

    Joined:
    Mar 19, 2008
    Messages:
    3,087
    Resources:
    20
    Spells:
    5
    Tutorials:
    1
    JASS:
    14
    Resources:
    20
    Because Nes's idea is to make it flexible. DDS is just a shell, plugins are made to fill it and provide additional stuff.
     
  20. Almia

    Almia

    Joined:
    Apr 24, 2012
    Messages:
    4,843
    Resources:
    35
    Spells:
    30
    Tutorials:
    4
    JASS:
    1
    Resources:
    35
    LOL,it seems that you reversed what i said :3