- Reaction score
Unit Aura Control
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!
. _ _ _ ___ | | | |/_\ / __| 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 isn'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. 'mainUnit' is the unit which has the aura spell. 'range' is the range of the aura. Units within this range of 'mainUnit' will be affected. See 'main' and 'range' members for more detail. If your struct has a custom create method, you will need to call 'allocate' 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 'mainUnit' 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 it'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 'whichUnit' is affected by the aura. It will return true if the unit is, and false if it isn'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, this'll return false if it's not affected by this exact instance. This is useful for auras such as 'double damage' auras. It'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 'main' 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). 'main' is a readonly member, meaning it cannot be set. Only retrieved. 'mainUnit' can also be used instead of 'main'. It does exactly the same thing, just with a different name. call SetWidgetLife(data.main, 500.) call SetWidgetLife(data.mainUnit, 500.) ________________ real range 'range' 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 'pause' 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 it's paused. When a struct is paused, all affected units will become unaffected. if data.pause then set data.pause = false endif ________________ boolean single 'single' 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 it'll just remain paused forever). 'single' 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 'count' 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. 'count' is a readonly member, meaning it cannot be set. Only retrieved. call SetWidgetLife(data.main, 1000 / data.count) ________________ real ticker 'ticker' 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 aren'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. 'whichUnit' 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. 'whichUnit' 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 'single' 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 doesn't take a unit argument. It's simply called regardless of any conditions, units, etc. Can save you a timer. Useable as a 'unitPeriodic' for the mainUnit only, as unitPeriodic isn'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. It'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 'effect' and 'attachpoint' 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 'Single' 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" data-shortname=":p" />) 1.5 - Added the .Destroy method. When 'set data.Destroy = true' 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 'And' and 'Or' filters. At least one of the 'Or' ones has to be true, and all of the 'And's have to be true. They are all optional. - As a result of the filters being remade, the filter library is now removed. (Didn't last long!) 1.3 - Fixed a bug which occured when the 'main' unit died. - Added a library of common filters, so the user doesn'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 should'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 'aura units' - Removed a useless variable from a function 1.1 - Fixed a coding error involving index recycling 1.0: - Initial Release
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]
52.8 KB Views: 552