• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

BuffHandler

Status
Not open for further replies.
Level 14
Joined
Jun 27, 2008
Messages
1,325
System BuffHandler
requires:

Review plz ~

JASS:
library BuffHandler requires AIDS, TimerUtils
//*******************************************
//*        .___.
//*       /     \
//*      | O _ O |
//*      /  \_/  \ 
//*    .' /     \ `.
//*   / _|       |_ \
//*  (_/ |       | \_)
//*      \       /
//*     __\_>-<_/__
//*     ~;/     \;~
//*
//*  muzzels Spellbox presents:
//*
//*  BuffHandler
//*  Version 1.1
//*
//*******************************************
//*  Documentation
//*
//*
//*  1. Basics
//*  =========
//*  1.1 Defining bufftypes
//*  Each bufftype basically exists of one struct that holds all the variable
//*  members and methods your buff needs. The struct has to extend "Buff" which
//*  allows you to access most of the functionality of this system and registers
//*  your buff for the automated bufflists of your units.
//*  For a cleaner code it is recommended that you use a new library for every
//*  bufftype you define.
//*  
//*  struct MyBuff extends Buff
//*    // ...
//*  endstruct
//*  
//*  1.2 Applying buffs to units
//*  To bring a buff on a unit you first have to create a new instance by calling:
//*  
//*  local MyBuff b = MyBuff.create()
//*  
//*  Then you can apply the newly created buff to a unit:
//*  call b.apply(someUnit)
//*  
//*  To destroy a buff simply call the destructor method:
//*  
//*  call b.destroy()
//*  
//*  1.3 Buff events
//*  A very important thing to remember is that you may not define the normal
//*  constructor and destructor methods for your bufftype, namely the "create"
//*  and "onDestroy" methods. They are used by the system and writing your own
//*  will throw an error.
//*  Instead you may optionally implement the following methods:
//*  
//*  method onCreate takes nothing returns nothing
//*  endmethod
//*  // Will be run immediately after the buff is created.
//*  // Use this as a replacement for the "create" method.
//*  
//*  method onApply takes nothing returns nothing
//*  endmethod
//*  // Will be run immediately after the buff is applied to a unit.
//*  
//*  method onRemove takes nothing returns nothing
//*  endmethod
//*  // Will be run right before the buff is removed from the unit.
//*  // (e.g. applied to another unit or destroyed)
//*  
//*  method preDestroy takes nothing returns nothing
//*  endmethod
//*  // Will be run right before the buff is destroyed.
//*  // Use this as a replacement for the "onDestroy" method.
//*  
//*  To access the unit the buff is currently applied to you may use the
//*  method ".getUnit()".
//*  If the buff is created but not yet applied it will return "null".
//*  
//*  1.4 Eyecandy
//*  Defining a bufficon involves some object data creation.
//*  First you need to create a new ability based off "Slow-Aura (Tornad)"
//*  ('Aasl') with a buff based on "Tornado (Slow-Aura)" ('Basl'). Change the
//*  name, tooltip and icon of the buff to whatever you want.
//*  The name of the buff is by default displayed in red. To change it to green
//*  add the following colortags: "|cff00ff00BuffName|r".
//*  
//*  To apply that bufficon to your buff just add the following method to your
//*  struct which returns the raw id of your newly created ability:
//*  
//*  method getRaw takes nothing returns integer
//*  	return 'A000'
//*  endmethod
//*  
//*  In addition to "getRaw" to remove the delay between the removal of the
//*  buff and its icon you can optionally define the function:
//*  
//*  method getBuffRaw takes nothing returns integer
//*     return 'B000'
//*  endmethod
//*  
//*  You may also define an optional name for your buff. Note that this name
//*  is not required and also not displayed anywhere, but some of the debugging
//*  functions make use of it and its a good convention to define names for all
//*  your buffs. To define a name for your buff just add the following method
//*  to your struct which returns the desired name:
//*  
//*  method getName takes nothing returns string
//*  	return "MyBuff"
//*  endmethod
//*  
//*  2. Advanced Features
//*  ====================
//*  2.1 Unapplying and Reapplying
//*  Once applied buffs are not bound to a specific unit but can be Reapplied
//*  (moved) to another unit which may even be "null" (the buff is then in a
//*  not applied state, but does not get deleted). Do do this simply call the
//*  apply method again:
//*  
//*  call b.apply(someOtherUnit) // reapplying the buff to someOtherUnit
//*  call b.apply(null) // unapplying the buff
//*  
//*  When reapplying a buff to another unit the methods "onRemove" and
//*  "onApply" will, if implemented, be called.
//*  Note that in "onRemove" the method "getUnit()" will refer to the old unit
//*  while in "onApply" it will already return the new unit.
//*  
//*  2.2 Buffs with Duration
//*  To easily create a buff with limited duration you can make use of the
//*  following method that starts a timer and destroys the buff after the
//*  specified timeout:
//*  
//*  method setDuration takes real duration returns nothing
//*  
//*  You can either call this function inside of "onCreate" or inside of
//*  "onApply". This will have two different effects:
//*  When called in "onCreate" the timer will continue when the buff is being
//*  reapplied to another unit. When called in "onApply" the timer will
//*  restart when reapplied.
//*  
//*  2.3 Periodic Buffs
//*  To easily create buffs that perform a certain action periodically you
//*  can define the following method:
//*  
//*  method periodic takes nothing returns nothing
//*    // Do something
//*  endmethod
//*  
//*  You can start and stop the periodical loop with the following methods:
//*  method startPeriodic takes real timeout returns nothing
//*  method stopPeriodic takes nothing returns nothing
//*  
//*  3. BuffLists
//*  ============
//*  3.1 Accessing BuffLists
//*  When applied to a unit buffs get stored in the units bufflist. Any units
//*  bufflist can be accessed using the following function:
//*  
//*  function getUnitBuffList takes unit u returns BuffList
//*  
//*  To access the bufflist a buff is currently applied you can use the
//*  following method of the buffs struct:
//*  
//*  method getParentBufflist takes nothing returns BuffList
//*  
//*  Note that this is faster than using "getUnitBuffList(this.getUnit())"
//*  since it does not require the unit indexing system.
//*  
//*  3.2 BuffType-Ids
//*  Something that might be useful when working with BuffLists are the
//*  BuffType-Ids. BuffType-Ids are automatically assigned to each bufftype
//*  and provide a simple way to compare the type of different buffs.
//*  
//*  To get a buffs BuffType-Id call the function:
//*  
//*  method getBuffTypeId takes nothing returns integer
//*  
//*  For technical reasons there is currently no static method that returns
//*  the BuffType-Id, but this might be added in future versions.
//*  
//*  3.3 BuffList API
//*  To get the unit a bufflist is applied to you can use the following method:
//*  
//*  method getUnit takes nothing returns unit
//*  
//*  To iterate through a bufflist object you can use its iterator:
//*  
//*  method iterator takes nothing returns BuffListIterator
//*  
//*  The BuffListIterator then offers the following methods:
//*  
//*  method next takes nothing returns Buff
//*  // Returns the next Buff in the BuffList.
//*  // You should only use this function if ".hasNext()" returned "true".
//*  
//*  method hasNext takes nothing returns boolean
//*  // Returns "true" if the next ".next()" call will return a valid Buff.
//*  // Returns "false" if there are no more buffs in the BuffList.
//*  
//*  method hasNoNext takes nothing returns boolean
//*  // Negated version of "hasNext" for easier use with "endloop".
//*  
//*  Note that the iterators are exclusively designed for use immediately after
//*  creation. Iterators will not work correctly after the BuffList is altered.
//*  After using the iterator dont forget to destroy it by calling its
//*  ".destroy()" method.
//*  
//*  Lets make a short example of a buff that, when applied to a unit, destroys
//*  all other buffs from that unit except itself and other buffs of this type:
//*  
//*  struct MyNewBuff
//*  	method onApply takes nothing returns nothing
//*  		local BuffListIterator iter = this.getParentBufflist().iterator()
//*  		local Buff b
//*  		loop
//*  			exitwhen iter.hasNoNext()
//*  			set b = iter.next()
//*  			if (b.getBuffTypeId() != this.getBuffTypeId()) then
//*  				call b.destroy()
//*  			endif
//*  		endloop
//*  		call iter.destroy()
//*  	endmethod
//*  endstruct
//*  
//*  To count the amount of buffs of a specified type you can use the following
//*  To count the amount of buffs of a certain type you can use the following 
//*   of a BuffList object:
//*  
//*  method countBuffsOfType takes Buff buff returns integer
//*  
//*  method countBuffsOfTypeId takes integer buffTypeId returns integer
//*  // Use this if you only have the BuffType-Id to specify the buff type.
//*  
//*  4. Stacking Policies
//*  ====================
//*  There are different possibilities to define how multiple buffs of the same 
//*   behave when applied to the same unit.
//*  4.1 Premade policies
//*  For most situations selecting the right one of the premade stacking
//*  policies will suffice. Currently there are:
//*  
//*  BUFF_POLICY_STACKING (default)
//*  // An unlimited amout of buffs of this type can coexist on all units.
//*  
//*  BUFF_POLICY_LAST
//*  // Buffs of this type are limited to one instance per target unit.
//*  // The last applied buf will destroy existing ones.
//*  
//*  To define the policy for a bufftype you can just add the following method
//*  to your buff struct which returns the the policy type:
//*  
//*  method getPolicy takes nothing returns integer
//*  	return BUFF_POLICY_LAST
//*  endmethod
//*  
//*  Default is BUFF_POLICY_STACKING.
//*  
//*  There might be more premade policies in future versions.
//*  
//*  4.2 Customize Stacking Behaviour
//*  If you require a stacking behaviour that the premade policies cannot provide
//*  you can customize it by defining the following function:
//*  
//*  method checkApply takes nothing returns boolean
//*  	return true // if you simply return true this is equal to BUFF_POLICY_STACKING
//*  endmethod
//*  
//*  If defined this function runs before the buff is applied to unit and before
//*  "onApply" fires.
//*  In the method body you can check the bufflist for other instances of this
//*  type, remove them, modify them etc.
//*  If this function returns true the buff will get applied.
//*  If this function returns false the buff will not get applied but als not
//*  deleted. If you want it deleted you may call ".delete()" right before
//*  the return statement.
//*  
//*  5. Custom Members
//*  =================
//*  A quick way to add custom members to all buff types is the CustomMembers
//*  module. Its defined in the top of this system and is automatically implemented
//*  by all buff structs you define. Its method "initCustomMembers" can be used to
//*  initialize default values to the custom members.
//*
//*  Changelog
//*  =========
//*  Version 1.1:
//*  - Making use of "UnitMakeAbilityPermanent"
//*  - Added the optional possibility to remove the buff directly, avoiding the small
//*    delay between the removal of the buff and the removal of the buff icon
//*  Version 1.0:
//*  - Initial Release
//*
//*  Credits
//*  =======
//*  - Menag
//*
//*******************************************
//*
//* Add custom members here:
    module CustomMembers
        
        // Initialize default values to the custom members here:
        method initCustomMembers takes nothing returns nothing
        endmethod
    endmodule
