package ClosureEvents import public EventHelper import public RegisterEvents import ClosureTimers import UnitIndexer import HashMap import ErrorHandling /** Use this chat event if you want to access the entered message */ public constant EVENT_PLAYER_CHAT_FILTER = ConvertPlayerEvent(96) /** Configure this function if you want some logic to prevent events being fired for specific units */ @configurable function fireEvents(unit _u) returns boolean return true /** This package is a comfort wrapper around wc3 events, for easy global and per-unit event listening via closures. To listen to an event, simply #add, optionally passing a target unit: > EventListener.add(EVENT_PLAYER_UNIT_DEATH) -> > > EventListener.add(someUnit, EVENT_PLAYER_UNIT_DEATH) -> > For spell handling there are comfort wrappers via the `on` prefix: > EventListener.onCast(MY_SPELL_ID) (caster) -> > > EventListener.onCast(myUnit, MY_SPELL_ID) (caster) -> > > EventListener.onPointCast(myUnit, MY_SPELL_ID) (caster, target) -> > **IMPORTANT**: Make sure to always use the "EVENT_PLAYER_UNIT_*" variants of the eventids. Other supported events: - EVENT_PLAYER_LEAVE - EVENT_UNIT_DAMAGED - EVENT_PLAYER_CHAT_FILTER - EVENT_PLAYER_ARROW_* */ public abstract class OnCastListener OnCastListener next = null OnCastListener prev = null int abilId unit eventUnit = null abstract function fire(unit caster) ondestroy if eventUnit != null let index = eventUnit.getIndex() let listener = EventListener.castMapCasters[index] if listener == this if next != null EventListener.castMapCasters[index] = next else EventListener.castMapCasters[index] = null else if prev != null prev.next = next else let listener = EventListener.castMap.get(abilId) if listener == this if next != null EventListener.castMap.put(abilId, next) else EventListener.castMap.remove(abilId) else if prev != null prev.next = next next.prev = prev next = null prev = null public abstract class OnCast extends OnCastListener override function fire(unit caster) fireEx(GetSpellAbilityId()) abstract function fireEx(int id) public abstract class OnPointCast extends OnCastListener override function fire(unit caster) fireEx(caster, EventData.getSpellTargetPos()) abstract function fireEx(unit caster, vec2 target) public abstract class OnUnitCast extends OnCastListener override function fire(unit caster) fireEx(caster, GetSpellTargetUnit()) abstract function fireEx(unit caster, unit target) public abstract class EventListener static constant castMap = new HashMap static OnCastListener array castMapCasters static EventListener array generalListenersFirsts static EventListener array unitListenersFirsts static var useMouseEvents = false var eventId = 0 var uid = -1 EventListener next = null EventListener prev = null abstract function onEvent() static function add(eventid eventId, EventListener listener) returns EventListener listener.eventId = eventId.toIntId() if generalListenersFirsts[listener.eventId] != null generalListenersFirsts[listener.eventId].prev = listener listener.next = generalListenersFirsts[listener.eventId] generalListenersFirsts[listener.eventId] = listener return listener static function add(unit u, eventid eventId, EventListener listener) returns EventListener let uid = u.getIndex() if uid <= 0 Log.warn("Attempting to register event listener for a unit that is not indexed by UnitIndexer. " + "If this happens during initialization, consider registering the EventListener in a callback to nullTimer() instead.") return listener listener.eventId = eventId.toIntId() listener.uid = uid if unitListenersFirsts[uid] != null unitListenersFirsts[uid].prev = listener listener.next = unitListenersFirsts[uid] unitListenersFirsts[uid] = listener return listener static function onCast(unit u, int abilId, OnCastListener listener) returns OnCastListener return addSpellInternal(u, abilId, listener) static function onTargetCast(unit u, int abilId, OnUnitCast listener) returns OnUnitCast return addSpellInternal(u, abilId, listener) castTo OnUnitCast static function onPointCast(unit u, int abilId, OnPointCast listener) returns OnPointCast return addSpellInternal(u, abilId, listener) castTo OnPointCast static function onCast(int abilId, OnCastListener listener) returns OnCastListener return addSpellInternal(null, abilId, listener) static function onCast(unit u, OnCast listener) returns OnCast return addSpellInternal(u, -1, listener) castTo OnCast static function onTargetCast(int abilId, OnUnitCast listener) returns OnUnitCast return addSpellInternal(null, abilId, listener) castTo OnUnitCast static function onPointCast(int abilId, OnPointCast listener) returns OnPointCast return addSpellInternal(null, abilId, listener) castTo OnPointCast static private function addSpellInternal(unit u, int abilId, OnCastListener listener) returns OnCastListener listener.abilId = abilId if u != null listener.eventUnit = u let index = u.getIndex() if castMapCasters[index] != null castMapCasters[index].prev = listener listener.next = castMapCasters[index] castMapCasters[index] = listener else if castMap.has(abilId) castMap.get(abilId).prev = listener listener.next = castMap.get(abilId) castMap.put(abilId, listener) return listener static function generalEventCallback() let trigUnit = GetTriggerUnit() let id = GetTriggerEventId().toIntId() // Unit Listeners if trigUnit != null and fireEvents(trigUnit) and trigUnit.getIndex() > 0 if unitListenersFirsts[trigUnit.getIndex()] != null var listener = unitListenersFirsts[trigUnit.getIndex()] while listener != null let nextListener = listener.next if listener.eventId == id listener.onEvent() listener = nextListener // Global Listeners if generalListenersFirsts[id] != null var listener = generalListenersFirsts[id] while listener != null let nextListener = listener.next listener.onEvent() listener = nextListener static function onSpellEffect() let trigUnit = GetTriggerUnit() let abilId = GetSpellAbilityId() let index = trigUnit.getIndex() if castMapCasters[index] != null var listener = castMapCasters[index] while listener != null let nextListener = listener.next if listener.abilId == -1 or listener.abilId == abilId listener.fire(GetSpellAbilityUnit()) listener = nextListener if castMap.has(abilId) var listener = castMap.get(abilId) while listener != null let nextListener = listener.next if listener.eventUnit == null or listener.eventUnit == trigUnit listener.fire(GetSpellAbilityUnit()) listener = nextListener ondestroy if uid < 0 let listener = generalListenersFirsts[this.eventId] if listener == this generalListenersFirsts[this.eventId] = next else if prev != null prev.next = next else let listener = unitListenersFirsts[this.uid] if listener == this unitListenersFirsts[this.uid] = next else if prev != null prev.next = next if next != null next.prev = prev next = null prev = null let unitTrig = CreateTrigger() let leaveTrig = CreateTrigger() let keyTrig = CreateTrigger() public function eventid.toIntId() returns int var id = eventidToIndex[this.getHandleId()] if id == 0 id = registerEventId(this) return id int array eventidToIndex var eventTypeCounter = 0 function registerEventId(eventid evnt) returns int let eventId = evnt.getHandleId() eventTypeCounter++ eventidToIndex[eventId] = eventTypeCounter if evnt.isPlayerunitEvent() registerPlayerUnitEvent(ConvertPlayerUnitEvent(eventId), function EventListener.generalEventCallback) else if evnt != EVENT_UNIT_DAMAGED and evnt != EVENT_PLAYER_LEAVE and evnt != EVENT_PLAYER_CHAT_FILTER and not evnt.isKeyboardEvent() and not evnt.isMouseEvent() error("registering handleid: " + eventId.toString() + " non-playerunitevent. Except EVENT_UNIT_DAMAGED and EVENT_PLAYER_LEAVE these are not supported right now.") if evnt.isMouseEvent() and not EventListener.useMouseEvents EventListener.useMouseEvents = true for i = 0 to bj_MAX_PLAYERS - 1 keyTrig..registerPlayerEvent(players[i], EVENT_PLAYER_MOUSE_UP) ..registerPlayerEvent(players[i], EVENT_PLAYER_MOUSE_DOWN) ..registerPlayerEvent(players[i], EVENT_PLAYER_MOUSE_MOVE) return eventTypeCounter function registerEventsForUnit(unit u) if fireEvents(u) unitTrig.registerUnitEvent(u, EVENT_UNIT_DAMAGED) public function unregisterEventsForUnit(unit u) if fireEvents(u) let index = u.getIndex() unregisterEvents(index) if EventListener.castMapCasters[index] != null var listener = EventListener.castMapCasters[index] EventListener.castMapCasters[index] = null while listener != null let t = listener listener = listener.next destroy t public function unregisterEvents(int id) if id > 0 if EventListener.unitListenersFirsts[id] != null Log.trace("unregister unit has listeners. startid: " + id.toString()) var listener = EventListener.unitListenersFirsts[id] EventListener.unitListenersFirsts[id] = null while listener != null let t = listener listener = listener.next destroy t init // Un/Register Events when unit enters map onUnitIndex(() -> registerEventsForUnit(getIndexingUnit())) onUnitDeindex(() -> unregisterEventsForUnit(getIndexingUnit())) nullTimer() -> unitTrig.addAction(() -> EventListener.generalEventCallback()) leaveTrig.addAction(() -> EventListener.generalEventCallback()) keyTrig.addAction(() -> EventListener.generalEventCallback()) for i = 0 to bj_MAX_PLAYERS - 1 leaveTrig.registerPlayerEvent(players[i], EVENT_PLAYER_LEAVE) keyTrig..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_DOWN_DOWN) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_DOWN_UP) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_UP_DOWN) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_UP_UP) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_LEFT_DOWN) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_LEFT_UP) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_RIGHT_DOWN) ..registerPlayerEvent(players[i], EVENT_PLAYER_ARROW_RIGHT_UP) ..registerPlayerEvent(players[i], EVENT_PLAYER_END_CINEMATIC) ..registerPlayerChatEvent(players[i], "", false) registerPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, null, () -> EventListener.onSpellEffect(), null)