System BuffHandler
requires:
Review plz ~
Changelog:
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
Last edited: