1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  5. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  6. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  7. The results are out! Check them out.
    Dismiss Notice
  8. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  9. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  10. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

MeleeRanged(1).w3x
Variables
Metee/Ranged
ReadMe
MeleeRanged
Systems
Systems by Nestharus
Last Order
Unit Indexer
Event
World Bounds
Test
Create Units
MeleeRanged Copy 2
OrderID
Untitled Trigger 001
Untitled Trigger 002
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.

		
Name Type Is Array Initial Value
ab abilcode No
b boolean No
i integer No
id integer No
int integer No
it itemcode No
MR_AttackMode boolean Yes
MR_CanChange boolean Yes
MR_Group group No
MR_HT hashtable No
MR_Range real No
MR_Timer timer No
p1 location No
p2 location No
r real No
u unit No
u2 unit No
ut unitcode No
ut2 unitcode No
//TESH.scrollpos=2
//TESH.alwaysfold=0
/*

How to import:

1. Copy MeleeRanged trigger to your map.
2. Copy UnitIndexer, Event and World Bounds triggers to your map
3. Copy Last Order trigger to your map. (Optional)
4. Copy and paste on of the transform abilities into your map.
5. If you set AUTO to false, for manual mode, you need to copy
   Toggle Attack ability into your map.

Last Order trigger allows the unit not to stop when it switches melee/ranged.

How to configure:

1. In MeleeRanged trigger, adjust the mode
private constant    boolean     AUTO = true

If it is true, units will automatically switch to melee/ranged
based on distance to target.
If it is false, the user has to change the type with an ability.

Set the toggle ability's id. Click ctrl + D to find
out the ability's id in object editor.
private constant    integer     TOGGLEABI  = 'A005'

Create a melee and ranged versions of your unit.
Modify the transform ability for the unit, set normal form to
the melee version, and alternate form to the ranged.

Then set up the unit type in the Setup function in
MeleeRanged library.




//TESH.scrollpos=0
//TESH.alwaysfold=0
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
 *      Melee/Ranged system v1.1.1.1 by Maker                           *
 *                                                                      *
 *      Melee/ranged switching system allows a unit to switch between   *
 *      ranged and melee attacks.                                       *
 *                                                                      *
 *      Has two modes, automatic and manual.                            *
 *                                                                      *
 *      In auto mode, the unit switches automatically based on          *
 *      distance to target. In manual mode, player can switch           *
 *      the mode with an ability.                                       *
 *                                                                      *
 *      Supports Last Order, the unit doesn't stop when switching.      *
 *                                                                      *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/


library MeleeRanged initializer Init requires UnitIndexer optional LastOrder

    globals
        // Configure unit types at Setup function
        // at the bottom of the library
   
        // Do units change tome melee/ranged automatically
        private constant    boolean     AUTO        = true
        // Ability with which unit can toggle attack
        private constant    integer     TOGGLEABI   = 'A005'
        // Order id of the transform, default = metamorphosis
        private constant    integer     TOGGLEOID   = 852180
        // Order id of toggle activation, default = magicdefence
        private constant    integer     TOGGLEON    = 852478
       
       
        private             hashtable   HT          = InitHashtable()
        private             trigger     TRG1
        private             trigger     TRG2         = CreateTrigger()
        private             timer       T1
        private             timer       T2
        private             group       G1
        private             group       G2
        private             group       U
        private             group       TEMP        
       
        private             integer     LEAKS       = 0
        private             integer     LEAKS_MAX   = 10
    endglobals
   
    // Disables given ability for players, won't show up in UI
    private function DisableAbil takes integer a returns nothing
        local integer i = 0
        loop
            call SetPlayerAbilityAvailable(Player(i), a, false)
            exitwhen i == 15
            set i = i + 1
        endloop
    endfunction
   
    // Adds a delay to order, allows to akncowledge the
    // change in unit's attack range
    private function Delay3 takes nothing returns nothing
        static if  LIBRARY_LastOrder then
            local unit u
            call DisableTrigger(TRG2)
            loop
                set u = FirstOfGroup(G1)
                exitwhen u == null
                if GetPastOrder(u, 3) != null then
                    call IssuePastOrder(u, 3)
                endif
                call GroupRemoveUnit(G1, u)
            endloop
            call EnableTrigger(TRG2)
        endif
    endfunction
   
    // Adds a delay to order, allows to akncowledge the
    // change in unit's attack range
    private function Delay1 takes nothing returns nothing
        local unit u
        call DisableTrigger(TRG2)
        loop
            set u = FirstOfGroup(G1)
            exitwhen u == null
            call IssueTargetOrderById(u, 851983, LoadUnitHandle(HT, GetHandleId(u), 2))
            call FlushChildHashtable(HT, GetHandleId(u))
            call GroupRemoveUnit(G1, u)
        endloop
        call EnableTrigger(TRG2)
    endfunction
   
    function ToggleMeleeRanged takes unit u, unit target, boolean toRanged returns nothing
        // Load correct ability for the unit type
        local integer a = LoadInteger(HT, GetUnitTypeId(u), 1)
        // Enable ability to be able to use it, then disable it to hide it
        //call GroupRemoveUnit(U, u)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u), a, true)
        call IssueImmediateOrderById(u, TOGGLEOID)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u), a, false)
        //call GroupAddUnit(U, u)
        // Save whether unit is melee or ranged
        static if AUTO then
            // Makes the unit attack the target
            // Use delay so the new range is used
            call SaveUnitHandle(HT, GetHandleId(u), 2, target)
            call GroupAddUnit(G1, u)
            call TimerStart(T1, 0.00, false, function Delay1)
        elseif LIBRARY_LastOrder then
            // Makes the unit stop when it transforms
            call GroupAddUnit(G1, u)
            call TimerStart(T1, 0.00, false, function Delay3)
        endif
    endfunction
   
    // Detects which event was triggered and whether the unit
    // should switch to melee/ranged or not
    private function Actions takes nothing returns boolean
        local unit u1
        local unit u2
        local real r1
        local real r2
        local integer uid
        local integer id = GetHandleId(GetTriggerEventId())
        if id == 18 then // A Unit is Attacked
            set u1 = GetAttacker()
            set uid = GetUnitTypeId(u1)
            if HaveSavedInteger(HT, uid, 1) then
                set u2 = GetTriggerUnit()
            else
                set u1 = null
                return false
            endif
        else
            set u1 = GetTriggerUnit()
            set uid = GetUnitTypeId(u1)
            if HaveSavedInteger(HT, uid, 1) then
                if id == 60 then // A Unit Acquires a Target
                    set u2 = GetEventTargetUnit()
                else // A Unit Is Issued an Order Targeting an Object
                    set u2 = GetOrderTargetUnit()
                    if GetIssuedOrderId() == 851971  then // Order is smart
                        if not IsUnitEnemy(u2, GetOwningPlayer(u1)) or u2 == null then
                            set u1 = null
                            set u2 = null
                            return false
                        endif
                    elseif GetIssuedOrderId() != 851983 then // Order is not attack
                        set u1 = null
                        set u2 = null
                        return false // Spells won't trigger the system
                    endif
                endif
            else
                set u1 = null
                return false
            endif
        endif
       
        set r1 = GetUnitX(u1)-GetUnitX(u2)
        set r2 = GetUnitY(u1)-GetUnitY(u2)
       
        // Checks melee distance
        if r1*r1+r2*r2 > LoadReal(HT, GetUnitTypeId(u1), 0) then
            if LoadInteger(HT, uid, 2) == 0 then
                call ToggleMeleeRanged(u1, u2, true)
            endif
        elseif LoadInteger(HT, uid, 2) == 1 then
            call ToggleMeleeRanged(u1, u2, false)
        endif
       
        set u1 = null
        set u2 = null
        return false
    endfunction
   
    // Recreates the trigger
    private function Recreate takes nothing returns nothing
        static if AUTO then
            local unit u
            call DestroyTrigger(TRG1)
            set TRG1 = CreateTrigger()
            call TriggerAddCondition(TRG1, Condition(function Actions))
            loop
                set u = FirstOfGroup(U)
                exitwhen u == null
                call GroupAddUnit(TEMP, u)
                call TriggerRegisterUnitEvent(TRG1, u, EVENT_UNIT_ACQUIRED_TARGET)
                call GroupRemoveUnit(U, u)
            endloop
            loop
                set u = FirstOfGroup(TEMP)
                exitwhen u == null
                call GroupAddUnit(U, u)
                call GroupRemoveUnit(TEMP, u)
            endloop
        endif
    endfunction

    // Adds delay to transform order to prevent the toggle
    // order firing twice, due to the instant transform
    private function Delay2 takes nothing returns nothing
        static if not AUTO then
            local unit u
            loop
                set u = FirstOfGroup(G2)
                exitwhen u == null
                call ToggleMeleeRanged(u, null, not LoadBoolean(HT, GetHandleId(u), 0))
                call GroupRemoveUnit(G2, u)
            endloop
        endif
    endfunction
   
    // Detects the toggle order
    private function Toggle takes nothing returns boolean
        static if not AUTO then
            if GetIssuedOrderId() == TOGGLEON then
                call GroupAddUnit(G2, GetTriggerUnit())
                call TimerStart(T2, 0.0, false, function Delay2)
            endif
        endif
        return false
    endfunction
   
    // Initializes a spesific unit for the system
    private function AddUnit takes unit u returns nothing
        local integer ut = GetUnitTypeId(u)
        local integer ab = LoadInteger(HT, ut, 1)
       
        call UnitAddAbility(u, ab)
        call UnitMakeAbilityPermanent(u, true, ab)
        if LoadInteger(HT, ut, 2) == 1 then // Is it the ranged version
            call SetPlayerAbilityAvailable(GetOwningPlayer(u), ab, true)
            call IssueImmediateOrderById(u, TOGGLEOID)
            call SetPlayerAbilityAvailable(GetOwningPlayer(u), ab, false)
        endif
       
        static if AUTO then
            call GroupAddUnit(U, u)
            call TriggerRegisterUnitEvent(TRG1, u, EVENT_UNIT_ACQUIRED_TARGET)
        else
            call UnitAddAbility(u, TOGGLEABI)
            call UnitMakeAbilityPermanent(u, true, TOGGLEABI)
        endif
    endfunction
   
    // Detects possible even leaks
    private function Die takes nothing returns boolean
        static if AUTO then
            if HaveSavedInteger(HT, GetUnitTypeId(GetIndexedUnit()), 1) then
                call GroupRemoveUnit(U, GetIndexedUnit())
                call GroupRemoveUnit(G1, GetIndexedUnit())
                if LEAKS == LEAKS_MAX then
                    set LEAKS = 0
                    call Recreate()
                else
                    set LEAKS = LEAKS + 1
                endif
            endif
        else
            call GroupRemoveUnit(G2, GetIndexedUnit())
            static if LIBRARY_LastOrder then
                call GroupRemoveUnit(G1, GetIndexedUnit())
            endif
        endif
        return false
    endfunction
   
    // Is unit type valid
    private function UnitFilt takes nothing returns boolean
        if HaveSavedInteger(HT, GetUnitTypeId(GetFilterUnit()), 1) then
            call AddUnit(GetFilterUnit())
        endif
        return false
    endfunction
   
    // Initializes unit types
    private function AddUT takes integer i1, integer i2, integer a, real r returns nothing
        call SaveReal(HT, i1, 0, r*r)
        call SaveReal(HT, i2, 0, r*r)
        call SaveInteger(HT, i1 ,1, a)
        call SaveInteger(HT, i2 ,1, a)
        call SaveInteger(HT, i1, 2, 0)  // Is melee type
        call SaveInteger(HT, i2, 2, 1)  // Is ranged type
        call DisableAbil(a)            
    endfunction
   
     private function Setup takes nothing returns nothing
        // Parameters are:
        // (Melee unit type, ranged unit type, transform ability, melee range)
        call AddUT('Hpal', 'H000', 'A000', 200)
        call AddUT('H001', 'Hamg', 'A001', 220)
        call AddUT('e000', 'earc', 'A002', 180)
        call AddUT('n000', 'n001', 'A003', 200)
        call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, function UnitFilt)
    endfunction
   
    private function Init takes nothing returns nothing
        local region r = CreateRegion()
       
        call RegionAddRect(r, bj_mapInitialPlayableArea)
        call TriggerRegisterEnterRegion(CreateTrigger(), r, function UnitFilt)
        call RegisterUnitIndexEvent(Condition(function Die), UnitIndexer.DEINDEX)
       
        static if AUTO then
            set T1 = CreateTimer()
            set G1 = CreateGroup()
            set U = CreateGroup()
            set TEMP = CreateGroup()
            set TRG1 = CreateTrigger()
       
            call TriggerAddCondition(TRG1, function Actions)
            call TriggerAddCondition(TRG2, function Actions)
           
            call TriggerRegisterAnyUnitEventBJ(TRG2, EVENT_PLAYER_UNIT_ATTACKED)
            call TriggerRegisterAnyUnitEventBJ(TRG2, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
        else
            static if LIBRARY_LastOrder then
                set T1 = CreateTimer()
                set G1 = CreateGroup()
            endif
            set T2 = CreateTimer()
            set G2 = CreateGroup()
            call TriggerAddCondition(TRG2, function Toggle)
            call TriggerRegisterAnyUnitEventBJ(TRG2, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        endif
        call Setup()
        set r = null
    endfunction

endlibrary

 
//TESH.scrollpos=266
//TESH.alwaysfold=0
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

 
//TESH.scrollpos=0
//TESH.alwaysfold=0
library UnitIndexer /* v4.0.2.5
*************************************************************************************
*
*   Assigns unique indexes to units via unit user data.
*
*************************************************************************************
*
*   */
uses/*
*       */
WorldBounds /*       hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
*       */
Event /*             hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*
************************************************************************************
*
*   SETTINGS
*/

