System BuffStruct

I never really understood why .isOn was there in the first place. I can't think of any situation I would ever use it. If I ever did need to find out if a unit had the buff I think I would rather just have a function in the buff struct that returns the unit the buff is applied to instead of a function that takes a unit as a parameter and returns a boolean. And even that would be so situational I'd probably just write that function in whatever buff struct I needed it in instead of a part of the system that's included in every buffstruct.

Is there a reason why .isOn checks if the unit has the ability instead of comparing it to the unit that the buff was applied to?
The .isOn function is not meant fo find out wether the buff is on the unit, but if *any* buff is on the unit of that type.
And you need this when you have like lots of buffs and do not want to check for the Ability IDs in the slow-as-hell object editor all the time.

i was referring to making an instance from another function/trigger.
then calling .destroy on another func/trig.
This also works fine. You just need to get the right struct instance from somewhere. Of course you can not destroy the struct of another trigger instance without getting the buffstruct instance of the other trigger somehow. That is working as intended if you ask me and perfectly fine. Are you sure you understand the usage of structs entirely?
 
I never really understood why .isOn was there in the first place. I can't think of any situation I would ever use it. If I ever did need to find out if a unit had the buff I think I would rather just have a function in the buff struct that returns the unit the buff is applied to instead of a function that takes a unit as a parameter and returns a boolean.
.isOn is static. It checks if any instance of the buff is on the unit. And Zwiebelchen is correct, it currently bugs with multi-instancing because it does read whether or not the cosmetic is applied. :p
Oh well, I've noted it and intend to update this anyway. I don't have stacks of time like I used to..
 
Oh well, I've noted it and intend to update this anyway. I don't have stacks of time like I used to..
Don't worry. Once you actually start to change it, you will notice that it is not that hard to do and not as time consuming as you might think.
 
i made these simple buffs.. check them out..
FEAR (commonly requested)
JASS:
//! runtextmacro BuffType("Fear")
    //! runtextmacro SetBuffName("Fear")
    //! runtextmacro SetBuffAlignment("NEGATIVE")
    //! runtextmacro SetBuffTooltip("This unit is in fear.")
    //! runtextmacro SetBuffIcon("ReplaceableTextures\\CommandButtons\\BTNHowlOfTerror.blp")
//! runtextmacro BuffStruct()
    real fx
    real fy
    real angle
    integer i
    
    private method periodic takes nothing returns nothing
        set this.i = this.i+1
        if this.i==15 then
            set this.i = 0
            set this.angle = GetRandomReal(1,360)
            set this.fx = GetUnitX(this.unit)+300*Cos(bj_RADTODEG*this.angle)
            set this.fy = GetUnitY(this.unit)+300*Sin(bj_RADTODEG*this.angle)
            call IssuePointOrder(this.unit,"move",this.fx,this.fy)
        endif
        call SelectUnitRemoveForPlayer( this.unit, GetOwningPlayer(this.unit) )
    endmethod
    
    implement T32xs
    
    method onApply takes nothing returns nothing
        call SelectUnitAddForPlayer( this.unit, GetOwningPlayer(this.unit) )
        set this.i = 0
        set this.angle = GetRandomReal(1,360)
        set this.fx = GetUnitX(this.unit)+300*Cos(bj_RADTODEG*this.angle)
        set this.fy = GetUnitY(this.unit)+300*Sin(bj_RADTODEG*this.angle)
        call this.startPeriodic()
   endmethod
    
    method onRemove takes nothing returns nothing
        call this.stopPeriodic()
        call SelectUnitAddForPlayer( this.unit, GetOwningPlayer(this.unit) )
    endmethod
//! runtextmacro EndBuff()



TAUNTED (of course, set this.caster before applying...)
JASS:
//! runtextmacro BuffType("Taunt")
    //! runtextmacro SetBuffName("Taunted")
    //! runtextmacro SetBuffAlignment("NEGATIVE")
    //! runtextmacro SetBuffTooltip("This unit is taunted.")
    //! runtextmacro SetBuffIcon("ReplaceableTextures\\CommandButtons\\BTNUnholyFrenzy.blp")
//! runtextmacro BuffStruct()
    unit caster
    unit target
    integer i
    
    private method periodic takes nothing returns nothing
        set this.i = this.i+1
        if this.i==15 then
            set this.i = 0
            call IssueTargetOrder(this.unit,"attack",this.target)
        endif
        call SelectUnitRemoveForPlayer( this.unit, GetOwningPlayer(this.unit) )
    endmethod
    
    implement T32xs
    
    method onApply takes nothing returns nothing
        call SelectUnitAddForPlayer( this.unit, GetOwningPlayer(this.unit) )
        call this.startPeriodic()
   endmethod
    
    method onRemove takes nothing returns nothing
        call this.stopPeriodic()
        set this.caster = null
        set this.target = null
        call SelectUnitAddForPlayer( this.unit, GetOwningPlayer(this.unit) )
    endmethod
//! runtextmacro EndBuff()



TOSSED
little bit more complicated.
before applying,

set this.tfinal to match expire time (since you cant get it automatically)
play with this.accel vor aesthetics.

JASS:
//! runtextmacro PUI_PROPERTY("", "boolean", "UNIT_IS_TOSSED", "false")

//! runtextmacro BuffType("Toss")
    //! runtextmacro SetBuffName("Tossed")
    //! runtextmacro SetBuffAlignment("NEGATIVE")
    //! runtextmacro SetBuffTooltip("This unit is tossed into the air.")
    //! runtextmacro SetBuffIcon("ReplaceableTextures\\CommandButtons\\BTNHowlOfTerror.blp")
