Romek
Super Moderator
- Reaction score
- 964
Unit Aura Control
By Romek
By Romek
Version 3.1
Introduction:UAC is a system that was originally designed to make creating Aura-type spells much easier than it currently is, although it can be used for anything which requires "Unit In Range" type events. See the documentation (in the map) and the example spell for more information!
The Documentation:
JASS:
.
_ _ _ ___
| | | |/_\ / __| U N I T A U R A C O N T R O L Last Update: 22nd Nov 2009
| |_| / _ \ (__ B y R o m e k
\___/_/ \_\___| Version 3.1
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Contact Information:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
My Profile:
<a href="http://www.thehelper.net/forums/member.php?u=7807" class="link link--internal">www.thehelper.net/forums/member.php?u=7807</a>
UAC Thread:
<a href="http://www.thehelper.net/forums/showthread.php?t=112374" class="link link--internal">www.thehelper.net/forums/showthread.php?t=112374</a>
What is UAC?
¯¯¯¯¯¯¯¯¯¯¯¯
UAC is a system which allows the creation of aura-type abilities in an efficient, user-friendly way.
Think of it as the aura equivalent of the caster system.
Although UAC alone can be used to create incredible auras very easily, optional modules are also
available, which can provide extra options and methods.
UAC requires vJASS knowledge.
How to Implement:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- Open your map, open the trigger editor (F4)
- Create a new trigger object (CTRL + T), and name it whatever you want (preferably UAC).
- Go to Edit -> Convert to Custom Text
- Delete all the text that appears
- Copy everything in the UAC trigger, and paste it into the trigger you created
- You may want to change the configurable, though this isn039;t recommended.
- Save, and enjoy!
How to Use:
¯¯¯¯¯¯¯¯¯¯¯
- Create a new JASS trigger.
- Create a struct, and make it extend UAC. (struct Name extends UAC)
- Then you can use all the methods, members and interface methods listed below
on (and in) this new struct you created.
- See the example spell(s) for real usage.
Methods:
¯¯¯¯¯¯¯¯
_______________________________________________________________________
static method create takes unit mainUnit, real range returns thistype
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This creates an instance for you to use. 039;mainUnit039; is the unit which
has the aura spell. 039;range039; is the range of the aura. Units within this range
of 039;mainUnit039; will be affected. See 039;main039; and 039;range039; members for more detail.
If your struct has a custom create method, you will need to call 039;allocate039; instead
of create. The arguments will be exactly the same though.
Remember to use <YourStructName>.create(...), not UAC.create(...).
local Aura data = Aura.create(GetTriggerUnit(), 600.)
________________________________________________________
static method get takes unit mainUnit returns thistype
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This returns the aura instance which 039;mainUnit039; is the main unit for.
This is most useful when you need to check if a unit leveled up the aura, or learned it,
as it039;d be bad to create a new instance everytime a unit levels a spell.
This will return 0 if there is no data.
This will only return a struct of the type that the method is used from. So if a unit has two
auras, X and Y; X.get(u) and Y.get(u) will return different structs. Similarly, Z.get(u) will
return 0.
if Aura.get(u) == 0 then
set data = Aura.create(...)
else
call BJDebugMsg("Aura already learned. Levelling up!")
endif
________________________________________________________
method isAffected takes unit whichUnit returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method is used to check if 039;whichUnit039; is affected by the aura.
It will return true if the unit is, and false if it isn039;t.
This will only return true if the unit is affected by the current instance.
So even if the unit is affected by an aura of the same type as the current instance,
this039;ll return false if it039;s not affected by this exact instance.
This is useful for auras such as 039;double damage039; auras. It039;s impossible to give a unit
double the damage when it enters or leaves the auras range; nor is it possible to do so
every 0.03125 (by default) seconds. However, if using damage detection, you could check if
the damage source is affected by the aura. If it is, then deal the damage again.
if data.isAffected(GetEventDamageSource()) then
call KillUnit(GetTriggerUnit())
endif
Members:
¯¯¯¯¯¯¯¯
________________
unit main 039;main039; can be used to retrieve the main unit for the aura. The main unit is the unit
unit mainUnit that has the aura ability, and units come within range of this unit to become affected.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ This can be used in all the interface methods (more below).
039;main039; is a readonly member, meaning it cannot be set. Only retrieved.
039;mainUnit039; can also be used instead of 039;main039;. It does exactly the same thing, just with a different name.
call SetWidgetLife(data.main, 500.)
call SetWidgetLife(data.mainUnit, 500.)
________________
real range 039;range039; is the range of the aura. Units that come within this range of the main unit
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ become affected. If this is set to 0 or less, the instance will be paused, and the
range will need to be reset to a higher number to resume the aura.
set data.range = data.range + 100
________________
boolean pause 039;pause039; determines whether or not a struct instance is paused. When an instance is
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ paused, no units are affected by the aura, and no effects happen. The struct is
virtually destroyed during the time it039;s paused.
When a struct is paused, all affected units will become unaffected.
if data.pause then
set data.pause = false
endif
________________
boolean single 039;single039; determines whether or not the struct will be destroyed when the main unit dies.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ If single is true, then the struct will be destroyed when the main unit dies. Otherwise,
the struct will be paused until the unit is revived (or it039;ll just remain paused forever).
039;single039; is automatically set to true if the main unit is not a hero. Otherwise, it stays false.
if IsUnitType(u, UNIT_TYPE_HERO) == true then
set data.single = false
endif
________________
integer count 039;count039; simply returns the amount of units that are currently affected by an aura.
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ This is useful if you want certain effects to happen depending on the amount of units
within range. For example, divide some damage between all targets, or heal the caster for targets * 100.
039;count039; is a readonly member, meaning it cannot be set. Only retrieved.
call SetWidgetLife(data.main, 1000 / data.count)
________________
real ticker 039;ticker039; is, in seconds, how often the periodic functions of UAC are called for the
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ particular instance. This includes the frequency at which units can become affected
or unaffected, and how often unitPeriodic and timerLoop are called. This is automatically rounded down
to a multiple of TIMEOUT. This is TIMEOUT by default.
set data.ticker = data.ticker + TIMEOUT
Interface Methods:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- All of these methods must be declared in your struct. They aren039;t used like normal methods are,
but are simply created within the struct, then automagically called when appropriate.
- All of these methods are optional.
- The arguments do not have to be named the same as they are in the codes below.
________________________________________________________
method unitEnter takes unit whichUnit returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method is called when a unit comes within range of the mainunit. 039;whichUnit039; is the unit
that came within range, and is now affected by the aura.
method unitEnter takes unit u returns nothing
call SetUnitMoveSpeed(u, 100.)
endmethod
________________________________________________________
method unitLeave takes unit whichUnit returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method goes hand-in-hand with unitEnter. It does the complete opposite.
This method is called when a unit that was affected by the aura stops being affected.
039;whichUnit039; is the unit that left the range of the aura.
This method is often used to remove any effects that were created in unitEnter.
method unitLeave takes unit u returns nothing
call SetUnitMoveSpeed(u, GetUnitDefaultMoveSpeed(u))
endmethod
__________________________________________________________
method unitPeriodic takes unit whichUnit returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method is called every 0.03125 (by default) seconds, for every unit that is affected
by the aura. Useful for continous effects which need to be updated constantly for as long
as the unit is affected.
method unitPeriodic takes unit u returns nothing
call SetUnitMoveSpeed(u, GetUnitMoveSpeed(u) - 1)
endmethod
________________________________________________
method mainDeath takes nothing returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method is called when the main unit dies. Useful for removing any aura effects attached
to the main unit, or undoing any main-unit only effects. unitLeave is automatically called for
every unit affected.
method mainDeath takes nothing returns nothing
call DestroyEffect(this.effect)
endmethod
_________________________________________________
method mainRevive takes nothing returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method is called when the main unit is revived. This method will never be called if
039;single039; was true. This is useful for recreating anything that was removed in mainDeath.
All units that would be affected become affected automatically.
method mainRevive takes nothing returns nothing
set this.effect = AddSpecialEffectTarget("model", .main, "origin")
endmethod
________________________________________________
method timerLoop takes nothing returns nothing
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This method is called every 0.03125 seconds (by default). Unlike unitPeriodic, it doesn039;t take
a unit argument. It039;s simply called regardless of any conditions, units, etc.
Can save you a timer. Useable as a 039;unitPeriodic039; for the mainUnit only, as unitPeriodic isn039;t
suitable for this (it could be called any amount of times, depending on the amount of units affected).
This method is called only once every 0.03125 seconds.
method timerLoop takes nothing returns nothing
call SetUnitPosition(.dummy, GetUnitX(.mainUnit), GetUnitX(.main))
endmethod
Filter Methods:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- These are similar to Interface Methods, in the sense that they also have to be declared manually
in the struct.
- These are used to prevent unwanted units from being affected by the aura.
- If these return true, then the unit that was passed is a valid target.
- All of these are optional.
________________________________________________________
method andFilterA takes unit whichUnit returns boolean
method andFilterB takes unit whichUnit returns boolean
method andFilterC takes unit whichUnit returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- All of the declared and filters must be true for the unit to be a valid taget
_______________________________________________________
method orFilterA takes unit whichUnit returns boolean
method orFilterB takes unit whichUnit returns boolean
method orFilterC takes unit whichUnit returns boolean
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- At least one of the declared or filters must be true for the unit to be a valid target.
Changelog:
¯¯¯¯¯¯¯¯¯¯
3.1
- Readded Tickers
3.0
- Complete rewrite of script. It039;s now more efficient, stable and virtually limitless.
- Code is also littered with comments.
- Renamed most methods:
- UnitEnter -> unitEnter
- UnitLeave -> unitLeave
- unitPeriodic -> unitPeriodic
- OrFilter% -> orFilter%
- AndFilter% -> andFilter%
- MainDeath -> mainDeath
- MainRevive -> mainRevive
- TimerLoop -> timerLoop
- MainUnit -> main
- Added optional use of mainUnit instead of main
- Some additional features have been removed (such as effects)
- Added support for optional extensions
- Created UAC Effects
- Created UAC Filters
- Created new readme
- Added more safety and debugging options
- pausing now works as it should
- This should be the last major update. (Enough with the interface changes (sorry!))
2.2
- Added the TimerLoop method
- Now works with the new 1.23b patch. (And is more stable as a result!)
2.1
- Fixed a minor coding error in the create method
- Added "count" method operator
- Fixed a bug which involved leaking an instance with heroes which had more than one aura
- Lots of changes related to multiple auras per unit.
- Added 039;effect039; and 039;attachpoint039; operators, for the newly added Effects.
- TIMEOUT is now public. It can be accessed by using UAC_TIMEOUT.
2.0b
- Removed the limitation of 1 aura ability per unit.
2.0
- Re-wrote the entire system to meet my standards
- Added MainDeath and MainRevive methods
- Changed most of the current methods
- Added internal attachment, so the system no longer depends on PUI
- Added IsAffected Method
- Added 039;Single039; operator
1.8
- Changed the SetRange method to an operator
- Implemented Pausing
1.7
- Added Tickers
1.6
- Fixed some small coding errors
- Fixed a bug which continously called "UnitLeave" for the main unit. (Silly me <img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" class="smilie smilie--sprite smilie--sprite7" alt=":p" title="Stick Out Tongue :p" loading="lazy" data-shortname=":p" />)
1.5
- Added the .Destroy method. When 039;set data.Destroy = true039; is used; when the main
unit dies, the struct will be destroyed automatically.
1.4
- Recoded the entire system to improve on efficiency and fix some sloppy coding.
- The main functions now take 2 arguments. (enumerated unit, and main unit)
- Renamed UnitInRange to UnitPeriodic
- Renamed UnitEntersRange to UnitEnter
- Renamed UnitLeavesRange to UnitLeave
- Filters have been completely revamped. They are now optional functions, and there are 039;And039; and 039;Or039;
filters. At least one of the 039;Or039; ones has to be true, and all of the 039;And039;s have to be true.
They are all optional.
- As a result of the filters being remade, the filter library is now removed. (Didn039;t last long!)
1.3
- Fixed a bug which occured when the 039;main039; unit died.
- Added a library of common filters, so the user doesn039;t have to create identical functions
for most auras.
- Fixed a bug which kept calling UnitInRange for units which where enumerated at least
once, but then should039;ve been filtered. (Maybe the unit became flying, etc)
been filtered.
1.2
- Changed another small coding error
- Made TrueGroup non-static to avoid bugs involving units in range of
multiple 039;aura units039;
- Removed a useless variable from a function
1.1
- Fixed a coding error involving index recycling
1.0:
- Initial Release
The Code:
JASS:
library UAC
globals
// _______________________________________________________________
// +---------------------------------------------------------------+
// || _ _ _ ___ ||
// || | | | |/_\ / __| U N I T A U R A C O N T R O L ||
// || | |_| / _ \ (__ B y R o m e k ||
// || \___/_/ \_\___| Version 3.1 ||
// _______||_____________________________________________________________||_______
// ||¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯||
// || U A C allows users to create aura-type spells in a simple, user-friendly ||
// || way, with the use of 3 main methods, and numerous members, giving maximum ||
// || flexibility and efficieny. For more information, -= see the READ ME =- ||
// |+-----------------------------------------------------------------------------+|
// || The newer versions (3.0 and above) also allow optional extensions, which ||
// || enhance UAC with additional members and methods if they're present. ||
// |+-----------------------------------------------------------------------------+|
// || This controls how often units are checked to see if they are affected by ||
public constant real TIMEOUT = 0.03125 // The auras. timerLoop and unitPeriodic ||
// || are called at this frequency. .03125 is recommended. ||
// |+-----------------------------------------------------------------------------+|
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// =======================================================================================
// if debug mode is enabled AND this is true, then error/warning messages will be
// displayed when something goes wrong or could potentially go wrong.
debug constant boolean UAC__DEBUG = true
endglobals
// Optional Modules:
// ------------------
// At top of struct
private module MainModules
implement optional UACEffects
implement optional UACFilters
endmodule
// When instance is created
private module CreateModules
implement optional UACEffects_Create
endmodule
// When a unit leaves
private module LeaveModules
implement optional UACEffects_Leave
endmodule
// When a unit enters
private module EnterModules
implement optional UACEffects_Enter
endmodule
// Every TIMEOUT seconds.
private module PeriodicModules
endmodule
// During the filter stage
private module FilterModules
implement optional UACFilters_Filter
endmodule
// ---------------------
// Interface for optional methods
private interface UACInterface
method unitEnter takes unit whichUnit returns nothing defaults nothing
method unitLeave takes unit whichUnit returns nothing defaults nothing
method unitPeriodic takes unit whichUnit returns nothing defaults nothing
method mainDeath takes nothing returns nothing defaults nothing
method mainRevive takes nothing returns nothing defaults nothing
method timerLoop takes nothing returns nothing defaults nothing
// It's a shame that '.exists' can't be used with stub methods.
method orFilterA takes unit whichUnit returns boolean defaults false
method orFilterB takes unit whichUnit returns boolean defaults false
method orFilterC takes unit whichUnit returns boolean defaults false
endinterface
// Filter methods.
//! textmacro UAC__Filter takes X
stub method andFilter$X$ takes unit whichUnit returns boolean
return true
endmethod
//! endtextmacro
struct UAC extends UACInterface
// Main unit:
readonly unit main
// mainUnit can be used instead of main. =)
method operator mainUnit takes nothing returns unit
return .main
endmethod
// 'u' prefix because the names would be taken for safer method operators.
private real urange // aura range
private boolean upause // is the unit paused?
private group affected // holds the affected units
boolean single // should it be destroyed when the mainunit dies?
readonly integer count = 0 // the amount of units affected by an aura.
// Tickers
private real uticker = TIMEOUT
private real ticks = 0.
// struct stack
private static timer T = CreateTimer()
private static integer N = 0
private static thistype array D
private integer index
// temporary group filled with all units in range
private static group fill = CreateGroup()
// code variables used so I can order the methods in the struct as I please.
// (As long as these are at the top, and onInit is at the bottom)
private static code funcexpire
private static code callbacka
private static code callbackb
private static code unaffect
private static boolexpr mainfilt
// Temporary globals
private static unit tempunit
private static boolean tempbool
private static thistype temp
private static hashtable allUACs = InitHashtable() // .typeid, handleId
// Optional Modules:
implement MainModules
// -----------------
static method create takes unit mainUnit, real range returns thistype
local thistype this
// What's the point in creating the struct if the unit could be null?
if mainUnit == null then
debug if UAC__DEBUG then
debug call BJDebugMsg("|cFFFF0000UAC Error:|r Attempt to create aura on null unit.")
debug endif
return thistype(0)
endif
set this = .allocate()
// Setting basic members
set .main = mainUnit
set .urange = range
// If the range is <= 0, then we pause the struct. It'd be pointless to be constantly calling functions
// if no units are going to be affected anyway.
set .upause = range <= 0
debug if UAC__DEBUG and range <= 0 then
debug call BJDebugMsg("|cffffcc00UAC Warning:|r Instance created with 0 range. It has been automatically paused.")
debug endif
// By default, the struct will be automatically destroyed if mainunit isn't a hero.
// Otherwise, it'll remain, but be paused.
set .single = IsUnitType(mainUnit, UNIT_TYPE_HERO) == false
if .affected == null then
set .affected = CreateGroup()
else
call GroupClear(.affected)
endif
// Optional Modules:
implement CreateModules
// -----------------
// 'Attaching' the struct to the main unit for use with .get method.
call SaveInteger(.allUACs, .typeid, GetHandleId(mainUnit), this)
set .D[.N] = this
set .index = .N
set .N = .N + 1
if .N == 1 then
call TimerStart(.T, TIMEOUT, true, .funcexpire)
endif
return this
endmethod
// Returns the struct for the aura for a main unit.
static method get takes unit mainUnit returns thistype
debug if UAC__DEBUG and mainUnit == null then
debug call BJDebugMsg("|cFFFF0000UAC Error:|r Attempt to get a struct attached to a null unit.")
debug endif
return LoadInteger(.allUACs, .typeid, GetHandleId(mainUnit))
endmethod
// If the unit is affected by the aura, this'll return true.
method isAffected takes unit whichUnit returns boolean
debug if UAC__DEBUG and whichUnit == null then
debug call BJDebugMsg("|cFFFF0000UAC Error:|r Attempt to check if null unit is affected")
debug endif
return IsUnitInGroup(whichUnit, .affected)
endmethod
// Pause/unpause the struct. If it's being paused, then un-affect all units.
method operator pause= takes boolean shouldPause returns nothing
if not .upause and shouldPause then
set .temp = this
call ForGroup(.affected, .unaffect)
endif
// Attempting to unpause a struct with 0 range?
if .urange <= 0 and shouldPause == false then
debug if UAC__DEBUG then
debug call BJDebugMsg("|cffffcc00UAC Warning:|r Attempt to unpause struct with 0 range - unpause failed.")
debug endif
set .upause = true
return
else
set .upause = shouldPause
endif
endmethod
// Is the struct paused?
method operator pause takes nothing returns boolean
return .upause
endmethod
// Set how often the periodic functions should be called.
method operator ticker= takes real timeout returns nothing
// Below TIMEOUT?
if timeout < TIMEOUT then
debug if UAC__DEBUG then
debug call BJDebugMsg("|cffffcc00UAC Warning:|r Attempt to set a ticker to below TIMEOUT seconds.")
debug endif
set .uticker = TIMEOUT
else
// Must be a multiple of TIMEOUT
set .uticker = I2R(R2I(timeout / TIMEOUT)) * TIMEOUT
endif
set .ticks = 0.
endmethod
// How often does it expire?
method operator ticker takes nothing returns real
return .uticker
endmethod
// Sets the range of the aura
method operator range= takes real range returns nothing
if range <= 0 then // Fool! Setting range to <= 0
debug if UAC__DEBUG then
debug call BJDebugMsg("|cffffcc00UAC Warning:|r Attempt to set a structs range to 0. Instance paused.")
debug endif
// We'll need to pause it.
set .pause = true
endif
// If it was paused previously due to invalid range, and the range is valid,
// now would be the time to unpause it.
if .urange <= 0 and .upause then
set .pause = false
endif
set .urange = range
endmethod
// What's my range again?
method operator range takes nothing returns real
return .urange
endmethod
static method expire takes nothing returns nothing
local integer i = 0
local thistype this
loop
exitwhen i == .N
set this = .D<i>
set .ticks = .ticks + TIMEOUT // Increasing the ticker
// Is it time to expire?
if .ticks >= .uticker then
set .ticks = 0. // Resetting the ticks.
if not .upause then // We don't want to fill paused instances.
set .temp = this
// Fill is first filled with all units in range, matching the condition.
// Then the units that are affected, and the units in fill are compared;
// if there are units in fill that aren't affected, they become affected.
// If there are units that are affected, but aren't in fill, they stop being affected.
call GroupEnumUnitsInRange(.fill, GetUnitX(.main), GetUnitY(.main), .urange, .mainfilt)
call ForGroup(.fill, .callbacka)
call ForGroup(.affected, .callbackb)
endif
// timerLoop is just a time-saving method which is called every TIMEOUT, regardless of any conditions.
if .timerLoop.exists then
call .timerLoop()
endif
endif
set i = i + 1
endloop
endmethod
static method callbackA takes nothing returns nothing
set .tempunit = GetEnumUnit()
// Every unit here is a valid target for the aura. If the unit
// is not affected, it means it recently came within range, or
// is now meeting the conditions. We need to affect the unit with
// the aura.
if not IsUnitInGroup(.tempunit, .temp.affected) then
// Optional Modules:
implement EnterModules
// -----------------
set .temp.count = .temp.count + 1
if .temp.unitEnter.exists then
call .temp.unitEnter(.tempunit)
endif
call GroupAddUnit(.temp.affected, .tempunit)
endif
endmethod
static method callbackB takes nothing returns nothing
set .tempunit = GetEnumUnit()
// Every unit here is affected by the aura, though some of which
// may no longer be valid targets. We compare the units here to
// the ones that were valid targets. If it's not a valid target,
// then the unit becomes no longer affected.
if not IsUnitInGroup(.tempunit, .fill) then
// Optional Modules:
implement LeaveModules
// -----------------
set .temp.count = .temp.count - 1
if .temp.unitLeave.exists then
call .temp.unitLeave(.tempunit)
endif
call GroupRemoveUnit(.temp.affected, .tempunit)
else
// Optional Modules:
implement PeriodicModules
// -----------------
// If the unit is a valid target, then we need to call the
// periodic function (called for every valid target).
// This wasn't called previously as some targets might've been invalid.
if .temp.unitPeriodic.exists then
call .temp.unitPeriodic(.tempunit)
endif
endif
endmethod
private static method clearAffected takes nothing returns nothing
set .tempunit = GetEnumUnit()
// Optional Modules:
implement LeaveModules
// -----------------
set .temp.count = .temp.count - 1
if .temp.unitLeave.exists then
call .temp.unitLeave(.tempunit)
endif
call GroupRemoveUnit(.temp.affected, .tempunit)
endmethod
// Filters:
//! runtextmacro UAC__Filter("A")
//! runtextmacro UAC__Filter("B")
//! runtextmacro UAC__Filter("C")
private static method unitFilter takes nothing returns boolean
// Checking if units in range can be affected by the aura
set .tempunit = GetFilterUnit()
if not .temp.orFilterA.exists and not .temp.orFilterB.exists and not .temp.orFilterC.exists then
set .tempbool = (.temp.andFilterA(.tempunit) and .temp.andFilterB(.tempunit) and .temp.andFilterC(.tempunit))
else
set .tempbool = (.temp.andFilterA(.tempunit) and .temp.andFilterB(.tempunit) and .temp.andFilterC(.tempunit)) and (.temp.orFilterA(.tempunit) or .temp.orFilterB(.tempunit) or .temp.orFilterC(.tempunit))
endif
// Optional Module:
implement FilterModules
// ----------------
return .tempbool
endmethod
// This method is called when a unit dies.
private static method deathCond takes nothing returns boolean
set .tempunit = GetTriggerUnit()
set .temp = .get(.tempunit)
// The dying unit didn't have an aura. =(
if .temp == 0 then
return false
endif
if .temp.mainDeath.exists then
call .temp.mainDeath()
endif
// Both of these actions clear the affected units.
if .temp.single then
// The main unit died. Destroy the struct if it's meant to only last for one 'life'
call .destroy(.temp)
else
// Else, pause it.
set .temp.pause = true
endif
return false
endmethod
// This method is called when a hero revives
private static method awakenCond takes nothing returns boolean
set .tempunit = GetRevivingUnit()
set .temp = .get(.tempunit)
// The reviving hero didn't have an aura. =(
if .temp == 0 then
return false
endif
if .temp.mainRevive.exists then
call .temp.mainRevive()
endif
// Unpause it.. If possible.
set .temp.pause = false
return false
endmethod
method onDestroy takes nothing returns nothing
// Clearing the struct 'attachment'
// call RemoveSavedInteger(.allUACs, .typeid, GetHandleId(.main))
// Apparently, RemoveSavedInteger doesn't work.
call SaveInteger(.allUACs, .typeid, GetHandleId(.main), 0)
call ForGroup(.affected, .unaffect) // And unaffecting the units
set .N = .N - 1
set .D[.index] = .D[.N]
set .D[.index].index = .index
if .N == 0 then
call PauseTimer(.T)
endif
endmethod
private static method onInit takes nothing returns nothing
// Trigger created so that the struct can be appropriately handled when the mainunit dies.
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Filter(function thistype.deathCond))
// Trigger created so that the struct can be resumed if the mainunit is revived.
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_HERO_REVIVE_FINISH)
call TriggerAddCondition(t, Filter(function thistype.awakenCond))
// Using variables so that the code can be arranged in any order.
set .funcexpire = function thistype.expire
set .mainfilt = Filter(function thistype.unitFilter)
set .callbacka = function thistype.callbackA
set .callbackb = function thistype.callbackB
set .unaffect = function thistype.clearAffected
endmethod
endstruct
endlibrary</i>
Note that this requires Jasshelper 0.9.G.0 or newer, and Patch 1.23b or newer!
The Extensions and an Example are in the [POST=912022]next post![/POST]