//*
//*
//*******************************************

    // For using with AIDS
    struct UnitBuffs extends array
        //! runtextmacro AIDS()
        BuffList bl
        method AIDS_onCreate takes nothing returns nothing
            set this.bl = BuffList.create(this.unit)
        endmethod
        method AIDS_onDestroy takes nothing returns nothing
            call this.bl.destroy()
        endmethod
    endstruct
    
    // Make sure this returns the specified units bufflist.
    function getUnitBuffList takes unit u returns BuffList
        return UnitBuffs[u].bl
    endfunction
    
//*******************************************

    globals
        constant key BUFF_POLICY_STACKING
        constant key BUFF_POLICY_LAST
    endglobals
    
    struct Buff
        // For LinkedList:
        thistype llPrev
        thistype llNext
        
        method setLl takes thistype prev, thistype next returns nothing
            set this.llPrev = prev
            set this.llNext = next
        endmethod
        
        method getNext takes nothing returns thistype
            return this.llNext
        endmethod
        
        private BuffList parent
        private timer durationTimer
        private timer periodicTimer
        
        implement CustomMembers
        
        // Returns an id that identifies the buff type.
        method getBuffTypeId takes nothing returns integer
            return this.getType()
        endmethod
        
        // Returns the bufflist this buff is currently applied to.
        method getParentBufflist takes nothing returns BuffList
            return this.parent
        endmethod
        
        // Optional: You can set a name for the buff.
        stub method getName takes nothing returns string
            return ""
        endmethod
        
        // Optional: You can set a bufficon for the buff. Return the auras raw id.
        stub method getRaw takes nothing returns integer
            return 0
        endmethod
        
        // Optional: You can set the bufficons buff raw. This removes the delay on
        // the removal of the bufficon. Only ised if "getRaw" is defined.
        stub method getBuffRaw takes nothing returns integer
            return 0
        endmethod
        
        // Optional: You can set a policy for the buff type.
        // Default is BUFF_POLICY_STACKING
        stub method getPolicy takes nothing returns integer
            return BUFF_POLICY_STACKING
        endmethod
        
        // Optional: If and only if this returns true the buff gets applied.
        // You can use this to define your own buff policy and, if
        // needed, remove other instances from the target unit.
        stub method checkApply takes nothing returns boolean
            local BuffListIterator iter = 0
            local Buff b = 0
            if this.getPolicy() == BUFF_POLICY_STACKING then
                return true
            elseif this.getPolicy() == BUFF_POLICY_LAST then
                set iter = this.parent.iterator()
                loop
                    exitwhen iter.hasNoNext()
                    set b = iter.next()
                    if ((b.getBuffTypeId() == this.getBuffTypeId()) and (b != this)) then
                        call b.destroy()
                    endif
                endloop
                call iter.destroy()
                return true
            endif
            return true
        endmethod
        
        // Optional: Runs periodically.
        // Start by calling startPeriodic(real timeout).
        // Stop by calling stopPeriodic().
        stub method periodic takes nothing returns nothing
        endmethod
        
		// Optional: Fires after the buff is created.
        stub method onCreate takes nothing returns nothing
        endmethod
        
		// Optional: Fires after the buff is applied to a unit.
        // The new unit is accessable via "this.u".
        stub method onApply takes nothing returns nothing
        endmethod
        
		// Optional: Fires before the buff is removed from the unit.
        // The unit is still accessable via "this.u".
        stub method onRemove takes nothing returns nothing
        endmethod
        
		// Optional: Fires before the buff is destroyed.
        stub method preDestroy takes nothing returns nothing
        endmethod
        
        // Gets the unit the buff is applied to. Returns null if not applied to any unit.
        method getUnit takes nothing returns unit
            if this.parent != 0 then
                return this.parent.getUnit()
            else
                return null
            endif
        endmethod
        
        method startPeriodic takes real timeout returns nothing
            if this.periodicTimer == null then
                set this.periodicTimer = NewTimer()
            else
                call PauseTimer(this.periodicTimer)
            endif
            call SetTimerData(this.periodicTimer, this)
            call TimerStart(this.periodicTimer, timeout, true, function thistype.invokePeriodic)
        endmethod
        
        method stopPeriodic takes nothing returns nothing
            call ReleaseTimer(this.periodicTimer)
            set this.periodicTimer = null
        endmethod
        
        // Starts a timer that destroys the buff after the specified duration.
        method setDuration takes real duration returns nothing
            if this.durationTimer == null then
                set this.durationTimer = NewTimer()
            else
                call PauseTimer(this.durationTimer)
            endif
            call SetTimerData(this.durationTimer, this)
            call TimerStart(this.durationTimer, duration, false, function thistype.durationEnd)
        endmethod
        
        // Returns true if a buff of this type is currently applied on the specified unit.
        static method isOn takes unit u returns boolean
            // TODO: doesnt work without a static version of .getBufTypeId()
            return false
        endmethod
        
        private static method durationEnd takes nothing returns nothing
            local thistype b = GetTimerData(GetExpiredTimer())
            call ReleaseTimer(b.durationTimer)
            call b.destroy()
        endmethod
        
        private static method invokePeriodic takes nothing returns nothing
            local thistype b = GetTimerData(GetExpiredTimer())
            call b.periodic()
        endmethod
        
        static method create takes nothing returns thistype
            local thistype this = thistype.allocate()
            set this.parent = 0
            call initCustomMembers()
            call this.onCreate()
            return this
        endmethod
        
        private method addBuffIcon takes nothing returns nothing
            local integer raw = this.getRaw()
            if raw != null then
                if parent.countBuffsOfType(this) <= 1 then
                    call UnitAddAbility(parent.getUnit(), raw)
                    call UnitMakeAbilityPermanent(parent.getUnit(), true, raw)
                endif
            endif
        endmethod
        
        private method removeBuffIcon takes nothing returns nothing
            local integer raw = this.getRaw()
            local integer buffRaw
            if raw != null then
                if parent.countBuffsOfType(this) <= 1 then
                    call UnitMakeAbilityPermanent(parent.getUnit(), false, raw)
                    call UnitRemoveAbility(parent.getUnit(), raw)
                    set buffRaw = this.getBuffRaw()
                    if buffRaw != 0 then
                        call UnitRemoveAbility(parent.getUnit(), buffRaw)
                    endif
                endif
            endif
        endmethod
        
        // Applies the buff to the specified unit. The specified unit can be null.
        method apply takes unit target returns nothing
            if this.parent != 0 then
                call this.onRemove()
                call this.removeBuffIcon()
                call this.parent.removeBuff(this)
                set this.parent = 0
            endif
            if target != null then
                set this.parent = getUnitBuffList(target)
                if this.checkApply() then
                    call this.parent.addBuff(this)
                    call this.addBuffIcon()
                    call this.onApply()
                endif
            endif
        endmethod
        
        method onDestroy takes nothing returns nothing
            call this.apply(null)
            call this.preDestroy()
            // Clean duration timer
            if this.durationTimer != null then
                call ReleaseTimer(this.durationTimer)
                set this.durationTimer = null
            endif
            // Clean periodic timer
            if this.periodicTimer != null then
                call ReleaseTimer(this.periodicTimer)
                set this.periodicTimer = null
            endif
        endmethod
    endstruct
    
    struct BuffList
        // For LinkedList:
        private Buff llHead
        private integer llSize
        
        private unit u
        
        static method create takes unit u returns thistype
            local thistype this = thistype.allocate()
            set this.llHead = 0
            set this.u = u
            return this
        endmethod
        
        // Returns the unit the BuffList belongs to.
        method getUnit takes nothing returns unit
            return this.u
        endmethod
        
        private method llAdd takes Buff b returns nothing
            set b.llNext = this.llHead
            if this.llHead != 0 then
                set this.llHead.llPrev = b
            endif
            set this.llHead = b
            set b.llPrev = 0
        endmethod
        
        private method llRemove takes Buff b returns nothing
            if b.llPrev != 0 then
                set b.llPrev.llNext = b.llNext
            else
                set this.llHead = b.llNext
            endif
            if b.llNext != 0 then
                set b.llNext.llPrev = b.llPrev
            endif
        endmethod
        
        method getFirstBuff takes nothing returns Buff
            return this.llHead
        endmethod
        
        method iterator takes nothing returns BuffListIterator
            return BuffListIterator.create(this)
        endmethod
        
        method addBuff takes Buff b returns nothing
            call this.llAdd(b)
        endmethod
        
        method removeBuff takes Buff b returns nothing
            call this.llRemove(b)
        endmethod
        
        method onDestroy takes nothing returns nothing
            local BuffListIterator iter = this.iterator()
            loop
                exitwhen iter.hasNoNext()
                call this.llRemove(iter.next())
            endloop
            call iter.destroy()
        endmethod
        
        method countBuffsOfTypeId takes integer typeId returns integer
            local BuffListIterator iter = this.iterator()
            local integer n = 0
            loop
                exitwhen iter.hasNoNext()
                if (iter.next().getBuffTypeId() == typeId) then
                    set n = n + 1
                endif
            endloop
            call iter.destroy()
            return n
        endmethod
        
        method countBuffsOfType takes Buff b returns integer
            local integer typeId = b.getBuffTypeId()
            return countBuffsOfTypeId(typeId)
        endmethod
        
        method debugPrint takes nothing returns nothing
            local BuffListIterator iter = this.iterator()
            local Buff tmp
            loop
                exitwhen iter.hasNoNext()
                set tmp = iter.next()
                call BJDebugMsg("Buff: " + tmp.getName() + " (id: " + I2S(tmp) + " )")
            endloop
            call iter.destroy()
        endmethod
    endstruct
    
    struct BuffListIterator
        private BuffList buffList
        private Buff nextBuff
        
        static method create takes BuffList buffList returns thistype
            local thistype this = thistype.allocate()
            set this.buffList = buffList
            set this.nextBuff = buffList.getFirstBuff()
            return this
        endmethod
        
        method hasNext takes nothing returns boolean
            return this.nextBuff != 0
        endmethod
        
        method hasNoNext takes nothing returns boolean
            return this.hasNext() == false
        endmethod
        
        method next takes nothing returns Buff
            local Buff tmp = this.nextBuff
            set this.nextBuff = this.nextBuff.getNext()
            return tmp
        endmethod
    endstruct

