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
/*****************************************************************************
*
* 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
library DockingSystem /*
DockingSystem v1.05.3
By Spellbound
Special thanks to MyPad for finding out all the bugs and errors and helping me make this system better.
/*/*==== DESCRIPTION ====*/*/
DockingSystem intends to replicate the Undead Haunted Gold Mine system by allowing you to
'dock' units (aka plugs) into a stationary unit, usually a building. The system comes with
the ability to move docking ports (aka sockets) so if you want to dock units to another
unit that's in motion, you may do that as well. This system comes with a lot of features
so take a moment and read through the API.
/*/*==== INSTALLATION ====*/*/
• Copy-paste this trigger into your map.
• Copy-paste the required libraries into your map.
• The system is installed! Copy-paste the onDock Event trigger or make your own
event trigger and start coding.
*/ requires /*
*/ SmartTrack /* https://www.hiveworkshop.com/threads/smarttrack-v1-02-1.299957/
*/ Table /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/
NB: SmartTrack requires a unit indexer that uses custom value. Either TriggerHappy's UnitDex or
Bribe's GUI Unit Event/Unit Indexer is recommended.
Additionally, if you wish to dock units at different heights, you will need an Autofly
library to allow ground units from changing height freely. The Autofly library in the
DockingSystem demo map has been modified to work with UnitDex.
*/ optional /*
*/ WorldBounds /* https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
*/ ListT /* https://www.hiveworkshop.com/threads/containers-list-t.249011/
WorldBounds is really not necessary at all, but if you have it in your map (which you generally
should), the constant MAX_DISTANCE can use its variabled to calculate the longest map distance.
/*/*==== API ====*/*/
/*SETUP and MODIFICATION________*/
DockingSystem.createStation( takes unit station, real socket_trigger_distance )
This sets up a unit as the station that will then be given sockets for units to 'plug'
into. Sockets are added separately.
DockingSystem.terminateStation( takes unit station )
This terminates a station instance. Doing do will destroy all added sockets and undock
any docked units.
DockingSystem.addSocket( takes unit station, real x, real y, real z, real facing_angle )
Creates a socket and adds it to the desires station. Sockets can have any coordinates
you desire them to have so they can be where ever you want them to be. The last argument,
facing_angle, will determine the facing of the plug when it docks.
IMPORTANT: This method will return the socket instance. If you wish to refer to this
specific socket at a later date (eg: if you want to move it) you can do so by storing
the instance in an integer variable.
DockingSystem.removeSocket( takes Socket socket_instance )
Removes a socket from a station. Socket instances are obtained upon addSocket.
DockingSystem.moveSocket( takes Socket socket_instance, real x, real y, real z, real facing_angle )
Alter the parameters of a socket. If a unit is currently docked in that socket, it will
move with it. Socket instances are obtained upon addSocket.
DockingSystem.addSocketPointer( takes unit pointer, real range, boolean kill_pointer, Socket socket_instance )
Creates a unit that acts as a pointer to a specific socket. When units dock into a station
the chosen socket is done at random, but with a pointer you can tell the unit to dock into
a specific socket. Pointers are proxies for the station itself with their own docking range,
so placing one too far from the station will defacto teleport plugs around. If the
socket_isntance is null, then they will teleport units to random open sockets. Socket
instances are obtained upon addSocket.
DockinSystem.dock( takes unit plug, Socket socket_instance )
Docks a unit into an empty plug. If the plug is currently occupied, nothing will happen.
DockinSystem.undock( takes unit plug )
Undocks a unit from the station it is currently plugged into and fires an undocking event.
Useful for when you want to handle the undocking manually.
DockingSystem.undockAll( takes unit station )
Flushes all docked unit from a station. The sockets themselves remain intact.
RegisterNativeEvent( takes integer ev, code c )
Sets up events for docking. The available events are EVENT_ON_PRE_DOCK, EVENT_ON_DOCK and
EVENT_ON_UNDOCK. They should be fairly self-explanatory, but just in case I'm just
projecting, here's what they do:
EVENT_ON_PRE_DOCK == BEFORE the plug has moved into place.
EVENT_ON_DOCK == After the plug has moved into place.
EVENT_ON_UNDOCK == When docking ends.
EVENT_ON_REDOCK == When a docked unit right-clicks its current station.
Can be used to issue error messages or reset animations.
RegisterIndexNativeEvent( takes integer playerNumber, integer ev, code c )
Same as above, but player-specific.
/*PRE-DOCKING________*/
InterruptDocking()
call this on EVENT_ON_PRE_DOCK to stop docking from happening.
SetDockingManual()
call this on EVENT_ON_PRE_DOCK to stop automatic undocking triggers from being created.
This means that ordering plugs to move or to stop will no undock them, so you will have
to undock them manually.
/*UNDOCKING________*/
ResetUnitFlyHeight()
call this on EVENT_ON_UNDOCK to reset a unit's flying height to its default value.
/*REFERRENCE________*/
call GetPlug()
returns the plug during a docking event
call GetStation()
returns the station during a docking event
call GetRandomSocket( takes unit station )
returns the instance of a random socket.
call GetRandomSocketWithState( takes unit station, boolean is_socket_occupied )
returns the instance of a socket. If the boolean is true, returns an occupied socket. If
false, returns a vacant one.
call GetClosestSocketWithState( takes unit station, unit plug, boolean is_socket_occupied )
returns the station's socket that is nearest to the plug.
call GetNumberOfSockets( takes unit station )
returns the total number of sockets the station has.
call GetNumberOfOccupiedSockets( takes unit station )
returns the total number of a station's occupied sockets.
call GetNumberOfEmptySockets( takes unit station )
returns the total number of a station's vacant sockets.
call GetSocketPlug( takes Socket socket_instance )
returns the unit currently docked in that socket. If the socket is empty, returns null.
call GetSocketFromPlug( takes unit plug )
returns the socket in which a unit is plugged into.
call GetSocketX( takes Socket socket_instance )
returns the x co-ordinate of the socket.
call GetSocketY( takes Socket socket_instance )
returns the y co-ordinate of the socket.
call GetSocketZ( takes Socket socket_instance )
returns the z co-ordinate of the socket.
call GetSocketFacing( takes Socket socket_instance )
returns the facing angle of the socket in degrees.
call GetSocketAngleFromStation( takes Socket socket_instance, real offset)
returns the angle between the socket and the station in radians. This is useful for when
you want to rotate the socket around the station with DockinSystem.moveSocket(), for
example. The offset value is for when you want to make sure your sockets are referrencing
the actual center of the station model as some models tend to offset their center by -16
both on their x and y coordinate. If you station center appears off, try setting the offset
to 16 or modifying the collision size of the station unit in the object editor.
call GetStationAngleFromSocket( takes Socket socket_instance, real offset)
Same as above, but the returns the angle from the station to the socket.
*/
globals
private DockingSystem array DS_Instance
private integer array DS_SocketId
private integer array DS_PlugCount
private unit eventPlug = null
private unit eventStation = null
private boolean interruptDocking = false
private boolean manualDocking = false
integer EVENT_ON_PRE_DOCK
integer EVENT_ON_DOCK
integer EVENT_ON_UNDOCK
integer EVENT_ON_REDOCK
private real MAX_DISTANCE = 0.
endglobals
private struct Station
static Table s
endstruct
// picks a socket at random and check if it's occupied or not depending on the state of the boolean
// LookForOccupiedSocket. If that first random try doesn't return anything, cycle through the rest
// of the sockets.
function GetRandomSocketWithState takes unit station, boolean lookForOccupiedSocket returns Socket
local DockingSystem this = DS_Instance[GetUnitUserData(station)]
local integer i = GetRandomInt(1, this.maxSockets)
local integer iLoop = i
local boolean firstPass = false
local Socket s
set Station.s = this.tableInstance
set s = Station.s.integer[i]
if lookForOccupiedSocket then
if not (s.plug == null) then
return s
endif
else
if s.plug == null then
return s
endif
endif
// this loop starts from integer i. Since i is unlikely to be zero, the loop must cycle back
// to 1 to ensure all sockets are checked.
loop
set iLoop = iLoop + 1
if iLoop > this.maxSockets then
set firstPass = true // this tells the loop that all values above integer i have already been checked
set iLoop = 1 // this sets iLoop to the first socket and moves upwards from there.
endif
exitwhen (iLoop >= i and firstPass) // if this returns true, then all sockets have been checked.
set s = Station.s.integer[iLoop]
// (s.plug != null) returns true if the socket is occupied and false if empty.
// Eg: if true, and LookForOccupiedSocket is also true, then it returns s.
// If false, but LookForOccupiedSocket is true, the loop moves on to the next socket.
if (s.plug != null) == lookForOccupiedSocket then
return s
endif
endloop
return 0
endfunction
function GetRandomSocket takes unit station returns Socket
local DockingSystem this = DS_Instance[GetUnitUserData(station)]
local Socket s
set Station.s = this.tableInstance
set s = Station.s.integer[GetRandomInt(1, this.maxSockets)]
return s
endfunction
function GetSocketFromPlug takes unit u returns Socket
return DS_SocketId[GetUnitUserData(u)]
endfunction
// since the purpose of this is to only find which Socket is closest, there is no need to
// perform a costly square root operation since (x < y) is the same as (x*x < y*y).
private function GetDistanceSquared takes real ax, real ay, real bx, real by returns real
local real dx = bx - ax
local real dy = by - ay
return dx * dx + dy * dy
endfunction
// returns the Socket closest to the unit that's about to dock into a Station. This loop through all
// of a Station's sockets and, depending on whether you are looking for an occupied or unoccupied
// Socket, will return the one with the closest unit distance from the Plug.
function GetClosestSocketWithState takes unit station, unit plug, boolean lookForOccupiedSocket returns Socket
local DockingSystem this = DS_Instance[GetUnitUserData(station)]
local real tempDist
local real maxRange = MAX_DISTANCE
local real xPlug = GetUnitX(plug)
local real yPlug = GetUnitY(plug)
local integer i = 0
local Socket closestSocket = 0
local Socket s
static if LIBRARY_ListT then
local IntegerListItem node
local IntegerListItem nodeNext
if lookForOccupiedSocket then
set node = this.busySockets.first
else
set node = this.freeSockets.first
endif
loop
exitwhen node == 0
set s = node.data
set nodeNext = node.next
set tempDist = GetDistanceSquared(s.x, s.y, xPlug, yPlug)
if tempDist < maxRange then
set maxRange = tempDist
set closestSocket = s
endif
set node = nodeNext
endloop
else
set Station.s = this.tableInstance
loop
set i = i + 1
exitwhen i > this.maxSockets
set s = Station.s.integer[i]
if (s.plug == null) != lookForOccupiedSocket then
set tempDist = GetDistanceSquared(s.x, s.y, xPlug, yPlug)
if tempDist < maxRange then
set maxRange = tempDist
set closestSocket = s
endif
endif
endloop
endif
return closestSocket
endfunction
function GetNumberOfSockets takes unit station returns integer
return DS_Instance[GetUnitUserData(station)].maxSockets
endfunction
function GetNumberOfOccupiedSockets takes unit station returns integer
return DS_PlugCount[GetUnitUserData(station)]
endfunction
function GetNumberOfEmptySockets takes unit station returns integer
local integer id_S = GetUnitUserData(station)
return DS_Instance[id_S].maxSockets - DS_PlugCount[id_S]
endfunction
function GetPlug takes nothing returns unit
return eventPlug
endfunction
function GetStation takes nothing returns unit
return eventStation
endfunction
function GetSocketPlug takes Socket s returns unit
return s.plug
endfunction
function GetSocketX takes Socket s returns real
return s.x
endfunction
function GetSocketY takes Socket s returns real
return s.y
endfunction
function GetSocketZ takes Socket s returns real
return s.z
endfunction
function GetSocketFacing takes Socket s returns real
return s.facing
endfunction
function GetSocketAngleFromStation takes Socket s, real Offset returns real
return Atan2((GetUnitY(s.station) + Offset) - s.y, (GetUnitX(s.station) + Offset) - s.x)
endfunction
function GetStationAngleFromSocket takes Socket s, real Offset returns real
return Atan2(s.y - (GetUnitY(s.station) + Offset), s.x - (GetUnitX(s.station) + Offset))
endfunction
// Backward compatibility
function GetNumberOfSocketsWithState takes unit station, boolean occupied returns integer
if occupied then
return GetNumberOfOccupiedSockets(station)
endif
return GetNumberOfEmptySockets(station)
endfunction
// Pre-dock actions
// Units will not automatically un-dock when this is called on pre-dock.
function SetDockingManual takes nothing returns nothing
set manualDocking = true
endfunction
// This will interrupt docking when called on pre-dock
function InterruptDocking takes nothing returns nothing
set interruptDocking = true
endfunction
// This will interrupt docking when called on pre-dock
function ResetUnitFlyHeight takes unit u returns nothing
call SetUnitFlyHeight(u, GetUnitDefaultFlyHeight(u), 0.)
endfunction
struct Socket
real x
real y
real z
real facing
unit plug
unit station
unit socketPointer
boolean killSocketPointer
boolean ignoreOrder
integer slot
trigger trig
method destroy takes nothing returns nothing
if this.plug != null then
call DockingSystem.undock.evaluate(this.plug)
endif
set this.station = null
set this.slot = 0
if this.killSocketPointer then
call UnitApplyTimedLife(this.socketPointer, 'BTLF', .01)
set this.killSocketPointer = false
endif
if this.socketPointer != null then
set DS_SocketId[GetUnitUserData(this.socketPointer)] = 0
set this.socketPointer = null
endif
call this.deallocate()
endmethod
// Safety measure
static if LIBRARY_WorldBounds then
method secureBounds takes nothing returns nothing
if this.x > WorldBounds.maxX then
set this.x = WorldBounds.maxX
elseif this.x < WorldBounds.minX then
set this.x = WorldBounds.minX
endif
if this.y > WorldBounds.maxY then
set this.y = WorldBounds.maxY
elseif this.y < WorldBounds.minY then
set this.y = WorldBounds.minY
endif
endmethod
endif
endstruct
// Fires one of the four custom events of DockingSystem. These are registered with RegisterDockEvent above.
private function FireEvent takes unit plug, unit station, integer ev returns nothing
local integer playerId = GetPlayerId(GetOwningPlayer(plug))
local unit prevPlug = eventPlug
local unit prevStation = eventStation
set eventPlug = plug
set eventStation = station
call TriggerEvaluate(GetNativeEventTrigger(ev))
if IsNativeEventRegistered(playerId, ev) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, ev))
endif
set eventPlug = prevPlug
set eventStation = prevStation
set prevPlug = null
set prevStation = null
endfunction
struct DockingSystem
readonly Table tableInstance
readonly integer maxSockets
static if LIBRARY_ListT then
readonly IntegerList busySockets
readonly IntegerList freeSockets
endif
// stuns the unit after 0 seconds. Needed to prevent docked units from keeping on walking
// once they get in range of a station. Also used to stop plugs from walking into a building
// when they right-click on their current station.
private static method zeroTimerStun takes nothing returns nothing
local timer t = GetExpiredTimer()
local Socket s = GetTimerData(t)
set s.ignoreOrder = true
call IssueImmediateOrderById(s.plug, 851973)//order stunned
set s.ignoreOrder = false
call ReleaseTimer(t)
set t = null
endmethod
// core undocking method.
static method undock takes unit plug returns nothing
local integer id_P = GetUnitUserData(plug)
local Socket s = DS_SocketId[id_P]
local integer id_S
// if s is 0 that means the unit is not docked and thus nothing happens.
if s != 0 then
set id_S = GetUnitUserData(s.station)
set DS_PlugCount[id_S] = DS_PlugCount[id_S] - 1
call SetUnitPathing(plug, true)
set s.plug = null
call DestroyTrigger(s.trig)
set s.trig = null
static if LIBRARY_ListT then
call DS_Instance[id_S].busySockets.removeElem(s)
call DS_Instance[id_S].freeSockets.push(s)
endif
call FireEvent(plug, s.station, EVENT_ON_UNDOCK)
set DS_SocketId[id_P] = 0
endif
endmethod
// this function cycles through all of a station's sockets and, if occupied, undocks them.
static method undockAll takes unit station returns nothing
local integer id_S = GetUnitUserData(station)
local thistype this = DS_Instance[id_S]
local integer i
local Socket s
static if LIBRARY_ListT then
local IntegerListItem node = this.busySockets.first
local IntegerListItem nodeNext
endif
// if unit station is not an actual station or has no sockets, this does nothing.
if this != 0 then
static if LIBRARY_ListT then
loop
exitwhen node == 0
set nodeNext = node.next
set s = node.data
call DockingSystem.undock(s.plug)
set node = nodeNext
endloop
else
set Station.s = this.tableInstance
set i = this.maxSockets // goes from top to bottom.
loop
set s = Station.s.integer[i]
if s.plug != null then
call DockingSystem.undock(s.plug)
endif
set i = i - 1
exitwhen i <= 0
endloop
endif
endif
endmethod
// this function handles the order events of docked unit (aka plugs). Right-clicking on the
// unit the plug is currently docked at will do nothing - this is handled in dockPrep.
private static method trackOrders takes nothing returns nothing
local unit plug = GetTriggerUnit()
local unit station = GetOrderTargetUnit()
local Socket s = DS_SocketId[GetUnitUserData(plug)]
if not s.ignoreOrder then
if not (station == s.station and GetIssuedOrderId() == ORDER_SMART) then
call DockingSystem.undock(plug)
endif
endif
set plug = null
set station = null
endmethod
// core dock method.
static method dock takes unit plug, Socket s returns nothing
local integer id_S = GetUnitUserData(s.station)
call FireEvent(plug, s.station, EVENT_ON_PRE_DOCK)
if not interruptDocking then
call TimerStart(NewTimerEx(s), 0., false, function thistype.zeroTimerStun)
call SetUnitPathing(plug, false)
call SetUnitX(plug, s.x)
call SetUnitY(plug, s.y)
if GetUnitAbilityLevel(plug, 852155) > 0 then
call UnitAddAbility(plug, 852155)
call UnitRemoveAbility(plug, 852155)
endif
call SetUnitFlyHeight(plug, s.z, 0.)
call SetUnitFacing(plug, s.facing)
set s.plug = plug
set DS_SocketId[GetUnitUserData(plug)] = s
if not manualDocking then
set s.trig = CreateTrigger()
call TriggerRegisterUnitEvent(s.trig, plug, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterUnitEvent(s.trig, plug, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(s.trig, plug, EVENT_UNIT_ISSUED_ORDER)
call TriggerAddCondition(s.trig, function thistype.trackOrders)
else
set manualDocking = false
endif
set DS_PlugCount[id_S] = DS_PlugCount[id_S] + 1
static if LIBRARY_ListT then
call DS_Instance[id_S].freeSockets.removeElem(s)
call DS_Instance[id_S].busySockets.push(s)
endif
call FireEvent(plug, s.station, EVENT_ON_DOCK)
endif
set interruptDocking = false
endmethod
// this function runs when a right-clicker is in range of a station.
static method dockPrep takes nothing returns nothing
local unit station = GetTracker()
local unit plug = GetSmartUnit()
local boolean firstPass = false
local boolean exitLoop = false
local thistype this
local Socket s = DS_SocketId[GetUnitUserData(plug)]
// if s has no value, then the unit is not docked anywhere.
if s == 0 then
set s = GetClosestSocketWithState(station, plug, false)
if s != 0 then
call DockingSystem.dock(plug, s)
endif
// if s has a value, then the unit is already docked somewhere.
else
// if the station you right-clicked is the same station the unit is docked at, issue a stunned order and fires a EVENT_ON_REDOCK event.
if s.station == station then
call TimerStart(NewTimerEx(s), 0., false, function thistype.zeroTimerStun)
call FireEvent(plug, s.station, EVENT_ON_REDOCK)
else // undock the plug and dock it in the other station
call DockingSystem.undock(plug)
set s = GetClosestSocketWithState(station, plug, false)
if s != 0 then
call DockingSystem.dock(plug, s)
endif
endif
endif
set station = null
set plug = null
endmethod
// removes a socket from a station. If the socket is occupied, the unit is first undocked.
static method removeSocket takes Socket s returns nothing
local thistype this = DS_Instance[GetUnitUserData(s.station)]
local integer i = s.slot
if this != 0 then
set Station.s = this.tableInstance
static if LIBRARY_ListT then
if s.plug == null then
call this.freeSockets.removeElem(s)
else
call this.busySockets.removeElem(s)
endif
endif
call s.destroy()
call Station.s.remove(i)
loop
set Station.s.integer[i] = Station.s.integer[i + 1]
set i = i + 1
exitwhen i >= this.maxSockets
endloop
set this.maxSockets = this.maxSockets - 1
endif
endmethod
// changes the x/y coordinates of a socket. If there a unit docked into it, the unit moves as well.
static method moveSocket takes Socket s, real x, real y, real z, real facing returns nothing
set s.x = x
set s.y = y
set s.z = z
set s.facing = facing
static if LIBRARY_WorldBounds then
call s.secureBounds()
endif
if s.plug != null then
//Move plug
call SetUnitX(s.plug, x)
call SetUnitY(s.plug, y)
call SetUnitFacing(s.plug, facing)
call SetUnitFlyHeight(s.plug, z, 0.)
endif
endmethod
// socket pointers are proxies that allow you to dock a unit to a specific socket. See documentation for more info.
static method addSocketPointer takes unit pointer, real range, boolean killPointer, Socket s returns nothing
if pointer != null then
set s.socketPointer = pointer
set s.killSocketPointer = killPointer
set DS_SocketId[GetUnitUserData(pointer)] = s
call CreateSmartOrderTracker(pointer, range)
endif
endmethod
// adds a socket to a station.
static method addSocket takes unit station, real x, real y, real z, real facingAngle returns Socket
local thistype this = DS_Instance[GetUnitUserData(station)]
local Socket s
// if the unit is not a Station this function does nothing.
if this != 0 then
set Station.s = this.tableInstance
set this.maxSockets = this.maxSockets + 1
set s = Socket.create()
set s.x = x
set s.y = y
set s.z = z
set s.facing = facingAngle
set s.station = station
set s.slot = this.maxSockets
set Station.s.integer[this.maxSockets] = s
static if LIBRARY_WorldBounds then
call s.secureBounds()
endif
static if LIBRARY_ListT then
call this.freeSockets.push(s)
endif
endif
return s
endmethod
// this terminates the Station instance bound to a unit. Will undock all currectly-docked units.
static method terminateStation takes unit station returns nothing
local integer id_S = GetUnitUserData(station)
local thistype this = DS_Instance[id_S]
local integer i
if this != 0 then
set Station.s = this.tableInstance
set i = this.maxSockets
loop
call removeSocket(Station.s.integer[i])
set i = i - 1
exitwhen i <= 0
endloop
set this.maxSockets = 0
call CancelSmartOrderTracker(station)
set DS_Instance[id_S] = 0
call Station.s.destroy()
static if LIBRARY_ListT then
call this.busySockets.destroy()
call this.freeSockets.destroy()
endif
call this.deallocate()
endif
endmethod
// establishes a unit as a Station. Sockets are added separately. Only 1 Station instance
// allowed per unit.
static method createStation takes unit station, real socketTriggerDistance returns nothing
local integer id_S = GetUnitUserData(station)
local thistype this = DS_Instance[id_S]
if this == 0 then
set Station.s = Table.create()
set this = allocate()
set this.tableInstance = Station.s
set this.maxSockets = 0
call CreateSmartOrderTracker(station, socketTriggerDistance)
set DS_Instance[id_S] = this
set DS_PlugCount[id_S] = 0
static if LIBRARY_ListT then
set this.freeSockets = IntegerList.create()
set this.busySockets = IntegerList.create()
endif
endif
endmethod
endstruct
// this function sets up all events related to DockingSystem. It also sets up SmartTrack to fire
// DockingSystem.dockPrep whenever a unit gets in range of a tracker.
private module DockingSystemInit
private static method onInit takes nothing returns nothing
static if LIBRARY_WorldBounds then
set MAX_DISTANCE = (WorldBounds.maxX - WorldBounds.minX)*(WorldBounds.maxX - WorldBounds.minX) + /*
*/ (WorldBounds.maxY - WorldBounds.minY)*(WorldBounds.maxY - WorldBounds.minY)
else
set MAX_DISTANCE = 999999. * 999999.
endif
set EVENT_ON_PRE_DOCK = CreateNativeEvent()
set EVENT_ON_DOCK = CreateNativeEvent()
set EVENT_ON_UNDOCK = CreateNativeEvent()
set EVENT_ON_REDOCK = CreateNativeEvent()
call RegisterNativeEvent(EVENT_SMART_TRACK_IN_RANGE, function DockingSystem.dockPrep)
endmethod
endmodule
private struct init
implement DockingSystemInit
endstruct
endlibrary
scope onDockEvent initializer init
globals
private effect array wispTail
private constant string WISP_TAIL_FX = "Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
endglobals
/*
GetPlug() == Unit that is docking into the station
GetStation() == The station with the docking ports
I recommend storing those in a local variable if they will be called more than once.
Do not forget to null the locals afterward to prevents leaks.
Examples can be seen down below with the functions Actions_onDock, Actions_onUndock and
Actions_onRedock.
*/
private function onTrackStart takes nothing returns boolean
/*
The following are examples of how to filter out units using SmartTrack events.
*/
local unit smarty = GetSmartUnit()
local unit tracker = GetTracker()
local integer smartyTypeId = GetUnitTypeId(smarty)
local integer trackerTypeId = GetUnitTypeId(tracker)
//Tauren Totem - Witch Doctors only
if trackerTypeId == 'otto' then
if smartyTypeId != 'odoc' then
call SmartTrack.stop(smarty, true)
call BJDebugMsg(" Witch Doctors only")
endif
endif
//Chimera Roost - Archers only
if trackerTypeId == 'edos' then
if smartyTypeId != 'earc' then
call SmartTrack.stop(smarty, false)
call BJDebugMsg(" Archers only")
endif
endif
//Spirit Tower - Wisps only
if trackerTypeId == 'uzg1' then
if smartyTypeId != 'ewsp' then
call SmartTrack.stop(smarty, false)
call BJDebugMsg(" Wisps only")
endif
endif
set smarty = null
set tracker = null
return false
endfunction
private function Actions_onPreDock takes nothing returns boolean
/*/*
You can call /* InterruptDocking() */ in this function to prevent docking from
happening.
You can also call /* SetDockingManual() */ to prevent automatic undocking triggers from
being created. For example, any orders that is NOT a right-click on the currently docked
station will undock the plug. With SetDockingManual() you will have to undock the unit
by youself. This can be useful if you wish to have docked units function as turrets, for
example. Give them an ability upon docking that will undock them when used.
*/*/
//Archer
if GetUnitTypeId(GetPlug()) == 'earc' then
call SetDockingManual()
endif
return false
endfunction
private function Actions_onDock takes nothing returns boolean
local unit plug = GetPlug()
local unit station = GetStation()
local integer plugTypeId = GetUnitTypeId(plug)
local integer stationTypeId = GetUnitTypeId(station)
local integer plugId
//Witch Doctor
if plugTypeId == 'odoc' then
call SetUnitAnimationWithRarity(plug, "stand victory", RARITY_FREQUENT)
//Archer
elseif plugTypeId == 'earc' then
call SetUnitPropWindow(plug, 0.)
call UnitAddAbility(plug, 'A000')
//Wisp
elseif plugTypeId == 'ewsp' then
set plugId = GetUnitUserData(plug)
if wispTail[plugId] == null then
set wispTail[plugId] = AddSpecialEffectTarget(WISP_TAIL_FX, plug, "chest")
endif
endif
//Chimera Roost
if stationTypeId == 'edos' then
if GetNumberOfOccupiedSockets(station) == 1 then
call UnitAddAbility(station, 'A001')
endif
endif
set plug = null
set station = null
return false
endfunction
private function Actions_onUndock takes nothing returns boolean
/*/*
You can call /* ResetUnitFlyHeight(plug) */ in this function to reset your undocking
unit's flying height to its default value.
*/*/
local unit plug = GetPlug()
local unit station = GetStation()
local integer plugTypeId = GetUnitTypeId(plug)
local integer stationTypeId = GetUnitTypeId(station)
local integer plugId
//Witch Doctor
if plugTypeId == 'odoc' then
if not UnitAlive(plug) then
call SetUnitAnimation(plug, "death")
else
call SetUnitAnimation(plug, "stand")
endif
//Archer
elseif plugTypeId == 'earc' then
call SetUnitPropWindow(plug, GetUnitDefaultPropWindow(plug))
call UnitRemoveAbility(plug, 'A000')
call SetUnitPosition(plug, GetUnitX(station) + Cos(270. * bj_DEGTORAD) * 150., /*
*/ GetUnitY(station) + Sin(270. * bj_DEGTORAD) * 150.)
call ResetUnitFlyHeight(plug)
//Wisp
elseif plugTypeId == 'ewsp' then
set plugId = GetUnitUserData(plug)
if wispTail[plugId] != null then
call DestroyEffect(wispTail[plugId])
set wispTail[plugId] = null
endif
call ResetUnitFlyHeight(plug)
endif
//Chimera Roost
if stationTypeId == 'edos' then
if GetNumberOfOccupiedSockets(station) == 0 then
call UnitRemoveAbility(station, 'A001')
endif
endif
set plug = null
set station = null
return false
endfunction
private function Actions_onRedock takes nothing returns boolean
local unit plug = GetPlug()
local unit station = GetStation()
local integer plugTypeId = GetUnitTypeId(plug)
//Witch Doctor
if plugTypeId == 'odoc' then
call SetUnitAnimation(plug, "stand")
call SetUnitAnimationWithRarity(plug, "stand victory", RARITY_FREQUENT)
//Wisp
elseif plugTypeId == 'ewsp' then
call BJDebugMsg("This wisp is already docked at this Spirit Tower")
endif
set plug = null
set station = null
return false
endfunction
private function init takes nothing returns nothing
call RegisterNativeEvent(EVENT_ON_PRE_DOCK, function Actions_onPreDock)
call RegisterNativeEvent(EVENT_ON_DOCK, function Actions_onDock)
call RegisterNativeEvent(EVENT_ON_UNDOCK, function Actions_onUndock)
call RegisterNativeEvent(EVENT_ON_REDOCK, function Actions_onRedock)
//Filter - use SmartTrack
call RegisterNativeEvent(EVENT_SMART_TRACK_STARTED, function onTrackStart)
endfunction
endscope
library Table /* made by Bribe, special thanks to Vexorian & Nestharus, version 4.1.0.1.
One map, one hashtable. Welcome to NewTable 4.1.0.1
This newest iteration of Table introduces the new HashTable struct.
You can now instantiate HashTables which enables the use of large
parent and large child keys, just like a standard hashtable. Previously,
the user would have to instantiate a Table to do this on their own which -
while doable - is something the user should not have to do if I can add it
to this resource myself (especially if they are inexperienced).
This library was originally called NewTable so it didn't conflict with
the API of Table by Vexorian. However, the damage is done and it's too
late to change the library name now. To help with damage control, I
have provided an extension library called TableBC, which bridges all
the functionality of Vexorian's Table except for 2-D string arrays &
the ".flush(integer)" method. I use ".flush()" to flush a child hash-
table, because I wanted the API in NewTable to reflect the API of real
hashtables (I thought this would be more intuitive).
API
------------
struct Table
| static method create takes nothing returns Table
| create a new Table
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush all stored values inside of it
|
| method remove takes integer key returns nothing
| remove the value at index "key"
|
| method operator []= takes integer key, $TYPE$ value returns nothing
| assign "value" to index "key"
|
| method operator [] takes integer key returns $TYPE$
| load the value at index "key"
|
| method has takes integer key returns boolean
| whether or not the key was assigned
|
----------------
struct TableArray
| static method operator [] takes integer array_size returns TableArray
| create a new array of Tables of size "array_size"
|
| method destroy takes nothing returns nothing
| destroy it
|
| method flush takes nothing returns nothing
| flush and destroy it
|
| method operator size takes nothing returns integer
| returns the size of the TableArray
|
| method operator [] takes integer key returns Table
| returns a Table accessible exclusively to index "key"
*/
globals
private integer less = 0 //Index generation for TableArrays (below 0).
private integer more = 8190 //Index generation for Tables.
//Configure it if you use more than 8190 "key" variables in your map (this will never happen though).
private hashtable ht = InitHashtable()
private key sizeK
private key listK
endglobals
private struct dex extends array
static method operator size takes nothing returns Table
return sizeK
endmethod
static method operator list takes nothing returns Table
return listK
endmethod
endstruct
private struct handles extends array
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private struct agents extends array
method operator []= takes integer key, agent value returns nothing
call SaveAgentHandle(ht, this, key, value)
endmethod
endstruct
//! textmacro NEW_ARRAY_BASIC takes SUPER, FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSaved$SUPER$(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSaved$SUPER$(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//! textmacro NEW_ARRAY takes FUNC, TYPE
private struct $TYPE$s extends array
method operator [] takes integer key returns $TYPE$
return Load$FUNC$Handle(ht, this, key)
endmethod
method operator []= takes integer key, $TYPE$ value returns nothing
call Save$FUNC$Handle(ht, this, key, value)
endmethod
method has takes integer key returns boolean
return HaveSavedHandle(ht, this, key)
endmethod
method remove takes integer key returns nothing
call RemoveSavedHandle(ht, this, key)
endmethod
endstruct
private module $TYPE$m
method operator $TYPE$ takes nothing returns $TYPE$s
return this
endmethod
endmodule
//! endtextmacro
//Run these textmacros to include the entire hashtable API as wrappers.
//Don't be intimidated by the number of macros - Vexorian's map optimizer is
//supposed to kill functions which inline (all of these functions inline).
//! runtextmacro NEW_ARRAY_BASIC("Real", "Real", "real")
//! runtextmacro NEW_ARRAY_BASIC("Boolean", "Boolean", "boolean")
//! runtextmacro NEW_ARRAY_BASIC("String", "Str", "string")
//New textmacro to allow table.integer[] syntax for compatibility with textmacros that might desire it.
//! runtextmacro NEW_ARRAY_BASIC("Integer", "Integer", "integer")
//! runtextmacro NEW_ARRAY("Player", "player")
//! runtextmacro NEW_ARRAY("Widget", "widget")
//! runtextmacro NEW_ARRAY("Destructable", "destructable")
//! runtextmacro NEW_ARRAY("Item", "item")
//! runtextmacro NEW_ARRAY("Unit", "unit")
//! runtextmacro NEW_ARRAY("Ability", "ability")
//! runtextmacro NEW_ARRAY("Timer", "timer")
//! runtextmacro NEW_ARRAY("Trigger", "trigger")
//! runtextmacro NEW_ARRAY("TriggerCondition", "triggercondition")
//! runtextmacro NEW_ARRAY("TriggerAction", "triggeraction")
//! runtextmacro NEW_ARRAY("TriggerEvent", "event")
//! runtextmacro NEW_ARRAY("Force", "force")
//! runtextmacro NEW_ARRAY("Group", "group")
//! runtextmacro NEW_ARRAY("Location", "location")
//! runtextmacro NEW_ARRAY("Rect", "rect")
//! runtextmacro NEW_ARRAY("BooleanExpr", "boolexpr")
//! runtextmacro NEW_ARRAY("Sound", "sound")
//! runtextmacro NEW_ARRAY("Effect", "effect")
//! runtextmacro NEW_ARRAY("UnitPool", "unitpool")
//! runtextmacro NEW_ARRAY("ItemPool", "itempool")
//! runtextmacro NEW_ARRAY("Quest", "quest")
//! runtextmacro NEW_ARRAY("QuestItem", "questitem")
//! runtextmacro NEW_ARRAY("DefeatCondition", "defeatcondition")
//! runtextmacro NEW_ARRAY("TimerDialog", "timerdialog")
//! runtextmacro NEW_ARRAY("Leaderboard", "leaderboard")
//! runtextmacro NEW_ARRAY("Multiboard", "multiboard")
//! runtextmacro NEW_ARRAY("MultiboardItem", "multiboarditem")
//! runtextmacro NEW_ARRAY("Trackable", "trackable")
//! runtextmacro NEW_ARRAY("Dialog", "dialog")
//! runtextmacro NEW_ARRAY("Button", "button")
//! runtextmacro NEW_ARRAY("TextTag", "texttag")
//! runtextmacro NEW_ARRAY("Lightning", "lightning")
//! runtextmacro NEW_ARRAY("Image", "image")
//! runtextmacro NEW_ARRAY("Ubersplat", "ubersplat")
//! runtextmacro NEW_ARRAY("Region", "region")
//! runtextmacro NEW_ARRAY("FogState", "fogstate")
//! runtextmacro NEW_ARRAY("FogModifier", "fogmodifier")
//! runtextmacro NEW_ARRAY("Hashtable", "hashtable")
struct Table extends array
// Implement modules for intuitive syntax (tb.handle; tb.unit; etc.)
implement realm
implement integerm
implement booleanm
implement stringm
implement playerm
implement widgetm
implement destructablem
implement itemm
implement unitm
implement abilitym
implement timerm
implement triggerm
implement triggerconditionm
implement triggeractionm
implement eventm
implement forcem
implement groupm
implement locationm
implement rectm
implement boolexprm
implement soundm
implement effectm
implement unitpoolm
implement itempoolm
implement questm
implement questitemm
implement defeatconditionm
implement timerdialogm
implement leaderboardm
implement multiboardm
implement multiboarditemm
implement trackablem
implement dialogm
implement buttonm
implement texttagm
implement lightningm
implement imagem
implement ubersplatm
implement regionm
implement fogstatem
implement fogmodifierm
implement hashtablem
method operator handle takes nothing returns handles
return this
endmethod
method operator agent takes nothing returns agents
return this
endmethod
//set this = tb[GetSpellAbilityId()]
method operator [] takes integer key returns Table
return LoadInteger(ht, this, key) //return this.integer[key]
endmethod
//set tb[389034] = 8192
method operator []= takes integer key, Table tb returns nothing
call SaveInteger(ht, this, key, tb) //set this.integer[key] = tb
endmethod
//set b = tb.has(2493223)
method has takes integer key returns boolean
return HaveSavedInteger(ht, this, key) //return this.integer.has(key)
endmethod
//call tb.remove(294080)
method remove takes integer key returns nothing
call RemoveSavedInteger(ht, this, key) //call this.integer.remove(key)
endmethod
//Remove all data from a Table instance
method flush takes nothing returns nothing
call FlushChildHashtable(ht, this)
endmethod
//local Table tb = Table.create()
static method create takes nothing returns Table
local Table this = dex.list[0]
if this == 0 then
set this = more + 1
set more = this
else
set dex.list[0] = dex.list[this]
call dex.list.remove(this) //Clear hashed memory
endif
debug set dex.list[this] = -1
return this
endmethod
// Removes all data from a Table instance and recycles its index.
//
// call tb.destroy()
//
method destroy takes nothing returns nothing
debug if dex.list[this] != -1 then
debug call BJDebugMsg("Table Error: Tried to double-free instance: " + I2S(this))
debug return
debug endif
call this.flush()
set dex.list[this] = dex.list[0]
set dex.list[0] = this
endmethod
//! runtextmacro optional TABLE_BC_METHODS()
endstruct
//! runtextmacro optional TABLE_BC_STRUCTS()
struct TableArray extends array
//Returns a new TableArray to do your bidding. Simply use:
//
// local TableArray ta = TableArray[array_size]
//
static method operator [] takes integer array_size returns TableArray
local Table tb = dex.size[array_size] //Get the unique recycle list for this array size
local TableArray this = tb[0] //The last-destroyed TableArray that had this array size
debug if array_size <= 0 then
debug call BJDebugMsg("TypeError: Invalid specified TableArray size: " + I2S(array_size))
debug return 0
debug endif
if this == 0 then
set this = less - array_size
set less = this
else
set tb[0] = tb[this] //Set the last destroyed to the last-last destroyed
call tb.remove(this) //Clear hashed memory
endif
set dex.size[this] = array_size //This remembers the array size
return this
endmethod
//Returns the size of the TableArray
method operator size takes nothing returns integer
return dex.size[this]
endmethod
//This magic method enables two-dimensional[array][syntax] for Tables,
//similar to the two-dimensional utility provided by hashtables them-
//selves.
//
//ta[integer a].unit[integer b] = unit u
//ta[integer a][integer c] = integer d
//
//Inline-friendly when not running in debug mode
//
method operator [] takes integer key returns Table
static if DEBUG_MODE then
local integer i = this.size
if i == 0 then
call BJDebugMsg("IndexError: Tried to get key from invalid TableArray instance: " + I2S(this))
return 0
elseif key < 0 or key >= i then
call BJDebugMsg("IndexError: Tried to get key [" + I2S(key) + "] from outside TableArray bounds: " + I2S(i))
return 0
endif
endif
return this + key
endmethod
//Destroys a TableArray without flushing it; I assume you call .flush()
//if you want it flushed too. This is a public method so that you don't
//have to loop through all TableArray indices to flush them if you don't
//need to (ie. if you were flushing all child-keys as you used them).
//
method destroy takes nothing returns nothing
local Table tb = dex.size[this.size]
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to destroy an invalid TableArray: " + I2S(this))
debug return
debug endif
if tb == 0 then
//Create a Table to index recycled instances with their array size
set tb = Table.create()
set dex.size[this.size] = tb
endif
call dex.size.remove(this) //Clear the array size from hash memory
set tb[this] = tb[0]
set tb[0] = this
endmethod
private static Table tempTable
private static integer tempEnd
//Avoids hitting the op limit
private static method clean takes nothing returns nothing
local Table tb = .tempTable
local integer end = tb + 0x1000
if end < .tempEnd then
set .tempTable = end
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
else
set end = .tempEnd
endif
loop
call tb.flush()
set tb = tb + 1
exitwhen tb == end
endloop
endmethod
//Flushes the TableArray and also destroys it. Doesn't get any more
//similar to the FlushParentHashtable native than this.
//
method flush takes nothing returns nothing
debug if this.size == 0 then
debug call BJDebugMsg("TypeError: Tried to flush an invalid TableArray instance: " + I2S(this))
debug return
debug endif
set .tempTable = this
set .tempEnd = this + this.size
call ForForce(bj_FORCE_PLAYER[0], function thistype.clean)
call this.destroy()
endmethod
endstruct
//NEW: Added in Table 4.0. A fairly simple struct but allows you to do more
//than that which was previously possible.
struct HashTable extends array
//Enables myHash[parentKey][childKey] syntax.
//Basically, it creates a Table in the place of the parent key if
//it didn't already get created earlier.
method operator [] takes integer index returns Table
local Table t = Table(this)[index]
if t == 0 then
set t = Table.create()
set Table(this)[index] = t //whoops! Forgot that line. I'm out of practice!
endif
return t
endmethod
//You need to call this on each parent key that you used if you
//intend to destroy the HashTable or simply no longer need that key.
method remove takes integer index returns nothing
local Table t = Table(this)[index]
if t != 0 then
call t.destroy()
call Table(this).remove(index)
endif
endmethod
//Added in version 4.1
method has takes integer index returns boolean
return Table(this).has(index)
endmethod
//HashTables are just fancy Table indices.
method destroy takes nothing returns nothing
call Table(this).destroy()
endmethod
//Like I said above...
static method create takes nothing returns thistype
return Table.create()
endmethod
endstruct
endlibrary
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = false
private constant boolean USE_FLEXIBLE_OFFSET = true
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==ARRAY_SIZE) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
library AutoFly requires UnitDex
private function Actions takes nothing returns nothing
local unit u = GetIndexedUnit()
// By Magtheridon96
// This trigger will run whenever a unit is indexed.
// This means that it runs whenever a new unit enters the map.
// First, I'm adding the crow form ability to the indexed unit.
// The reason we're using it inside an if block is because the "UnitAddAbility" function returns a boolean.
// This boolean represents the successfulness of the ability adding. We need this here, because if a unit
// already had a crow form ability, it couldn't be added. The reason we're also using UnitRemoveAbility
// in the same line is because JASS has a short-circuiting feature. If one of the expressions in an 'and'
// expression returns false, then it stops.
// We do not want to call UnitRemoveAbility for a unit that failed to have the ability added.
// Without this form of protection, units that already had the crow form ability would have it
// removed completely.
if UnitAddAbility(u, 'Amrf') and UnitRemoveAbility(u, 'Amrf') then
endif
set u = null
endfunction
private module Init
private static method onInit takes nothing returns nothing
call RegisterUnitIndexEvent(Filter(function Actions), EVENT_UNIT_INDEX)
endmethod
endmodule
private struct init
implement Init
endstruct
endlibrary
library WorldBounds /* v2.0.0.0
************************************************************************************
*
* struct WorldBounds extends array
*
* Fields
* -------------------------
*
* readonly static integer maxX
* readonly static integer maxY
* readonly static integer minX
* readonly static integer minY
*
* readonly static integer centerX
* readonly static integer centerY
*
* readonly static rect world
* readonly static region worldRegion
*
************************************************************************************/
private module WorldBoundInit
private static method onInit takes nothing returns nothing
set world=GetWorldBounds()
set maxX = R2I(GetRectMaxX(world))
set maxY = R2I(GetRectMaxY(world))
set minX = R2I(GetRectMinX(world))
set minY = R2I(GetRectMinY(world))
set centerX = R2I((maxX + minX)/2)
set centerY = R2I((minY + maxY)/2)
set worldRegion = CreateRegion()
call RegionAddRect(worldRegion, world)
endmethod
endmodule
struct WorldBounds extends array
readonly static integer maxX
readonly static integer maxY
readonly static integer minX
readonly static integer minY
readonly static integer centerX
readonly static integer centerY
readonly static rect world
readonly static region worldRegion
implement WorldBoundInit
endstruct
endlibrary
/*****************************************************************************
*
* List<T> v2.1.2.3
* by Bannar
*
* Doubly-linked list.
*
******************************************************************************
*
* Requirements:
*
* Table by Bribe
* hiveworkshop.com/threads/snippet-new-table.188084/
*
* Alloc - choose whatever you like
* e.g.: by Sevion hiveworkshop.com/threads/snippet-alloc.192348/
*
******************************************************************************
*
* Implementation:
*
* macro DEFINE_LIST takes ACCESS, NAME, TYPE
*
* macro DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
*
* ACCESS - encapsulation, choose restriction access
* NAME - name of list type
* TYPE - type of values stored
*
* Implementation notes:
*
* - DEFINE_STRUCT_LIST macro purpose is to provide natural typecasting syntax for struct types.
* - <NAME>Item structs inline directly into hashtable operations thus generate basically no code.
* - Lists defined with DEFINE_STRUCT_LIST are inlined nicely into single create method and single integer array.
*
******************************************************************************
*
* struct API:
*
* struct <NAME>Item:
*
* | <TYPE> data
* | <NAME>Item next
* | <NAME>Item prev
*
*
* General:
*
* | static method create takes nothing returns thistype
* | Default ctor.
* |
* | static method operator [] takes thistype other returns thistype
* | Copy ctor.
* |
* | method destroy takes nothing returns nothing
* | Default dctor.
* |
* | method empty takes nothing returns boolean
* | Checks whether the list is empty.
* |
* | method size takes nothing returns integer
* | Returns size of a list.
*
*
* Access:
*
* | readonly <NAME>Item first
* | readonly <NAME>Item last
* |
* | method front takes nothing returns $TYPE$
* | Retrieves first element.
* |
* | method back takes nothing returns $TYPE$
* | Retrieves last element.
*
*
* Modifiers:
*
* | method clear takes nothing returns nothing
* | Flushes list and recycles its nodes.
* |
* | method push takes $TYPE$ value returns thistype
* | Adds elements to the end.
* |
* | method unshift takes $TYPE$ value returns thistype
* | Adds elements to the front.
* |
* | method pop takes nothing returns thistype
* | Removes the last element.
* |
* | method shift takes nothing returns thistype
* | Removes the first element.
* |
* | method find takes $TYPE$ value returns $NAME$Item
* | Returns the first node which data equals value.
* |
* | method erase takes $NAME$Item node returns boolean
* | Removes node from the list, returns true on success.
* |
* | method removeElem takes $TYPE$ value returns thistype
* | Removes first element that equals value from the list.
*
*
*****************************************************************************/
library ListT requires Table, Alloc
//! runtextmacro DEFINE_LIST("", "IntegerList", "integer")
// Run here any global list types you want to be defined.
//! textmacro_once DEFINE_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// No default ctor and dctor due to limited array size
method operator data takes nothing returns $TYPE$
return Table(this).$TYPE$[-1] // hashtable[ node, -1 ] = data
endmethod
method operator data= takes $TYPE$ value returns nothing
set Table(this).$TYPE$[-1] = value
endmethod
method operator next takes nothing returns thistype
return Table(this)[-2] // hashtable[ node, -2 ] = next
endmethod
method operator next= takes thistype value returns nothing
set Table(this)[-2] = value
endmethod
method operator prev takes nothing returns thistype
return Table(this)[-3] // hashtable[ node, -3 ] = prev
endmethod
method operator prev= takes thistype value returns nothing
set Table(this)[-3] = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
readonly $NAME$Item first
readonly $NAME$Item last
private integer count
implement Alloc
private static method setNodeOwner takes $NAME$Item node, $NAME$ owner returns nothing
set Table(node)[-4] = owner
endmethod
private static method getNodeOwner takes $NAME$Item node returns thistype
return Table(node)[-4]
endmethod
private method createNode takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = Table.create()
set node.data = value
call setNodeOwner(node, this) // ownership
return node
endmethod
private method deleteNode takes $NAME$Item node returns nothing
call Table(node).destroy() // also removes ownership
endmethod
static method create takes nothing returns thistype
local thistype this = allocate()
set count = 0
return this
endmethod
method clear takes nothing returns nothing
local $NAME$Item node = first
local $NAME$Item temp
loop // recycle all Table indexes
exitwhen 0 == node
set temp = node.next
call deleteNode(node)
set node = temp
endloop
set first = 0
set last = 0
set count = 0
endmethod
method destroy takes nothing returns nothing
call clear()
call deallocate()
endmethod
method front takes nothing returns $TYPE$
return first.data
endmethod
method back takes nothing returns $TYPE$
return last.data
endmethod
method empty takes nothing returns boolean
return count == 0
endmethod
method size takes nothing returns integer
return count
endmethod
method push takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set last.next = node
set node.prev = last
else
set first = node
set node.prev = 0
endif
set last = node
set node.next = 0
set count = count + 1
return this
endmethod
method unshift takes $TYPE$ value returns thistype
local $NAME$Item node = createNode(value)
if not empty() then
set first.prev = node
set node.next = first
else
set last = node
set node.next = 0
endif
set first = node
set node.prev = 0
set count = count + 1
return this
endmethod
method pop takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = last
set last = last.prev
if last == 0 then
set first = 0
else
set last.next = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::pop failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
method shift takes nothing returns thistype
local $NAME$Item node
if not empty() then
set node = first
set first = first.next
if first == 0 then
set last = 0
else
set first.prev = 0
endif
call deleteNode(node)
set count = count - 1
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::shift failed for instance "+I2S(this)+". List is empty.")
endif
return this
endmethod
static method operator [] takes thistype other returns thistype
local thistype instance = create()
local $NAME$Item node = other.first
loop
exitwhen node == 0
call instance.push(node.data)
set node = node.next
endloop
return instance
endmethod
method find takes $TYPE$ value returns $NAME$Item
local $NAME$Item node = first
loop
exitwhen node == 0 or node.data == value
set node = node.next
endloop
return node
endmethod
method erase takes $NAME$Item node returns boolean
if getNodeOwner(node) == this then // match ownership
if node == first then
call shift()
elseif node == last then
call pop()
else
set node.prev.next = node.next
set node.next.prev = node.prev
call deleteNode(node)
set count = count - 1
endif
return true
debug else
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"$NAME$::erase failed for instance "+I2S(this)+". Attempted to remove invalid node "+I2S(node)+".")
endif
return false
endmethod
method remove takes $NAME$Item node returns boolean
debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"Method $NAME$::remove is obsolete, use $NAME$::erase instead.")
return erase(node)
endmethod
method removeElem takes $TYPE$ value returns thistype
local $NAME$Item node = find(value)
if node != 0 then
call erase(node)
endif
return this
endmethod
endstruct
//! endtextmacro
//! textmacro_once DEFINE_STRUCT_LIST takes ACCESS, NAME, TYPE
$ACCESS$ struct $NAME$Item extends array
// Cannot inherit methods via delegate due to limited array size
method operator data takes nothing returns $TYPE$
return IntegerListItem(this).data
endmethod
method operator data= takes $TYPE$ value returns nothing
set IntegerListItem(this).data = value
endmethod
method operator next takes nothing returns thistype
return IntegerListItem(this).next
endmethod
method operator next= takes thistype value returns nothing
set IntegerListItem(this).next = value
endmethod
method operator prev takes nothing returns thistype
return IntegerListItem(this).prev
endmethod
method operator prev= takes thistype value returns nothing
set IntegerListItem(this).prev = value
endmethod
endstruct
$ACCESS$ struct $NAME$ extends array
private delegate IntegerList parent
static method create takes nothing returns thistype
local thistype this = IntegerList.create()
set parent = this
return this
endmethod
method front takes nothing returns $TYPE$
return parent.front()
endmethod
method back takes nothing returns $TYPE$
return parent.back()
endmethod
endstruct
//! endtextmacro
endlibrary
library Alloc /* v1.3.1.1
*************************************************************************************
*
* */ uses /*
*
* */ optional ErrorMessage /* github.com/nestharus/JASS/tree/master/jass/Systems/ErrorMessage
* */ optional MemoryAnalysis /*
*
*************************************************************************************
*
* Minimizes code generation and global variables while maintaining
* excellent performance.
*
* local thistype this = recycler[0]
*
* if (recycler[this] == 0) then
* set recycler[0] = this + 1
* else
* set recycler[0] = recycler[this]
* endif
*
************************************************************************************
*
* module Alloc
*
* static method allocate takes nothing returns thistype
* method deallocate takes nothing returns nothing
*
* The Following Require Error Message To Be In The Map
* --------------------------------------------------------
*
* debug readonly boolean allocated
*
* The Following Require Memory Analysis To Be In The Map
* --------------------------------------------------------
*
* debug readonly integer monitorCount
* - the amount of global memory being monitored by this
* debug readonly integer monitorString
* - gets a string representation of all global memory being monitored by this
* debug readonly integer address
* - global memory address for debugging
* - used with monitor and stopMonitor
*
* debug static method calculateMemoryUsage takes nothing returns integer
* debug static method getAllocatedMemoryAsString takes nothing returns string
*
* debug method monitor takes string label, integer address returns nothing
* - monitor a global memory address with a label
* - used to identify memory leaks
* - should be memory that ought to be destroyed by the time this is destroyed
* debug method stopMonitor takes integer address returns nothing
* - stops monitoring global memory
* debug method stopMonitorValue takes handle monitoredHandle returns nothing
* - stops monitoring handle values
*
* The Following Are Used To Monitor Handle Values
*
* debug method monitor_widget takes string label, widget handleToTrack returns nothing
* debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
* debug method monitor_item takes string label, item handleToTrack returns nothing
* debug method monitor_unit takes string label, unit handleToTrack returns nothing
* debug method monitor_timer takes string label, timer handleToTrack returns nothing
* debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
* debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
* debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
* debug method monitor_force takes string label, force handleToTrack returns nothing
* debug method monitor_group takes string label, group handleToTrack returns nothing
* debug method monitor_location takes string label, location handleToTrack returns nothing
* debug method monitor_rect takes string label, rect handleToTrack returns nothing
* debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
* debug method monitor_effect takes string label, effect handleToTrack returns nothing
* debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
* debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
* debug method monitor_quest takes string label, quest handleToTrack returns nothing
* debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
* debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
* debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
* debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
* debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
* debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
* debug method monitor_button takes string label, button handleToTrack returns nothing
* debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
* debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
* debug method monitor_image takes string label, image handleToTrack returns nothing
* debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
* debug method monitor_region takes string label, region handleToTrack returns nothing
* debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
* debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
*
*
* Thanks to Ruke for the algorithm
************************************************************************************/
module Alloc
/*
* stack
*/
private static integer array recycler
static if LIBRARY_MemoryAnalysis then
debug private MemoryMonitor globalAddress
debug method operator address takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "address", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress
debug endmethod
endif
/*
* allocation
*/
static method allocate takes nothing returns thistype
local thistype this = recycler[0]
static if LIBRARY_ErrorMessage then
debug call ThrowError(this == 8192, "Alloc", "allocate", "thistype", 0, "Overflow.")
endif
if (recycler[this] == 0) then
set recycler[0] = this + 1
else
set recycler[0] = recycler[this]
endif
static if LIBRARY_ErrorMessage then
debug set recycler[this] = -1
endif
static if LIBRARY_MemoryAnalysis then
debug set globalAddress = MemoryMonitor.allocate("thistype")
endif
return this
endmethod
method deallocate takes nothing returns nothing
static if LIBRARY_ErrorMessage then
debug call ThrowError(recycler[this] != -1, "Alloc", "deallocate", "thistype", this, "Attempted To Deallocate Null Instance.")
endif
static if LIBRARY_MemoryAnalysis then
debug call globalAddress.deallocate()
debug set globalAddress = 0
endif
set recycler[this] = recycler[0]
set recycler[0] = this
endmethod
static if LIBRARY_MemoryAnalysis then
debug method monitor takes string label, integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor(label, address)
debug endmethod
debug method stopMonitor takes integer address returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitor", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitor(address)
debug endmethod
debug method stopMonitorValue takes handle monitoredHandle returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "stopMonitorValue", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.stopMonitorValue(monitoredHandle)
debug endmethod
debug method operator monitorCount takes nothing returns integer
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorCount", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorCount
debug endmethod
debug method operator monitorString takes nothing returns string
debug call ThrowError(recycler[this] != -1, "Alloc", "monitorString", "thistype", this, "Attempted To Access Null Instance.")
debug return globalAddress.monitorString
debug endmethod
debug method monitor_widget takes string label, widget handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_widget", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_widget(label, handleToTrack)
debug endmethod
debug method monitor_destructable takes string label, destructable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_destructable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_destructable(label, handleToTrack)
debug endmethod
debug method monitor_item takes string label, item handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_item", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_item(label, handleToTrack)
debug endmethod
debug method monitor_unit takes string label, unit handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unit", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unit(label, handleToTrack)
debug endmethod
debug method monitor_timer takes string label, timer handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timer", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timer(label, handleToTrack)
debug endmethod
debug method monitor_trigger takes string label, trigger handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_trigger", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_trigger(label, handleToTrack)
debug endmethod
debug method monitor_triggercondition takes string label, triggercondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggercondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggercondition(label, handleToTrack)
debug endmethod
debug method monitor_triggeraction takes string label, triggeraction handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_triggeraction", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_triggeraction(label, handleToTrack)
debug endmethod
debug method monitor_force takes string label, force handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_force", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_force(label, handleToTrack)
debug endmethod
debug method monitor_group takes string label, group handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_group", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_group(label, handleToTrack)
debug endmethod
debug method monitor_location takes string label, location handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_location", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_location(label, handleToTrack)
debug endmethod
debug method monitor_rect takes string label, rect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_rect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_rect(label, handleToTrack)
debug endmethod
debug method monitor_boolexpr takes string label, boolexpr handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_boolexpr", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_boolexpr(label, handleToTrack)
debug endmethod
debug method monitor_effect takes string label, effect handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_effect", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_effect(label, handleToTrack)
debug endmethod
debug method monitor_unitpool takes string label, unitpool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_unitpool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_unitpool(label, handleToTrack)
debug endmethod
debug method monitor_itempool takes string label, itempool handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_itempool", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_itempool(label, handleToTrack)
debug endmethod
debug method monitor_quest takes string label, quest handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_quest", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_quest(label, handleToTrack)
debug endmethod
debug method monitor_defeatcondition takes string label, defeatcondition handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_defeatcondition", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_defeatcondition(label, handleToTrack)
debug endmethod
debug method monitor_timerdialog takes string label, timerdialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_timerdialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_timerdialog(label, handleToTrack)
debug endmethod
debug method monitor_leaderboard takes string label, leaderboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_leaderboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_leaderboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboard takes string label, multiboard handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboard", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboard(label, handleToTrack)
debug endmethod
debug method monitor_multiboarditem takes string label, multiboarditem handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_multiboarditem", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_multiboarditem(label, handleToTrack)
debug endmethod
debug method monitor_dialog takes string label, dialog handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_dialog", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_dialog(label, handleToTrack)
debug endmethod
debug method monitor_button takes string label, button handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_button", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_button(label, handleToTrack)
debug endmethod
debug method monitor_texttag takes string label, texttag handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_texttag", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_texttag(label, handleToTrack)
debug endmethod
debug method monitor_lightning takes string label, lightning handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_lightning", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_lightning(label, handleToTrack)
debug endmethod
debug method monitor_image takes string label, image handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_image", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_image(label, handleToTrack)
debug endmethod
debug method monitor_ubersplat takes string label, ubersplat handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_ubersplat", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_ubersplat(label, handleToTrack)
debug endmethod
debug method monitor_region takes string label, region handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_region", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_region(label, handleToTrack)
debug endmethod
debug method monitor_fogmodifier takes string label, fogmodifier handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_fogmodifier", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_fogmodifier(label, handleToTrack)
debug endmethod
debug method monitor_hashtable takes string label, hashtable handleToTrack returns nothing
debug call ThrowError(recycler[this] != -1, "Alloc", "monitor_hashtable", "thistype", this, "Attempted To Access Null Instance.")
debug call globalAddress.monitor_hashtable(label, handleToTrack)
debug endmethod
static if DEBUG_MODE then
//! runtextmacro optional MEMORY_ANALYSIS_STATIC_FIELD_NEW("recycler")
static method calculateMemoryUsage takes nothing returns integer
return calculateAllocatedMemory__recycler()
endmethod
static method getAllocatedMemoryAsString takes nothing returns string
return allocatedMemoryString__recycler()
endmethod
endif
endif
/*
* analysis
*/
static if LIBRARY_ErrorMessage then
debug method operator allocated takes nothing returns boolean
debug return recycler[this] == -1
debug endmethod
endif
/*
* initialization
*/
private static method onInit takes nothing returns nothing
set recycler[0] = 1
endmethod
endmodule
endlibrary
scope Initialisation initializer init
globals
//private constant string GLOWING_RUNES = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
private constant string GLOWING_RUNES = "Doodads\\Cinematic\\GlowingRunes\\GlowingRunes4.mdl"
//private constant string NO_RUNES = "Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl"
private constant string NO_RUNES = "Abilities\\Weapons\\SpiritOfVengeanceMissile\\SpiritOfVengeanceMissile.mdl"
endglobals
private function Actions takes nothing returns boolean
local integer i_main = 0
local integer i = 0
local unit array u
local real x
local real y
local real z
local real angle
local real facing
local real distance
local real xNew
local real yNew
local RS rs
call FogEnableOff()
call FogMaskEnableOff()
call SetTimeOfDay(10)
call BJDebugMsg("Right-click on any of the buildings to dock the units into them.\n\nPress ESC to clear the screen of all the text.")
set u[11] = gg_unit_otto_0027
set u[12] = gg_unit_uzg1_0036
set u[13] = gg_unit_edos_0043
set u[14] = gg_unit_edos_0008
//Tauren Totem
/*
Basic functionality of the Docking System that was designed primarily to mimic how
Acolytes 'dock' into Haunted Gold Mines to collect gold. What the following loop is
doing is that it's, firstly, setting the Tauren Totem up as a station, and then the
loop starts. The loop will cycle 7 times (exiting on the eigth time), creating a
socket for the Tauren Totem each time with call DockingSystem.addSocket().
*/
call DockingSystem.createStation(u[11], 250.)
loop
set i = i + 1
exitwhen i > 7
set angle = ( 270. + (360 / 7) * i ) * bj_DEGTORAD
set distance = 250.
set x = GetUnitX(u[11])
set y = GetUnitY(u[11])
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
set facing = Atan2(y - yNew, x - xNew) * bj_RADTODEG
call DockingSystem.addSocket(u[11], xNew, yNew, 0., facing)
endloop
//Spirit Tower
/*
The Spirit Tower demonstrates how moving sockets can be used. A Table is created
and every added socket is stored inside. Keep in mind that DockingSystem.addSocket()
returns a unique number associated with that socket, so that's what the Table is
storing. Then, the Table is called inside RotatingSockets.initRotation(), which is
a method in a library specifically designed for this demo. You can find more info on
it in the Rotating Sockets trigger.
*/
set i = 0
call DockingSystem.createStation(u[12], 250.)
set RS.table = Table.create()
loop
set i = i + 1
exitwhen i > 5
set RS.socketTable = Table.create()
set RS.table.integer[i] = RS.socketTable
set x = GetUnitX(u[12])
set y = GetUnitY(u[12])
set RS.socketTable.integer[1] = DockingSystem.addSocket(u[12], x + GetRandomReal(-100., 100.), y + GetRandomReal(-100., 100.), 0., 0.)
set angle = GetRandomReal(4., 5.5) * bj_DEGTORAD
if GetRandomInt(1,2) == 1 then
set angle = angle * -1
endif
set RS.socketTable.real[2] = angle //Angle
set RS.socketTable.real[3] = 80 + GetRandomReal(30., 45.) * i //Distance
set RS.socketTable.real[4] = GetRandomReal(30., 120.) //Fly Height
endloop
call RotatingSockets.initRotation(RS.table, x, y, 5, 16.)
//Chimera Roost
/*
The Chimera Roost has specifically placed sockets that match the position and height
of its perches. This way, when a unit docks onto the Chimera Roost, it will be placed
if one of those sockets, appearing as though perched on one of the... perches of the
building.
*/
call DockingSystem.createStation(u[13], 250.)
set x = GetUnitX(u[13])
set y = GetUnitY(u[13])
call DockingSystem.addSocket(u[13], x, y, 300., 270.)
set distance = 80.
set angle = 225. * bj_DEGTORAD
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
call DockingSystem.addSocket(u[13], xNew, yNew, 220., 315.)
set angle = 45. * bj_DEGTORAD
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
call DockingSystem.addSocket(u[13], xNew, yNew, 220., 315.)
set angle = 270. * bj_DEGTORAD
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
call DockingSystem.addSocket(u[13], xNew, yNew, 150., 0.)
call DockingSystem.createStation(u[14], 250.)
set x = GetUnitX(u[14])
set y = GetUnitY(u[14])
call DockingSystem.addSocket(u[14], x, y, 300., 270.)
set distance = 80.
set angle = 225. * bj_DEGTORAD
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
call DockingSystem.addSocket(u[14], xNew, yNew, 220., 315.)
set angle = 45. * bj_DEGTORAD
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
call DockingSystem.addSocket(u[14], xNew, yNew, 220., 315.)
set angle = 270. * bj_DEGTORAD
set xNew = x + Cos(angle) * distance
set yNew = y + Sin(angle) * distance
call DockingSystem.addSocket(u[14], xNew, yNew, 150., 0.)
//Nulling local variables
set u[11] = null
set u[12] = null
set u[13] = null
set u[14] = null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
call TriggerRegisterTimerEvent(t, 0., false)
call TriggerAddCondition(t, function Actions)
set t = null
endfunction
endscope
library RotatingSockets requires TimerUtils, DockingSystem, Table
/*
The Spirit Tower has rotating sockets and this library controls their movement. The way
this is done is by storing all sockets created for the Spirit Tower and then adding them
to a Table. This table is then called inside RotatingSockets.initRotation() along with x
and y co-ordinates (which determine the center of rotation) and an integer that tells the
system how many sockets the Table contains. The offset value should be set to 16 if it
appears that the sockets don't appear to be rotating exactly around the center of the
station.
*/
struct RS
static Table table
static Table socketTable
endstruct
struct RotatingSockets
integer tableInstance
integer limit
real x
real y
real offset
private static method Timer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local integer i = 0
local real xNew
local real yNew
local real angleNew
local Socket s
set RS.table = this.tableInstance
loop
set i = i + 1
exitwhen i > this.limit
set RS.socketTable = RS.table.integer[i]
set s = RS.socketTable.integer[1]
set angleNew = GetStationAngleFromSocket(s, this.offset) + RS.socketTable.real[2]
set xNew = this.x + Cos(angleNew) * RS.socketTable.real[3]
set yNew = this.y + Sin(angleNew) * RS.socketTable.real[3]
call DockingSystem.moveSocket(s, xNew, yNew, RS.socketTable.real[4], angleNew * bj_RADTODEG)
endloop
set t = null
endmethod
static method initRotation takes Table tableInstance, real x, real y, integer limit, real offset returns nothing
local thistype this = allocate()
set this.tableInstance = tableInstance
set this.limit = limit
set this.x = x + offset
set this.y = y + offset
set this.offset = offset
call TimerStart(NewTimerEx(this), .03125, true, function thistype.Timer)
endmethod
endstruct
endlibrary
scope UndockAbilities initializer init
/*
This is a simple example on how to use the DockingSystem.undock() method by calling it
after using an ability. Usually, the DockingSystem.undock method will only ever be needed
if you've decided to make undocking manual by setting DS_MANUAL_UNDOCKING to true on
DS_ON_PRE_DOCK event.
*/
private function Undock takes nothing returns nothing
call DockingSystem.undock(GetTriggerUnit())
endfunction
private function UndockAll takes nothing returns nothing
call DockingSystem.undockAll(GetTriggerUnit())
endfunction
private function init takes nothing returns nothing
call RegisterSpellEffectEvent('A000', function Undock)
call RegisterSpellEffectEvent('A001', function UndockAll)
endfunction
endscope
//============================================================================
// SpellEffectEvent
// - Version 1.1.0.0
//
// API
// ---
// RegisterSpellEffectEvent(integer abil, code onCast)
//
// Requires
// --------
// RegisterPlayerUnitEvent: hiveworkshop.com/forums/showthread.php?t=203338
//
// Optional
// --------
// Table: hiveworkshop.com/forums/showthread.php?t=188084
//
library SpellEffectEvent requires RegisterPlayerUnitEvent, optional Table
//============================================================================
private module M
static if LIBRARY_Table then
static Table tb
else
static hashtable ht = InitHashtable()
endif
static method onCast takes nothing returns nothing
static if LIBRARY_Table then
call TriggerEvaluate(.tb.trigger[GetSpellAbilityId()])
else
call TriggerEvaluate(LoadTriggerHandle(.ht, 0, GetSpellAbilityId()))
endif
endmethod
private static method onInit takes nothing returns nothing
static if LIBRARY_Table then
set .tb = Table.create()
endif
call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onCast)
endmethod
endmodule
//============================================================================
private struct S extends array
implement M
endstruct
//============================================================================
function RegisterSpellEffectEvent takes integer abil, code onCast returns nothing
static if LIBRARY_Table then
if not S.tb.handle.has(abil) then
set S.tb.trigger[abil] = CreateTrigger()
endif
call TriggerAddCondition(S.tb.trigger[abil], Filter(onCast))
else
if not HaveSavedHandle(S.ht, 0, abil) then
call SaveTriggerHandle(S.ht, 0, abil, CreateTrigger())
endif
call TriggerAddCondition(LoadTriggerHandle(S.ht, 0, abil), Filter(onCast))
endif
endfunction
endlibrary