//! runtextmacro BuffStruct()
    real z
    real time = 0
    real vinit
    real accel = 2000
    real tfinal
    real defz
    boolean paused = true
    boolean started = false
    
    private method periodic takes nothing returns nothing
        set this.time = this.time + .03125
        set this.z = ((-this.accel/2)*(this.time*this.time)) + (this.vinit*this.time)
        call SetUnitFlyHeight(this.unit,this.z,1000)
    endmethod
    
    implement T32xs
    
    method onApply takes nothing returns nothing
        if not UNIT_IS_TOSSED[this.unit] then
            set UNIT_IS_TOSSED[this.unit] = true
            set this.started = true
            set this.defz = GetUnitFlyHeight(this.unit)
            
            if this.paused then
                call PauseUnit(this.unit,true)
            endif
            
            call UnitAddAbility(this.unit,'Amrf')
            call UnitRemoveAbility(this.unit,'Amrf')
            
            set this.vinit = (this.tfinal*this.accel)/2
            
            call this.startPeriodic()
        endif
   endmethod
    
    method onRemove takes nothing returns nothing
        if this.started then
            set UNIT_IS_TOSSED[this.unit] = false
            
            if this.paused then
                call PauseUnit(this.unit,false)
            endif
            
            //call SetUnitFlyHeight(this.unit,this.defz,0)
            call this.stopPeriodic()
        endif
    endmethod
//! runtextmacro EndBuff()
 
In the create method.. you should put.. [lJASS]if whichUnit!=null[/lJASS] before [lJASS]//! runtextmacro BuffStruct__AttachToWhichUnit()[/lJASS].

Because if I want to create buff without any attaching to unit, AIDS shows me an error, only in debug mode but still.

EDIT: I really miss a way to extend the duration of the buff in the middle of old duration.
 
extension to the post above...

13lade's BIG BUG REPORT!!

i was coding a spell, figured i coded inefficiently checked multiple times: lag resulted from buffstruct failing.

Buff.create(null) fails on so many levels.
1. caused AIDS alarm.
2. you are doing lots of stuff to a null unit.
3. UnitAddAbility and UnitMakeAbilityPermanent on a null unit generate TREMENDOUS LAG.

3 was tested with this code:

JASS:
    set g = CreateGroup()
    
    call GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),650,Condition(function LiveUnitFilter))
    loop
        set u = FirstOfGroup(g)
        call GroupRemoveUnit(g,u)
        exitwhen u==null
        if not IsUnitEnemy(u,GetOwningPlayer(caster)) then
            call NewTextTagPointFuncSide("+"+I2S(stacks),9,GetUnitX(u),GetUnitY(u),100,220,15,15,false,60,1.2,1)

            set co = CounterOffe.create(null)
            call co.setUnit(u)
            call co.destroyTimed(10)
        endif

failed miserably, dropped my fps down to a single digit in multiple casts.


Old code:
JASS:
        static method create takes unit whichUnit returns thistype
            local thistype this=thistype.allocate()
            set this.buffUnit=whichUnit
            call UnitAddAbility(whichUnit,thistype.typeStruct.abilType)
            call UnitMakeAbilityPermanent(whichUnit,true,thistype.typeStruct.abilType)
            //! runtextmacro BuffStruct__AttachToWhichUnit()
            call this.onCreate()
            if whichUnit!=null then
                call this.onApply()
            endif
            return this
        endmethod


MY FIX: simply dont do stuff to null unit.
(de-alarmed AIDS, totally removed lag.)

modified BuffStruct, ability code remains same.
JASS:
        static method create takes unit whichUnit returns thistype
            local thistype this=thistype.allocate()
            call this.onCreate()
            if whichUnit!=null then
                set this.buffUnit=whichUnit
                call UnitAddAbility(whichUnit,thistype.typeStruct.abilType)
                call UnitMakeAbilityPermanent(whichUnit,true,thistype.typeStruct.abilType)
                //! runtextmacro BuffStruct__AttachToWhichUnit()
                call this.onApply()
            endif
 
Can you make some function for this as you made for SpellStruct? I find it little harder to make trigger in BuffStruct.. because I hafta save instance for trigger.. and such.. and if you would make it like in SpellStruct it'd be a way easier.. with that "Method callback".
 
Can you make some function for this as you made for SpellStruct? I find it little harder to make trigger in BuffStruct.. because I hafta save instance for trigger.. and such.. and if you would make it like in SpellStruct it'd be a way easier.. with that "Method callback".
Dunno exactly what you mean, but wheres the problem storing your struct data to a timer? I mean; the system even provides a timed destruction function. All you have to do is writing your onRemove and onApply method (and possibly your periodic) and there you go. Usually, you do not need to attach your struct instances anywhere ... as long as you do not want to dispel certain buffs before the duration ends - which can be done by using the EnumBuffs function he provided.
 
Dunno exactly what you mean, but wheres the problem storing your struct data to a timer? I mean; the system even provides a timed destruction function. All you have to do is writing your onRemove and onApply method (and possibly your periodic) and there you go. Usually, you do not need to attach your struct instances anywhere ... as long as you do not want to dispel certain buffs before the duration ends - which can be done by using the EnumBuffs function he provided.

I know all this. I meant trigger not timer. And that timed destroying isn't that good, because it won't destroy timer if I destroy the struct sooner.
 
..still has efficiency issues though..

extension to the post above...

13lade's BIG BUG REPORT!!

i was coding a spell, figured i coded inefficiently checked multiple times: lag resulted from buffstruct failing.

Buff.create(null) fails on so many levels.
1. caused AIDS alarm.
2. you are doing lots of stuff to a null unit.
3. UnitAddAbility and UnitMakeAbilityPermanent on a null unit generate TREMENDOUS LAG.

3 was tested with this code:

JASS:

    set g = CreateGroup()
    
    call GroupEnumUnitsInRange(g,GetUnitX(caster),GetUnitY(caster),650,Condition(function LiveUnitFilter))
    loop
        set u = FirstOfGroup(g)
        call GroupRemoveUnit(g,u)
        exitwhen u==null
        if not IsUnitEnemy(u,GetOwningPlayer(caster)) then
            call NewTextTagPointFuncSide("+"+I2S(stacks),9,GetUnitX(u),GetUnitY(u),100,220,15,15,false,60,1.2,1)

            set co = CounterOffe.create(null)
            call co.setUnit(u)
            call co.destroyTimed(10)
        endif