endlibrary

Changelog:
Code:
Version 1.1:
- Making use of "UnitMakeAbilityPermanent"
- Added the optional possibility to remove the buff directly, avoiding the small delay between the removal of the buff and the removal of the buff icon

Version 1.0:
- Initial Release
 

Attachments

  • BuffHandler_1.1.w3x
    63.6 KB · Views: 151
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
So... why AIDS?

It's like inferior in every way to the thing we have on THW :\

You also shouldn't make your own list, we have amazing collections here in the JASS Section.

edit
I don't think that this is a good approach

JASS:
    module CustomMembers
        
        // Initialize default values to the custom members here:
        method initCustomMembers takes nothing returns nothing
        endmethod
    endmodule
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
Thx for the input.

So... why AIDS?

It's like inferior in every way to the thing we have on THW :\
mostly because the map i wrote this system for uses AIDS. But its not much work to replace it with any other unit indexer...

You also shouldn't make your own list, we have amazing collections here in the JASS Section.
I didnt intend to, but i also didnt find a good one which includes iterators and stuff... but maybe i didnt look hard enough. But i agree, inlining it into the system isnt the nicest solution, i guess i was too lazy to do this properly.

edit
I don't think that this is a good approach

JASS:
    module CustomMembers
        
        // Initialize default values to the custom members here:
        method initCustomMembers takes nothing returns nothing
        endmethod
    endmodule

