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
//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