System BuffStruct

Zwiebelchen

You can change this now in User CP.
Reaction score
60
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?
 

Jesus4Lyf

Good Idea™
Reaction score
397
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..
 

Zwiebelchen

You can change this now in User CP.
Reaction score
60
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.
 

13lade619

is now a game developer :)
Reaction score
399
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()
 

Furby

Current occupation: News poster
Reaction score
144
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.
 

13lade619

is now a game developer :)
Reaction score
399
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
 

Furby

Current occupation: News poster
Reaction score
144
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".
 

Zwiebelchen

You can change this now in User CP.
Reaction score
60
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.
 

Furby

Current occupation: News poster
Reaction score
144
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.
 

13lade619

is now a game developer :)
Reaction score
399
..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
 

Zwiebelchen

You can change this now in User CP.
Reaction score
60
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?
 

Furby

Current occupation: News poster
Reaction score
144
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>
 

Jesus4Lyf

Good Idea™
Reaction score
397
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:
 

Furby

Current occupation: News poster
Reaction score
144
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.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top