agree, but do you have a better idea? I mean, its doing the job..
 
Last edited:
Level 16
Joined
Aug 7, 2009
Messages
1,403
About buff icons:

You should make the ability permanent (UnitMakeAbilityPermanent - true after the ability has been added, false before it's removed), otherwise it's prone to inappropriate behavior. Also, the buff's rawcode should be stored too, and it should also be removed from the unit upon removal (UnitRemoveAbility), otherwise it'll stick around for a few more seconds.

It's also unnecessary to store the rawcode in a local integer, as the getter method will be inlined anyways. This is a small thing, but I thought I'd point out that you should always keep in mind inlining.
 
Level 14
Joined
Jun 27, 2008
Messages
1,325
About buff icons:

You should make the ability permanent (UnitMakeAbilityPermanent - true after the ability has been added, false before it's removed), otherwise it's prone to inappropriate behavior.
thx

Also, the buff's rawcode should be stored too, and it should also be removed from the unit upon removal (UnitRemoveAbility), otherwise it'll stick around for a few more seconds.
It's also unnecessary to store the rawcode in a local integer, as the getter method will be inlined anyways. This is a small thing, but I thought I'd point out that you should always keep in mind inlining.
It does get removed:
JASS:
private method removeBuffIcon takes nothing returns nothing
            local integer raw = this.getRaw()
            if raw != null then
                if parent.countBuffsOfType(this) <= 1 then
                    call UnitRemoveAbility(parent.getUnit(), raw)
                endif
            endif
        endmethod

Also simply storing the rawcode in a member variable does not work as far as i know since different subclasses of "Buff" may have different rawcodes. That is exactly why im using stub methods, which cannot be inlined. Or is there a way to use dynamic dispatch with member variables?
 
Level 16
Joined
Aug 7, 2009
Messages
1,403
It does get removed:
JASS:
private method removeBuffIcon takes nothing returns nothing
            local integer raw = this.getRaw()
            if raw != null then
                if parent.countBuffsOfType(this) <= 1 then
                    call UnitRemoveAbility(parent.getUnit(), raw)
                endif
            endif
        endmethod

The aura ability, yes, but not the buff it applies. It stays for a few more seconds, unless you remove it manually.

This is from BuffStruct:

JASS:
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

Also simply storing the rawcode in a member variable does not work as far as i know since different subclasses of "Buff" may have different rawcodes. That is exactly why im using stub methods, which cannot be inlined. Or is there a way to use dynamic dispatch with member variables?

Well, I didn't actually check how it was implemented, only a few functions. I thought it used the popular module ("speedfreak") approach, but now I see that it's based on inheritence. It makes sense in that case.
 
Status
Not open for further replies.
Top