globals
    constant integer ABILITIES_UNIT_INDEXER = 'A004'
endglobals
/*
************************************************************************************
*
*    Functions
*
*       function RegisterUnitIndexEvent takes boolexpr codeToRegister, Event unitIndexEvent returns nothing
*       function TriggerRegisterUnitIndexEvent takes trigger triggerToRegister, Event unitIndexEvent returns nothing
*
*       function GetUnitById takes integer index returns unit
*           -   Returns unit given a unit index
*       function GetUnitId takes unit u returns integer
*           -   Returns unit index given a unit
*
*       function IsUnitIndexed takes unit u returns boolean
*       function IsUnitDeindexing takes unit u returns boolean
*
*       function GetIndexedUnitId takes nothing returns integer
*       function GetIndexedUnit takes nothing returns unit
*
************************************************************************************
*
*   module UnitIndexStruct
*
*       -   A pseudo module interface that runs a set of methods if they exist and provides
*       -   a few fields and operators. Runs on static ifs to minimize code.
*
*       static method operator [] takes unit u returns thistype
*           -   Return GetUnitUserData(u)
*
*       readonly unit unit
*           -   The indexed unit of the struct
*       readonly boolean allocated
*           -   Is unit allocated for the struct
*
*       Interface:
*
*           -   These methods don't have to exist. If they don't exist, the code
*           -   that calls them won't even be in the module.
*
*           private method index takes nothing returns nothing
*               -   called when a unit is indexed and passes the filter.
*               -
*               -   thistype this:              Unit's index
*           private method deindex takes nothing returns nothing
*               -   called when a unit is deindexed and is allocated for struct
*               -
*               -   thistype this:              Unit's index
*           private static method filter takes unit unitToIndex returns boolean
*               -   Determines whether or not to allocate struct for unit
*               -
*               -   unit unitToIndex:           Unit being filtered
*
************************************************************************************
*
*   struct UnitIndexer extends array
*
*        -    Controls the unit indexer system.
*
*       static constant Event UnitIndexer.INDEX
*       static constant Event UnitIndexer.DEINDEX
*           -   Don't register functions and triggers directly to the events. Register them via
*           -   RegisterUnitIndexEvent and TriggerRegisterUnitIndexEvent.
*
*       static boolean enabled
*           -   Enables and disables unit indexing. Useful for filtering out dummy units.
*
************************************************************************************
*
*   struct UnitIndex extends array
*
*       -    Constrols specific unit indexes.
*
*       method lock takes nothing returns nothing
*           -   Locks an index. When an index is locked, it will not be recycled
*           -   when the unit is deindexed until all locks are removed. Deindex
*           -   events still fire at the appropriate times, the index just doesn't
*           -   get thrown into the recycler.
*       method unlock takes nothing returns nothing
*           -   Unlocks an index.
*
************************************************************************************/

    globals
        private trigger q=CreateTrigger()
        private trigger l=CreateTrigger()
        private unit array e
        private integer r=0
        private integer y=0
        private integer o=0
        private boolean a=false
        private integer array n
        private integer array p
        private integer array lc
    endglobals
    function GetIndexedUnitId takes nothing returns integer
        return o
    endfunction
    function GetIndexedUnit takes nothing returns unit
        return e[o]
    endfunction
    //! runtextmacro optional UNIT_LIST_LIB()
    private struct PreLoader extends array
        public static method run takes nothing returns nothing
            call DestroyTimer(GetExpiredTimer())
            set a=true
        endmethod
        public static method eval takes trigger t returns nothing
            local integer f=n[0]
            local integer d=o
            loop
                exitwhen 0==f
                if (IsTriggerEnabled(t)) then
                    set o=f
                    if (TriggerEvaluate(t)) then
                        call TriggerExecute(t)
                    endif
                else
                    exitwhen true
                endif
                set f=n[f]
            endloop
            set o=d
        endmethod
        public static method evalb takes boolexpr c returns nothing
            local trigger t=CreateTrigger()
            local thistype f=n[0]
            local integer d=o
            call TriggerAddCondition(t,c)
            loop
                exitwhen 0==f
                set o=f
                call TriggerEvaluate(t)
                set f=n[f]
            endloop
            call DestroyTrigger(t)
            set t=null
            set o=d
        endmethod
    endstruct
    //! runtextmacro optional UNIT_EVENT_MACRO()
    private module UnitIndexerInit
        private static method onInit takes nothing returns nothing
            local integer i=15
            local boolexpr bc=Condition(function thistype.onLeave)
            local boolexpr bc2=Condition(function thistype.onEnter)
            local group g=CreateGroup()
            local player p
            set INDEX=CreateEvent()
            set DEINDEX=CreateEvent()
            call TriggerRegisterEnterRegion(q,WorldBounds.worldRegion,bc2)
            loop
                set p=Player(i)
                call TriggerRegisterPlayerUnitEvent(l,p,EVENT_PLAYER_UNIT_ISSUED_ORDER,bc)
                call SetPlayerAbilityAvailable(p,ABILITIES_UNIT_INDEXER,false)
                call GroupEnumUnitsOfPlayer(g,p,bc2)
                exitwhen 0==i
                set i=i-1
            endloop
            call DestroyGroup(g)
            set bc=null
            set g=null
            set bc2=null
            set p=null
            call TimerStart(CreateTimer(),0,false,function PreLoader.run)
        endmethod
    endmodule
    struct UnitIndex extends array
        method lock takes nothing returns nothing
            debug if (null!=e[this]) then
                set lc[this]=lc[this]+1
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO LOCK NULL INDEX")
            debug endif
        endmethod
        method unlock takes nothing returns nothing
            debug if (0<lc[this]) then
                set lc[this]=lc[this]-1
                if (0==lc[this] and null==e[this]) then
                    set n[this]=y
                    set y=this
                endif
            debug else
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,"UNIT INDEXER ERROR: ATTEMPT TO UNLOCK UNLOCKED INDEX")
            debug endif
        endmethod
    endstruct
    struct UnitIndexer extends array
        readonly static Event INDEX
        readonly static Event DEINDEX
        static boolean enabled=true
        private static method onEnter takes nothing returns boolean
            local unit Q=GetFilterUnit()
            local integer i
            local integer d=o
            if (enabled and Q!=e[GetUnitUserData(Q)]) then
                if (0==y) then
                    set r=r+1
                    set i=r
                else
                    set i=y
                    set y=n[y]
                endif
                call UnitAddAbility(Q,ABILITIES_UNIT_INDEXER)
                call UnitMakeAbilityPermanent(Q,true,ABILITIES_UNIT_INDEXER)
                call SetUnitUserData(Q,i)
                set e[i]=Q
                static if not LIBRARY_UnitList then
                    if (not a)then
                        set p[i]=p[0]
                        set n[p[0]]=i
                        set n[i]=0
                        set p[0]=i
                    endif
                else
                    set p[i]=p[0]
                    set n[p[0]]=i
                    set n[i]=0
                    set p[0]=i
                    call GroupAddUnit(g,e[i])
                endif
                set o=i
                call FireEvent(INDEX)
                set o=d
            endif
            set Q=null
            return false
        endmethod
        private static method onLeave takes nothing returns boolean
            static if LIBRARY_UnitEvent then
                implement optional UnitEventModule
            else
                local unit u=GetFilterUnit()
                local integer i=GetUnitUserData(u)
                local integer d=o
                if (0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER) and u==e[i]) then
                    static if not LIBRARY_UnitList then
                        if (not a)then
                            set n[p[i]]=n[i]
                            set p[n[i]]=p[i]
                        endif
                    else
                        set n[p[i]]=n[i]
                        set p[n[i]]=p[i]
                        call GroupRemoveUnit(g,e[i])
                    endif
                    set o=i
                    call FireEvent(DEINDEX)
                    set o=d
                    if (0==lc[i]) then
                        set n[i]=y
                        set y=i
                    endif
                    set e[i]=null
                endif
                set u=null
            endif
            return false
        endmethod
        implement UnitIndexerInit
    endstruct
    //! runtextmacro optional UNIT_EVENT_MACRO_2()
    function RegisterUnitIndexEvent takes boolexpr c,integer ev returns nothing
        call RegisterEvent(c, ev)
        if (not a and ev==UnitIndexer.INDEX and 0!=n[0]) then
            call PreLoader.evalb(c)
        endif
    endfunction
    function TriggerRegisterUnitIndexEvent takes trigger t,integer ev returns nothing
        call TriggerRegisterEvent(t,ev)
        if (not a and ev == UnitIndexer.INDEX and 0!=n[0]) then
            call PreLoader.eval(t)
        endif
    endfunction
    function GetUnitById takes integer W returns unit
        return e[W]
    endfunction
    function GetUnitId takes unit u returns integer
        return GetUnitUserData(u)
    endfunction
    function IsUnitIndexed takes unit u returns boolean
        return u==e[GetUnitUserData(u)]
    endfunction
    function IsUnitDeindexing takes unit u returns boolean
        return IsUnitIndexed(u) and 0==GetUnitAbilityLevel(u,ABILITIES_UNIT_INDEXER)
    endfunction
    module UnitIndexStruct
        static method operator [] takes unit u returns thistype
            return GetUnitUserData(u)
        endmethod
        method operator unit takes nothing returns unit
            return e[this]
        endmethod
        static if thistype.filter.exists then
            static if thistype.index.exists then
                static if thistype.deindex.exists then
                    readonly boolean allocated
                else
                    method operator allocated takes nothing returns boolean
                        return filter(e[this])
                    endmethod
                endif
            else
                method operator allocated takes nothing returns boolean
                    return filter(e[this])
                endmethod
            endif
        elseif (thistype.index.exists) then
            static if thistype.deindex.exists then
                readonly boolean allocated
            else
                method operator allocated takes nothing returns boolean
                    return this==GetUnitUserData(e[this])
                endmethod
            endif
        else
            method operator allocated takes nothing returns boolean
                return this==GetUnitUserData(e[this])
            endmethod
        endif
        static if thistype.index.exists then
            private static method onIndexEvent takes nothing returns boolean
                static if thistype.filter.exists then
                    if (filter(e[o])) then
                        static if thistype.deindex.exists then
                            set thistype(o).allocated=true
                        endif
                        call thistype(o).index()
                    endif
                else
                    static if thistype.deindex.exists then
                        set thistype(o).allocated=true
                    endif
                    call thistype(o).index()
                endif
                return false
            endmethod
        endif
        static if thistype.deindex.exists then
            private static method onDeindexEvent takes nothing returns boolean
                static if thistype.filter.exists then
                    static if thistype.index.exists then
                        if (thistype(o).allocated) then
                            set thistype(o).allocated=false
                            call thistype(o).deindex()
                        endif
                    else
                        if (filter(e[o])) then
                            call thistype(o).deindex()
                        endif
                    endif
                else
                    static if thistype.index.exists then
                        set thistype(o).allocated=false
                    endif
                    call thistype(o).deindex()
                endif
                return false
            endmethod
        endif
        static if thistype.index.exists then
            static if thistype.deindex.exists then
                private static method onInit takes nothing returns nothing
                    call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
                    call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
                endmethod
            else
                private static method onInit takes nothing returns nothing
                    call RegisterUnitIndexEvent(Condition(function thistype.onIndexEvent),UnitIndexer.INDEX)
                endmethod
            endif
        elseif thistype.deindex.exists then
            private static method onInit takes nothing returns nothing
                call RegisterUnitIndexEvent(Condition(function thistype.onDeindexEvent),UnitIndexer.DEINDEX)
            endmethod
        endif
    endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Event