failed miserably, dropped my fps down to a single digit in multiple casts.


Old code:
JASS:

        static method create takes unit whichUnit returns thistype
            local thistype this=thistype.allocate()
            set this.buffUnit=whichUnit
            call UnitAddAbility(whichUnit,thistype.typeStruct.abilType)
            call UnitMakeAbilityPermanent(whichUnit,true,thistype.typeStruct.abilType)
            //! runtextmacro BuffStruct__AttachToWhichUnit()
            call this.onCreate()
            if whichUnit!=null then
                call this.onApply()
            endif
            return this
        endmethod


MY FIX: simply dont do stuff to null unit.
(de-alarmed AIDS, totally removed lag.)

modified BuffStruct, ability code remains same.
JASS:

        static method create takes unit whichUnit returns thistype
            local thistype this=thistype.allocate()
            call this.onCreate()
            if whichUnit!=null then
                set this.buffUnit=whichUnit
                call UnitAddAbility(whichUnit,thistype.typeStruct.abilType)
                call UnitMakeAbilityPermanent(whichUnit,true,thistype.typeStruct.abilType)
                //! runtextmacro BuffStruct__AttachToWhichUnit()
                call this.onApply()
            endif
 
Did you fix the problem that removing a buff from a unit also removes the buff icon, even if another buff of the same type is still active?
 
Buffs doesn't disappear after holder's death. Wrong.

- You should add option for that.​

Buffs always "stack" with same type(struct). Wrong.

- You should add option for that, too.​

Here's how I did it, because I needed for my map.
I also added classes of buffs, movement buff, universal.. whatever you want.

JASS:
// THIS IS EDITED VERSION, BY FURBY!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//      ___ _   _ ___ ___ ___ _____ ___ _   _  ___ _____ 
//     | _ ) | | | __| __/ __|_   _| _ \ | | |/ __|_   _|
//     | _ \ |_| | _|| _|\__ \ | | |   / |_| | (__  | |
//     |___/\___/|_| |_| |___/ |_| |_|_\\___/ \___| |_|
//                                         By Jesus4Lyf
//                                                                    v 1.0.3
//      What is BuffStruct?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          BuffStruct is a simple buff adding/removal/tracking system designed for
//          rapid map development. It automatically generates all object data and
//          backing code for adding and removing buffs to and from units.
//          
//      How to implement?
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          1. Create a new trigger named BuffStructHeader. Go to 'Edit -> Convert
//          to Custom Text', and replace everything that's there with this script.
//
//          2. Create a new trigger immediately after BuffStructHeader, named
//          BuffStructFooter. go to 'Edit -> Convert to Custom Text', and replace
//          everything that's there with: //! endexternalblock
//
//          3. To make a BuffStruct, make a new trigger between BuffStructHeader
//          and BuffStructFooter, and use the template below.
//
//      BuffStruct Template:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          Example:
/*
//! runtextmacro BuffType("Return")
    //! runtextmacro SetBuffName("Return")
    //! runtextmacro SetBuffAlignment("NEUTRAL")
    //! runtextmacro SetBuffTooltip("This unit is being Returned; it will return to its previous location soon.")
    //! runtextmacro SetBuffIcon("ReplaceableTextures\\CommandButtons\\BTNDaggerOfEscape.blp")
//! runtextmacro BuffStruct()
    real x
    real y
    method onCreate takes nothing returns nothing
        set this.x = GetUnitX(this.unit)
        set this.y = GetUnitY(this.unit)
    endmethod
    method preDestroy takes nothing returns nothing
        call SetUnitX(this.unit, this.x)
        call SetUnitY(this.unit, this.y)
    endmethod
//! runtextmacro EndBuff()
*/
//          Documentation:
//
//              //! runtextmacro BuffType("Identifier")
//                  This must contain a 1 word identifier for the buff. This is
//                  used to create new instances with Identifier.create(unit)
//
//              //! runtextmacro SetBuffName("Name")
//                  This is the name that will display in the buff tray.
//
//              //! runtextmacro SetBuffAlignment("NEUTRAL")
//                  This is whether the buff is POSITIVE, NEGATIVE or NEUTRAL.
//
//              //! runtextmacro SetBuffTooltip("Tooltip Missing!")
//                  This is the tooltip that will display in the buff tray.
//
//              //! runtextmacro SetBuffIcon("ReplaceableTextures\\CommandButtons\\BTN???.blp")
//                  This is the icon used for the buff in the buff tray.
//
//              //! runtextmacro BuffStruct()
//                  This denotes the end of textmacro configuration for a buff,
//                  and the start of the struct that will be instanciated for
//                  each instance of a buff.
//                  You may use the optional event response methods here, which
//                  are listed below.
//
//              //! runtextmacro EndBuff()
//                  This denotes the end of the BuffStruct.
//
//      Event Response Methods:
//     ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//          In each BuffStruct, you may optionally implement the following methods:
//
/*              method onApply takes nothing returns nothing
*/              // Fires just after the buff is applied to a unit.
//
/*              method onRemove takes nothing returns nothing
*/              // Fires just before the buff is removed from a unit.
//
/*              method onCreate takes nothing returns nothing
*/              // Fires immediately after the buff is created and applied to
//              // the first unit. onApply will fire immediately after this.
//
/*              method preDestroy takes nothing returns nothing
*/              // Fires just before the buff instance is destroyed. onRemove
//              // will fire just before this.
//
/*              method onDamageReceived takes nothing returns nothing
*/              // Fires when the buffed unit takes damage. Use GetEventDamageSource()
//              // to refer to the damaging unit.
//              // All functions from "Damage" are available here.
//
/*              method onDamageDealt takes nothing returns nothing
*/              // Fires when the buffed unit deals damage. Use GetTriggerUnit()
//              // to refer to the unit taking the damage.
//              // All functions from "Damage" are available here.
//
//      Other:
//     ¯¯¯¯¯¯¯¯
//          All BuffStructs' instances have the ".unit" field, which is the unit
//          the buff is currently on.
//
//          All BuffStructs have the .setUnit(unit) method, which will remove
//          the buff instance from the current unit, and apply it to the given unit.
//          Use .setUnit(null) to unapply a buff without destroying it.
//
//          Use .isOn(unit) --> boolean to see if a buff is on a unit.
//
//          To do something for each buff on a unit, use:
/*          call BuffList[unit].forEachBuff(BUFF_ALIGNMENT_??, myMethod)
*/          // ?? should be substituted for POSITIVE, NEGATIVE or NEUTRAL.
//          // The function used for myMethod must take only a BuffStruct argument.
//
//          You may destroy a buff in x seconds using:
/*          call myBuffInstance.destroyTimed(real)
*/          // "real" is the time to destruction.
//
library BuffStruct requires AIDS, Damage, optional TimerUtils
//! externalblock extension=lua ObjectMerger $FILENAME$
    //! runtextmacro BuffStruct__StartConfig()

