Name | Type | is_array | initial_value |
CargoEvent | real | No | |
CargoTransportGroup | group | Yes | |
CargoTransportUnit | unit | Yes | |
CenterOfMap | location | No | |
CheckDeathList | integer | Yes | |
CheckDeathTimer | timer | No | |
DeathEvent | real | No | |
DetectRemoveAbility | abilcode | No | |
DetectTransformAbility | abilcode | No | |
IsUnitAlive | boolean | Yes | |
IsUnitBeingUnloaded | boolean | Yes | |
IsUnitNew | boolean | Yes | |
IsUnitPreplaced | boolean | Yes | |
IsUnitReincarnating | boolean | Yes | |
IsUnitRemoved | boolean | Yes | |
IsUnitTransforming | boolean | Yes | |
KillerOfUnit | unit | Yes | |
SummonerOfUnit | unit | Yes | |
UDex | integer | No | |
UDexLastRecycled | integer | No | |
UDexMax | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexUnits | unit | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitTypeEvent | real | No | |
UnitTypeOf | unitcode | Yes | |
WorldMaxX | real | No | |
WorldMaxY | real | No |
library SmartTrack requires RegisterPlayerUnitEvent optional TimerUtils
/*
SmartTrack v1.05.1
By Spellbound
==== Description ====
SmartTrack will track when a unit is ordered to right-click on another unit. For this to
happen, the 'Tracker' will have to first be identified with CreateSmartOrderTracker, which
takes the unit itself (usually a building) and the range with which it will detect a unit
that right-clicked it.
SmartTrack can thus be used in conjunction with other systems like a custom waygate system
or docking system.
==== Requirements ====
RegisterPlayerUnitEvent (by Bannar)
https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
Any custom value unit indexer such as:
Unit Event: https://www.hiveworkshop.com/threads/gui-unit-event-v2-5-0-0.201641/ or
UnitDex: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
Optionally uses TimerUtils for the SmartTrack.stop for the interruption boolean.
==== API ====
function GetSmartUnit() returns the unit that did the right-clicking
function GetTracker() is the building/unit that tracks right-clickers.
function CreateSmartOrderTracker takes unit source, real range returns nothing
^ this sets up a unit as a Tracker that will fire an event whenever another unit that
has right-clicked on it comes into range.
function CancelSmartOrderTracker takes unit source returns nothing
^ this undoes the setup. Call this function when the unit dies or is de-indexed.
function RegisterNativeEvent takes whichEvent, function code returns nothing
^ this uses RegisterNativeEvent's event-creation to register SmartTrack's many events. They
are as followed:
EVENT_SMART_TRACK_IN_RANGE ---- will detect when a unit is in range. Range is determined by
the function CreateSmartOrderTracker()
EVENT_SMART_TRACK_STARTED ----- will fire when tracking has begun. If a unit is already in
range it will fire first, then EVENT_SMART_TRACK_IN_RANGE will fire.
EVENT_SMART_TRACK_TERMINATED -- will fire when tracking has ended, either from cancellation
or a SmartUnit has come within range of its Tracker.
function RegisterIndexNativeEvent takes integer playerNumber, integer whichEvent, function code returns nothing
^ Same as above, but per-player. If you wish SmartTrack to only work for specific players,
use this instead.
SmartTrack.stop( takes unit smarty, boolean interruptOrders )
^ call this method when you want to prevent units from carrying out the right-click
on EVENT_SMART_TRACK_STARTED event. This is useful for when you want to filter out units for
whatever reasons. For example, if you are using DockingSystem you'll want to prevent
some units from docking into the building. You can prevent that from happening by
calling the above method.
smarty is the unit you wish to interrupt
interruptOrders will tell the system whether you want the unit to stop moving.
*/
globals
constant integer ORDER_SMART = 851971
private unit eventSmarty = null
private unit eventTracker = null
private boolean array stopTracking
public boolean ignoreOrders = false // This boolean is used by other libraries that have SmartTrack as a dependency.
integer EVENT_SMART_TRACK_IN_RANGE
integer EVENT_SMART_TRACK_STARTED
integer EVENT_SMART_TRACK_TERMINATED
private hashtable TRIGGER_TRACK = InitHashtable()
private hashtable TimerHash
private trigger SmartyTrack = CreateTrigger()
private integer TrackerCount = 0
private integer MaxSmarties = 0
private integer array SmartySlot
private unit array Smarty
private unit array Tracker
private real array TrackerRange
private trigger array TrackerTrig
private trigger array EventTrig
public boolean array IsATracker
endglobals
native UnitAlive takes unit u returns boolean
// Getter function
function GetSmartUnit takes nothing returns unit
return eventSmarty
endfunction
function GetTracker takes nothing returns unit
return eventTracker
endfunction
// This is the function that controls what trigger is run.
private function FireEvent takes unit tracker, unit u, integer ev returns nothing
local integer playerId = GetPlayerId(GetOwningPlayer(u))
local integer id = GetUnitUserData(u)
local unit prevSmarty = eventSmarty
local unit prevTracker = eventTracker
// If SmartTrack.stop is called, stopTracking[id] will be set to true to prevent custom events from firing.
if not stopTracking[id] then
set eventSmarty = u
set eventTracker = tracker
call TriggerEvaluate(GetNativeEventTrigger(ev))
if IsNativeEventRegistered(playerId, ev) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, ev))
endif
set eventSmarty = prevSmarty
set eventTracker = prevTracker
endif
set prevSmarty = null
set prevTracker = null
endfunction
// This function handles removing right-clickers from the Smarty[] list. It checks where the
// right-clicker is on the list and then move all elements above it in the list down by 1.
private function RemoveSmarty takes unit u returns nothing
local integer id = GetUnitUserData(u)
local integer i = SmartySlot[id]
local unit tracker = Tracker[id]
if i != 0 then
set Tracker[id] = null
set SmartySlot[id] = 0
loop
set Smarty[i] = Smarty[i + 1]
set SmartySlot[GetUnitUserData(Smarty[i])] = i
set i = i + 1
exitwhen i > MaxSmarties
endloop
set MaxSmarties = MaxSmarties - 1
call FireEvent(tracker, u, EVENT_SMART_TRACK_TERMINATED)
endif
set tracker = null
endfunction
// This function fires when a unit comes in range of a tracker. It goes through the Smart[] list
// and if the unit correspond to a Smarty in the list as well as if the tracker is the one the
// unit had right-clicked on, a EVENT_SMART_TRACK_IN_RANGE event will fire.
private function FilterSmarties takes nothing returns boolean
local unit tracker = LoadUnitHandle(TRIGGER_TRACK, GetHandleId(GetTriggeringTrigger()), 1)
local unit u = GetTriggerUnit()
local integer idU = GetUnitUserData(u)
local integer i = 0
loop
set i = i + 1
exitwhen i > MaxSmarties
if u == Smarty[i] and Tracker[idU] == tracker then
// Events
call FireEvent(tracker, u, EVENT_SMART_TRACK_IN_RANGE)
call RemoveSmarty(u)
endif
endloop
set u = null
set tracker = null
return false
endfunction
// This is the main function that tracks all right-clicks on trackers.
private function TrackOrders takes nothing returns boolean
local unit smarty = GetTriggerUnit()
local unit tracker = GetOrderTargetUnit()
local integer idSmarty = GetUnitUserData(smarty)
local integer idTracker = GetUnitUserData(tracker)
if not ignoreOrders then
// if tracker is not null that means you have right-clicked on a unit.
if tracker != null and GetIssuedOrderId() == ORDER_SMART then
if IsATracker[idTracker] then
if Tracker[idSmarty] == null then // If the right-clicker has no tracker identified...
if IsUnitInRange(smarty, tracker, TrackerRange[idTracker]) then
// Events
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_IN_RANGE)
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_TERMINATED)
else
// If not in range, add right-clicker to the Smarty[] list.
set MaxSmarties = MaxSmarties + 1
set Smarty[MaxSmarties] = smarty
set SmartySlot[idSmarty] = MaxSmarties
set Tracker[idSmarty] = tracker
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
endif
else
// If the right-clicker was already being tracked by another tracker buy the
// right-clicked on another tracker, check if that new tracker is in range.
if IsUnitInRange(smarty, tracker, TrackerRange[idTracker]) then
call RemoveSmarty(smarty)
// Events
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_IN_RANGE)
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_TERMINATED)
else
set Tracker[idSmarty] = tracker
call FireEvent(tracker, smarty, EVENT_SMART_TRACK_STARTED)
endif
endif
endif
else
// if you had a tracker but did not right-click on a unit, remove from Smarty[] list.
if Tracker[idSmarty] != null then
call RemoveSmarty(smarty)
endif
endif
endif
set smarty = null
set tracker = null
set stopTracking[idSmarty] = false
return false
endfunction
// This function removes tracking from a unit so that right-clicking on it will not trigger any
// custom events from this library.
function CancelSmartOrderTracker takes unit source returns nothing
local integer id = GetUnitUserData(source)
local integer i
call FlushChildHashtable(TRIGGER_TRACK, GetHandleId(TrackerTrig[id]))
call DestroyTrigger(TrackerTrig[id])
set TrackerTrig[id] = null
set IsATracker[id] = false
set TrackerCount = TrackerCount - 1
if TrackerCount < 1 then
call DisableTrigger(SmartyTrack)
set i = 0
loop
set i = i + 1
exitwhen i > MaxSmarties
set Tracker[GetUnitUserData(Smarty[i])] = null
set Smarty[i] = null
endloop
set MaxSmarties = 0
endif
endfunction
// This function will identify a unit as a tracker as well as at what range will the custom
// EVENT_SMART_TRACK_IN_RANGE event will trigger.
function CreateSmartOrderTracker takes unit source, real range returns nothing
local integer id = GetUnitUserData(source)
if TrackerTrig[id] == null then
set TrackerTrig[id] = CreateTrigger()
else
// If TrackerTrig[id] already exists, it is destroyed and re-created rather than ignored
// to overwrite the old tracking. This is useful in the event that you wish to modify
// The range at which a unit tracks.
call DestroyTrigger(TrackerTrig[id])
set TrackerTrig[id] = CreateTrigger()
endif
call TriggerRegisterUnitInRange(TrackerTrig[id], source, range, null)
call TriggerAddCondition(TrackerTrig[id], function FilterSmarties)
call SaveUnitHandle(TRIGGER_TRACK, GetHandleId(TrackerTrig[id]), 1, source)
set TrackerCount = TrackerCount + 1
set TrackerRange[id] = range //necessary for units that right-click inside the tracker's range.
set IsATracker[id] = true
if not IsTriggerEnabled(SmartyTrack) then
call EnableTrigger(SmartyTrack)
endif
endfunction
// This struct exists solely for prevent a tracking from occuring. It comes with the option to
// force a unit to stop. It orders the unit in question to stop after a zero-second timer, a
// necessary workaround because issuing a stop or stunned command in a function triggered by
// an order event will not work.
struct SmartTrack
unit u
private static method zeroTimerStun takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this
static if LIBRARY_TimerUtils then
set this = GetTimerData(t)
call IssueImmediateOrderById(this.u, 851973) // order stunned
set this.u = null
call ReleaseTimer(t)
call this.deallocate()
else
call IssueImmediateOrderById(LoadUnitHandle(TimerHash, GetHandleId(t), 1), 851973) // order stunned
call FlushChildHashtable(TimerHash, GetHandleId(t))
call DestroyTimer(t)
endif
set t = null
endmethod
static method stop takes unit smarty, boolean interruptOrders returns nothing
local thistype this
local timer t
call RemoveSmarty(smarty)
set stopTracking[GetUnitUserData(smarty)] = true
if interruptOrders then
static if LIBRARY_TimerUtils then
set this = allocate()
set this.u = smarty
call TimerStart(NewTimerEx(this), 0., false, function thistype.zeroTimerStun)
else
set t = CreateTimer()
call SaveUnitHandle(TimerHash, GetHandleId(t), 1, smarty)
call TimerStart(t, 0., false, function thistype.zeroTimerStun)
set t = null
endif
endif
endmethod
endstruct
// This module sets up the various events the library uses. Uses RegisterEvent
// that fire when a unit comes within range of a tracker, or tracking has started or ended.
private module Init
private static method onInit takes nothing returns nothing
static if not LIBRARY_TimerUtils then
set TimerHash = InitHashtable()
endif
set EVENT_SMART_TRACK_IN_RANGE = CreateNativeEvent()
set EVENT_SMART_TRACK_STARTED = CreateNativeEvent()
set EVENT_SMART_TRACK_TERMINATED = CreateNativeEvent()
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function TrackOrders)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function TrackOrders)
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function TrackOrders)
endmethod
endmodule
private struct init
implement Init
endstruct
endlibrary
scope onSmartTrack initializer init
/*
GetSmartUnit() == Unit that enters within range of the tracker
GetTracker() == The tracker
*/
private function Actions_inRange takes nothing returns boolean
local unit smarty = GetSmartUnit()
local unit tracker = GetTracker()
local real life = GetUnitState(smarty, UNIT_STATE_LIFE)
//Hunter's Hall
if GetUnitTypeId(tracker) == 'edob' then
if GetUnitTypeId(smarty) == 'earc' then //Archers
//Do a passive transformation
call UnitAddAbility(smarty, 'pt00')
call UnitRemoveAbility(smarty, 'pt00')
elseif GetUnitTypeId(smarty) == 'esen' then //Huntresses
//Do a passive transformation
call UnitAddAbility(smarty, 'pt01')
call UnitRemoveAbility(smarty, 'pt01')
endif
//Arcane Tower
elseif GetUnitTypeId(tracker) == 'hatw' then
if life < GetUnitState(smarty, UNIT_STATE_MAX_LIFE) then
call SetUnitState(smarty, UNIT_STATE_LIFE, GetUnitState(smarty, UNIT_STATE_LIFE) + 50.)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdl", smarty, "chest"))
else
call BJDebugMsg("This unit is already at full health")
endif
//Orc Burrow
elseif GetUnitTypeId(tracker) == 'otrb' then
call SetUnitState(smarty, UNIT_STATE_LIFE, life - 50.)
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Other\\Stampede\\StampedeMissileDeath.mdl", smarty, "chest"))
endif
set smarty = null
set tracker = null
return false
endfunction
private function Actions_started takes nothing returns boolean
local unit smarty = GetSmartUnit()
local unit tracker = GetTracker()
//Hunter's Hall - filter example
if GetUnitTypeId(tracker) == 'edob' then
if not (GetUnitTypeId(smarty) == 'earc' or GetUnitTypeId(smarty) == 'esen') then //Archers or Huntresses
call SmartTrack.stop(smarty, true)
call BJDebugMsg("Only Archers or Huntresses allowed")
endif
endif
set smarty = null
set tracker = null
return false
endfunction
private function Actions_terminated takes nothing returns boolean
call BJDebugMsg("Tracking terminated")
return false
endfunction
private function init takes nothing returns nothing
call RegisterNativeEvent(EVENT_SMART_TRACK_IN_RANGE, function Actions_inRange)
call RegisterNativeEvent(EVENT_SMART_TRACK_STARTED, function Actions_started)
call RegisterNativeEvent(EVENT_SMART_TRACK_TERMINATED, function Actions_terminated)
endfunction
endscope
/*****************************************************************************
*
* RegisterNativeEvent v1.1.1.2
* by Bannar
*
* Storage of trigger handles for native events.
*
******************************************************************************
*
* Optional requirements:
*
* Table by Bribe
* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
*
******************************************************************************
*
* Important:
*
* Avoid using TriggerSleepAction within functions registered.
* Destroy native event trigger on your own responsibility.
*
******************************************************************************
*
* Core:
*
* function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
* Whether index whichIndex has already been attached to event whichEvent.
*
* function RegisterNativeEventTrigger takes integer whichIndex, integer eventId returns boolean
* Registers whichIndex within whichEvent scope and assigns new trigger handle for it.
*
* function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent specific to provided index whichIndex.
*
* function GetNativeEventTrigger takes integer whichEvent returns trigger
* Retrieves trigger handle for event whichEvent.
*
*
* Custom events:
*
* function CreateNativeEvent takes nothing returns integer
* Returns unique id for new event and registers it with RegisterNativeEvent.
*
* function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
* Registers new event handler func for event whichEvent specific to index whichIndex.
*
* function RegisterNativeEvent takes integer whichEvent, code func returns nothing
* Registers new event handler func for specified event whichEvent.
*
*****************************************************************************/
library RegisterNativeEvent uses optional Table
globals
private integer eventIndex = 500 // 0-499 reserved for Blizzard native events
endglobals
private module NativeEventInit
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set table = TableArray[0x2000]
endif
endmethod
endmodule
private struct NativeEvent extends array
static if LIBRARY_Table then
static TableArray table
else
static hashtable table = InitHashtable()
endif
implement NativeEventInit
endstruct
function IsNativeEventRegistered takes integer whichIndex, integer whichEvent returns boolean
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger.has(whichIndex)
else
return HaveSavedHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function RegisterNativeEventTrigger takes integer whichIndex, integer whichEvent returns boolean
if not IsNativeEventRegistered(whichIndex, whichEvent) then
static if LIBRARY_Table then
set NativeEvent.table[whichEvent].trigger[whichIndex] = CreateTrigger()
else
call SaveTriggerHandle(NativeEvent.table, whichEvent, whichIndex, CreateTrigger())
endif
return true
endif
return false
endfunction
function GetIndexNativeEventTrigger takes integer whichIndex, integer whichEvent returns trigger
static if LIBRARY_Table then
return NativeEvent.table[whichEvent].trigger[whichIndex]
else
return LoadTriggerHandle(NativeEvent.table, whichEvent, whichIndex)
endif
endfunction
function GetNativeEventTrigger takes integer whichEvent returns trigger
return GetIndexNativeEventTrigger(bj_MAX_PLAYER_SLOTS, whichEvent)
endfunction
function CreateNativeEvent takes nothing returns integer
local integer eventId = eventIndex
call RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId)
set eventIndex = eventIndex + 1
return eventId
endfunction
function RegisterIndexNativeEvent takes integer whichIndex, integer whichEvent, code func returns nothing
call RegisterNativeEventTrigger(whichIndex, whichEvent)
call TriggerAddCondition(GetIndexNativeEventTrigger(whichIndex, whichEvent), Condition(func))
endfunction
function RegisterNativeEvent takes integer whichEvent, code func returns nothing
call RegisterIndexNativeEvent(bj_MAX_PLAYER_SLOTS, whichEvent, func)
endfunction
endlibrary
/*****************************************************************************
*
* RegisterPlayerUnitEvent v1.0.3.2
* by Bannar
*
* Register version of TriggerRegisterPlayerUnitEvent.
*
* Special thanks to Magtheridon96, Bribe, azlier and BBQ for the original library version.
*
******************************************************************************
*
* Requirements:
*
* RegisterNativeEvent by Bannar
* hiveworkshop.com/threads/snippet-registerevent-pack.250266/
*
******************************************************************************
*
* Functions:
*
* function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent.
*
* function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
* Retrieves trigger handle for playerunitevent whichEvent specific to player whichPlayer.
*
* function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
* Registers generic playerunitevent whichEvent adding code func as callback.
*
* function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
* Registers playerunitevent whichEvent for player whichPlayer adding code func as callback.
*
*****************************************************************************/
library RegisterPlayerUnitEvent requires RegisterNativeEvent
function GetAnyPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
return GetNativeEventTrigger(GetHandleId(whichEvent))
endfunction
function GetPlayerUnitEventTrigger takes player whichPlayer, playerunitevent whichEvent returns trigger
return GetIndexNativeEventTrigger(GetPlayerId(whichPlayer), GetHandleId(whichEvent))
endfunction
function RegisterAnyPlayerUnitEvent takes playerunitevent whichEvent, code func returns nothing
local integer eventId = GetHandleId(whichEvent)
local integer index = 0
local trigger t = null
if RegisterNativeEventTrigger(bj_MAX_PLAYER_SLOTS, eventId) then
set t = GetNativeEventTrigger(eventId)
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), whichEvent, null)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
set t = null
endif
call RegisterNativeEvent(eventId, func)
endfunction
function RegisterPlayerUnitEvent takes player whichPlayer, playerunitevent whichEvent, code func returns nothing
local integer playerId = GetPlayerId(whichPlayer)
local integer eventId = GetHandleId(whichEvent)
if RegisterNativeEventTrigger(playerId, eventId) then
call TriggerRegisterPlayerUnitEvent(GetIndexNativeEventTrigger(playerId, eventId), whichPlayer, whichEvent, null)
endif
call RegisterIndexNativeEvent(playerId, eventId, func)
endfunction
endlibrary
library UnitDex uses optional WorldBounds, optional GroupUtils
/***************************************************************
*
* v1.2.1, by TriggerHappy
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex assigns every unit an unique integer. It attempts to make that number within the
* maximum array limit so you can associate it with one.
* _________________________________________________________________________
* 1. Installation
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Copy the script to your map, save it, then restart the editor and comment out the line below.
*/
///! external ObjectMerger w3a Adef uDex anam "Detect Leave" ansf "(UnitDex)" aart "" acat "" arac 0
/* ________________________________________________________________________
* 2. Configuration
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
private module UnitDexConfig
// The raw code of the leave detection ability.
static constant integer DETECT_LEAVE_ABILITY = 'uDex'
// Allow debug messages (debug mode must also be on)
static constant boolean ALLOW_DEBUGGING = true
// Uncomment the lines below to define a filter for units being indexed
/*static method onFilter takes unit u returns boolean
return true
endmethod*/
endmodule
/* _________________________________________________________________________
* 3. Function API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* Every function inlines except for UnitDexRemove
*
* function GetUnitId takes unit whichUnit returns integer
* function GetUnitById takes integer index returns unit
*
* function UnitDexEnable takes boolean flag returns nothing
* function UnitDexRemove takes unit u, boolean runEvents returns boolean
*
* function IsUnitIndexed takes unit u returns boolean
* function IsIndexingEnabled takes nothing returns boolean
*
* function GetIndexedUnit takes nothing returns unit
* function GetIndexedUnitId takes nothing returns integer
*
* function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns indexevent
* function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
* function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
*
* function OnUnitIndex takes code func returns triggercondition
* function OnUnitDeidex takes code func returns triggercondition
* _________________________________________________________________________
* 4. Struct API
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* UnitDex.Enabled = false // toggle the indexer
* UnitDex.Initialized // returns true if the preload timer has finished
* UnitDex.Count // returns the amount of units indexed
* UnitDex.Unit[0] // access the UnitDex array directly
* UnitDex.Group // a unit group containing every indexed unit (for enumeration)
* UnitDex.LastIndex // returns the last indexed unit's id
* _________________________________________________________________________
* 5. Public Variables
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* These are to be used with the "eventtype" argument of the event API:
*
* constant integer EVENT_UNIT_INDEX = 0
* constant integer EVENT_UNIT_DEINDEX = 1
* _________________________________________________________________________
* 6. Examples
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Associate a unit with a variable
*
* set UnitFlag[GetUnitId(yourUnit)] = true
*
* 2. Allocate a struct instance using a units assigned ID
*
* local somestruct data = GetUnitId(yourUnit)
*
* 3. Detect when a unit leaves the map
*
* function Exit takes nothing returns nothing
* call BJDebugMsg("The unit " + GetUnitName(GetIndexedUnit()) + " has left the map.")
* endfunction
*
* call OnUnitDeindex(function Exit)
* // or
* call RegisterUnitIndexEvent(Filter(function Exit), EVENT_UNIT_DEINDEX)
*
*
* _________________________________________________________________________
* 7. How it works
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* 1. Enumerate all preplaced units
* 2. TriggerRegisterEnterRegion native to detect when a unit enters the map
* 3. Assign each unit that enters the map a unique integer
* 4. Give every unit an ability based off of defend. There is no native to accurately
* detect when a unit leaves the map but when a unit dies or is removed from the game
* they are issued the "undefend" order
* 5. Catch the "undefend" order and remove unit from the queue
* _________________________________________________________________________
* 8. Notes
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
* - This system is compatable with GUI because it utilizes UnitUserData (custom values for units).
* - The object merger line should be commented out after saving and restarting.
* - All public functions are inlined except UnitDexRemove.
*
* -http://www.hiveworkshop.com/forums/submissions-414/unitdex-lightweight-unit-indexer-248209/
*
* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*/
globals
// Event types
constant integer EVENT_UNIT_INDEX = 0
constant integer EVENT_UNIT_DEINDEX = 1
// System variables
private trigger array IndexTrig
private integer Index = 0
private real E=-1
private boolexpr FilterEnter
endglobals
function GetUnitId takes unit whichUnit returns integer
return GetUnitUserData(whichUnit)
endfunction
function GetUnitById takes integer index returns unit
return UnitDex.Unit[index]
endfunction
function GetIndexedUnit takes nothing returns unit
return UnitDex.Unit[UnitDex.LastIndex]
endfunction
function GetIndexedUnitId takes nothing returns integer
return UnitDex.LastIndex
endfunction
function IsUnitIndexed takes unit u returns boolean
return (GetUnitById(GetUnitId(u)) != null)
endfunction
function UnitDexEnable takes boolean flag returns nothing
set UnitDex.Enabled = flag
endfunction
function IsIndexingEnabled takes nothing returns boolean
return UnitDex.Enabled
endfunction
function RegisterUnitIndexEvent takes boolexpr func, integer eventtype returns triggercondition
return TriggerAddCondition(IndexTrig[eventtype], func)
endfunction
function RemoveUnitIndexEvent takes triggercondition c, integer eventtype returns nothing
call TriggerRemoveCondition(IndexTrig[eventtype], c)
endfunction
function TriggerRegisterUnitIndexEvent takes trigger t, integer eventtype returns nothing
call TriggerRegisterVariableEvent(t, SCOPE_PRIVATE + "E", EQUAL, eventtype)
endfunction
function OnUnitIndex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_INDEX], Filter(func))
endfunction
function OnUnitDeindex takes code func returns triggercondition
return TriggerAddCondition(IndexTrig[EVENT_UNIT_DEINDEX], Filter(func))
endfunction
function UnitDexRemove takes unit u, boolean runEvents returns boolean
return UnitDex.Remove.evaluate(u, runEvents)
endfunction
/****************************************************************/
private keyword UnitDexCore
struct UnitDex extends array
static boolean Enabled = true
readonly static integer LastIndex
readonly static boolean Initialized=false
readonly static group Group=CreateGroup()
readonly static unit array Unit
readonly static integer Count = 0
readonly static integer array List
implement UnitDexConfig
private static integer Counter = 0
implement UnitDexCore
endstruct
/****************************************************************/
private module UnitDexCore
static method Remove takes unit u, boolean runEvents returns boolean
local integer i
if (IsUnitIndexed(u)) then
set i = GetUnitId(u)
set UnitDex.List[i] = Index
set Index = i
call GroupRemoveUnit(UnitDex.Group, u)
call SetUnitUserData(u, 0)
if (runEvents) then
set UnitDex.LastIndex = i
set E = EVENT_UNIT_DEINDEX
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
set E = -1
endif
set UnitDex.Unit[i] = null
set UnitDex.Count = UnitDex.Count - 1
return true
endif
return false
endmethod
private static method onGameStart takes nothing returns nothing
local integer i = 0
static if (not LIBRARY_GroupUtils) then // Check if GroupUtils exists so we can use it's enumeration group.
local group ENUM_GROUP = CreateGroup() // If not, create the group.
endif
// Index preplaced units
loop
call GroupEnumUnitsOfPlayer(ENUM_GROUP, Player(i), FilterEnter)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
static if (not LIBRARY_GroupUtils) then
call DestroyGroup(ENUM_GROUP)
set ENUM_GROUP = null
endif
// run init triggers
set i = 1
loop
exitwhen i > Counter
set LastIndex = i
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
set E = EVENT_UNIT_INDEX
set E = -1
set i = i + 1
endloop
set LastIndex = Counter
set Initialized = true
set FilterEnter = null
call DestroyTimer(GetExpiredTimer())
endmethod
private static method onEnter takes nothing returns boolean
local unit u = GetFilterUnit()
local integer i = GetUnitId(u)
local integer t = Index
if (i == 0 and thistype.Enabled) then
// If a filter was defined pass the unit through it.
static if (thistype.onFilter.exists) then
if (not thistype.onFilter(u)) then
set u = null
return false // check failed
endif
endif
// Handle debugging
static if (thistype.DEBUG_MODE and thistype.ALLOW_DEBUGGING) then
if (t == 0 and Counter+1 >= JASS_MAX_ARRAY_SIZE) then
call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "UnitDex: Maximum number of units reached!")
set u = null
return false
endif
endif
// Add to group of indexed units
call GroupAddUnit(thistype.Group, u)
// Give unit the leave detection ability
call UnitAddAbility(u, thistype.DETECT_LEAVE_ABILITY)
call UnitMakeAbilityPermanent(u, true, thistype.DETECT_LEAVE_ABILITY)
// Allocate index
if (Index != 0) then
set Index = List[t]
else
set Counter = Counter + 1
set t = Counter
endif
set List[t] = -1
set LastIndex = t
set thistype.Unit[t] = u
set Count = Count + 1
// Store the index
call SetUnitUserData(u, t)
if (thistype.Initialized) then
// Execute custom events registered with RegisterUnitIndexEvent
call TriggerEvaluate(IndexTrig[EVENT_UNIT_INDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_INDEX
// Reset so the event can occur again
set E = -1
endif
endif
set u = null
return false
endmethod
private static method onLeave takes nothing returns boolean
local unit u
local integer i
// Check if order is undefend.
if (thistype.Enabled and GetIssuedOrderId() == 852056) then
set u = GetTriggerUnit()
// If unit was killed (not removed) then don't continue
if (GetUnitAbilityLevel(u, thistype.DETECT_LEAVE_ABILITY) != 0) then
set u = null
return false
endif
set i = GetUnitId(u)
// If unit has been indexed then deindex it
if (i > 0 and i <= Counter and u == GetUnitById(i)) then
// Recycle the index
set List[i] = Index
set Index = i
set LastIndex = i
// Remove to group of indexed units
call GroupRemoveUnit(thistype.Group, u)
// Execute custom events without any associated triggers
call TriggerEvaluate(IndexTrig[EVENT_UNIT_DEINDEX])
// Handle TriggerRegisterUnitIndexEvent
set E = EVENT_UNIT_DEINDEX
// Remove entry
call SetUnitUserData(u, 0)
set thistype.Unit[i] = null
// Decrement unit count
set Count = Count - 1
// Reset so the event can occur again
set E = -1
endif
set u = null
endif
return false
endmethod
private static method onInit takes nothing returns nothing
local trigger t = CreateTrigger()
local integer i = 0
local player p
local unit u
static if (not LIBRARY_WorldBounds) then // Check if WorldBounts exists, if not then define the necessary vars
local region reg = CreateRegion() // If WorldBounds wasn't found, create the region manually
local rect world = GetWorldBounds()
endif
set FilterEnter = Filter(function thistype.onEnter)
// Begin to index units when they enter the map
static if (LIBRARY_WorldBounds) then
call TriggerRegisterEnterRegion(CreateTrigger(), WorldBounds.worldRegion, FilterEnter)
else
call RegionAddRect(reg, world)
call TriggerRegisterEnterRegion(CreateTrigger(), reg, FilterEnter)
call RemoveRect(world)
set world = null
endif
call TriggerAddCondition(t, Filter(function thistype.onLeave))
set IndexTrig[EVENT_UNIT_INDEX] = CreateTrigger()
set IndexTrig[EVENT_UNIT_DEINDEX] = CreateTrigger()
loop
set p = Player(i)
// Detect "undefend"
call TriggerRegisterPlayerUnitEvent(t, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
// Hide the detect ability from players
call SetPlayerAbilityAvailable(p, thistype.DETECT_LEAVE_ABILITY, false)
set i = i + 1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call TimerStart(CreateTimer(), 0, false, function thistype.onGameStart)
endmethod
endmodule
endlibrary
scope OnIndex
private function onIndex takes nothing returns nothing
local unit u = GetIndexedUnit()
if GetUnitTypeId(u) == 'hatw' then
call CreateSmartOrderTracker(u, 250.)
elseif GetUnitTypeId(u) == 'edob' then
call CreateSmartOrderTracker(u, 300.)
elseif GetUnitTypeId(u) == 'otrb' then
call CreateSmartOrderTracker(u, 175.)
endif
set u = null
endfunction
private module Init
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Filter(function onIndex), EVENT_UNIT_INDEX)
endmethod
endmodule
private struct init
implement Init
endstruct
endscope