//2.0.0.1
/////////////////////////////////////////////////////////////////////////
    //function CreateEvent takes nothing returns integer
    //function TriggerRegisterEvent takes trigger t, integer ev returns nothing
    //function RegisterEvent takes boolexpr c, integer ev returns nothing
    //function FireEvent takes integer ev returns nothing
   
    //struct Event extends array
        //static method create takes nothing returns thistype
        //method registerTrigger takes trigger t returns nothing
        //method register takes boolexpr c returns nothing
        //method fire takes nothing returns nothing
/////////////////////////////////////////////////////////////////////////
    globals
        private real q=0
    endglobals
    struct Event extends array
        private static integer w=0
        private static trigger array e
        static method create takes nothing returns thistype
            set w=w+1
            set e[w]=CreateTrigger()
            return w
        endmethod
        method registerTrigger takes trigger t returns nothing
            call TriggerRegisterVariableEvent(t,SCOPE_PRIVATE+"q",EQUAL,this)
        endmethod
        method register takes boolexpr c returns nothing
            call TriggerAddCondition(e[this],c)
        endmethod
        method fire takes nothing returns nothing
            set q=0
            set q=this
            call TriggerEvaluate(e[this])
        endmethod
    endstruct
    function CreateEvent takes nothing returns Event
        return Event.create()
    endfunction
    function TriggerRegisterEvent takes trigger t,Event ev returns nothing
        call ev.registerTrigger(t)
    endfunction
    function RegisterEvent takes boolexpr c,Event ev returns nothing
        call ev.register(c)
    endfunction
    function FireEvent takes Event ev returns nothing
        call ev.fire()
    endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library WorldBounds
    //struct WorldBounds extends array

    //static readonly rect world
    //  same as GetWorldBounds()
   
    //static readonly region worldRegion
    //  contains world for triggers
   
    //static readonly real maxX
    //static readonly real maxY
    //static readonly real minX
    //static readonly real minY
    //static readonly real centerX
    //static readonly real centerY
   
    private module WorldBoundInit
        private static method onInit takes nothing returns nothing
            set world=GetWorldBounds()
            set maxX=GetRectMaxX(world)
            set maxY=GetRectMaxY(world)
            set minX=GetRectMinX(world)
            set minY=GetRectMinY(world)
            set centerX=(maxX+minX)/2
            set centerY=(minY+maxY)/2
            set worldRegion=CreateRegion()
            call RegionAddRect(worldRegion,world)
        endmethod
    endmodule
    struct WorldBounds extends array
        readonly static real maxX
        readonly static real maxY
        readonly static real minX
        readonly static real minY
        readonly static real centerX
        readonly static real centerY
        readonly static rect world
        readonly static region worldRegion
        implement WorldBoundInit
    endstruct
