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 SmartTrackInit
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 SmartTrackInit
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 CustomWaygate/*
CustomWaygate v1.04
By Spellbound
/*/*==== DESCRIPTION ====*/*/
Custom Waygate allows two units to be connected via one-way 'tunnels'. A unit can have
multiple tunnels, which creates a network that allows the player to chose, through
right-clicking, the destination portal.
/*/*==== INSTALLATION ====*/*/
• Copy-paste this trigger into your map.
• Copy-paste the required libraries into your map.
• The system is installed! Copy-paste the onCustomWaygate Event trigger or make your own
event trigger and start coding.
*/ requires /*
*/ SmartTrack, /* https://www.hiveworkshop.com/threads/smarttrack-v1-02-1.299957/
*/ TimerUtils, /* http://www.wc3c.net/showthread.php?t=101322
*/ Table /* https://www.hiveworkshop.com/threads/snippet-new-table.188084/
*/ optional /*
*/ BlizzardMessage /* https://www.hiveworkshop.com/threads/snippet-blizzardmessage.237845/
/*/*==== API ====*/*/
/*SETUP________*/
CustomWaygate.createTunnel( takes unit source, unit target, real teleport_distance, real timeout )
Sets up a tunnel that will teleport unit from the source to the target. The teleport_dsitance
determines how close to the source the teleporting units have to be to use it. The timeout
determines if there is a channeling time before teleportation itself occurs.
To create a two-way teleporter, simply repeat this function but set the target as the source
and the source as the target.
A waygate become a network the moment it has more than one tunnel.
CustomWaygate.destroyTunnel( takes unit source, unit target )
Destroys a tunnel. This will only destroy the tunnel going from the source to the target.
If the target has a tunnel going to the source, it will be unaffected.
/*EVENTS________*/
RegisterNativeEvent( integer whichEvent, code whichFunction )
This uses RegisterNativeEvent's event-creation to register CustomWaygate's many events. They
are as followed:
EVENT_WAYGATE_PRE_TELEPORT ---- Fires right before a unit is moved.
EVENT_WAYGATE_POST_TELEPORT --- Fires directly after moving a unit.
EVENT_WAYGATE_DURATION_START -- When teleportation has a duration, this fires as that duration
starts. It ends EVENT_WAYGATE_PRE_TELEPORT
EVENT_WAYGATE_NETWORK_PRIMED -- When teleportation is primed and ready to execute in a network.
Useful if you want to attach special effects to your units to
indicate readiness, for example.
EVENT_WAYGATE_CANCELLED ------- When teleportation has been cancelled, either manually or
through StopTeleport(). See below.
RegisterIndexNativeEvent( integer index, integer whichEvent, code whichFunction )
Same as above, but per-player or per-unit. If you wish for CustomWaygate to only work for
specific players, set index to the player id. For units, set index to the handle of the unit.
GetWaygateSource()
The waygate unit itself. Origin waygate.
GetWaygateTarget()
The waygate your units teleport to. Destination waygate.
GetWaygateTraveller()
The units that are using the waygate.
CountEntrances( unit whichGate )
Returns an integer of how many gates have whichGate as a destination.
CountExits( unit whichGate )
Returns an integer of how many destination gates whichGate has.
StopTeleport()
Call this when WAYGATE_EVENT == 0.50 (pre-teleportaton) if you wish to interrupt the
process and handle teleportation yourself. Such situations are, eg, if you want to have
your teleporter shoot your traveller as a missile towards the destination instead of just
instantly displacing them. It's also useful if your teleporter has 'charges' so that you can
prevent teleportation altogether if the charges are insufficient.
*/
globals
private constant real NETWORK_TIMEOUT = .03125
private constant real DESTINATION_MESSAGE_COOLDOWN = 5. // see PreventMessageSpam below
private constant string MSG_INVALID = "Invalid target." // the message that shows up if you click on a unit that's not part of the network your unit is currently in.
private constant string MSG_PICK_DESTINATION = "Right-click on your destination to teleport."
private integer array NumberOfEntrances
private integer array NumberOfExits
private integer array EntrancesNetworkInstance
private integer array ExitsNetworkInstance
private integer array TeleportTimeInstance
private integer array TravellerInstance
private integer array NumberOfInstances
private boolean array isPlayMessageOnCooldown
//Events
integer EVENT_WAYGATE_PRE_TELEPORT
integer EVENT_WAYGATE_POST_TELEPORT
integer EVENT_WAYGATE_DURATION_START
integer EVENT_WAYGATE_NETWORK_PRIMED
integer EVENT_WAYGATE_CANCELLED
private unit waygateSource = null
private unit waygateTarget = null
private unit waygateTraveller = null
private boolean interruptTeleport = false
endglobals
// G stands for Gate
private struct G
static Table EXITS
static Table ENTRANCES
static Table TIMEOUT
endstruct
// Getters and interruption
function GetWaygateSource takes nothing returns unit
return waygateSource
endfunction
function GetWaygateTarget takes nothing returns unit
return waygateTarget
endfunction
function GetWaygateTraveller takes nothing returns unit
return waygateTraveller
endfunction
function StopTeleport takes nothing returns nothing
set interruptTeleport = true
endfunction
function CountEntrances takes unit u returns integer
return NumberOfEntrances[GetUnitUserData(u)]
endfunction
function CountExits takes unit u returns integer
return NumberOfExits[GetUnitUserData(u)]
endfunction
// This is the function that controls what trigger is run.
private function FireEvent takes unit source, unit target, unit traveller, integer ev returns nothing
local integer playerId = GetPlayerId(GetOwningPlayer(traveller))
local integer id = GetHandleId(traveller)
local unit prevSource = waygateSource
local unit prevTarget = waygateTarget
local unit prevTraveller = waygateTraveller
set waygateSource = source
set waygateTarget = target
set waygateTraveller = traveller
call TriggerEvaluate(GetNativeEventTrigger(ev))
if IsNativeEventRegistered(playerId, ev) then
call TriggerEvaluate(GetIndexNativeEventTrigger(playerId, ev))
endif
if IsNativeEventRegistered(id, ev) then
call TriggerEvaluate(GetIndexNativeEventTrigger(id, ev))
endif
set waygateSource = prevSource
set waygateTarget = prevTarget
set waygateTraveller = prevTraveller
set prevSource = null
set prevTarget = null
set prevTraveller = null
endfunction
// A little quality of life struct that prevents an error message from showing up for every single
// invalid teleportation. Instead, it shows up once and then not for another DESTINATION_MESSAGE_COOLDOWN seconds.
private struct PreventMessageSpam
integer play_num
private method destroy takes nothing returns nothing
call this.deallocate()
endmethod
private static method Timer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
set isPlayMessageOnCooldown[this.play_num] = false
call ReleaseTimer(t)
call this.destroy()
set t = null
endmethod
static method start takes integer player_number returns nothing
local thistype this = allocate()
set this.play_num = player_number
set isPlayMessageOnCooldown[player_number] = true
call TimerStart(NewTimerEx(this), DESTINATION_MESSAGE_COOLDOWN, false, function thistype.Timer)
endmethod
endstruct
struct orderStunTimer
unit u
boolean b
// ST_IGNORE_ORDERS is specific to SmartTrack that prevents orders given by this library to
// interfere with it.
private static method zeroTimerStun takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local CustomWaygate ti = TravellerInstance[GetUnitUserData(this.u)]
set ti.ignoreOrder = true
set SmartTrack_ignoreOrders = this.b
call IssueImmediateOrderById(this.u, 851973)//order stunned
set SmartTrack_ignoreOrders = false
set ti.ignoreOrder = false
set this.u = null
set this.b = false
call ReleaseTimer(t)
call this.deallocate()
set t = null
endmethod
static method start takes unit u, boolean b returns nothing
local thistype this = allocate()
set this.u = u
set this.b = b
call TimerStart(NewTimerEx(this), 0., false, function thistype.zeroTimerStun)
endmethod
endstruct
struct CustomWaygate
unit source
unit target
unit traveller
real countdown
trigger trig
trigger networkTrig
boolean isTeleportCancelled
boolean ignoreOrder
boolean teleportReady
integer timeoutSlot
integer tunnelInstance
timer clock
// destruction method for teleport instances, not tunnels.
private method destroy takes nothing returns nothing
set this.source = null
set this.target = null
set this.traveller = null
if this.trig != null then
call DestroyTrigger(this.trig)
set this.trig = null
endif
if this.clock != null then
call ReleaseTimer(this.clock)
set this.clock = null
endif
set this.networkTrig = null
call this.deallocate()
endmethod
// core teleporter method. Relocates units to the destination by keeping their relative x/y
// position to the source.
private static method simpleTeleport takes unit source, unit target, unit traveller returns nothing
local real xSource
local real ySource
local real xTraveller
local real yTraveller
local real xDestination
local real yDestination
local real angle
local real distance
local thistype this = allocate()
set xTraveller = GetUnitX(traveller)
set yTraveller = GetUnitY(traveller)
set xSource = GetUnitX(source)
set ySource = GetUnitY(source)
set angle = Atan2(yTraveller - ySource, xTraveller - xSource)
set distance = SquareRoot( (xTraveller - xSource)*(xTraveller - xSource) + (yTraveller - ySource)*(yTraveller - ySource) )
set xDestination = GetUnitX(target) + Cos(angle) * distance
set yDestination = GetUnitY(target) + Sin(angle) * distance
call SetUnitX(traveller, xDestination)
call SetUnitY(traveller, yDestination)
call orderStunTimer.start(traveller, true)
endmethod
// this function handles triggers created for each teleport instance that catches orders
// given. This is exclusively for non-network teleportations.
private static method cancelTeleport takes nothing returns nothing
local unit traveller = GetTriggerUnit()
local unit source = GetOrderTargetUnit()
local integer id_T = GetUnitUserData(traveller)
local thistype this = TravellerInstance[id_T]
// if there is a source mismatch (aka you've right-clicked on another teleporter while
// already in a teleport instance) or the order was not a right-click, cancel teleportation.
if not (source == this.source and GetIssuedOrderId() == ORDER_SMART) then
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_CANCELLED)
if this.clock == null then
call this.destroy()
else
set this.isTeleportCancelled = true
//This instance is bound to isTeleportCancelled, which doesn't repeat and will
//terminate on its own when it expires.
endif
endif
set source = null
set traveller = null
endmethod
// this function handles the timer for non-network teleportations.
private static method teleportTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local integer id_T = GetUnitUserData(this.traveller)
call DestroyTrigger(this.trig)
set this.trig = null
set G.EXITS = this.tunnelInstance
if not this.isTeleportCancelled and UnitAlive(this.source) and UnitAlive(this.traveller) and G.EXITS.unit[1] != null and TravellerInstance[id_T] == this then
//Events
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_PRE_TELEPORT)
if not interruptTeleport then
call CustomWaygate.simpleTeleport(this.source, this.target, this.traveller)
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_POST_TELEPORT)
else
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_CANCELLED)
endif
set interruptTeleport = false
endif
set NumberOfInstances[id_T] = NumberOfInstances[id_T] - 1
if NumberOfInstances[id_T] == 0 then
set TravellerInstance[id_T] = 0
endif
call this.destroy()
set t = null
endmethod
// if the teleporter has a timeout, create a trigger to track orders given and fire an initiate teleport event.
static method initiateTeleportation takes unit source, unit target, unit traveller, real timeout, integer tunnel_id returns nothing
local integer id_T
local thistype this
if timeout > 0. then
set this = allocate()
set id_T = GetUnitUserData(traveller)
set NumberOfInstances[id_T] = NumberOfInstances[id_T] + 1
set TravellerInstance[id_T] = this
set this.source = source
set this.target = target
set this.traveller = traveller
set this.isTeleportCancelled = false
set this.tunnelInstance = tunnel_id
set this.trig = CreateTrigger()
call TriggerRegisterUnitEvent(this.trig, traveller, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterUnitEvent(this.trig, traveller, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(this.trig, traveller, EVENT_UNIT_ISSUED_ORDER)
call TriggerAddCondition(this.trig, function thistype.cancelTeleport)
set this.clock = NewTimerEx(this)
call TimerStart(this.clock, timeout, false, function thistype.teleportTimer)
//Events
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_DURATION_START)
else
//Events
call FireEvent(source, target, traveller, EVENT_WAYGATE_PRE_TELEPORT)
if not interruptTeleport then
call CustomWaygate.simpleTeleport(source, target, traveller)
call FireEvent(source, target, traveller, EVENT_WAYGATE_POST_TELEPORT)
else
call FireEvent(source, target, traveller, EVENT_WAYGATE_CANCELLED)
endif
set interruptTeleport = false
endif
endmethod
//NETWORK TELEPORT
// this function allows units to right-click on possible destinations in a network without
// terminating their teleport instance. It catches orders exclusively for network teleportations.
static method networkTeleport takes nothing returns nothing
local unit traveller = GetTriggerUnit()
local unit target = GetOrderTargetUnit()
local thistype this = TravellerInstance[GetUnitUserData(traveller)]
local integer id_S = GetUnitUserData(this.source)
local integer i = 0
local boolean isUnitValid = false
local player play
// this.ignoredOrder is used in orderStunTimer.start() to prevent the stun order from
// affecting the unit and thus accidentally interrupting the teleportation.
if not this.ignoreOrder then
if target != null and GetIssuedOrderId() == ORDER_SMART then
if target != this.source then
set G.EXITS = ExitsNetworkInstance[id_S]
loop
set i = i + 1
if target == G.EXITS.unit[i] then
set isUnitValid = true
endif
exitwhen i > NumberOfExits[id_S] or isUnitValid
endloop
// if the destination unit is viable, the right-clicker is stunned to prevent
// it from moving.
if isUnitValid then
set this.target = target
set this.timeoutSlot = i
call orderStunTimer.start(traveller, false)
else
if SmartTrack_IsATracker[GetUnitUserData(target)] then
set this.isTeleportCancelled = true
else
static if LIBRARY_BlizzardMessage then
call BlizzardMessage(MSG_INVALID, "|cffffcc00", 31, GetOwningPlayer(traveller))
else
set play = GetOwningPlayer(traveller)
if play == GetLocalPlayer() then
call DisplayTimedTextToPlayer(play, 0, 0, 5., MSG_INVALID)
endif
endif
call orderStunTimer.start(traveller, false)
endif
endif
endif
else
set this.isTeleportCancelled = true
endif
endif
set traveller = null
set target = null
endmethod
// this function handles the countdown for network-type teleporter. Right-click orders are
private static method networkTeleportCountdown takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local integer id_T = GetUnitUserData(this.traveller)
// this.teleportReady will turn to true after the countdown has elapsed. When it turns
// true, a network-teleport-is-event will fire.
if not this.teleportReady then
set this.countdown = this.countdown - NETWORK_TIMEOUT
if this.countdown <= 0. then
set this.teleportReady = true
//Events
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_NETWORK_PRIMED)
endif
endif
// if the teleportation has been cancelled, terminate the teleportation isntance.
if this.isTeleportCancelled then
set NumberOfInstances[id_T] = NumberOfInstances[id_T] - 1
if NumberOfInstances[id_T] == 0 then
set TravellerInstance[id_T] = 0
endif
call DestroyTrigger(this.networkTrig)
set this.networkTrig = null
//Events
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_CANCELLED)
call this.destroy()
// if there is a teleport target (aka a destination) and this.teleportReady is true, call
// pre-teleportation, terminate the teleport instance and if no interruption is called,
// proceed with the teleportation.
elseif this.target != null and this.teleportReady then
//Events
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_PRE_TELEPORT)
set NumberOfInstances[id_T] = NumberOfInstances[id_T] - 1
if NumberOfInstances[id_T] == 0 then
set TravellerInstance[id_T] = 0
endif
call DestroyTrigger(this.networkTrig)
set this.networkTrig = null
if not interruptTeleport then
call CustomWaygate.simpleTeleport(this.source, this.target, this.traveller)
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_POST_TELEPORT)
else
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_CANCELLED)
endif
set interruptTeleport = false
call this.destroy()
endif
set t = null
endmethod
// This is the first method that is called when a right-clicker comes in range of a teleporter.
static method preTeleportation takes nothing returns nothing
local unit source = GetTracker()
local unit traveller = GetSmartUnit()
local integer id_S = GetUnitUserData(source) //The Waygate
local integer id_T = GetUnitUserData(traveller)
local integer player_number
local integer i
local player play
local thistype this = TravellerInstance[id_T]
if this.source != source then
if this != 0 then
set this.isTeleportCancelled = true
endif
set G.EXITS = ExitsNetworkInstance[id_S]
set G.TIMEOUT = TeleportTimeInstance[id_S]
// if there's only one exit connected to this source, then it's not a network. Initiate teleportation directly.
if NumberOfExits[id_S] == 1 then
call initiateTeleportation(source, G.EXITS.unit[1], traveller, G.TIMEOUT.real[1], G.EXITS)
// if ther are multiple exits, the unit has to then chose the destination with another right-click. Queued orders work well for that.
elseif NumberOfExits[id_S] > 1 then
set this = allocate()
set NumberOfInstances[id_T] = NumberOfInstances[id_T] + 1
set TravellerInstance[id_T] = this
set this.source = source
set this.target = null
set this.traveller = traveller
set this.isTeleportCancelled = false
set this.teleportReady = false
set this.ignoreOrder = false
set this.countdown = G.TIMEOUT.real[1]
set this.networkTrig = CreateTrigger()
call TriggerRegisterUnitEvent(this.networkTrig, traveller, EVENT_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterUnitEvent(this.networkTrig, traveller, EVENT_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterUnitEvent(this.networkTrig, traveller, EVENT_UNIT_ISSUED_ORDER)
call TriggerAddCondition(this.networkTrig, function thistype.networkTeleport)
set this.clock = NewTimerEx(this)
call TimerStart(this.clock, NETWORK_TIMEOUT, true, function thistype.networkTeleportCountdown)
set play = GetOwningPlayer(traveller)
set player_number = GetPlayerId(play)
if not isPlayMessageOnCooldown[player_number] then
static if LIBRARY_BlizzardMessage then
call BlizzardMessage(MSG_PICK_DESTINATION, "|cffffcc00", 132, play)
else
if play == GetLocalPlayer() then
call DisplayTimedTextToPlayer(play, 0, 0, 5., MSG_PICK_DESTINATION)
endif
endif
call PreventMessageSpam.start(player_number)
endif
//Events
call FireEvent(this.source, this.target, this.traveller, EVENT_WAYGATE_DURATION_START)
endif
endif
set source = null
set traveller = null
endmethod
// this method destroys a SINGLE tunnel going from unit A to unit B. If there's another
// tunnel that goes from B to A, this tunnel is unaffected.
static method destroyTunnel takes unit source, unit target returns nothing
local integer id_S = GetUnitUserData(source)
local integer id_T = GetUnitUserData(target)
local integer i = 0
local boolean isUnitValid = false
//Source
if ExitsNetworkInstance[id_S] != 0 then
set G.EXITS = ExitsNetworkInstance[id_S]
set G.TIMEOUT = TeleportTimeInstance[id_S]
loop // loop through all of the source's exits and return true on isUnitValid if there's a match.
set i = i + 1
if target == G.EXITS.unit[i] then
set isUnitValid = true
endif
exitwhen i > NumberOfExits[id_S] or isUnitValid
endloop
if isUnitValid then // if isUnitValid, clear all associated variables and cascade all elements of the list down by 1.
call G.EXITS.remove(i)
call G.TIMEOUT.remove(i)
loop
exitwhen i > NumberOfExits[id_S]
set G.EXITS.unit[i] = G.EXITS.unit[i + 1]
set G.TIMEOUT.real[i] = G.TIMEOUT.real[i + 1]
set i = i + 1
endloop
call G.EXITS.remove(NumberOfExits[id_S])
call G.TIMEOUT.remove(NumberOfExits[id_S])
set NumberOfExits[id_S] = NumberOfExits[id_S] - 1
endif
// if the source no longer has any exits, the Tables are destroyed.
if NumberOfExits[id_S] < 1 then
call G.EXITS.destroy()
call G.TIMEOUT.destroy()
set ExitsNetworkInstance[id_S] = 0
set TeleportTimeInstance[id_S] = 0
endif
set i = 0
set isUnitValid = false
endif
//Target
if EntrancesNetworkInstance[id_T] != 0 then
set G.ENTRANCES = EntrancesNetworkInstance[id_T]
loop // just like above loop checks if the source matches any registered entrances.
set i = i + 1
if source == G.ENTRANCES.unit[i] then
set isUnitValid = true
endif
exitwhen i > NumberOfEntrances[id_T] or isUnitValid
endloop
if isUnitValid then
call G.ENTRANCES.remove(i)
loop
exitwhen i > NumberOfEntrances[id_T]
set G.ENTRANCES.unit[i] = G.ENTRANCES.unit[i + 1]
set i = i + 1
endloop
call G.ENTRANCES.remove(NumberOfEntrances[id_T])
set NumberOfEntrances[id_T] = NumberOfEntrances[id_T] - 1
endif
if NumberOfEntrances[id_T] < 1 then
call G.ENTRANCES.destroy()
set EntrancesNetworkInstance[id_T] = 0
endif
endif
endmethod
// This method will establish a ONE-WAY connection between two units called a tunnel. The
// tunnel is not automatically destroyed on death so it's up to the user to determine how
// they are removed.
static method createTunnel takes unit source, unit target, real teleport_distance, real timeout returns nothing
local integer id_S = GetUnitUserData(source)
local integer id_T = GetUnitUserData(target)
local integer i
// if either the source unit or the destination unit are null, this trigger does nothing.
if source != null and target != null then
//Source
set G.EXITS = ExitsNetworkInstance[id_S]
if G.EXITS == 0 then // this identifies whether the source has any prior connections
set G.EXITS = Table.create()
set G.TIMEOUT = Table.create()
set ExitsNetworkInstance[id_S] = G.EXITS
set TeleportTimeInstance[id_S] = G.TIMEOUT
call CreateSmartOrderTracker(source, teleport_distance)
else
// if the source already has at least 1 tunnel, check if the destination unit
// is already one of its registered targets.
set i = 1
loop
// if destination is already registered, end the function.
if target == G.EXITS.unit[i] then
return
endif
exitwhen i > NumberOfExits[id_S]
set i = i + 1
endloop
endif
set NumberOfExits[id_S] = NumberOfExits[id_S] + 1
set G.EXITS.unit[NumberOfExits[id_S]] = target
set G.TIMEOUT.real[NumberOfExits[id_S]] = timeout
//Target
set NumberOfEntrances[id_T] = NumberOfEntrances[id_T] + 1
set G.ENTRANCES = EntrancesNetworkInstance[id_T]
if G.ENTRANCES == 0 then
set G.ENTRANCES = Table.create()
set EntrancesNetworkInstance[id_T] = G.ENTRANCES
endif
set G.ENTRANCES.unit[NumberOfEntrances[id_T]] = source
endif
endmethod
endstruct
// here a SmartTrack event is registered to detect units that come in range of a teleporter.
// Whenever SmartTrack fires a ST_UNIT_IN_RANGE, CustomWaygate.preTeleportation will run.
private module CustomWaygateInit
private static method onInit takes nothing returns nothing
set EVENT_WAYGATE_PRE_TELEPORT = CreateNativeEvent()
set EVENT_WAYGATE_POST_TELEPORT = CreateNativeEvent()
set EVENT_WAYGATE_DURATION_START = CreateNativeEvent()
set EVENT_WAYGATE_NETWORK_PRIMED = CreateNativeEvent()
set EVENT_WAYGATE_CANCELLED = CreateNativeEvent()
call RegisterNativeEvent(EVENT_SMART_TRACK_IN_RANGE, function CustomWaygate.preTeleportation)
endmethod
endmodule
private struct init
implement CustomWaygateInit
endstruct
endlibrary
scope onCustomWaygateEvents initializer init
globals
private effect array TeleportFX
private effect array TeleportReadyFX
endglobals
private function onTrackStart takes nothing returns nothing
local unit smarty = GetSmartUnit()
/*
The following are examples of how to filter out units.
*/
// Player restriction
if GetUnitTypeId(GetTracker()) == 'emow' and GetUnitTypeId(smarty) != 'ewsp' then
call SmartTrack.stop(smarty, true)// if true, the units will stop in their tracks
call BJDebugMsg("Only Wisps may teleport through Moon Wells.")
endif
set smarty = null
endfunction
// Pre-Teleportation
private function onPreTeleport takes nothing returns nothing
local unit source = GetWaygateSource()
local unit traveller = GetWaygateTraveller()
local real xTraveller = GetUnitX(traveller)
local real yTraveller = GetUnitY(traveller)
local integer id_traveller = GetUnitUserData(traveller)
// call StopTeleport() if you wish to cancel the internal teleportation
// And do you're own here.
if TeleportReadyFX[id_traveller] != null then
call DestroyEffect(TeleportReadyFX[id_traveller])
set TeleportReadyFX[id_traveller] = null
endif
// Arcane Vault (Moon Bowl)
if GetUnitTypeId(source) == 'hvlt' then
if NumberOfRunes[GetUnitUserData(source)] > 0 then
call ManaRunes.consumeRune(source)
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl", xTraveller, yTraveller))
else
call StopTeleport()
call BlizzardMessage("Insufficient Mana Runes", "|cffffcc00", 31, GetOwningPlayer(traveller))
endif
// Arcane Tower
elseif GetUnitTypeId(source) == 'hatw' then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl", xTraveller, yTraveller))
// Moon Well
elseif GetUnitTypeId(source) == 'emow' then
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl", xTraveller, yTraveller))
// Orc Burrow
elseif GetUnitTypeId(source) == 'otrb' then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\StrongDrink\\BrewmasterMissile.mdl", xTraveller, yTraveller))
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", xTraveller, yTraveller))
endif
set source = null
set traveller = null
endfunction
// Post-teleportation
private function onPostTeleport takes nothing returns nothing
local unit source = GetWaygateSource()
local unit traveller = GetWaygateTraveller()
local real xTraveller = GetUnitX(traveller)
local real yTraveller = GetUnitY(traveller)
local integer id_traveller = GetUnitUserData(traveller)
if TeleportFX[id_traveller] != null then
call DestroyEffect(TeleportFX[id_traveller])
set TeleportFX[id_traveller] = null
endif
if TeleportReadyFX[id_traveller] != null then
call DestroyEffect(TeleportReadyFX[id_traveller])
set TeleportReadyFX[id_traveller] = null
endif
// Arcane Vault (Moon Bowl)
if GetUnitTypeId(source) == 'hvlt' then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl", xTraveller, yTraveller))
// Arcane Tower
elseif GetUnitTypeId(source) == 'hatw' then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl", xTraveller, yTraveller))
// Moon Well
elseif GetUnitTypeId(source) == 'emow' then
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl", xTraveller, yTraveller))
// Orb Burrow
elseif GetUnitTypeId(source) == 'otrb' then
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Other\\StrongDrink\\BrewmasterMissile.mdl", xTraveller, yTraveller))
call DestroyEffect(AddSpecialEffect("Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl", xTraveller, yTraveller))
endif
set source = null
set traveller = null
endfunction
// Teleport Duration Start
private function onTeleportDuration takes nothing returns nothing
// There is no Target in this event for networks.
local unit source = GetWaygateSource()
local unit traveller = GetWaygateTraveller()
local integer id_traveller = GetUnitUserData(traveller)
if TeleportFX[id_traveller] != null then
call DestroyEffect(TeleportFX[id_traveller])
set TeleportFX[id_traveller] = null
endif
if TeleportReadyFX[id_traveller] != null then
call DestroyEffect(TeleportReadyFX[id_traveller])
set TeleportReadyFX[id_traveller] = null
endif
// Arcane Tower
if GetUnitTypeId(source) == 'hatw' then
set TeleportFX[id_traveller] = AddSpecialEffectTarget("war3mapImported\\WarpingIn_NoGeo.mdx", traveller, "origin")
// Orc Burrow
elseif GetUnitTypeId(source) == 'otrb' then
set TeleportFX[id_traveller] = AddSpecialEffectTarget("war3mapImported\\DustyMissile.mdx", traveller, "origin")
endif
set source = null
set traveller = null
endfunction
// Network primed
private function onNetworkPrimed takes nothing returns nothing
local unit traveller = GetWaygateTraveller()
local integer id_traveller = GetUnitUserData(traveller)
if TeleportReadyFX[id_traveller] != null then
call DestroyEffect(TeleportReadyFX[id_traveller])
set TeleportReadyFX[id_traveller] = null
endif
// Arcane Tower
if GetUnitTypeId(GetWaygateSource()) == 'hatw' then
set TeleportReadyFX[GetUnitUserData(traveller)] = AddSpecialEffectTarget("Abilities\\Spells\\Human\\ManaFlare\\ManaFlareTarget.mdl", traveller, "overhead")
endif
set traveller = null
endfunction
// Teleport interrupted
private function onTeleportInterrupted takes nothing returns nothing
local integer id_traveller = GetUnitUserData(GetWaygateTraveller())
if TeleportFX[id_traveller] != null then
call DestroyEffect(TeleportFX[id_traveller])
set TeleportFX[id_traveller] = null
endif
if TeleportReadyFX[id_traveller] != null then
call DestroyEffect(TeleportReadyFX[id_traveller])
set TeleportReadyFX[id_traveller] = null
endif
endfunction
//===========================================================================
private function init takes nothing returns nothing
call RegisterNativeEvent(EVENT_WAYGATE_PRE_TELEPORT, function onPreTeleport)
call RegisterNativeEvent(EVENT_WAYGATE_POST_TELEPORT, function onPostTeleport)
call RegisterNativeEvent(EVENT_WAYGATE_DURATION_START, function onTeleportDuration)
call RegisterNativeEvent(EVENT_WAYGATE_NETWORK_PRIMED, function onNetworkPrimed)
call RegisterNativeEvent(EVENT_WAYGATE_CANCELLED, function onTeleportInterrupted)
// Filter - use SmartTrack
call RegisterNativeEvent(EVENT_SMART_TRACK_STARTED, function onTrackStart)
endfunction
endscope
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 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 LastOrder initializer Init
//******************************************************************************
//* BY: Rising_Dusk
//*
//* LastOrder is a library that was designed to allow interfacing with the last
//* N orders any unit on your map has received. This library was also designed
//* to be used as a means to reissue lost orders to a unit either after
//* preventing a spell cast or in many other situations.
//*
//* There are two configuration constants for you to play with in using this
//* script. ORDERS_TO_HOLD is basically the size of the game's memory for each
//* individual unit. The larger the number is, the further back you can retrace
//* a unit's order list. Setting this value to 3 suffices for all actual
//* mechanics purposes. Raise it only if you have a specific application where
//* you need more. Lowering it to 2 covers most cases, but may miss a few, and
//* lowering it to 1 prevents you from adequately canceling spells and properly
//* reissuing previous orders. I recommend leaving it alone at 3. The MAX_ORDERS
//* constant is the number of orders that the system holds over all units at a
//* given time. If you are worried about running out, go ahead and increase it,
//* but be aware that it will use big arrays (which are slower) if you use a
//* number over 8191. Don't lower it under 8191, there's no reason to. Don't
//* change the non-private constants, there's no reason to.
//*
//* function GetPastOrder takes unit u, integer whichOrder returns order
//* function GetPastOrderId takes unit u, integer whichOrder returns integer
//* function GetPastOrderString takes unit u, integer whichOrder returns string
//* function GetPastOrderType takes unit u, integer whichOrder returns integer
//* function GetPastOrderX takes unit u, integer whichOrder returns real
//* function GetPastOrderY takes unit u, integer whichOrder returns real
//* function GetPastOrderTarget takes unit u, integer whichOrder returns widget
//*
//* The above API is the main list of functions the user can use to interface
//* with past orders. If you want the immediate last order for a player, use 1
//* for the whichOrder integer. The 'order' returned by GetPastOrder is a struct
//* that contains all information of an issued order. If you want to interface
//* directly with the struct and skip all of the interfacing wrappers, you have
//* access to them via the following commands:
//*
//* [unit] .u The unit being ordered.
//* [integer] .id The order id of the past order.
//* [integer] .typ The type of the past order. (See constants)
//* [boolean] .fin A flag indicating whether the order was finished.
//* [widget] .tar The target widget of the past order.
//* [real] .x The x coordinate of the target point of the order.
//* [real] .y The y coordinate of the target point of the order.
//*
//* There is also a sizable API for backwards compatibility with older versions
//* of the library. This API assumes that you're talking about a specific past
//* order, either the lastmost order or the second lastmost. In most cases,
//* these are still useful because they remove an argument from the call.
//*
//* function GetLastOrder takes unit u returns order
//* function GetLastOrderId takes unit u returns integer
//* function GetLastOrderString takes unit u returns string
//* function GetLastOrderType takes unit u returns integer
//* function GetLastOrderX takes unit u returns real
//* function GetLastOrderY takes unit u returns real
//* function GetLastOrderTarget takes unit u returns widget
//* function IsLastOrderFinished takes unit u returns boolean
//*
//* Besides being able to get information about all of the past orders a unit
//* has been issued, the most useful part of this system is actually reissuing
//* those orders as a means to fix lost orders or intercept and prevent spell
//* casting. The following API is then available to the user:
//*
//* function IssuePastOrder takes unit u, integer whichOrder returns boolean
//* function IssueLastOrder takes unit u returns boolean
//* function IssueSecondLastOrder takes unit u returns boolean
//* function AbortOrder takes unit u returns boolean
//*
//* If you want to reissue a past order for a given unit, IssuePastOrder is the
//* function that you'll want to use on a unit. To issue the last or second last
//* orders, there are functions for those as well. (They mostly exist for
//* backwards compatibility) AbortOrder is a means for anyone using this script
//* to stop a unit from running any given order that they happen to have at any
//* moment. AbortOrder is used in conjunction with a supplementary library,
//* AbortSpell, to seamlessly replicate WC3's spell error messages.
//*
//* function IssueArbitraryOrder takes unit u, order o returns boolean
//*
//* IssueArbitraryOrder is special in that it ignores many of the normal checks
//* and automatically forces a unit to take on a specific order. This can be
//* convenient if you want to make one unit take on another unit's order, for
//* instance. Be forewarned that this overwrites whatever the unit was doing and
//* issues the order no matter what, so use only as needed.
//*
//* If you have any further questions regarding LastOrder or how to use it, feel
//* free to visit [url]www.wc3c.net[/url] and ask questions there. This library should only
//* ever be released at WC3C and at no other site. Please give credits if this
//* library finds its way into your maps, and otherwise thanks for reading!
//*
//* Enjoy!
//*
globals
//Order type variables
constant integer ORDER_TYPE_TARGET = 1
constant integer ORDER_TYPE_POINT = 2
constant integer ORDER_TYPE_IMMEDIATE = 3
//System constants
private constant integer ORDERS_TO_HOLD = 3 //How many past orders the
//system holds for each unit.
//Should be at least 2.
private constant integer MAX_ORDERS = 8191 //The max number of orders
//the system can maintain.
//Going over 8191 uses big
//arrays, which are slower
//than normal arrays.
endglobals
globals
private hashtable ht = InitHashtable()
endglobals
struct order[MAX_ORDERS]
unit u
integer id
integer typ
boolean fin
widget tar
real x
real y
static method create takes unit ordered, integer ordid, integer ordtyp, widget target, real ordx, real ordy returns order
local order o = order.allocate()
local integer i = ORDERS_TO_HOLD
local integer hid = GetHandleId(ordered)
set o.u = ordered
set o.id = ordid
set o.typ = ordtyp
set o.fin = false
set o.tar = target
set o.x = ordx
set o.y = ordy
//Handle stored orders in the hashtable
loop
//We hold up to the constant ORDERS_TO_HOLD in the table
exitwhen i == 1
//Moves the N-1th order to the Nth slot, etc. except storing new last order
if HaveSavedInteger(ht, hid, i-1) then
if i == ORDERS_TO_HOLD and HaveSavedInteger(ht, hid, i) then
//Destroy lastmost order struct
call order.destroy(order(LoadInteger(ht, hid, i)))
endif
//Can only do this if the N-1th order exists
call SaveInteger(ht, hid, i, LoadInteger(ht, hid, i-1))
endif
set i = i - 1
endloop
//Store the new order to the hashtable as 1th last order
call SaveInteger(ht, hid, 1, integer(o))
return o
endmethod
endstruct
//******************************************************************************
//! textmacro LastOrderDebug takes ORDER, RETURN
debug if $ORDER$ > ORDERS_TO_HOLD then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Order out of range")
debug return $RETURN$
debug endif
debug if not HaveSavedInteger(ht, GetHandleId(u), $ORDER$) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: The "+I2S($ORDER$)+"th order doesn't exist")
debug return $RETURN$
debug endif
//! endtextmacro
function GetPastOrder takes unit u, integer whichOrder returns order
//! runtextmacro LastOrderDebug("whichOrder", "0")
return order(LoadInteger(ht, GetHandleId(u), whichOrder))
endfunction
function GetPastOrderId takes unit u, integer whichOrder returns integer
//! runtextmacro LastOrderDebug("whichOrder", "0")
return order(LoadInteger(ht, GetHandleId(u), whichOrder)).id
endfunction
function GetPastOrderString takes unit u, integer whichOrder returns string
//! runtextmacro LastOrderDebug("whichOrder", "\"\"")
return OrderId2String(order(LoadInteger(ht, GetHandleId(u), whichOrder)).id)
endfunction
function GetPastOrderType takes unit u, integer whichOrder returns integer
//! runtextmacro LastOrderDebug("whichOrder", "0")
return order(LoadInteger(ht, GetHandleId(u), whichOrder)).typ
endfunction
function GetPastOrderX takes unit u, integer whichOrder returns real
//! runtextmacro LastOrderDebug("whichOrder", "0.")
return order(LoadInteger(ht, GetHandleId(u), whichOrder)).x
endfunction
function GetPastOrderY takes unit u, integer whichOrder returns real
//! runtextmacro LastOrderDebug("whichOrder", "0.")
return order(LoadInteger(ht, GetHandleId(u), whichOrder)).y
endfunction
function GetPastOrderTarget takes unit u, integer whichOrder returns widget
//! runtextmacro LastOrderDebug("whichOrder", "null")
return order(LoadInteger(ht, GetHandleId(u), whichOrder)).tar
endfunction
//******************************************************************************
function GetLastOrder takes unit u returns order
return GetPastOrder(u, 1)
endfunction
function GetLastOrderId takes unit u returns integer
return GetPastOrderId(u, 1)
endfunction
function GetLastOrderString takes unit u returns string
return GetPastOrderString(u, 1)
endfunction
function GetLastOrderType takes unit u returns integer
return GetPastOrderType(u, 1)
endfunction
function GetLastOrderX takes unit u returns real
return GetPastOrderX(u, 1)
endfunction
function GetLastOrderY takes unit u returns real
return GetPastOrderY(u, 1)
endfunction
function GetLastOrderTarget takes unit u returns widget
return GetPastOrderTarget(u, 1)
endfunction
function IsLastOrderFinished takes unit u returns boolean
//! runtextmacro LastOrderDebug("1", "false")
return GetUnitCurrentOrder(u) == 0 or order(LoadInteger(ht, GetHandleId(u), 1)).fin
endfunction
//******************************************************************************
private function OrderFilter takes unit u, integer id returns boolean
//* Excludes specific orders or unit types from registering with the system
//*
//* 851972: stop
//* Stop is excluded from the system because it is the order that
//* tells a unit to do nothing. It should be ignored by the system.
//*
//* 851971: smart
//* 851986: move
//* 851983: attack
//* 851984: attackground
//* 851990: patrol
//* 851993: holdposition
//* These are the UI orders that are passed to the system.
//*
//* 851973: stunned
//* This order is issued when a unit is stunned onto the stunner
//* It's ignored by the system, since you'd never want to reissue it
//*
//* >= 852055, <= 852762
//* These are all spell IDs from defend to incineratearrowoff with
//* a bit of leeway at the ends for orders with no strings.
//*
return id == 851971 or id == 851986 or id == 851983 or id == 851984 or id == 851990 or id == 851993 or (id >= 852055 and id <= 852762)
endfunction
private function IssuePastOrderFilter takes unit u, integer whichOrder returns boolean
//* Some criteria for whether or not a unit's last order should be given
//*
//* INSTANT type orders are excluded because generally, reissuing an instant
//* order doesn't make sense. You can remove that check below if you'd like,
//* though.
//*
//* The Type check is really just to ensure that no spell recursion can
//* occur with IssueLastOrder. The problem with intercepting the spell cast
//* event is that it happens after the order is 'caught' and registered to
//* this system. Therefore, to just IssueLastOrder tells it to recast the
//* spell! That's a problem, so we need a method to eliminate it.
//*
return GetUnitTypeId(u) != 0 and not IsUnitType(u, UNIT_TYPE_DEAD) and GetPastOrderType(u, whichOrder) != 0 and GetPastOrderType(u, whichOrder) != ORDER_TYPE_IMMEDIATE
endfunction
//******************************************************************************
function IssuePastOrder takes unit u, integer whichOrder returns boolean
local order o = GetPastOrder(u, whichOrder)
if IssuePastOrderFilter(u, whichOrder) and not o.fin then
if o.typ == ORDER_TYPE_TARGET then
return IssueTargetOrderById(u, o.id, o.tar)
elseif o.typ == ORDER_TYPE_POINT then
if o.id == 851971 then
//This adjusts for a bug in the point order's boolean return
//when issuing a smart order
call IssuePointOrderById(u, o.id, o.x, o.y)
return true
else
return IssuePointOrderById(u, o.id, o.x, o.y)
endif
elseif o.typ == ORDER_TYPE_IMMEDIATE then
return IssueImmediateOrderById(u, o.id)
endif
endif
return false
endfunction
function IssueLastOrder takes unit u returns boolean
return IssuePastOrder(u, 1)
endfunction
function IssueSecondLastOrder takes unit u returns boolean
return IssuePastOrder(u, 2)
endfunction
function IssueArbitraryOrder takes unit u, order o returns boolean
if o.typ == ORDER_TYPE_TARGET then
return IssueTargetOrderById(u, o.id, o.tar)
elseif o.typ == ORDER_TYPE_POINT then
if o.id == 851971 then
//This adjusts for a bug in the point order's boolean return
//when issuing a smart order
call IssuePointOrderById(u, o.id, o.x, o.y)
return true
else
return IssuePointOrderById(u, o.id, o.x, o.y)
endif
elseif o.typ == ORDER_TYPE_IMMEDIATE then
return IssueImmediateOrderById(u, o.id)
endif
return false
endfunction
function AbortOrder takes unit u returns boolean
if IsUnitPaused(u) then
return false
else
call PauseUnit(u, true)
call IssueImmediateOrder(u, "stop")
call PauseUnit(u, false)
endif
return true
endfunction
//**********************************************************
private function Conditions takes nothing returns boolean
return OrderFilter(GetTriggerUnit(), GetIssuedOrderId())
endfunction
private function Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local widget t = GetOrderTarget()
local integer oid = GetIssuedOrderId()
local integer oty = 0
if GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER then
call order.create(u, oid, ORDER_TYPE_TARGET, t, GetWidgetX(t), GetWidgetY(t))
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER then
call order.create(u, oid, ORDER_TYPE_POINT, null, GetOrderPointX(), GetOrderPointY())
elseif GetTriggerEventId() == EVENT_PLAYER_UNIT_ISSUED_ORDER then
call order.create(u, oid, ORDER_TYPE_IMMEDIATE, null, GetUnitX(u), GetUnitY(u))
debug else
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Invalid order type")
endif
set u = null
set t = null
endfunction
//**********************************************************
private function SpellActions takes nothing returns nothing
local order o = GetPastOrder(GetTriggerUnit(), 1)
set o.fin = true
endfunction
//**********************************************************
private function OnAdd takes nothing returns boolean
local integer hid = GetHandleId(GetFilterUnit())
local integer i = ORDERS_TO_HOLD
//Handle stored orders in the hashtable
loop
//We hold up to the constant ORDERS_TO_HOLD in the table
exitwhen i == 0
//If any of the N orders exist for this handle id, kill them all
if HaveSavedInteger(ht, hid, i) then
call order.destroy(order(LoadInteger(ht, hid, i)))
call RemoveSavedInteger(ht, hid, i)
endif
set i = i - 1
endloop
return false
endfunction
//**********************************************************
private function Init takes nothing returns nothing
local trigger trg = CreateTrigger()
local region re = CreateRegion()
local rect m = GetWorldBounds()
//Main order catching trigger
call TriggerAddAction(trg, function Actions)
call TriggerAddCondition(trg, Condition(function Conditions))
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_ISSUED_ORDER)
//Spell trigger to set a flag that indicates a spell order's completion
set trg = CreateTrigger()
call TriggerAddAction(trg, function SpellActions)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
//Entering world trigger that clears old data from handle ids
set trg = CreateTrigger()
call RegionAddRect(re, m)
call TriggerRegisterEnterRegion(trg, re, Condition(function OnAdd))
call RemoveRect(m)
set trg = null
set re = null
set m = null
endfunction
endlibrary
library BlizzardMessage /* v1.0.0.4
*************************************************************************************
*
* For creating Blizzard-like messages with a variety of sounds.
*
*************************************************************************************
*
* Credits
*
* Vexorian
* -----------------------
*
* The original SimError
*
*
* Maker
* -----------------------
*
* Sound label help
*
************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* Function
* -----------------------
*
* function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
*
* Description
* -----------------------
*
* This function creates a timed Blizzard-like message with a sound for a player.
*
* Takes
* -----------------------
*
* string msg
* - The message to be displayed.
*
* string colorCode
* - Adds a color for the message through an ARBG color code. A null value creates a normal white text.
*
* real dur
* - The duration of the message.
*
* integer soundLabel
* - The sound to be played.
*
* player forPlayer
* - The player that receives the message.
*
* Returns
* -----------------------
*
* nothing
*
*************************************************************************************
*
* SETTINGS
* -----------------------
*
*/
globals
constant real MSG_DURATION = 2.00 //Message Duration
/*************************************************************************************
*
* Sound Labels
*
*************************************************************************************/
constant integer ALLY_HERO_DIES_HUMAN = 0
constant integer ALLY_HERO_DIES_NAGA = 1
constant integer ALLY_HERO_DIES_NIGHTELF = 2
constant integer ALLY_HERO_DIES_ORC = 3
constant integer ALLY_HERO_DIES_UNDEAD = 4
constant integer ALLY_TOWN_UNDER_ATTACK_HUMAN = 5
constant integer ALLY_TOWN_UNDER_ATTACK_NAGA = 6
constant integer ALLY_TOWN_UNDER_ATTACK_NIGHTELF = 7
constant integer ALLY_TOWN_UNDER_ATTACK_ORC = 8
constant integer ALLY_TOWN_UNDER_ATTACK_UNDEAD = 9
constant integer ALLY_UNDER_ATTACK_HUMAN = 10
constant integer ALLY_UNDER_ATTACK_NAGA = 11
constant integer ALLY_UNDER_ATTACK_NIGHTELF = 12
constant integer ALLY_UNDER_ATTACK_ORC = 13
constant integer ALLY_UNDER_ATTACK_UNDEAD = 14
constant integer ARRANGED_TEAM_INVITATION = 15
constant integer AUTO_CAST_BUTTON_CLICK = 16
constant integer CANT_PLACE_HUMAN = 17
constant integer CANT_PLACE_NAGA = 18
constant integer CANT_PLACE_NIGHTELF = 19
constant integer CANT_PLACE_ORC = 20
constant integer CANT_PLACE_UNDEAD = 21
constant integer CANT_ROOT_NIGHTELF = 22
constant integer CHATROOM_TIMER_TICK = 23
constant integer CLAN_INVITATION = 24
constant integer CONSTRUCTING_BUILDING_DEFAULT = 25
constant integer CONSTRUCTING_BUILDING_NAGA = 26
constant integer CONSTRUCTING_BUILDING_NIGHTELF = 27
constant integer CONSTRUCTING_BUILDING_ORC = 28
constant integer CONSTRUCTING_BUILDING_UNDEAD = 29
constant integer CREEP_AGGRO = 30
constant integer ERROR_MESSAGE = 31
constant integer GAME_FOUND = 32
constant integer GLUE_SCREEN_CLICK = 33
constant integer GOLD_MINE_COLLAPSE_HUMAN = 34
constant integer GOLD_MINE_COLLAPSE_NAGA = 35
constant integer GOLD_MINE_COLLAPSE_NIGHTELF = 36
constant integer GOLD_MINE_COLLAPSE_ORC = 37
constant integer GOLD_MINE_COLLAPSE_UNDEAD = 38
constant integer GOLD_MINE_LOW_GENERIC = 39
constant integer GOLD_MINE_LOW_HUMAN = 40
constant integer GOLD_MINE_LOW_NAGA = 41
constant integer GOLD_MINE_LOW_NIGHTELF = 42
constant integer GOLD_MINE_LOW_ORC = 43
constant integer GOLD_MINE_LOW_UNDEAD = 44
constant integer GOOD_JOB = 45
constant integer HERO_DIES_GENERIC = 46
constant integer HERO_DIES_HUMAN = 47
constant integer HERO_DIES_NAGA = 48
constant integer HERO_DIES_NIGHTELF = 49
constant integer HERO_DIES_ORC = 50
constant integer HERO_DIES_UNDEAD = 51
constant integer HINT = 52
constant integer IN_GAME_CHAT_WHAT = 53
constant integer INTERFACE_CLICK = 54
constant integer INTERFACE_ERROR = 55
constant integer INVENTORY_FULL_HUMAN = 56
constant integer INVENTORY_FULL_NAGA = 57
constant integer INVENTORY_FULL_NIGHTELF = 58
constant integer INVENTORY_FULL_ORC = 59
constant integer INVENTORY_FULL_UNDEAD = 60
constant integer ITEM_DROP = 61
constant integer ITEM_GET = 62
constant integer ITEM_REWARD = 63
constant integer JOB_DONE_SOUND_HUMAN = 64
constant integer JOB_DONE_SOUND_NAGA = 65
constant integer JOB_DONE_SOUND_NIGHTELF = 66
constant integer JOB_DONE_SOUND_ORC = 67
constant integer JOB_DONE_SOUND_UNDEAD = 68
constant integer MAP_PING = 69
constant integer MENU_BUTTON_CLICK = 70
constant integer NEW_TOURNAMENT = 71
constant integer NO_FOOD_HUMAN = 72
constant integer NO_FOOD_NAGA = 73
constant integer NO_FOOD_NIGHTELF = 74
constant integer NO_FOOD_ORC = 75
constant integer NO_FOOD_UNDEAD = 76
constant integer NO_GOLD_GENERIC = 77
constant integer NO_GOLD_HUMAN = 78
constant integer NO_GOLD_NAGA = 79
constant integer NO_GOLD_NIGHTELF = 80
constant integer NO_GOLD_ORC = 81
constant integer NO_GOLD_UNDEAD = 82
constant integer NO_LUMBER_HUMAN = 83
constant integer NO_LUMBER_NAGA = 84
constant integer NO_LUMBER_NIGHTELF = 85
constant integer NO_LUMBER_ORC = 86
constant integer NO_LUMBER_UNDEAD = 87
constant integer NO_MANA_GENERIC = 88
constant integer NO_MANA_HUMAN = 89
constant integer NO_MANA_NAGA = 90
constant integer NO_MANA_NIGHTELF = 91
constant integer NO_MANA_ORC = 92
constant integer NO_MANA_UNDEAD = 93
constant integer OFF_BLIGHT_UNDEAD = 94
constant integer PAUSE_GAME = 95
constant integer PLACE_BUILDING_DEFAULT = 96
constant integer QUEST_COMPLETED = 97
constant integer QUEST_FAILED = 98
constant integer QUEST_LOG_MODIFIED = 99
constant integer QUEST_NEW = 100
constant integer QUEST_UPDATE = 101
constant integer RALLY_POINT_PLACE = 102
constant integer RESCUE = 103
constant integer RESEARCH_COMPLETE_GENERIC = 104
constant integer RESEARCH_COMPLETE_HUMAN = 105
constant integer RESEARCH_COMPLETE_NAGA = 106
constant integer RESEARCH_COMPLETE_NIGHTELF = 107
constant integer RESEARCH_COMPLETE_ORC = 108
constant integer RESEARCH_COMPLETE_UNDEAD = 109
constant integer SCORE_SCREEN_TAB_CLICK = 110
constant integer SECRET_FOUND = 111
constant integer SUB_GROUP_SELECTION_CHANGE = 112
constant integer TOWN_ATTACK_GENERIC = 113
constant integer TOWN_ATTACK_HUMAN = 114
constant integer TOWN_ATTACK_NAGA = 115
constant integer TOWN_ATTACK_NIGHTELF = 116
constant integer TOWN_ATTACK_ORC = 117
constant integer TOWN_ATTACK_UNDEAD = 118
constant integer UNDER_ATTACK_HUMAN = 119
constant integer UNDER_ATTACK_NAGA = 120
constant integer UNDER_ATTACK_NIGHTELF = 121
constant integer UNDER_ATTACK_ORC = 122
constant integer UNDER_ATTACK_UNDEAD = 123
constant integer UPGRADE_COMPLETE_GENERIC = 124
constant integer UPGRADE_COMPLETE_HUMAN = 125
constant integer UPGRADE_COMPLETE_NAGA = 126
constant integer UPGRADE_COMPLETE_NIGHTELF = 127
constant integer UPGRADE_COMPLETE_ORC = 128
constant integer UPGRADE_COMPLETE_UNDEAD = 129
constant integer UPKEEP_LEVEL = 130
constant integer WARNING = 131
constant integer WAYPOINT = 132
/* private */ string array SOUND_LABEL
endglobals
/*
************************************************************************************
*/
function BlizzardMessage takes string msg, string colorCode, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, MSG_DURATION, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
function BlizzardMessageTimed takes string msg, string colorCode, real dur, integer soundLabel, player forPlayer returns nothing
local sound snd
local string s
debug if soundLabel >= 0 and soundLabel <= 132 and forPlayer != null then
set snd = CreateSoundFromLabel (SOUND_LABEL[soundLabel], false, false, false, 10, 10)
if colorCode != null then
set s = "|r"
else
set colorCode = ""
set s = ""
endif
if GetLocalPlayer () == forPlayer then
call ClearTextMessages ()
call DisplayTimedTextToPlayer (forPlayer, 0.52, 0.96, dur, "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n" + colorCode + msg + s)
call StartSound (snd)
endif
if s != null then
set s = ""
endif
set snd = null
debug else
debug if msg == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null message")
debug endif
debug if forPlayer == null
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Null forPlayer")
debug endif
debug if soundLabel < 0 or soundLabel > 132
debug call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "[BlizzardMessage] [BlizzardMessage] Invalid sound label")
debug endif
debug endif
endfunction
private module BMInit
private static method onInit takes nothing returns nothing
call init()
endmethod
endmodule
private struct InitStruct extends array
private static method init takes nothing returns nothing
set SOUND_LABEL[0] = "AllyHeroDiesHuman"
set SOUND_LABEL[1] = "AllyHeroDiesNaga"
set SOUND_LABEL[2] = "AllyHeroDiesNightElf"
set SOUND_LABEL[3] = "AllyHeroDiesOrc"
set SOUND_LABEL[4] = "AllyHeroDiesUndead"
set SOUND_LABEL[5] = "AllyTownUnderAttackHuman"
set SOUND_LABEL[6] = "AllyTownUnderAttackNaga"
set SOUND_LABEL[7] = "AllyTownUnderAttackNightElf"
set SOUND_LABEL[8] = "AllyTownUnderAttackOrc"
set SOUND_LABEL[9] = "AllyTownUnderAttackUndead"
set SOUND_LABEL[10] = "AllyUnderAttackHuman"
set SOUND_LABEL[11] = "AllyUnderAttackNaga"
set SOUND_LABEL[12] = "AllyUnderAttackNightElf"
set SOUND_LABEL[13] = "AllyUnderAttackOrc"
set SOUND_LABEL[14] = "AllyUnderAttackUndead"
set SOUND_LABEL[15] = "ArrangedTeamInvitation"
set SOUND_LABEL[16] = "AutoCastButtonClick"
set SOUND_LABEL[17] = "CantPlaceHuman"
set SOUND_LABEL[18] = "CantPlaceNaga"
set SOUND_LABEL[19] = "CantPlaceNightElf"
set SOUND_LABEL[20] = "CantPlaceOrc"
set SOUND_LABEL[21] = "CantPlaceUndead"
set SOUND_LABEL[22] = "CantRootNightElf"
set SOUND_LABEL[23] = "ChatroomTimerTick"
set SOUND_LABEL[24] = "ClanInvitation"
set SOUND_LABEL[25] = "ConstructingBuildingDefault"
set SOUND_LABEL[26] = "ConstructingBuildingNaga"
set SOUND_LABEL[27] = "ConstructingBuildingNightElf"
set SOUND_LABEL[28] = "ConstructingBuildingOrc"
set SOUND_LABEL[29] = "ConstructingBuildingUndead"
set SOUND_LABEL[30] = "CreepAggro"
set SOUND_LABEL[31] = "ErrorMessage"
set SOUND_LABEL[32] = "GameFound"
set SOUND_LABEL[33] = "GlueScreenClick"
set SOUND_LABEL[34] = "GoldMineCollapseHuman"
set SOUND_LABEL[35] = "GoldMineCollapseNaga"
set SOUND_LABEL[36] = "GoldMineCollapseNightElf"
set SOUND_LABEL[37] = "GoldMineCollapseOrc"
set SOUND_LABEL[38] = "GoldMineCollapseUndead"
set SOUND_LABEL[39] = "GoldMineLowGeneric"
set SOUND_LABEL[40] = "GoldMineLowHuman"
set SOUND_LABEL[41] = "GoldMineLowNaga"
set SOUND_LABEL[42] = "GoldMineLowNightElf"
set SOUND_LABEL[43] = "GoldMineLowOrc"
set SOUND_LABEL[44] = "GoldMineLowUndead"
set SOUND_LABEL[45] = "GoodJob"
set SOUND_LABEL[46] = "HeroDiesGeneric"
set SOUND_LABEL[47] = "HeroDiesHuman"
set SOUND_LABEL[48] = "HeroDiesNaga"
set SOUND_LABEL[49] = "HeroDiesNightElf"
set SOUND_LABEL[50] = "HeroDiesOrc"
set SOUND_LABEL[51] = "HeroDiesUndead"
set SOUND_LABEL[52] = "Hint"
set SOUND_LABEL[53] = "InGameChatWhat"
set SOUND_LABEL[54] = "InterfaceClick"
set SOUND_LABEL[55] = "InterfaceError"
set SOUND_LABEL[56] = "InventoryFullHuman"
set SOUND_LABEL[57] = "InventoryFullNaga"
set SOUND_LABEL[58] = "InventoryFullNightElf"
set SOUND_LABEL[59] = "InventoryFullOrc"
set SOUND_LABEL[60] = "InventoryFullUndead"
set SOUND_LABEL[61] = "ItemDrop"
set SOUND_LABEL[62] = "ItemGet"
set SOUND_LABEL[63] = "ItemReward"
set SOUND_LABEL[64] = "JobDoneSoundHuman"
set SOUND_LABEL[65] = "JobDoneSoundNaga"
set SOUND_LABEL[66] = "JobDoneSoundNightElf"
set SOUND_LABEL[67] = "JobDoneSoundOrc"
set SOUND_LABEL[68] = "JobDoneSoundUndead"
set SOUND_LABEL[69] = "MapPing"
set SOUND_LABEL[70] = "MenuButtonClick"
set SOUND_LABEL[71] = "NewTournament"
set SOUND_LABEL[72] = "NoFoodHuman"
set SOUND_LABEL[73] = "NoFoodNaga"
set SOUND_LABEL[74] = "NoFoodNightElf"
set SOUND_LABEL[75] = "NoFoodOrc"
set SOUND_LABEL[76] = "NoFoodUndead"
set SOUND_LABEL[77] = "NoGoldGeneric"
set SOUND_LABEL[78] = "NoGoldHuman"
set SOUND_LABEL[79] = "NoGoldNaga"
set SOUND_LABEL[80] = "NoGoldNightElf"
set SOUND_LABEL[81] = "NoGoldOrc"
set SOUND_LABEL[82] = "NoGoldUndead"
set SOUND_LABEL[83] = "NoLumberHuman"
set SOUND_LABEL[84] = "NoLumberNaga"
set SOUND_LABEL[85] = "NoLumberNightElf"
set SOUND_LABEL[86] = "NoLumberOrc"
set SOUND_LABEL[87] = "NoLumberUndead"
set SOUND_LABEL[88] = "NoManaGeneric"
set SOUND_LABEL[89] = "NoManaHuman"
set SOUND_LABEL[90] = "NoManaNaga"
set SOUND_LABEL[91] = "NoManaNightElf"
set SOUND_LABEL[92] = "NoManaOrc"
set SOUND_LABEL[93] = "NoManaUndead"
set SOUND_LABEL[94] = "OffBlightUndead"
set SOUND_LABEL[95] = "PauseGame"
set SOUND_LABEL[96] = "PlaceBuildingDefault"
set SOUND_LABEL[97] = "QuestCompleted"
set SOUND_LABEL[98] = "QuestFailed"
set SOUND_LABEL[99] = "QuestLogModified"
set SOUND_LABEL[100] = "QuestNew"
set SOUND_LABEL[101] = "QuestUpdate"
set SOUND_LABEL[102] = "RallyPointPlace"
set SOUND_LABEL[103] = "Rescue"
set SOUND_LABEL[104] = "ResearchCompleteGeneric"
set SOUND_LABEL[105] = "ResearchCompleteHuman"
set SOUND_LABEL[106] = "ResearchCompleteNaga"
set SOUND_LABEL[107] = "ResearchCompleteNightElf"
set SOUND_LABEL[108] = "ResearchCompleteOrc"
set SOUND_LABEL[109] = "ResearchCompleteUndead"
set SOUND_LABEL[110] = "ScoreScreenTabClick"
set SOUND_LABEL[111] = "SecretFound"
set SOUND_LABEL[112] = "SubGroupSelectionChange"
set SOUND_LABEL[113] = "TownAttackGeneric"
set SOUND_LABEL[114] = "TownAttackHuman"
set SOUND_LABEL[115] = "TownAttackNaga"
set SOUND_LABEL[116] = "TownAttackNightElf"
set SOUND_LABEL[117] = "TownAttackOrc"
set SOUND_LABEL[118] = "TownAttackUndead"
set SOUND_LABEL[119] = "UnderAttackHuman"
set SOUND_LABEL[120] = "UnderAttackNaga"
set SOUND_LABEL[121] = "UnderAttackNightElf"
set SOUND_LABEL[122] = "UnderAttackOrc"
set SOUND_LABEL[123] = "UnderAttackUndead"
set SOUND_LABEL[124] = "UpgradeCompleteGeneric"
set SOUND_LABEL[125] = "UpgradeCompleteHuman"
set SOUND_LABEL[126] = "UpgradeCompleteNaga"
set SOUND_LABEL[127] = "UpgradeCompleteNightElf"
set SOUND_LABEL[128] = "UpgradeCompleteOrc"
set SOUND_LABEL[129] = "UpgradeCompleteUndead"
set SOUND_LABEL[130] = "UpkeepLevel"
set SOUND_LABEL[131] = "Warning"
set SOUND_LABEL[132] = "WayPoint"
endmethod
implement BMInit
endstruct
endlibrary
library CustomWaygateCompatibilityPlugin uses CustomWaygate
/*
Backward compability plugin for CustomWaygate pre-1.03 versions. Usses TriggerRegisterVariableEvent
instead of RegisterNativeEvent.
WAYGATE_EVENT
The events that fire on pre-teleportation, post-teleportation, teleport prepping and
teleport cancellation
0.50 == right before a unit it moved.
1.00 == right after a unit is moved.
1.50 == When teleportation has a duration, this fires as that duration starts. It ends
at pre-teleportation, or 0.50.
1.75 == When teleportation is primed and ready to execute in a network. Useful if you want
to attach special effects to your units to indicate readiness, for example.
2.00 == When teleportation has been cancelled, either manually or through StopTeleport().
See below.
*/
globals
real WAYGATE_EVENT = 0.
endglobals
private function onPreTeleport takes nothing returns nothing
set WAYGATE_EVENT = .5
set WAYGATE_EVENT = 0.
endfunction
private function onPostTeleport takes nothing returns nothing
set WAYGATE_EVENT = 1.
set WAYGATE_EVENT = 0.
endfunction
private function onTeleportDuration takes nothing returns nothing
set WAYGATE_EVENT = 1.5
set WAYGATE_EVENT = 0.
endfunction
private function onNetworkPrimed takes nothing returns nothing
set WAYGATE_EVENT = 1.75
set WAYGATE_EVENT = 0.
endfunction
private function onTeleportInterrupted takes nothing returns nothing
set WAYGATE_EVENT = 2.
set WAYGATE_EVENT = 0.
endfunction
// Initialisation
private module Init
private static method onInit takes nothing returns nothing
call RegisterNativeEvent(EVENT_WAYGATE_PRE_TELEPORT, function onPreTeleport)
call RegisterNativeEvent(EVENT_WAYGATE_POST_TELEPORT, function onPostTeleport)
call RegisterNativeEvent(EVENT_WAYGATE_DURATION_START, function onTeleportDuration)
call RegisterNativeEvent(EVENT_WAYGATE_NETWORK_PRIMED, function onNetworkPrimed)
call RegisterNativeEvent(EVENT_WAYGATE_CANCELLED, function onTeleportInterrupted)
endmethod
endmodule
private struct init
implement Init
endstruct
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 player play = Player(0)
call FogEnableOff()
call FogMaskEnableOff()
call SetTimeOfDay(10)
call BJDebugMsg("Right-click on any of the buildings to test Custom Waygate. Arcane Towers are networked together to allow multiple destinations.\n\nPress ESC to clear the screen of all the text.")
set u[1] = CreateUnit(play, 'hatw', -960., 0., 270.)
set u[2] = CreateUnit(play, 'hatw', -580., 0., 270.)
set u[3] = CreateUnit(play, 'hatw', -200., 0., 270.)
set u[4] = CreateUnit(play, 'hatw', 200., 0., 270.)
set u[5] = CreateUnit(play, 'hatw', 580., 0., 270.)
set u[6] = CreateUnit(play, 'hatw', 960., 0., 270.)
set u[7] = CreateUnit(play, 'hvlt', -530., 760., 270.)
set u[8] = CreateUnit(play, 'hvlt', 530., 760., 270.)
set u[9] = CreateUnit(play, 'otrb', -480., -730., 270.)
set u[10] = CreateUnit(play, 'otrb', 480., -730., 270.)
set u[11] = CreateUnit(play, 'emow', -480., -1500., 270.)
set u[12] = CreateUnit(play, 'emow', 480., -1500., 270.)
//Moon Bowl
call CustomWaygate.createTunnel(u[7], u[8], 275., 0.)
call CustomWaygate.createTunnel(u[8], u[7], 275., 0.)
call ManaRunes.setup(u[7], 8, 180., 270., 4, GLOWING_RUNES, NO_RUNES, 5.)
call ManaRunes.setup(u[8], 8, 180., 270., 4, GLOWING_RUNES, NO_RUNES, 5.)
//Orc Burrow
call CustomWaygate.createTunnel(u[9], u[10], 240., 1.5)
call CustomWaygate.createTunnel(u[10], u[9], 240., 1.5)
//Moon Wells
call CustomWaygate.createTunnel(u[11], u[12], 215., 0.)
call CustomWaygate.createTunnel(u[12], u[11], 215., 0.)
//Arcane Tower Network
set i = 0
loop
set i_main = i_main + 1
loop
set i = i + 1
if i != i_main then
call CustomWaygate.createTunnel(u[i_main], u[i], 225., 3.)
endif
exitwhen i >= 6
endloop
set i = 0
exitwhen i_main >= 6
endloop
//Nulling local variables
set i = 0
loop
set i = i + 1
set u[i] = null
exitwhen i >= 10
endloop
call DestroyTrigger(GetTriggeringTrigger())
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 ManaRunes requires Table, TimerUtils, BlizzardMessage
globals
integer array MR_Instance
integer array NumberOfRunes
endglobals
private struct M
static Table RUNES
endstruct
private struct R
real x
real y
effect rune_fx
boolean isRunePresent
method destroy takes nothing returns nothing
set this.x = 0.
set this.y = 0.
set this.isRunePresent = false
if this.rune_fx != null then
call DestroyEffect(this.rune_fx)
set this.rune_fx = null
endif
call this.deallocate()
endmethod
endstruct
struct ManaRunes
unit source
real timeout
real distance
real starting_angle
integer tableInstance
integer rune_limit
integer start_count
string rune_fx_string
string empty_fx_string
timer clock
private method destroy takes nothing returns nothing
set this.source = null
set this.rune_fx_string = null
set this.empty_fx_string = null
call ReleaseTimer(this.clock)
set this.clock = null
call this.deallocate()
endmethod
private static method Timer takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local integer id = GetUnitUserData(this.source)
local boolean exitLoop = false
local boolean startCountEnd = false
local integer i = 0
local R r
set M.RUNES = this.tableInstance
loop
set i = i + 1
exitwhen i > this.rune_limit or exitLoop
set r = M.RUNES.integer[i]
if not r.isRunePresent then
if r.rune_fx != null then
call DestroyEffect(r.rune_fx)
endif
set r.rune_fx = AddSpecialEffect(this.rune_fx_string, r.x, r.y)
set r.isRunePresent = true
set exitLoop = true
set NumberOfRunes[id] = NumberOfRunes[id] + 1
if this.start_count > 0 then
set this.start_count = this.start_count - 1
if this.start_count < 1 then
set startCountEnd = true
endif
endif
endif
endloop
if startCountEnd then
call PauseTimer(t)
if NumberOfRunes[id] < this.rune_limit then
call TimerStart(t, this.timeout, true, function thistype.Timer)
endif
endif
if NumberOfRunes[id] == this.rune_limit then
call PauseTimer(t)
endif
set t = null
endmethod
static method consumeRune takes unit u returns nothing
local integer id = GetUnitUserData(u)
local integer i = NumberOfRunes[id]
local thistype this = MR_Instance[id]
local R r
if this != 0 then
if NumberOfRunes[id] > 0 then
set M.RUNES = this.tableInstance
loop
set r = M.RUNES.integer[i]
if r.isRunePresent then
if r.rune_fx != null then
call DestroyEffect(r.rune_fx)
endif
if this.empty_fx_string != null then
set r.rune_fx = AddSpecialEffect(this.empty_fx_string, r.x, r.y)
endif
set r.isRunePresent = false
set i = 0
endif
set i = i - 1
exitwhen i < 1
endloop
if NumberOfRunes[id] == this.rune_limit then
call TimerStart(this.clock, this.timeout, true, function thistype.Timer)
endif
set NumberOfRunes[id] = NumberOfRunes[id] - 1
endif
endif
endmethod
static method clearAll takes unit u returns nothing
local integer id = GetUnitUserData(u)
local integer i = 0
local thistype this = MR_Instance[id]
local R r
if this != 0 then
set M.RUNES = this.tableInstance
loop
set i = i + 1
exitwhen i > this.rune_limit
set r = M.RUNES.integer[i]
if r.isRunePresent then
call r.destroy()
endif
endloop
call this.destroy()
set NumberOfRunes[id] = 0
set MR_Instance[id] = 0
endif
endmethod
static method setup takes unit u, integer rune_limit, real distance, real starting_angle, /*
*/ integer starting_runes, string rune_fx_string, string empty_fx_string, real timeout /*
*/ returns nothing
local integer id = GetUnitUserData(u)
local integer i
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real angle = starting_angle * bj_DEGTORAD
local real a = (360. / rune_limit) * bj_DEGTORAD
local thistype this
local R r
if MR_Instance[id] == 0 then
set M.RUNES = Table.create()
set this = allocate()
set this.source = u
set this.timeout = timeout
set this.rune_limit = rune_limit
set this.rune_fx_string = rune_fx_string
set this.empty_fx_string = empty_fx_string
set this.tableInstance = M.RUNES
set MR_Instance[id] = this
set NumberOfRunes[id] = 0
set i = 0
loop
set i = i + 1
exitwhen i > rune_limit
set r = allocate()
set r.x = x + Cos(angle + (a * i)) * distance
set r.y = y + Sin(angle + (a * i)) * distance
set r.isRunePresent = false
if empty_fx_string != null then
set r.rune_fx = AddSpecialEffect(empty_fx_string, r.x, r.y)
endif
set M.RUNES.integer[i] = r
endloop
set this.clock = NewTimerEx(this)
if starting_runes > 0 then
set this.start_count = starting_runes
set timeout = 0.
endif
call TimerStart(this.clock, timeout, true, function thistype.Timer)
endif
endmethod
endstruct
endlibrary