AutoEvents

Scripts

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.

Code (jass) Select
1
2
library AutoEvents requires AutoIndex
3
//===========================================================================
4
// Information:
5
//==============
6
//
7
//     AutoEvents is an add-on library for AutoIndex. It gives you events that
8
// detect the following things: when units resurrect, when units are raised with
9
// Animate Dead, when units begin reincarnating, when units finish reincarnating,
10
// when transports load units, and when transports unload units. It also provides
11
// 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 a
13
// transport, get the passenger in a specific slot of a transport, and enumerate
14
// 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 when
21
// an event occurs. Unit-related events require a function matching the Stat-
22
// usEvent function interface, and transport-related events require a function
23
// matching the TransportEvent function interface:
24
//
25
// function interface AutoEvent takes unit u returns nothing
26
// function interface TransportEvent takes unit transport, unit passenger returns nothing
27
//
28
//     The following examples use the AutoEvent function interface:
29
/*
30
    function UnitDies takes unit u returns nothing
31
        call BJDebugMsg(GetUnitName(u)+" has died.")
32
    endfunction
33
34
    function UnitResurrects takes unit u returns nothing
35
        call BJDebugMsg(GetUnitName(u)+" has been resurrected.")
36
    endfunction
37
38
    function Init takes nothing returns nothing
39
        call OnUnitDeath(UnitDies)
40
        call OnUnitResurrect(UnitResurrects)
41
    endfunction
42
*/
43
//     And the following examples use the TransportEvents function interface:
44
/*
45
    function UnitLoads takes unit transport, unit passenger returns nothing
46
        call BJDebugMsg(GetUnitName(transport)+" loaded "+GetUnitName(passenger))
47
    endfunction
48
49
    function UnitUnloads takes unit transport, unit passenger returns nothing
50
        call BJDebugMsg(GetUnitName(transport)+" unloaded "+GetUnitName(passenger))
51
    endfunction
52
53
    function Init takes nothing returns nothing
54
        call OnUnitLoad(UnitLoads)
55
        call OnUnitUnload(UnitUnloads)
56
    endfunction
57
*/
58
//     Here is an example of using ForPassengers to enumerate each unit in
59
// a transport and heal them for 100 life:
60
/*
61
    function HealPassenger takes unit transport, unit passenger returns nothing
62
        call SetWidgetLife(passenger, GetWidgetLife(passenger) + 100.)
63
    endfunction
64
    function HealAllPassengers takes unit transport returns nothing
65
        call ForPassengers(transport, HealPassenger)
66
    endfunction
67
*/
68
//     GetPassengerBySlot provides an alternative way to enumerate the
69
// units within a transport. (The following example would heal a unit
70
// that occupies multiple slots in the transport only one time, since
71
// GetPassengerBySlot assumes that each unit occupies only one slot.)
72
/*
73
    function HealAllPassengers takes unit transport returns nothing
74
        local integer slot = 1 //Start at slot 1.
75
        local unit passenger
76
            loop
77
                set passenger = GetPassengerBySlot(transport, slot)
78
                exitwhen passenger == null
79
                call SetWidgetLife(passenger, GetWidgetLife(passenger) + 100.)
80
                set slot = slot + 1
81
            endloop
82
    endfunction
83
*/
84
//===========================================================================
85
// AutoEvents API:
86
//=================
87
//
88
// OnUnitDeath(AutoEvent)
89
//   This event runs when any unit dies. It fires after the unit is dead, but
90
//   before any death triggers fire.
91
//
92
// OnUnitResurrect(AutoEvent)
93
//   This event runs when any unit is resurrected. It also fires when units
94
//   are raised with Animate Dead or Reincarnation, as those are forms of
95
//   resurrection as well.
96
//
97
// OnUnitAnimateDead(AutoEvent)
98
//   This event runs when any unit is raised with Animate Dead. It fires after
99
//   the resurrection event.
100
//
101
// IsUnitAnimateDead(unit) -> boolean
102
//   This function returns a boolean that indicates if the specified unit
103
//   has been raised with Animate Dead.
104
//
105
// OnUnitReincarnateStart(AutoEvent)
106
//   This event runs when any unit begins reincarnating. The OnUnitDeath event
107
//   will run first.
108
//
109
// OnUnitReincarnateEnd(AutoEvent)
110
//   This event runs when any unit finishes reincarnating. The OnUnitResurrect
111
//   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 the
121
//   unit is not riding in any transport.
122
//
123
// CountPassengers(transport) -> integer
124
//   Returns the number of passengers in the specified transport.
125
//
126
// GetPassengerBySlot(transport, slot) -> unit
127
//   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 be
129
//   treated as occupying one transport slot.
130
//
131
// ForPassengers(transport, TransportEvent)
132
//   This function runs a TransportEvent immediately for each passenger in
133
//   the specified transport.
134
//
135
//===========================================================================
136
137
function interface AutoEvent takes unit u returns nothing
138
139
//! textmacro RunAutoEvent takes EVENT
140
    set n = 0
