library AutoEvents requires AutoIndex
//===========================================================================
// Information:
//==============
//
// 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 reincarnating,
// when transports load units, and when transports unload units. It also provides
// other useful functions: you can check if a unit is currently raised with Ani-
// mate 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.
//
//===========================================================================
// How to use AutoEvents:
//========================
//
// You can use the events in this library to run specific functions when
// an event occurs. Unit-related events require a function matching the Stat-
// usEvent function interface, and transport-related events require a function
// matching the TransportEvent function interface:
//
// function interface AutoEvent takes unit u returns nothing
// function interface TransportEvent takes unit transport, unit passenger returns nothing
//
// The following examples use the AutoEvent function interface:
/*
function UnitDies takes unit u returns nothing
call BJDebugMsg(GetUnitName(u)+" has died.")
endfunction
function UnitResurrects takes unit u returns nothing
call BJDebugMsg(GetUnitName(u)+" has been resurrected.")
endfunction
function Init takes nothing returns nothing
call OnUnitDeath(UnitDies)
call OnUnitResurrect(UnitResurrects)
endfunction
*/
// And the following examples use the TransportEvents function interface:
/*
function UnitLoads takes unit transport, unit passenger returns nothing
call BJDebugMsg(GetUnitName(transport)+" loaded "+GetUnitName(passenger))
endfunction
function UnitUnloads takes unit transport, unit passenger returns nothing
call BJDebugMsg(GetUnitName(transport)+" unloaded "+GetUnitName(passenger))
endfunction
function Init takes nothing returns nothing
call OnUnitLoad(UnitLoads)
call OnUnitUnload(UnitUnloads)
endfunction
*/
// Here is an example of using ForPassengers to enumerate each unit in
// a transport and heal them for 100 life:
/*
function HealPassenger takes unit transport, unit passenger returns nothing
call SetWidgetLife(passenger, GetWidgetLife(passenger) + 100.)
endfunction
function HealAllPassengers takes unit transport returns nothing
call ForPassengers(transport, HealPassenger)
endfunction
*/
// GetPassengerBySlot provides an alternative way to enumerate the
// units within a transport. (The following example would heal a unit
// that occupies multiple slots in the transport only one time, since
// GetPassengerBySlot assumes that each unit occupies only one slot.)
/*
function HealAllPassengers takes unit transport returns nothing
local integer slot = 1 //Start at slot 1.
local unit passenger
loop
set passenger = GetPassengerBySlot(transport, slot)
exitwhen passenger == null
call SetWidgetLife(passenger, GetWidgetLife(passenger) + 100.)
set slot = slot + 1
endloop
endfunction
*/
//===========================================================================
// AutoEvents API:
//=================
//
// OnUnitDeath(AutoEvent)
// This event runs when any unit dies. It fires after the unit is dead, but
// before any death triggers fire.
//
// OnUnitResurrect(AutoEvent)
// This event runs when any unit is resurrected. It also fires when units
// are raised with Animate Dead or Reincarnation, as those are forms of
// resurrection as well.
//
// OnUnitAnimateDead(AutoEvent)
// This event runs when any unit is raised with Animate Dead. It fires after
// the resurrection event.
//
// IsUnitAnimateDead(unit) -> boolean
// This function returns a boolean that indicates if the specified unit
// has been raised with Animate Dead.
//
// OnUnitReincarnateStart(AutoEvent)
// This event runs when any unit begins reincarnating. The OnUnitDeath event
// will run first.
//
// OnUnitReincarnateEnd(AutoEvent)
// This event runs when any unit finishes reincarnating. The OnUnitResurrect
// event will occur immediately after.
//
// OnUnitLoad(TransportEvent)
// This event runs when any transport loads a passenger.
//
// OnUnitUnload(TransportEvent)
// This event runs when any transport unloads a passenger.
//
// GetUnitTransport(unit)
// Returns the transport that a unit is loaded in. Returns null if the
// unit is not riding in any transport.
//
// CountPassengers(transport) -> integer
// Returns the number of passengers in the specified transport.
//
// GetPassengerBySlot(transport, slot) -> unit
// Returns the passenger in the given slot of the specified transport.
// However, if a unit takes more than one transport slot, it will only be
// treated as occupying one transport slot.
//
// ForPassengers(transport, TransportEvent)
// This function runs a TransportEvent immediately for each passenger in
// the specified transport.
//
//===========================================================================
function interface AutoEvent
takes unit u
returns nothing
//! textmacro RunAutoEvent takes EVENT
set n = 0
loop
exitwhen n > $EVENT$funcs_n
call $EVENT$funcs[n].evaluate(u)
set n = n + 1
endloop
//! endtextmacro
//Injecting this textmacro into AutoIndex will cause the events to actually run.
//! textmacro AutoEvent takes EVENT, EVENTTYPE
globals
$EVENTTYPE$ array $EVENT$funcs
endglobals
function OnUnit$EVENT$ takes $EVENTTYPE$ func returns nothing
set $EVENT$funcs_n = $EVENT$funcs_n + 1
set $EVENT$funcs[$EVENT$funcs_n] = func
endfunction
//! endtextmacro
//Instantiate the function to register events of each type.
//! runtextmacro AutoEvent("Death", "AutoEvent")
//! runtextmacro AutoEvent("Resurrect", "AutoEvent")
//! runtextmacro AutoEvent("AnimateDead", "AutoEvent")
//===========================================================================
//The code below this point adds Reincarnation support to AutoEvents.
//Credit to ToukoAozaki for the idea behind this detection method.
//! runtextmacro AutoEvent("ReincarnationStart", "AutoEvent")
//! runtextmacro AutoEvent("ReincarnationFinish", "AutoEvent")
//Create registration functions for reincarnation start and stop events.
globals
private unit array Reincarnating
private integer Reincarnating_N = -1
endglobals
private function OnResurrect
takes unit u
returns nothing if Reincarnated[index] then
set Reincarnated[index] = false
//If a resurrecting unit is flagged as reincarnating,
//it's time to run the ReincarnationFinish event.
//! runtextmacro RunAutoEvent("ReincarnationFinish")
endif
endfunction
private function ReincarnateCheck takes nothing returns nothing
loop
exitwhen n < 0
set u = Reincarnating[n]
//If the unit is still flagged as reincarnating, it means DeathDetect didn't run.
//The unit is actually reincarnating, so run the ReincarnationStart event.
//! runtextmacro RunAutoEvent("ReincarnationStart")
endif
set Reincarnating[n] = null
set n = n - 1
endloop
set Reincarnating_N = -1
set u = null
endfunction
private function OnDeath
takes unit u
returns nothing set Reincarnated[GetUnitId(u)] = true
//Assume any unit that dies is going to reincarnate, unless this
//flag is set to false later by the DeathDetect function.
set Reincarnating_N = Reincarnating_N + 1 //Add the dying unit to a stack and
set Reincarnating[Reincarnating_N] = u //check the flag 0. seconds later.
call TimerStart(ReincarnateTimer, 0.,
false,
function ReincarnateCheck)
endfunction
private function DeathDetect
takes nothing returns boolean return false //Set the Reincarnated flag to false if the unit will not reincarnate.
endfunction
private function OnEnter
takes unit u
returns nothing set Reincarnated[GetUnitId(u)] = false
//When a unit enters the map, initialize its Reincarnated flag to false.
endfunction
private struct ReincarnationInit extends array
private static method onInit takes nothing returns nothing
//This trigger runs 0. seconds after OnUnitDeath events,
//but does not fire if the unit is going to Reincarnate.
call OnUnitIndexed(OnEnter)
call OnUnitDeath(OnDeath)
call OnUnitResurrect(OnResurrect)
endmethod
endstruct
//===========================================================================
// All of the remaining code deals with transports.
function interface TransportEvent
takes unit transport,
unit passenger
returns nothing
//! runtextmacro AutoEvent("Load", "TransportEvent")
//! runtextmacro AutoEvent("Unload", "TransportEvent")
//Create registration functions for load and unload events.
//! textmacro RunTransportEvent takes EVENT
set n = 0
loop
exitwhen n > $EVENT$funcs_n
call $EVENT$funcs[n].evaluate(transport, passenger)
set n = n + 1
endloop
//! endtextmacro
//The above textmacro is used to run the Load/Unload events in the Transport struct below.
//===========================================================================
//A transport struct is created and attached to any unit detected loading another unit.
//It keeps track of the units within a transport and updates when they load or unload.
private keyword getUnitTransport
private keyword countPassengers
private keyword getPassengerBySlot
private keyword forPassengers
struct Transport
private static unit array loadedin
private static Transport array transports
private static integer array loadedindex
private static group array groups
private static integer groups_n = -1
private unit array loaded[10]
//Transports can only carry 10 units.
//===========================================================================
static method getUnitTransport
takes unit u
returns unit return loadedin[GetUnitId(u)]
endmethod
static method countPassengers
takes unit transport
returns integer return transports[GetUnitId(transport)].loaded_n + 1
endmethod
static method getPassengerBySlot
takes unit transport,
integer slot
returns unit if slot < 1 or slot > 10 then
return null
endif
return transports[GetUnitId(transport)].loaded[slot - 1]
endmethod
static method forPassengers
takes unit transport, TransportEvent func
returns nothing local Transport this = transports[GetUnitId(transport)]
if loaded_n == -1 then
return //Return if transport has no units loaded inside.
endif
loop
exitwhen n > loaded_n
call func.evaluate(transport, loaded[n])
//Loop through each passenger and call the TransportEvent func on it.
set n = n + 1
endloop
endmethod
//===========================================================================
static method loadUnit
takes nothing returns boolean local Transport this = transports[GetUnitId(transport)]
if this == 0 then //If this is the first unit loaded by this transport...
set this = allocate() //allocate a Transport struct,
set transports[GetUnitId(transport)] = this //and attach it to the transport.
endif
set loaded_n = loaded_n + 1 //Increment the passenger counter.
set loaded[loaded_n] = passenger //Put the passenger in the unit array.
set loadedindex[GetUnitId(passenger)] = loaded_n //Attach the index to the passenger.
set loadedin[GetUnitId(passenger)] = transport //Attach the transport struct to the transport.
//! runtextmacro RunTransportEvent("Load") //Run the OnUnitLoad events.
call SetUnitX(passenger, MaxX)
//Move the passenger to the edge of the map so that call SetUnitY(passenger, MaxY)
//unloading will trigger a "unit enters region" event. set transport = null
set passenger = null
return false
endmethod
static method unloadUnit
takes unit passenger
returns nothing local unit transport = getUnitTransport(passenger)
//Get the transport unit. local Transport this = transports[GetUnitId(transport)] //Get the transport struct.
local integer n = loadedindex[GetUnitId(passenger)]
//Get the passenger's index. loop
exitwhen n == loaded_n
set loaded[n] = loaded[n + 1]
set loadedindex[GetUnitId(loaded[n])] = n
set n = n + 1 //Starting from the position of the removed unit,
endloop //shift everything down by one and update the index.
set loaded[n] = null
set loaded_n = loaded_n - 1 //Decrement the passenger counter.
set loadedin[GetUnitId(passenger)] = null //Null the unloaded unit's transport.
//! runtextmacro RunTransportEvent("Unload") //Run the OnUnitUnload events.
if loaded_n == -1 then //If the transport is now empty...
call destroy() //Destroy the transport struct.
set transports[GetUnitId(transport)] = 0 //Disassociate it from the unit.
endif
set transport = null
endmethod
//===========================================================================
private static method unitEntersMap
takes nothing returns boolean if getUnitTransport(
GetFilterUnit()) !=
null then //If the entering unit is in a transport... endif
return false
endmethod
private static method unitDies
takes nothing returns boolean if getUnitTransport(
GetTriggerUnit()) !=
null then //If the dying unit is in a transport... call unloadUnit(
GetTriggerUnit())
//Unload the unit from its transport. endif
return false
endmethod
private static method onInit takes nothing returns nothing
call TriggerAddCondition(load,
function Transport.loadUnit)
//When a unit loads a unit, run the loadUnit method. call TriggerAddCondition(death,
function Transport.unitDies)
//Detect when a unit dies in order to unload it. call OnUnitDeindexed(Transport.unloadUnit) //When a unit leaves the game, unload it from its transport.
set Transport(0).loaded_n = -1 //Initialize this to -1 to make CountUnitsInTransport work properly.
set MaxX =
GetRectMaxX(bounds)
//Record the coordinates of a corner of the map so set MaxY =
GetRectMaxY(bounds)
//that loaded units can be moved to that location. set bounds = null
endmethod
endstruct
//===========================================================================
// User functions:
//=================
function IsUnitAnimateDead
takes unit u
returns boolean return AutoIndex.isUnitAnimateDead(u)
endfunction
function GetUnitTransport
takes unit u
returns unit return Transport.getUnitTransport(u)
endfunction
function CountPassengers
takes unit transport
returns integer return Transport.countPassengers(transport)
endfunction
function GetPassengerBySlot
takes unit transport,
integer slot
returns unit return Transport.getPassengerBySlot(transport, slot)
endfunction
function ForPassengers
takes unit transport, TransportEvent func
returns nothing call Transport.forPassengers(transport, func)
endfunction
endlibrary