//===========================================================================
// CONFIG AREA
//
    
    //===========================================================================
    // System Object Editor IDs.
    //
    
    //! runtextmacro BuffStruct__BeginSysIds()
        // Uses:
        //  - 'Bxx&' for buff type.
        //  - 'Axx&' for base ability.
        
        //! runtextmacro BuffStruct__AllowSysId("@a")
        //! runtextmacro BuffStruct__AllowSysId("@b")
        //! runtextmacro BuffStruct__AllowSysId("@c")
        //! runtextmacro BuffStruct__AllowSysId("@d")
        //! runtextmacro BuffStruct__AllowSysId("@e")
        //! runtextmacro BuffStruct__AllowSysId("@f")
        //! runtextmacro BuffStruct__AllowSysId("@g")
        //! runtextmacro BuffStruct__AllowSysId("@h")
        //! runtextmacro BuffStruct__AllowSysId("@i")
        //! runtextmacro BuffStruct__AllowSysId("@j")
        //! runtextmacro BuffStruct__AllowSysId("@k")
        //! runtextmacro BuffStruct__AllowSysId("@l")
        //! runtextmacro BuffStruct__AllowSysId("@m")
        //! runtextmacro BuffStruct__AllowSysId("@n")
        //! runtextmacro BuffStruct__AllowSysId("@o")
        //! runtextmacro BuffStruct__AllowSysId("@p")
        //! runtextmacro BuffStruct__AllowSysId("@q")
        //! runtextmacro BuffStruct__AllowSysId("@r")
        //! runtextmacro BuffStruct__AllowSysId("@s")
        //! runtextmacro BuffStruct__AllowSysId("@t")
        //! runtextmacro BuffStruct__AllowSysId("@u")
        //! runtextmacro BuffStruct__AllowSysId("@v")
        //! runtextmacro BuffStruct__AllowSysId("@w")
        //! runtextmacro BuffStruct__AllowSysId("@x")
        //! runtextmacro BuffStruct__AllowSysId("@y")
        //! runtextmacro BuffStruct__AllowSysId("@z")
        //! runtextmacro BuffStruct__AllowSysId("@A")
        //! runtextmacro BuffStruct__AllowSysId("@B")
        //! runtextmacro BuffStruct__AllowSysId("@C")
        //! runtextmacro BuffStruct__AllowSysId("@D")
        //! runtextmacro BuffStruct__AllowSysId("@E")
        //! runtextmacro BuffStruct__AllowSysId("@F")
        //! runtextmacro BuffStruct__AllowSysId("@G")
        //! runtextmacro BuffStruct__AllowSysId("@H")
        //! runtextmacro BuffStruct__AllowSysId("@I")
        //! runtextmacro BuffStruct__AllowSysId("@J")
        //! runtextmacro BuffStruct__AllowSysId("@K")
        //! runtextmacro BuffStruct__AllowSysId("@L")
        //! runtextmacro BuffStruct__AllowSysId("@M")
        //! runtextmacro BuffStruct__AllowSysId("@N")
        //! runtextmacro BuffStruct__AllowSysId("@O")
        //! runtextmacro BuffStruct__AllowSysId("@P")
        //! runtextmacro BuffStruct__AllowSysId("@Q")
        //! runtextmacro BuffStruct__AllowSysId("@R")
        //! runtextmacro BuffStruct__AllowSysId("@S")
        //! runtextmacro BuffStruct__AllowSysId("@T")
        //! runtextmacro BuffStruct__AllowSysId("@U")
        //! runtextmacro BuffStruct__AllowSysId("@V")
        //! runtextmacro BuffStruct__AllowSysId("@W")
        //! runtextmacro BuffStruct__AllowSysId("@X")
        //! runtextmacro BuffStruct__AllowSysId("@Y")
        //! runtextmacro BuffStruct__AllowSysId("@Z")
        
    //! runtextmacro BuffStruct__EndSysIds()
    
