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