- Joined
- Jan 9, 2005
- Messages
- 2,124
Reserved
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 with 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
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