//===========================================================================
// END CONFIG AREA
//
    //! runtextmacro BuffStruct__EndConfig()
    
    
    //===========================================================================
    // SYSTEM AREA
    //
    
    globals
        constant integer BUFF_ALIGNMENT_POSITIVE=1
        constant integer BUFF_ALIGNMENT_NEGATIVE=2
        constant integer BUFF_ALIGNMENT_NEUTRAL=3
        constant integer BUFF_TYPE_UNIVERSAL=1
        constant integer BUFF_TYPE_MOVEMENT=2
    endglobals
    
    private function interface Method takes integer this returns nothing
    
    //! textmacro BuffStruct__StartConfig
    //! endtextmacro
    //! textmacro BuffStruct__EndConfig
    //! endtextmacro
    
    
    //===========================================================================
    // Id Declaration
    //
    //  Exposes a module which implements the system ids, as per the system
    // configuration, automatically filling structs starting from struct ID #1.
    // 
    
    //! textmacro BuffStruct__BeginSysIds
        private keyword abilType
        private keyword buffType
        private keyword idMax
        
        private module SysIds
            /*libprivate*/ integer abilType
            /*libprivate*/ integer buffType
            /*libprivate*/ static integer idMax=0
            private static method onInit takes nothing returns nothing
                //! i sysIds={}
                //! i maxSysIds=0
    //! endtextmacro
    //! textmacro BuffStruct__EndSysIds
            endmethod
        endmodule
        //! i sysIdsUsed=0
    //! endtextmacro
    
    //! textmacro BuffStruct__AllowSysId takes SYSID
        set thistype.idMax=thistype.idMax+1
        //! i maxSysIds=maxSysIds+1
        
        set thistype(thistype.idMax).abilType='A$SYSID$&'
        set thistype(thistype.idMax).buffType='B$SYSID$&'
        //! i sysIds[maxSysIds]={abilType="A$SYSID$&", buffType="B$SYSID$&"}
    //! endtextmacro
    
    //===========================================================================
    // BuffType Struct
    //
    
    private struct BuffType
        implement SysIds // supplies abilType, buffType, preloaded.
    endstruct
    
    //===========================================================================
    // BuffStruct
    //
    private keyword buffUnit
    private keyword next
    private keyword prev
    private keyword temp
    
    private interface DEFAULTS
        static BuffStruct temp
        
        unit buffUnit
        BuffStruct next
        BuffStruct prev
        
        // User implementable
        method onCreate takes nothing returns nothing defaults nothing
        method onApply takes nothing returns nothing defaults nothing
        method onRemove takes nothing returns nothing defaults nothing
        method preDestroy takes nothing returns nothing defaults nothing
        method onDamageReceived takes nothing returns nothing defaults nothing
        method onDamageDealt takes nothing returns nothing defaults nothing
        
        // Module
        method setUnit takes unit whichUnit returns nothing defaults nothing
    endinterface
    
    struct BuffStruct extends DEFAULTS
        integer TYPE=BUFF_TYPE_UNIVERSAL
        boolean DEATH_DESTROY=true
        private static method timedDestroyCallback takes nothing returns nothing
            static if LIBRARY_TimerUtils then
                local timer t=GetExpiredTimer()
                call thistype(GetTimerData(t)).destroy()
                call ReleaseTimer(t)
            endif
        endmethod
        method destroyTimed takes real time returns nothing
            static if LIBRARY_TimerUtils then
                local timer t=NewTimer()
                call SetTimerData(t,this)
                call TimerStart(t,time,false,function thistype.timedDestroyCallback)
            else
                call BJDebugMsg("BuffStruct Error: Must import TimerUtils in order to use .timedDestroy method.")
            endif
        endmethod
        static method DestroyDeathBuffs takes BuffStruct whatBuff returns nothing
            if whatBuff.DEATH_DESTROY then
                call whatBuff.destroy()
            endif
        endmethod
        static method onDeath takes nothing returns boolean
            local unit u = GetTriggerUnit()
            call BuffList<u>.forEachBuff(BUFF_ALIGNMENT_POSITIVE, DestroyDeathBuffs)
            call BuffList<u>.forEachBuff(BUFF_ALIGNMENT_NEUTRAL, DestroyDeathBuffs)
            call BuffList<u>.forEachBuff(BUFF_ALIGNMENT_NEGATIVE, DestroyDeathBuffs)
            set u = null
            return false
        endmethod
        static method onInit takes nothing returns nothing 
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function thistype.onDeath))
        endmethod 
    endstruct
    
    module BuffStruct
        static integer ALIGNMENT=BUFF_ALIGNMENT_NEUTRAL
        static integer BUFF_TYPE=BUFF_TYPE_UNIVERSAL
        static boolean BUFF_DEATH_DESTROY=true
        static boolean BUFF_STACKS=false
        //! textmacro BuffStruct__AttachToWhichUnit
            set BuffStruct.temp=BuffList[whichUnit][thistype.ALIGNMENT]
            set BuffStruct.temp.prev.next=this
            set this.prev=BuffStruct.temp.prev
            set BuffStruct.temp.prev=this
            set this.next=BuffStruct.temp
        //! endtextmacro
        
        private static BuffType typeStruct=0
        
        static method isOn takes unit whichUnit returns boolean
            return GetUnitAbilityLevel(whichUnit,thistype.typeStruct.abilType)&gt;0
        endmethod
        
        private static method onInit takes nothing returns nothing
            set thistype.typeStruct=BuffType.create()
        endmethod
        
        method operator unit takes nothing returns unit
            return this.buffUnit
        endmethod
        
        static method destroyPrevious takes BuffStruct whatBuff returns nothing
            if whatBuff.getType() == thistype.typeid and not thistype.BUFF_STACKS then
                call whatBuff.destroy()
            endif
        endmethod
        
        method checkPrevious takes nothing returns nothing
            call BuffList[this.buffUnit].forEachBuff(this.ALIGNMENT, destroyPrevious)
        endmethod
        
        method setUnit takes unit whichUnit returns nothing
            if this.buffUnit!=null then
                call this.onRemove()
                set this.next.prev=this.prev
                set this.prev.next=this.next
                call UnitMakeAbilityPermanent(this.buffUnit,false,thistype.typeStruct.abilType)
                call UnitRemoveAbility(this.buffUnit,thistype.typeStruct.abilType)
                call UnitRemoveAbility(this.buffUnit,thistype.typeStruct.buffType)
            endif
            set this.buffUnit=whichUnit
            if whichUnit!=null then
                call UnitAddAbility(whichUnit,thistype.typeStruct.abilType)
                call UnitMakeAbilityPermanent(whichUnit,true,thistype.typeStruct.abilType)
                //! runtextmacro BuffStruct__AttachToWhichUnit()
                call this.checkPrevious()
                call this.onApply()
            endif
        endmethod
        
        private method onDestroy takes nothing returns nothing
            if this.buffUnit!=null then
                call this.onRemove()
                call this.preDestroy()
                set this.next.prev=this.prev
                set this.prev.next=this.next
                call UnitMakeAbilityPermanent(this.buffUnit,false,thistype.typeStruct.abilType)
                call UnitRemoveAbility(this.buffUnit,thistype.typeStruct.abilType)
                call UnitRemoveAbility(this.buffUnit,thistype.typeStruct.buffType)
            else
                call this.preDestroy()
            endif
        endmethod
        
        static method create takes unit whichUnit returns thistype
            local thistype this=thistype.allocate()
            set this.TYPE=BUFF_TYPE
            set this.DEATH_DESTROY=BUFF_DEATH_DESTROY
            set this.buffUnit=whichUnit
            if whichUnit!=null then
                call UnitAddAbility(whichUnit,thistype.typeStruct.abilType)
                call UnitMakeAbilityPermanent(whichUnit,true,thistype.typeStruct.abilType)
                //! runtextmacro BuffStruct__AttachToWhichUnit()
            endif
            call this.onCreate()
            if whichUnit!=null then
                call this.checkPrevious()
                call this.onApply()
            endif
            return this
        endmethod
    endmodule
    
    //! textmacro BuffType takes IDENTIFIER
        struct $IDENTIFIER$ extends BuffStruct
            implement BuffStruct
            //! i itemIdentifier=&quot;$IDENTIFIER$&quot;
            private static method onInit takes nothing returns nothing
                //! i sysIdsUsed=sysIdsUsed+1
                // Defaults.
                //! i buffName=&quot;$IDENTIFIER$&quot;
                //! i buffIcon=&quot;&quot;
                //! i buffDescription=&quot;&quot;
                //! i buffAlignment=&quot;NEUTRAL&quot;
                //! i buffType=&quot;UNIVERSAL&quot;
                //! i buffDeathDestroy=&quot;true&quot;
                //! i buffStacks=&quot;false&quot;
    //! endtextmacro
    
    //! textmacro SetBuffName takes VAL
        //! i buffName=&quot;$VAL$&quot;
    //! endtextmacro
    //! textmacro SetBuffIcon takes VAL
        //! i buffIcon=&quot;$VAL$&quot;
    //! endtextmacro
    //! textmacro SetBuffTooltip takes VAL
        //! i buffDescription=&quot;$VAL$&quot;
    //! endtextmacro
    //! textmacro SetBuffAlignment takes VAL
        set thistype.ALIGNMENT=BUFF_ALIGNMENT_$VAL$
        //! i buffAlignment=&quot;$VAL$&quot;
    //! endtextmacro
    //! textmacro SetBuffType takes VAL
        set thistype.BUFF_TYPE=BUFF_TYPE_$VAL$
        //! i buffType=&quot;$VAL$&quot;
    //! endtextmacro
    //! textmacro SetBuffDeathDestroy takes VAL
        set thistype.BUFF_DEATH_DESTROY=$VAL$
        //! i buffDeathDestroy=&quot;$VAL$&quot;
    //! endtextmacro
    //! textmacro SetBuffStacks takes VAL
        set thistype.BUFF_STACKS=$VAL$
        //! i buffStacks=&quot;$VAL$&quot;
    //! endtextmacro
    
    //! textmacro BuffStruct
            endmethod
    //! endtextmacro
    //! textmacro EndBuff
        endstruct
        // Make object data.
        // Actual buff
        //! i setobjecttype(&quot;buffs&quot;)
        //! i createobject(&quot;Basl&quot;,sysIds[sysIdsUsed].buffType)
        //! i makechange(current,&quot;ftat&quot;,&quot;&quot;)
        //! i makechange(current,&quot;fnsf&quot;,&quot;(BuffStruct)&quot;)
        //! i makechange(current,&quot;fart&quot;,buffIcon)
        //! i makechange(current,&quot;fube&quot;,buffDescription)
        //! i if buffAlignment==&quot;POSITIVE&quot; then
            //! i makechange(current,&quot;ftip&quot;,&quot;|cff00FF00&quot;..buffName..&quot;|r&quot;)
        //! i elseif buffAlignment==&quot;NEUTRAL&quot; then
            //! i makechange(current,&quot;ftip&quot;,&quot;|cff00FFFF&quot;..buffName..&quot;|r&quot;)
        //! i else
            //! i makechange(current,&quot;ftip&quot;,buffName)
        //! i end
        
        // Slow aura
        //! i setobjecttype(&quot;abilities&quot;)
        //! i createobject(&quot;Aasl&quot;,sysIds[sysIdsUsed].abilType)
        //! i makechange(current,&quot;ansf&quot;,&quot;(BuffStruct)&quot;)
        //! i makechange(current,&quot;Slo1&quot;,1,0)
        //! i makechange(current,&quot;aare&quot;,1,0)
        //! i makechange(current,&quot;abuf&quot;,1,sysIds[sysIdsUsed].buffType)
        //! i makechange(current,&quot;arac&quot;,&quot;other&quot;)
        //! i makechange(current,&quot;atar&quot;,1,&quot;self&quot;)
        //! i makechange(current,&quot;anam&quot;,buffName)
    //! endtextmacro
    
    //===========================================================================
    // On Damage
    //
    
    private module DamageEvent
        private static method onDamage takes nothing returns boolean
            // Attacked
            local thistype u=thistype[GetTriggerUnit()]
            local BuffStruct base=u[BUFF_ALIGNMENT_POSITIVE]
            local BuffStruct this=base.next
            loop
                exitwhen this==base
                call this.onDamageReceived()
                set this=this.next
            endloop
            set base=u[BUFF_ALIGNMENT_NEUTRAL]
            set this=base.next
            loop
                exitwhen this==base
                call this.onDamageReceived()
                set this=this.next
            endloop
            set base=u[BUFF_ALIGNMENT_NEGATIVE]
            set this=base.next
            loop
                exitwhen this==base
                call this.onDamageReceived()
                set this=this.next
            endloop
            // Attacker
            if GetEventDamageSource()!=null then
                set u=thistype[GetEventDamageSource()]
                set base=u[BUFF_ALIGNMENT_POSITIVE]
                set this=base.next
                loop
                    exitwhen this==base
                    call this.onDamageDealt()
                    set this=this.next
                endloop
                set base=u[BUFF_ALIGNMENT_NEUTRAL]
                set this=base.next
                loop
                    exitwhen this==base
                    call this.onDamageDealt()
                    set this=this.next
                endloop
                set base=u[BUFF_ALIGNMENT_NEGATIVE]
                set this=base.next
                loop
                    exitwhen this==base
                    call this.onDamageDealt()
                    set this=this.next
                endloop
            endif
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            local trigger t=CreateTrigger()
            call Damage_RegisterEvent(t)
            call TriggerAddCondition(t,Filter(function thistype.onDamage))
            set t=null
        endmethod
    endmodule
    
    //===========================================================================
    // Buff Listing
    //
    
    private struct LinkNode extends BuffStruct
    endstruct
    
    struct BuffList extends array
        private static LinkNode array linkNode
        method operator [] takes integer alignment returns LinkNode
            return thistype.linkNode[this*3+alignment]
        endmethod
        private method operator []= takes integer alignment, LinkNode node returns nothing
            set thistype.linkNode[this*3+alignment]=node
        endmethod
        
        private method AIDS_onCreate takes nothing returns nothing
            set BuffStruct.temp=LinkNode.create()
            set BuffStruct.temp.next=BuffStruct.temp
            set BuffStruct.temp.prev=BuffStruct.temp
            set this[BUFF_ALIGNMENT_POSITIVE]=BuffStruct.temp
            set BuffStruct.temp=LinkNode.create()
            set BuffStruct.temp.next=BuffStruct.temp
            set BuffStruct.temp.prev=BuffStruct.temp
            set this[BUFF_ALIGNMENT_NEGATIVE]=BuffStruct.temp
            set BuffStruct.temp=LinkNode.create()
            set BuffStruct.temp.next=BuffStruct.temp
            set BuffStruct.temp.prev=BuffStruct.temp
            set this[BUFF_ALIGNMENT_NEUTRAL]=BuffStruct.temp
        endmethod
        private method AIDS_onDestroy takes nothing returns nothing
            call this[BUFF_ALIGNMENT_POSITIVE].destroy()
            call this[BUFF_ALIGNMENT_NEGATIVE].destroy()
            call this[BUFF_ALIGNMENT_NEUTRAL].destroy()
        endmethod
        //! runtextmacro AIDS()
        
        implement DamageEvent
        
        method forEachBuff takes integer alignment, Method func returns nothing
            local LinkNode base=this[alignment]
            set this=thistype(base.next)
            loop
                exitwhen this==base
                call func.evaluate(this)
                set this=thistype(LinkNode(this).next)
            endloop
        endmethod
    endstruct
