14 years
edited 8 years
AutoEvents is an add-on library for AutoIndex. It gives you events that detect the following things: when units resurrect, when units are raised with Animate Dead, when units begin reincarnating, when units finish reincarnatinging, and when transports load and unload units. It also provides other useful functions. You can check if a unit is currently raised with Animate Dead, get the transport carrying a unit, get the number of a units in a transport, get the passenger in a specific slot of a transport, and enumerate through all of the units in a transport.
1 2 library AutoEvents requires AutoIndex
3 //===========================================================================4 // Information:5 //==============6 //7 // AutoEvents is an add-on library for AutoIndex. It gives you events that8 // detect the following things: when units resurrect, when units are raised with9 // Animate Dead, when units begin reincarnating, when units finish reincarnating,10 // when transports load units, and when transports unload units. It also provides11 // other useful functions: you can check if a unit is currently raised with Ani-12 // mate Dead, get the transport carrying a unit, get the number of a units in a13 // transport, get the passenger in a specific slot of a transport, and enumerate14 // through all of the units in a transport.15 //16 //===========================================================================17 // How to use AutoEvents:18 //========================19 //20 // You can use the events in this library to run specific functions when21 // an event occurs. Unit-related events require a function matching the Stat-22 // usEvent function interface, and transport-related events require a function23 // matching the TransportEvent function interface:24 //25 // function interface AutoEvent takes unit u returns nothing26 // function interface TransportEvent takes unit transport, unit passenger returns nothing27 //28 // The following examples use the AutoEvent function interface:29 /*30 function UnitDies takes unit u returns nothing31 call BJDebugMsg(GetUnitName(u)+" has died.")32 endfunction33
34 function UnitResurrects takes unit u returns nothing35 call BJDebugMsg(GetUnitName(u)+" has been resurrected.")36 endfunction37
38 function Init takes nothing returns nothing39 call OnUnitDeath(UnitDies)40 call OnUnitResurrect(UnitResurrects)41 endfunction42 */43 // And the following examples use the TransportEvents function interface:44 /*45 function UnitLoads takes unit transport, unit passenger returns nothing46 call BJDebugMsg(GetUnitName(transport)+" loaded "+GetUnitName(passenger))47 endfunction48
49 function UnitUnloads takes unit transport, unit passenger returns nothing50 call BJDebugMsg(GetUnitName(transport)+" unloaded "+GetUnitName(passenger))51 endfunction52
53 function Init takes nothing returns nothing54 call OnUnitLoad(UnitLoads)55 call OnUnitUnload(UnitUnloads)56 endfunction57 */58 // Here is an example of using ForPassengers to enumerate each unit in59 // a transport and heal them for 100 life:60 /*61 function HealPassenger takes unit transport, unit passenger returns nothing62 call SetWidgetLife(passenger, GetWidgetLife(passenger) + 100.)63 endfunction64 function HealAllPassengers takes unit transport returns nothing65 call ForPassengers(transport, HealPassenger)66 endfunction67 */68 // GetPassengerBySlot provides an alternative way to enumerate the69 // units within a transport. (The following example would heal a unit70 // that occupies multiple slots in the transport only one time, since71 // GetPassengerBySlot assumes that each unit occupies only one slot.)72 /*73 function HealAllPassengers takes unit transport returns nothing74 local integer slot = 1 //Start at slot 1.75 local unit passenger76 loop77 set passenger = GetPassengerBySlot(transport, slot)78 exitwhen passenger == null79 call SetWidgetLife(passenger, GetWidgetLife(passenger) + 100.)80 set slot = slot + 181 endloop82 endfunction83 */84 //===========================================================================85 // AutoEvents API:86 //=================87 //88 // OnUnitDeath(AutoEvent)89 // This event runs when any unit dies. It fires after the unit is dead, but90 // before any death triggers fire.91 //92 // OnUnitResurrect(AutoEvent)93 // This event runs when any unit is resurrected. It also fires when units94 // are raised with Animate Dead or Reincarnation, as those are forms of95 // resurrection as well.96 //97 // OnUnitAnimateDead(AutoEvent)98 // This event runs when any unit is raised with Animate Dead. It fires after99 // the resurrection event.100 //101 // IsUnitAnimateDead(unit) -> boolean102 // This function returns a boolean that indicates if the specified unit103 // has been raised with Animate Dead.104 //105 // OnUnitReincarnateStart(AutoEvent)106 // This event runs when any unit begins reincarnating. The OnUnitDeath event107 // will run first.108 //109 // OnUnitReincarnateEnd(AutoEvent)110 // This event runs when any unit finishes reincarnating. The OnUnitResurrect111 // event will occur immediately after.112 //113 // OnUnitLoad(TransportEvent)114 // This event runs when any transport loads a passenger. 115 //116 // OnUnitUnload(TransportEvent)117 // This event runs when any transport unloads a passenger.118 //119 // GetUnitTransport(unit)120 // Returns the transport that a unit is loaded in. Returns null if the121 // unit is not riding in any transport.122 //123 // CountPassengers(transport) -> integer124 // Returns the number of passengers in the specified transport.125 //126 // GetPassengerBySlot(transport, slot) -> unit127 // Returns the passenger in the given slot of the specified transport.128 // However, if a unit takes more than one transport slot, it will only be129 // treated as occupying one transport slot.130 //131 // ForPassengers(transport, TransportEvent)132 // This function runs a TransportEvent immediately for each passenger in133 // the specified transport.134 //135 //===========================================================================136 137 function interface AutoEvent takes unit u returns nothing
138 139 //! textmacro RunAutoEvent takes EVENT140 set n = 0
141 loop142 exitwhen n > $EVENT$funcs_n
143 call $EVENT$funcs[n].evaluate(u)
144 set n = n + 1
145 endloop146 //! endtextmacro147 148 //Injecting this textmacro into AutoIndex will cause the events to actually run.149 150 //! textmacro AutoEvent takes EVENT, EVENTTYPE151 globals152 $EVENTTYPE$ array $EVENT$funcs153 integer $EVENT$funcs_n = -1
154 endglobals155 function OnUnit$EVENT$ takes $EVENTTYPE$ func returns nothing
156 set $EVENT$funcs_n = $EVENT$funcs_n + 1
157 set $EVENT$funcs[$EVENT$funcs_n] = func
158 endfunction159 //! endtextmacro160 161 //Instantiate the function to register events of each type.162 //! runtextmacro AutoEvent("Death", "AutoEvent")163 //! runtextmacro AutoEvent("Resurrect", "AutoEvent")164 //! runtextmacro AutoEvent("AnimateDead", "AutoEvent")165 166 //===========================================================================167 //The code below this point adds Reincarnation support to AutoEvents.168 //Credit to ToukoAozaki for the idea behind this detection method.169 170 //! runtextmacro AutoEvent("ReincarnationStart", "AutoEvent")171 //! runtextmacro AutoEvent("ReincarnationFinish", "AutoEvent")172 //Create registration functions for reincarnation start and stop events.173 174 globals175 private timer ReincarnateTimer = CreateTimer()
176 private boolean array Reincarnated
177 private unit array Reincarnating
178 private integer Reincarnating_N = -1
179 endglobals180 181 private function OnResurrect takes unit u returns nothing
182 local integer index = GetUnitId(u)
183 local integer n
184 if Reincarnated[index] then
185 set Reincarnated[index] = false
186 //If a resurrecting unit is flagged as reincarnating,187 //it's time to run the ReincarnationFinish event.188 //! runtextmacro RunAutoEvent("ReincarnationFinish")189 endif190 endfunction191 192 private function ReincarnateCheck takes nothing returns nothing
193 local integer n = Reincarnating_N
194 local unit u
195 loop196 exitwhen n < 0
197 set u = Reincarnating[n]
198 if GetUnitTypeId(u) != 0 and Reincarnated[GetUnitId(u)] then
199 //If the unit is still flagged as reincarnating, it means DeathDetect didn't run.200 //The unit is actually reincarnating, so run the ReincarnationStart event.201 //! runtextmacro RunAutoEvent("ReincarnationStart")202 endif203 set Reincarnating[n] = null
204 set n = n - 1
205 endloop206 set Reincarnating_N = -1
207 set u = null
208 endfunction209 210 private function OnDeath takes unit u returns nothing
211 set Reincarnated[GetUnitId(u)] = true
212 //Assume any unit that dies is going to reincarnate, unless this213 //flag is set to false later by the DeathDetect function.214 set Reincarnating_N = Reincarnating_N + 1 //Add the dying unit to a stack and
215 set Reincarnating[Reincarnating_N] = u //check the flag 0. seconds later.
216 call TimerStart(ReincarnateTimer, 0., false, function ReincarnateCheck)
217 endfunction218
219 private function DeathDetect takes nothing returns boolean
220 set Reincarnated[GetUnitId(GetTriggerUnit())] = false
221 return false //Set the Reincarnated flag to false if the unit will not reincarnate.
222 endfunction223 224 private function OnEnter takes unit u returns nothing
225 set Reincarnated[GetUnitId(u)] = false
226 //When a unit enters the map, initialize its Reincarnated flag to false.227 endfunction228
229 private struct ReincarnationInit extends array
230 private static method onInit takes nothing returns nothing
231 local trigger deathdetect = CreateTrigger()
232 call TriggerRegisterAnyUnitEventBJ(deathdetect, EVENT_PLAYER_UNIT_DEATH)
233 call TriggerAddCondition(deathdetect, function DeathDetect)
234 //This trigger runs 0. seconds after OnUnitDeath events, 235 //but does not fire if the unit is going to Reincarnate.236 call OnUnitIndexed(OnEnter)
237 call OnUnitDeath(OnDeath)
238 call OnUnitResurrect(OnResurrect)
239 endmethod240 endstruct241 242 //===========================================================================243 // All of the remaining code deals with transports.244 245 function interface TransportEvent takes unit transport, unit passenger returns nothing
246 247 //! runtextmacro AutoEvent("Load", "TransportEvent")248 //! runtextmacro AutoEvent("Unload", "TransportEvent")249 //Create registration functions for load and unload events.250 251 //! textmacro RunTransportEvent takes EVENT252 set n = 0
253 loop254 exitwhen n > $EVENT$funcs_n
255 call $EVENT$funcs[n].evaluate(transport, passenger)
256 set n = n + 1
257 endloop258 //! endtextmacro259 //The above textmacro is used to run the Load/Unload events in the Transport struct below.260 261 //===========================================================================262 //A transport struct is created and attached to any unit detected loading another unit.263 //It keeps track of the units within a transport and updates when they load or unload.264 265 private keyword getUnitTransport
266 private keyword countPassengers
267 private keyword getPassengerBySlot
268 private keyword forPassengers
269 270 struct Transport271 private static unit array loadedin
272 private static Transport array transports
273 private static integer array loadedindex
274 private static group array groups
275 private static integer groups_n = -1
276 private static real MaxX
277 private static real MaxY
278
279 private unit array loaded[10] //Transports can only carry 10 units.
280 private integer loaded_n = -1
281
282 //===========================================================================283
284 static method getUnitTransport takes unit u returns unit
285 return loadedin[GetUnitId(u)]
286 endmethod287
288 static method countPassengers takes unit transport returns integer
289 return transports[GetUnitId(transport)].loaded_n + 1
290 endmethod291
292 static method getPassengerBySlot takes unit transport, integer slot returns unit
293 if slot < 1 or slot > 10 then
294 return null
295 endif296 return transports[GetUnitId(transport)].loaded[slot - 1]
297 endmethod298
299 static method forPassengers takes unit transport, TransportEvent func returns nothing
300 local Transport this = transports[GetUnitId(transport)]
301 local integer n = 0
302 if loaded_n == -1 then
303 return //Return if transport has no units loaded inside.
304 endif305 loop306 exitwhen n > loaded_n
307 call func.evaluate(transport, loaded[n])
308 //Loop through each passenger and call the TransportEvent func on it.309 set n = n + 1
310 endloop311 endmethod312
313 //===========================================================================314 315 static method loadUnit takes nothing returns boolean
316 local unit transport = GetTransportUnit()
317 local unit passenger = GetTriggerUnit()
318 local Transport this = transports[GetUnitId(transport)]
319 local integer n
320 if this == 0 then //If this is the first unit loaded by this transport...
321 set this = allocate() //allocate a Transport struct,
322 set transports[GetUnitId(transport)] = this //and attach it to the transport.
323 endif324 set loaded_n = loaded_n + 1 //Increment the passenger counter.
325 set loaded[loaded_n] = passenger //Put the passenger in the unit array.
326 set loadedindex[GetUnitId(passenger)] = loaded_n //Attach the index to the passenger.
327 set loadedin[GetUnitId(passenger)] = transport //Attach the transport struct to the transport.
328 //! runtextmacro RunTransportEvent("Load") //Run the OnUnitLoad events.329 call SetUnitX(passenger, MaxX) //Move the passenger to the edge of the map so that
330 call SetUnitY(passenger, MaxY) //unloading will trigger a "unit enters region" event.
331 set transport = null
332 set passenger = null
333 return false
334 endmethod335
336 static method unloadUnit takes unit passenger returns nothing
337 local unit transport = getUnitTransport(passenger) //Get the transport unit.
338 local Transport this = transports[GetUnitId(transport)] //Get the transport struct.
339 local integer n = loadedindex[GetUnitId(passenger)] //Get the passenger's index.
340 loop341 exitwhen n == loaded_n
342 set loaded[n] = loaded[n + 1]
343 set loadedindex[GetUnitId(loaded[n])] = n
344 set n = n + 1 //Starting from the position of the removed unit,
345 endloop //shift everything down by one and update the index.
346 set loaded[n] = null
347 set loaded_n = loaded_n - 1 //Decrement the passenger counter.
348 set loadedin[GetUnitId(passenger)] = null //Null the unloaded unit's transport.
349 //! runtextmacro RunTransportEvent("Unload") //Run the OnUnitUnload events.350 if loaded_n == -1 then //If the transport is now empty...
351 call destroy() //Destroy the transport struct.
352 set transports[GetUnitId(transport)] = 0 //Disassociate it from the unit.
353 endif354 set transport = null
355 endmethod356
357 //===========================================================================358
359 private static method unitEntersMap takes nothing returns boolean
360 if getUnitTransport(GetFilterUnit()) != null then //If the entering unit is in a transport...
361 call unloadUnit(GetFilterUnit()) //The unit was unloaded.
362 endif363 return false
364 endmethod365
366 private static method unitDies takes nothing returns boolean
367 if getUnitTransport(GetTriggerUnit()) != null then //If the dying unit is in a transport...
368 call unloadUnit(GetTriggerUnit()) //Unload the unit from its transport.
369 endif370 return false
371 endmethod372
373 private static method onInit takes nothing returns nothing
374 local region maparea = CreateRegion()
375 local rect bounds = GetWorldBounds()
376 local trigger unload = CreateTrigger()
377 local trigger load = CreateTrigger()
378 local trigger death = CreateTrigger()
379 call RegionAddRect(maparea, bounds)
380 call TriggerRegisterEnterRegion(unload, maparea, function Transport.unitEntersMap) //When a unit enters the map area,
381 call TriggerRegisterAnyUnitEventBJ(load, EVENT_PLAYER_UNIT_LOADED) //it may have been unloaded.
382 call TriggerAddCondition(load, function Transport.loadUnit) //When a unit loads a unit, run the loadUnit method.
383 call TriggerRegisterAnyUnitEventBJ(death, EVENT_PLAYER_UNIT_DEATH)
384 call TriggerAddCondition(death, function Transport.unitDies) //Detect when a unit dies in order to unload it.
385 call OnUnitDeindexed(Transport.unloadUnit) //When a unit leaves the game, unload it from its transport.
386 set Transport(0).loaded_n = -1 //Initialize this to -1 to make CountUnitsInTransport work properly.
387 set MaxX = GetRectMaxX(bounds) //Record the coordinates of a corner of the map so
388 set MaxY = GetRectMaxY(bounds) //that loaded units can be moved to that location.
389 call RemoveRect(bounds)
390 set bounds = null
391 endmethod392
393 endstruct394 395 //===========================================================================396 // User functions:397 //=================398 399 function IsUnitAnimateDead takes unit u returns boolean
400 return AutoIndex.isUnitAnimateDead(u)
401 endfunction402 403 function GetUnitTransport takes unit u returns unit
404 return Transport.getUnitTransport(u)
405 endfunction406 407 function CountPassengers takes unit transport returns integer
408 return Transport.countPassengers(transport)
409 endfunction410 411 function GetPassengerBySlot takes unit transport, integer slot returns unit
412 return Transport.getPassengerBySlot(transport, slot)
413 endfunction414 415 function ForPassengers takes unit transport, TransportEvent func returns nothing
416 call Transport.forPassengers(transport, func)
417 endfunction418 419 endlibrary