141
    loop
142
        exitwhen n > $EVENT$funcs_n
143
        call $EVENT$funcs[n].evaluate(u)
144
        set n = n + 1
145
    endloop
146
//! endtextmacro
147
148
//Injecting this textmacro into AutoIndex will cause the events to actually run.
149
150
//! textmacro AutoEvent takes EVENT, EVENTTYPE
151
    globals
152
        $EVENTTYPE$ array $EVENT$funcs
153
        integer $EVENT$funcs_n = -1
154
    endglobals
155
    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
    endfunction
159
//! endtextmacro
160
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
globals
175
    private timer ReincarnateTimer = CreateTimer()
176
    private boolean array Reincarnated
177
    private unit array Reincarnating
178
    private integer Reincarnating_N = -1
179
endglobals
180
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
        endif
190
endfunction
191
192
private function ReincarnateCheck takes nothing returns nothing
193
    local integer n = Reincarnating_N
194
    local unit u
195
        loop
196
            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
            endif
203
            set Reincarnating[n] = null
204
            set n = n - 1
205
        endloop
206
        set Reincarnating_N = -1
207
    set u = null
208
endfunction
209
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 this
213
    //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
endfunction
218
    
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
endfunction
223
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
endfunction
228
    
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
    endmethod
240
endstruct
241
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 EVENT
252
    set n = 0
253
    loop
254
        exitwhen n > $EVENT$funcs_n
255
        call $EVENT$funcs[n].evaluate(transport, passenger)
256
        set n = n + 1
257
    endloop
258
//! endtextmacro
259
//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 Transport
271
    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
    endmethod
287
    
288
    static method countPassengers takes unit transport returns integer
289
        return transports[GetUnitId(transport)].loaded_n + 1
290
    endmethod
291
    
292
    static method getPassengerBySlot takes unit transport, integer slot returns unit
293
            if slot < 1 or slot > 10 then
294
                return null
295
            endif
296
        return transports[GetUnitId(transport)].loaded[slot - 1]
297
    endmethod
298
    
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
            endif
305
            loop
306
                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
            endloop
311
    endmethod
312
    
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
            endif
324
            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
    endmethod
335
    
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
            loop
341
                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
            endif
354
        set transport = null
355
    endmethod
356
    
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
            endif
363
        return false
364
    endmethod
365
    
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
            endif
370
        return false
371
    endmethod
372
    
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
    endmethod
392
    
393
endstruct
394
395
//===========================================================================
396
// User functions:
397
//=================
398
399
function IsUnitAnimateDead takes unit u returns boolean
400
    return AutoIndex.isUnitAnimateDead(u)
401
endfunction
402
403
function GetUnitTransport takes unit u returns unit
404
    return Transport.getUnitTransport(u)
405
endfunction
406
407
function CountPassengers takes unit transport returns integer
408
    return Transport.countPassengers(transport)
409
endfunction
410
411
function GetPassengerBySlot takes unit transport, integer slot returns unit
412
    return Transport.getPassengerBySlot(transport, slot)
413
endfunction
414
415
function ForPassengers takes unit transport, TransportEvent func returns nothing
416
    call Transport.forPassengers(transport, func)
417
endfunction
418
419
endlibrary