endlibrary</u></u></u>
 
Buffs doesn't disappear after holder's death. Wrong.

- You should add option for that.​
...
JASS:
        static method DestroyDeathBuffs takes BuffStruct whatBuff returns nothing
            if whatBuff.DEATH_DESTROY then
                call whatBuff.destroy()
            endif
        endmethod
        static method onDeath takes nothing returns boolean
            local unit u = GetTriggerUnit()
            call BuffList<u>.forEachBuff(BUFF_ALIGNMENT_POSITIVE, DestroyDeathBuffs)
            call BuffList<u>.forEachBuff(BUFF_ALIGNMENT_NEUTRAL, DestroyDeathBuffs)
            call BuffList<u>.forEachBuff(BUFF_ALIGNMENT_NEGATIVE, DestroyDeathBuffs)
            set u = null
            return false
        endmethod
        static method onInit takes nothing returns nothing 
            local trigger t = CreateTrigger()
            call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
            call TriggerAddCondition(t, Condition(function thistype.onDeath))
        endmethod </u></u></u>

There is no reason for that to be inside the system itself - it can be done externally. In fact, even with the code you posted, you are more likely to double-free buffs than help an end user. You should move the buff to a null unit on death, and let the buff be destroyed as per usual, unless you handle special cases like death in the way you normally destroy buffs. Hence why buff destruction/null unit allocation is left up to the end user. :thup:

Don't forget, some buffs are very validly things which happen on death, like faster respawn time. :)
Buffs always "stack" with same type(struct). Wrong.

- You should add option for that, too.​
I'm not sure what you mean, here - the idea is if you want a buff that can only exist once on a unit, there will be a MyBuffType[unit] method operator which will return the oldest instance on the unit, as I've said before in this thread - I'm just yet to write it. That will return 0 if the unit doesn't have it. It should be pretty obvious how to use that to write "universal" buffs, as I think you called them.
JASS:
local MyBuff inst = MyBuff[GetSpellTargetUnit()]
if inst==0 then
    set inst=MyBuff.create(GetSpellTargetUnit())
endif
//.. do things to inst or w/e

This system is (or will be) flexible enough to code whatever you want, without it having to be hard coded specifically into the system. Feel free to write additional functionality modules to plug in. :thup:
I also added classes of buffs, movement buff, universal.. whatever you want.
I may have to take a look at that, some time. Once again, every buff is a unique integer so you can attach to it directly, should you need additional functionality which applies to every buff, such as a class - and I encourage you to write these things externally to the BuffStruct header itself, so that you can keep getting the latest version of BuffStruct for yourself. You may even submit your additional things as resources, if you like. :)

The way BuffStruct is written, I am sure you should never have cause for editing the system code itself - even if you need more features. :thup:
 