endlibrary
Create Units
  Events
    Time - Elapsed game time is 2.00 seconds
  Conditions
  Actions
    Custom script: if udg_MR_HT == null then
    Hashtable - Create a hashtable
    Set MR_HT = (Last created hashtable)
    Custom script: endif
    Game - Set the time of day to 12
    Game - Turn the day/night cycle Off
    Game - Display to Player Group - Player 1 (Red) the text: Test units without effects in the middle of the map or a unit with effects at the bottom of the map. To test the effects, enable test version of the system and disable the normal in the editor.
    Game - Display to Player Group - Player 1 (Red) the text: Test different modes by setting AUTO boolean to true/false in the system's constants in the editor.
    Set p1 = (Position of Paladin 0002 <gen>)
    Unit - Create 1 Archmage - melee for Player 1 (Red) at p1 facing Default building facing degrees
    Unit - Create 1 Archer - melee for Player 1 (Red) at p1 facing Default building facing degrees
    Custom script: call RemoveLocation(udg_p1)
    Trigger - Run Items <gen> (checking conditions)
    Player - Set Player 1 (Red) Current gold to 10000
    Player - Set Player 1 (Red) Current lumber to 10000
//TESH.scrollpos=326
//TESH.alwaysfold=0
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
 *      Melee/Ranged system v1.0.1.6 by Maker                           *
 *                                                                      *
 *      Melee/ranged switching system allows a unit to switch between   *
 *      ranged and melee attacks.                                       *
 *                                                                      *
 *      Has two modes, automatic and manual.                            *
 *                                                                      *
 *      In auto mode, the unit switches automatically based on          *
 *      distance to target. In manual mode, player can switch           *
 *      the mode with an ability.                                       *
 *                                                                      *
 *      Supports Last Order, the unit doesn't stop when switching.      *
 *                                                                      *
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/


library MeleeRanged initializer Init requires UnitIndexer optional LastOrder

    globals
        // Configure unit types at Setup function
        // at the bottom of the library
   
        // Do units change tome melee/ranged automatically
        private constant    boolean     AUTO        = true
        // Ability with which unit can toggle attack
        private constant    integer     TOGGLEABI   = 'A005'
        // Order id of the transform, default = metamorphosis
        private constant    integer     TOGGLEOID   = 852180
        // Order id of toggle activation, default = magicdefence
        private constant    integer     TOGGLEON    = 852478
       
       
        private             hashtable   HT          = InitHashtable()
        private             trigger     TRG         = CreateTrigger()
        private             timer       T1
        private             timer       T2
        private             group       G1
        private             group       G2
        private             group       U
        private             group       TEMP        
       
        private             integer     I1
        private             integer     I2
        private             integer     LEAKS       = 0
        private             integer     LEAKS_MAX   = 2
    endglobals
   
    // Disables given ability for players, won't show up in UI
    private function DisableAbil takes integer a returns nothing
        local integer i = 0
        loop
            call SetPlayerAbilityAvailable(Player(i), a, false)
            exitwhen i == 15
            set i = i + 1
        endloop
    endfunction
   
    // Adds a delay to order, allows to akncowledge the
    // change in unit's attack range
    private function Delay3 takes nothing returns nothing
        static if  LIBRARY_LastOrder then
            local unit u = FirstOfGroup(G1)
            call DisableTrigger(TRG)
            loop
                if GetPastOrder(u, 3) != null then
                    call IssuePastOrder(u, 3)
                endif
                call GroupRemoveUnit(G1, u)
                set u = FirstOfGroup(G1)
                exitwhen u == null
            endloop
            call EnableTrigger(TRG)
        endif
    endfunction
   
    // Adds a delay to order, allows to akncowledge the
    // change in unit's attack range
    private function Delay1 takes nothing returns nothing
        local unit u = FirstOfGroup(G1)
        call DisableTrigger(TRG)
        loop
            call IssueTargetOrderById(u, 851983, LoadUnitHandle(HT, GetHandleId(u), 2))
            call FlushChildHashtable(HT, GetHandleId(u))
            call GroupRemoveUnit(G1, u)
            set u = FirstOfGroup(G1)
            exitwhen u == null
        endloop
        call EnableTrigger(TRG)
    endfunction
   
    function ToggleMeleeRanged takes unit u, unit target, boolean toRanged returns nothing
        // Load correct ability for the unit type
        local integer a = LoadInteger(HT, GetUnitTypeId(u), 1)
        // Enable ability to be able to use it, then disable it to hide it
        //call GroupRemoveUnit(U, u)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u), a, true)
        call IssueImmediateOrderById(u, TOGGLEOID)
        call SetPlayerAbilityAvailable(GetOwningPlayer(u), a, false)
        //call GroupAddUnit(U, u)
        // Save whether unit is melee or ranged
        static if AUTO then
            // Makes the unit attack the target
            // Use delay so the new range is used
            call SaveUnitHandle(HT, GetHandleId(u), 2, target)
            call GroupAddUnit(G1, u)
            call TimerStart(T1, 0.00, false, function Delay1)
        else
            // Makes the unit stop when it transforms
            static if LIBRARY_LastOrder then
                call GroupAddUnit(G1, u)
                call TimerStart(T1, 0.00, false, function Delay3)
            endif
        endif
    endfunction
   
    // Detects which event was triggered and whether the unit
    // should switch to melee/ranged or not
    private function Actions takes nothing returns nothing
        local unit u1
        local unit u2
        local real r1
        local real r2
        local integer uid
        local integer id = GetHandleId(GetTriggerEventId())
        if id == 18 then // A Unit is Attacked
            set u1 = GetAttacker()
            set uid = GetUnitTypeId(u1)
            if HaveSavedInteger(HT, uid, 1) then
                set u2 = GetTriggerUnit()
            else
                set u1 = null
                return
            endif
        else
            set u1 = GetTriggerUnit()
            set uid = GetUnitTypeId(u1)
            if HaveSavedInteger(HT, uid, 1) then
                if id == 60 then // A Unit Acquires a Target
                    set u2 = GetEventTargetUnit()
                else // A Unit Is Issued an Order Targeting an Object
                    set u2 = GetOrderTargetUnit()
                    if GetIssuedOrderId() == 851971  then // Order is smart
                        if not IsUnitEnemy(u2, GetOwningPlayer(u1)) or u2 == null then
                            set u1 = null
                            set u2 = null
                            return
                        endif
                    elseif GetIssuedOrderId() != 851983 then // Order is not attack
                        set u1 = null
                        set u2 = null
                        return // Spells won't trigger the system
                    endif
                endif
            else
                set u1 = null
                return
            endif
        endif
       
        set r1 = GetUnitX(u1)-GetUnitX(u2)
        set r2 = GetUnitY(u1)-GetUnitY(u2)
       
        // Checks melee distance
        if r1*r1+r2*r2 > LoadReal(HT, GetUnitTypeId(u1), 0) then
            if LoadInteger(HT, uid, 2) == 0 then
                call ToggleMeleeRanged(u1, u2, true)
            endif
        elseif LoadInteger(HT, uid, 2) == 1 then
            call ToggleMeleeRanged(u1, u2, false)
        endif
       
        set u1 = null
        set u2 = null
    endfunction
   
    // Recreates the trigger
    private function Recreate takes nothing returns nothing
        static if AUTO then
            local unit u
            local integer i = 0
            local integer j = 0
            call DestroyTrigger(TRG)
            set TRG = CreateTrigger()
            call TriggerAddAction(TRG, function Actions)
            call TriggerRegisterAnyUnitEventBJ(TRG, EVENT_PLAYER_UNIT_ATTACKED)
            call TriggerRegisterAnyUnitEventBJ(TRG, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
            loop
                exitwhen FirstOfGroup(U) == null
                set u = FirstOfGroup(U)
                set i = i + 1
                call GroupAddUnit(TEMP, u)
                call TriggerRegisterUnitEvent(TRG, u, EVENT_UNIT_ACQUIRED_TARGET)
                call GroupRemoveUnit(U, u)
            endloop
            loop
                exitwhen FirstOfGroup(TEMP) == null
                set u = FirstOfGroup(TEMP)
                set j = j + 1
                call GroupAddUnit(U, u)
                call GroupRemoveUnit(TEMP, u)
            endloop
        endif
    endfunction
   
    // Adds delay to transform order to prevent the toggle
    // order firing twice, due to the instant transform
    private function Delay2 takes nothing returns nothing
        local unit u = FirstOfGroup(G2)
        loop
            call ToggleMeleeRanged(u, null, not LoadBoolean(HT, GetHandleId(u), 0))
            call GroupRemoveUnit(G2, u)
            set u = FirstOfGroup(G2)
            exitwhen u == null
        endloop
    endfunction
   
    // Detects the toggle order
    private function Toggle takes nothing returns boolean
        if GetIssuedOrderId() == TOGGLEON then
            call GroupAddUnit(G2, GetTriggerUnit())
            call TimerStart(T2, 0.0, false, function Delay2)
        endif
        return false
    endfunction
   
    // Initializes a spesific unit for the system
    private function AddUnit takes unit u returns nothing
        local integer ut = GetUnitTypeId(u)
        local integer ab = LoadInteger(HT, ut, 1)
       
        call UnitAddAbility(u, ab)
        call UnitMakeAbilityPermanent(u, true, ab)
        if ut == I2 then // Is it the ranged version
            call SetPlayerAbilityAvailable(GetOwningPlayer(u), ab, true)
            call IssueImmediateOrderById(u, TOGGLEOID)
            call SetPlayerAbilityAvailable(GetOwningPlayer(u), ab, false)
        endif
       
        static if AUTO then
            call GroupAddUnit(U, u)
            call TriggerRegisterUnitEvent(TRG, u, EVENT_UNIT_ACQUIRED_TARGET)
        else
            call UnitAddAbility(u, TOGGLEABI)
            call UnitMakeAbilityPermanent(u, true, TOGGLEABI)
        endif
    endfunction
   
    // Handles units that enter the map
    private function Enter takes nothing returns boolean
        if HaveSavedInteger(HT, GetUnitTypeId(GetTriggerUnit()), 1) then
            call AddUnit(GetTriggerUnit())
        endif
        return false
    endfunction
   
    // Flushes hashtable
    private function Die takes nothing returns boolean
        static if AUTO then
            if HaveSavedInteger(HT, GetUnitTypeId(GetIndexedUnit()), 1) then
                call GroupRemoveUnit(U, GetIndexedUnit())
                if LEAKS == LEAKS_MAX then
                    set LEAKS = 0
                    call Recreate()
                else
                    set LEAKS = LEAKS + 1
                endif
            endif
        endif
        return false
    endfunction
   
    // Is unit type valid
    private function UnitFilt takes nothing returns boolean
        local integer t = GetUnitTypeId(GetFilterUnit())
        if t == I1 or t == I2 then
            call AddUnit(GetFilterUnit())
        endif
        return false
    endfunction
   
    // Initializes unit types
    private function AddUT takes integer i1, integer i2, integer a, real r returns nothing
        call SaveReal(HT, i1, 0, r*r)
        call SaveReal(HT, i2, 0, r*r)
        call SaveInteger(HT, i1 ,1, a)
        call SaveInteger(HT, i2 ,1, a)
        call SaveInteger(HT, i1, 2, 0)  // Is melee type
        call SaveInteger(HT, i2, 2, 1)  // Is ranged type
        call DisableAbil(a)            
        set I1 = i1
        set I2 = i2
        call GroupEnumUnitsInRect(bj_lastCreatedGroup, bj_mapInitialPlayableArea, function UnitFilt)
    endfunction
   
     private function Setup takes nothing returns nothing
        local integer ut1
        local integer ut2
        local integer aid
        local real r
       
        set ut1 = 'Hpal'            // Normal unit type, melee
        set ut2 = 'H000'            // Alternate unit type, ranged
        set aid = 'A000'            // Transform ability id
        set r = 200                 // Melee range
        call AddUT(ut1, ut2, aid, r)
       
        set ut1 = 'H001'
        set ut2 = 'Hamg'
        set aid = 'A001'
        set r = 220
        call AddUT(ut1, ut2, aid, r)
       
        set ut1 = 'e000'
        set ut2 = 'earc'
        set aid = 'A002'
        set r = 180
        call AddUT(ut1, ut2, aid, r)
       
        set ut1 = 'n000'
        set ut2 = 'n001'
        set aid = 'A003'
        set r = 200
        call AddUT(ut1, ut2, aid, r)
    endfunction
   
    private function Init takes nothing returns nothing
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local region r = CreateRegion()
       
        call RegionAddRect(r, bj_mapInitialPlayableArea)
        call TriggerAddCondition(t1, function Enter)
        call TriggerRegisterEnterRegion(t1, r, null)
        call TriggerAddCondition(t2, function Die)
        call TriggerRegisterUnitIndexEvent(t2, UnitIndexer.DEINDEX)
       
        static if AUTO then
            set T1 = CreateTimer()
            set G1 = CreateGroup()
            set U = CreateGroup()
            set TEMP = CreateGroup()
       
            call TriggerAddAction(TRG, function Actions)
           
            call TriggerRegisterAnyUnitEventBJ(TRG, EVENT_PLAYER_UNIT_ATTACKED)
            call TriggerRegisterAnyUnitEventBJ(TRG, EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER)
        else
            static if LIBRARY_LastOrder then
                set T1 = CreateTimer()
                set G1 = CreateGroup()
            endif
            set T2 = CreateTimer()
            set G2 = CreateGroup()
            call TriggerAddCondition(TRG, function Toggle)
            call TriggerRegisterAnyUnitEventBJ(TRG, EVENT_PLAYER_UNIT_ISSUED_ORDER)
        endif
        call Setup()
        set t1 = null
        set t2 = null
        set r = null
    endfunction

endlibrary

 
OrderID
  Events
    Unit - A unit Is issued an order with no target
    Unit - A unit Is issued an order targeting a point
    Unit - A unit Is issued an order targeting an object
  Conditions
    b Equal to True
  Actions
    Custom script: set udg_i = GetIssuedOrderId()
    Custom script: call BJDebugMsg(OrderId2String(udg_i) + " " + I2S(udg_i))
Untitled Trigger 001
  Events
    Player - Player 1 (Red) skips a cinematic sequence
  Conditions
  Actions
    Unit - Create 1 Villager - ranged for Player 1 (Red) at ((Center of (Playable map area)) offset by 500.00 towards (Random real number between 0 and 360.00) degrees) facing Default building facing degrees
    Wait 0.00 seconds
    Unit - Kill (Last created unit)
Untitled Trigger 002
  Events
    Player - Player 1 (Red) Selects a unit
  Conditions
  Actions
    Game - Display to Player Group - Player 1 (Red) the text: (String((Custom value of (Triggering unit))))