There is no reason for that to be inside the system itself - it can be done externally. In fact, even with the code you posted, you are more likely to double-free buffs than help an end user. You should move the buff to a null unit on death, and let the buff be destroyed as per usual, unless you handle special cases like death in the way you normally destroy buffs. Hence why buff destruction/null unit allocation is left up to the end user. :thup:

There's should be at least option for that, I think.

You should move the buff to a null unit on death

Eh, that's more likely impossible for me. :D Because I can't access setUnit method in BuffStruct. And I'd have to make trigger for each of my buff structs which detects when unit dies.

I'm not sure what you mean, here - the idea is if you want a buff that can only exist once on a unit, there will be a MyBuffType[unit] method operator which will return the oldest instance on the unit, as I've said before in this thread - I'm just yet to write it. That will return 0 if the unit doesn't have it. It should be pretty obvious how to use that to write "universal" buffs, as I think you called them.
JASS:
local MyBuff inst = MyBuff[GetSpellTargetUnit()]
if inst==0 then
    set inst=MyBuff.create(GetSpellTargetUnit())
endif
//.. do things to inst or w/e

This system is (or will be) flexible enough to code whatever you want, without it having to be hard coded specifically into the system. Feel free to write additional functionality modules to plug in. :thup:

Can't wait to see this. :)

I may have to take a look at that, some time. Once again, every buff is a unique integer so you can attach to it directly, should you need additional functionality which applies to every buff, such as a class - and I encourage you to write these things externally to the BuffStruct header itself, so that you can keep getting the latest version of BuffStruct for yourself. You may even submit your additional things as resources, if you like. :)

The way BuffStruct is written, I am sure you should never have cause for editing the system code itself - even if you need more features. :thup:

Well, I ain't that good coder thus I couldn't find better way for "classes" of buff than add as static variable to BuffStruct module, and then added non-static variable to BuffStruct struct and set it when it is created, in create method.

I can't seem to find a way to make it externally. Because the only thing I can access are BuffStruct's variables because I don't know name of X buff.
 
General chit-chat
Help Users
  • No one is chatting at the moment.
  • Varine Varine:
    A probate is usually done with a will, yes? If so I am sorry for your loss
    +1
  • The Helper The Helper:
    Yeah Tom, me too sorry for your loss buddy my mom told me she finds out her olds friend died from Google searching them. She had not talked to one of her old friends in a year and found out she died from Google. Also another one in the same session. RIP all of them my sincere condolences Tom
    +1
  • Varine Varine:
    We have some elderly guests that regularly come hang out at the bar at the end of the night, and every once in a while we don't see someone for a few weeks and then someone shows up with their obituary.
  • Varine Varine:
    We usually let them do their memorials there in the morning if they want to and I'll make them some snacks and drinks. There was one guy named Tom that came in like every night and would sit by himself and get a bunch of soup and a glass of wine. idk why but he LOVED our fucking soup, like he would order a fucking quart of it at a time and would always get so sad when we stop doing it for the summer.
    +1
  • Varine Varine:
    But he also loved our calamari, which is another thing I hate but it sells super well so I can't change it. There was one day he came in and was asking me how to make it, because he tried to at home once in the off season when we stop running it and he really wanted it lol
  • Varine Varine:
    I think he's one of the only people I've made recipes for for free because he really wanted a broccoli cheddar, and it was like dude I don't have a recipe, it's just whatever I have, but here, this is how you do it
  • Varine Varine:
    I don't think he ever figured out how to do the calamari in a pan though, like idk how to do that either. He was afraid of the at home deep fryers though and it's like yeah, that's fair, I am too
  • Varine Varine:
    He was just such a sweet old man, we had two servers pregnant and they held a baby shower together, he was soooooo fucking excited to get to see a baby. Unfortunately he died a month or so before they were born
  • The Helper The Helper:
    So I decided to Google some people that I had not seen or heard from in a while and sure enough one of my old best friends, we had a falling out years ago but whatever, find out he died of Pancreatic Cancer in January. I have also lost a few of my closer acquaintances from growing up the last year. Getting old - people die - I kinda thought it was going to be this way a few years ago....
    +2
  • The Helper The Helper:
    Forum running super slow again
  • Ghan Ghan:
    Not really clear from the stats as to what is causing the slowness.
  • Ghan Ghan:
    We get a lot of guest traffic so it may just be the load is getting too high and not from any particular source.
  • Ghan Ghan:
    Looks like the server is maxed out on CPU.
  • Ghan Ghan:
    Oh it looks like a lot of the traffic is Silkroad Forums. That domain isn't protected by Cloudflare.
  • Ghan Ghan:
    But the old Silkroad site is still on its own server. I just had a test site set up on this server for it.
  • Ghan Ghan:
    I just disabled that test site. Let's see if that helps the load.
  • Ghan Ghan:
    Looks much better already.
  • The Helper The Helper:
    I had actually forgot about the Silkroad site. I had asked
  • The Helper The Helper:
    SD Ryoko about it and he said the couple of people left on there really like it, that was a few years ago, maybe I should check back
  • jonas jonas:
    I guess when you're getting old, and the last day of soup season draws near, you start wondering
  • jonas jonas:
    will I make it to the start of the next season? or was this the last time I'll ever have my favorite dish?
  • The Helper The Helper:
    I am doing my first Vibe Coding project. In installed the environment and tools according to instructions but it is all chat doing this for me at my direction. It is fun really and holy shit I might finish in 2 hours what it would have taken a day to in my Access and this would be an electron app complete new
  • Ghan Ghan:
    Good stuff.
  • Ghan Ghan:
    Just make sure it is secure. :)
  • The Helper The Helper:
    It will only be on internal network

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials
      Top