library Detachments /* v1.2.0.0
****************************************************************************************
*
* Detachments by Garfield1337
*
* Allows grouping unit types into lined up detachments or squads.
*
****************************************************************************************
*
* */uses/*
* */ AutoIndex /* wc3c.net/showthread.php?t=105643
* */ Table /* hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
* */ SimError /* wc3c.net/showthread.php?t=101260
* */ TimerUtils /* wc3c.net/showthread.php?t=101322
* */ II /* hiveworkshop.com/forums/pastebin.php?id=x178a8
* */optional/*
* */ RegisterPlayerUnitEvent /* hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/
*
****************************************************************************************
*
* How to use:
* ----------
*
* For each detachment type...
* - Create a leading and a member unit type (possibly based on same unit, but using different model.)
* - Give member units ward classification.
* - Create a spell for transforming leader into member using bear form, you can copy the spell from this map.
* In this spell, change the fields "Alternate Form Unit" and "Normal Form Unit" to member and leader unit type respectively.
* - Create a spell for reinforcing the detachment, you can copy the spell from this map.
* Other than aesthetics and text fields, nothing needs change here.
* - Create a spell for canceling training, you can copy the spell from this map.
* Same as for reinforcing, there are no important field changes here.
* - Initialize detachments using functions from the API.
*
* Tips:
* - When the leader dies, a member turns into a leader and takes over. For balance, leader and members should have
* same or similar stats.
* - Giving detachment units smaller collision than usual will help them move more freely and not bump one into another.
* - Members mimic the leader; if the leader casts a spell, they will too if they have it.
*
* API:
* ----
*
* To set up a detachment type, call the following function (Soldiers are detachment members.):
*
* function InitDetachment takes integer LeaderRawCode, integer SoldierRawCode, integer ReinforceRawcode,
* integer TransformRawcode, integer SoldierGoldCost, integer SoldierLumberCost, integer SoldierFoodCost, integer TrainTime
*
* You may optionally alter detachment size settings with the following function,
* otherwise the detachment will use default values:
*
* function SetDetachmentSize takes integer LeaderRawCode, integer MaximumMemberCount, integer SoldiersPerRow
* real VerticalSpacing*, real HorizontalSpacing*
*
* *Spacing determines distance between soldiers
*
****************************************************************************************
*
* Settings
*/
globals
private constant integer CANCEL = 'A004' //Rawcode of the cancel ability.
private constant string BAR = "||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||"
private constant string BAR_COLOR = "|c00006988" //Color code for the training bar.
private constant real TRAIN_RANGE = 600.0 //Range from the nearest friendly building at which a unit will finish training.
private constant real ARRIVAL_RANGE = 200.0 //How close will the newly trained unit to the leader.
private constant real WANDER_DISTANCE = 900.0 //How far may a unit go away from the leader before automatically going back.
private constant integer DEFAULT_SIZE = 12 //Default detachment size; can be changed per detachment on initialization.
private constant integer DEFAULT_SOLDIERS_PER_ROW = 4 //Member count in every row
private constant real DEFAULT_VERTICAL_SPACING = 150.0 //Distance between soldiers; Vertical is away from leader.
private constant real DEFAULT_HORIZONTAL_SPACING = 140.0 //Distance between soldiers; Horizontal is between soldiers in a row.
private constant string ERROR_DETACHMENT_FULL = "Detachment is full." //Error messages
private constant string ERROR_INSUFFICIENT_GOLD = "Not enough gold."
private constant string ERROR_INSUFFICIENT_LUMBER = "Not enough lumber."
private constant string ERROR_INSUFFICIENT_FOOD = "Not enough food."
private group enumGroup = CreateGroup() //Group used for dynamic enumeration
endglobals //If there is a global group with such role present in map, assign it here
/****************************************************************************************/
globals
private TableArray detachmentData
private constant real BAR_CONSTANT = StringLength(BAR)/32.00
endglobals
function InitDetachment takes integer leader, integer soldier, integer reinforce, integer transform, integer gold, integer lumber, integer food, integer time returns nothing
//Saving detachment type data into table
set detachmentData[0].boolean[leader] = true
set detachmentData[1].boolean[soldier] = true
set detachmentData[2].boolean[reinforce] = true
set detachmentData[0][leader] = soldier
set detachmentData[1][leader] = transform
set detachmentData[2][leader] = gold
set detachmentData[3][leader] = lumber
set detachmentData[4][leader] = food
set detachmentData[5][leader] = time
set detachmentData[6][leader] = DEFAULT_SIZE
set detachmentData[7][leader] = DEFAULT_SOLDIERS_PER_ROW
set detachmentData[0].real[leader] = DEFAULT_VERTICAL_SPACING
set detachmentData[1].real[leader] = DEFAULT_HORIZONTAL_SPACING
endfunction
function SetDetachmentSize takes integer leader, integer size, integer row, real vS, real hS returns nothing
//Additional detachment settings
set detachmentData[6][leader] = size
set detachmentData[7][leader] = row
set detachmentData[0].real[leader] = vS
set detachmentData[1].real[leader] = hS
endfunction
//Functions for retrieving detachment data
private function isLeader takes unit u returns boolean
return detachmentData[0].boolean[GetUnitTypeId(u)]
endfunction
private function isSoldier takes unit u returns boolean
return detachmentData[1].boolean[GetUnitTypeId(u)]
endfunction
private function isReinforce takes integer a returns boolean
return detachmentData[2].boolean[a]
endfunction
private function soldierCode takes integer l returns integer
return detachmentData[0][l]
endfunction
private function transformSoldier takes unit s, integer l returns nothing
call UnitAddAbility(s, detachmentData[1][l])
call UnitRemoveAbility(s, detachmentData[1][l])
endfunction
private function soldierGoldCost takes integer l returns integer
return detachmentData[2][l]
endfunction
private function soldierLumberCost takes integer l returns integer
return detachmentData[3][l]
endfunction
private function soldierFoodCost takes integer l returns integer
return detachmentData[4][l]
endfunction
private function soldierTrainTime takes integer l returns integer
return detachmentData[5][l]
endfunction
private function detachmentSize takes integer l returns integer
return detachmentData[6][l]
endfunction
private function detachmentSPR takes integer l returns integer
return detachmentData[7][l]
endfunction
private function vSpacing takes integer l returns real
return detachmentData[0].real[l]
endfunction
private function hSpacing takes integer l returns real
return detachmentData[1].real[l]
endfunction
private keyword Leader
private struct Soldier
//Soldier struct instances are attached to members of each detachment
//It holds pointers to next and previous soldiers in the linked list and its parent Leader struct
thistype next = 0
thistype prev = 0
Leader leader
//These fields are used to prevent orders issued by player and resume previous one
integer orderId = OrderId("stop")
boolean orderPoint = false
real orderX = 0.00
real orderY = 0.00
widget orderTarget = null
static boolean checkOrder = true //If true, the orders will be resumed (prevents infinite loops)
static method order takes nothing returns nothing
local thistype this = GetTimerData(GetExpiredTimer())
set .checkOrder = false
call IssueImmediateOrderById(.me, .orderId)
set .checkOrder = true
call ReleaseTimer(GetExpiredTimer())
endmethod
method resumeOrder takes nothing returns nothing
set .checkOrder = false
if .orderPoint then
call IssuePointOrderById(.me, .orderId, .orderX, .orderY)
elseif .orderTarget != null then
call IssueTargetOrderById(.me, .orderId, .orderTarget)
else
call TimerStart(NewTimerEx(this), 0.00, false, function thistype.order)
endif
set .checkOrder = true
endmethod
static method create takes unit s, Leader l returns thistype
local thistype this = thistype.allocate()
set .me = s
set .leader = l
return this
endmethod
//AutoData is neeeded to attach structs to units
implement AutoData
method remove takes nothing returns nothing
//On removal, the two adjacent nodes in the list are joined together
set .next.prev = .prev
set .prev.next = .next
set .leader.memberCount = .leader.memberCount - 1
if this == .leader.first then
//If the dying member is first in the list, the next member becomes first
set .leader.first = .next
endif
call .destroy()
endmethod
endstruct
private struct Leader
//Leader struct instances are attached to leading units
integer memberCount = 0
Soldier first = 0 //First member in the list from which the iteration begins
//Fields used during training
boolean training = false
texttag tag
integer trainCount = 0
string bar
string filled
real trainSpeed
real trainProgress = 0.00
//Although these can be retrieved anytime, since they're used in calculations, for optimization, they're saved here
integer spr
real vDistance
real hDistance
static method createFilter takes unit u returns boolean
return isLeader(u) //Unit of leading type that enter map are automatically assigned a Leader struct instance
endmethod
method iterate1 takes nothing returns nothing
//This method is executed every second and is used to check if a member wanders far off
local Soldier soldier = .first //Starting for first soldier in the list
local real lx = GetUnitX(.me)
local real ly = GetUnitY(.me)
local real a
loop
exitwhen soldier == 0 //When hitting the end of the list, the loop finishes
if not IsUnitInRange(soldier.me, .me, WANDER_DISTANCE) then
//If the member is too far, he/she is issued an order to go back
set a = Atan2(GetUnitY(soldier.me) - ly, GetUnitX(soldier.me) - lx)
set Soldier.checkOrder = false //To prevent infinite loop
set soldier.orderId = OrderId("move")
set soldier.orderPoint = true
set soldier.orderX = lx + ARRIVAL_RANGE*Cos(a)
set soldier.orderY = ly + ARRIVAL_RANGE*Sin(a)
set soldier.orderTarget = null
call IssuePointOrderById(soldier.me, soldier.orderId, soldier.orderX, soldier.orderY)
set Soldier.checkOrder = true
endif
set soldier = soldier.next
endloop
endmethod
static method create takes unit u returns thistype
local thistype this = thistype.allocate()
local integer id = GetUnitTypeId(u)
set .spr = detachmentSPR(id)
set .vDistance = vSpacing(id)
set .hDistance = hSpacing(id)
set .trainSpeed = BAR_CONSTANT/soldierTrainTime(id)
call .start1() //Starts the one second interval iteration to prevent wandering
return this
endmethod
static player owner
static unit building
static real distance
static thistype dis
static method filter takes nothing returns boolean
//This method is used in a unit enumeration to find the nearest friendly building
local unit f = GetFilterUnit()
local real x
local real y
local real d
local thistype this = .dis
if IsUnitType(f, UNIT_TYPE_STRUCTURE) and IsUnitAlly(f, .owner) then
set x = GetUnitX(.me) - GetUnitX(f)
set y = GetUnitY(.me) - GetUnitY(f)
set d = SquareRoot(x*x + y*y)
if d < .distance then
set .building = f
set .distance = d
endif
endif
set f = null
return false
endmethod
method iterate32 takes nothing returns nothing
//This method is executed 32 times a second and handles training bars
local Soldier new
local real a
call SetTextTagPos(.tag, GetUnitX(.me) - 100.0, GetUnitY(.me), GetUnitFlyHeight(.me) + 160.0)
if .bar == "" then //When the bar finishes, the training is done
set .owner = GetOwningPlayer(.me)
set .building = null
set .distance = TRAIN_RANGE + 100.0
set .dis = this
//Finding nearest friendly building
call GroupEnumUnitsInRange(enumGroup, GetUnitX(.me), GetUnitY(.me), TRAIN_RANGE, Filter(function thistype.filter))
if .building != null then //If there is one
//A unit is trained
set .trainCount = .trainCount - 1
call SetPlayerState(.owner, PLAYER_STATE_RESOURCE_FOOD_USED, GetPlayerState(.owner, PLAYER_STATE_RESOURCE_FOOD_USED) - soldierFoodCost(GetUnitTypeId(.me)))
set new = Soldier.create(CreateUnit(.owner, soldierCode(GetUnitTypeId(.me)), GetUnitX(.building), GetUnitY(.building), 270.0), this)
//This unit becomes the first in the list
set new.next = .first
set new.prev = 0
set .first.prev = new
set .first = new
set .memberCount = .memberCount + 1
set a = Atan2(GetUnitY(new.me) - GetUnitY(.me), GetUnitX(new.me) - GetUnitX(.me))
set Soldier.checkOrder = false
//Unit is issued to move close to the leader
set new.orderId = OrderId("move")
set new.orderPoint = true
set new.orderX = GetUnitX(.me) + ARRIVAL_RANGE*Cos(a)
set new.orderY = GetUnitY(.me) + ARRIVAL_RANGE*Sin(a)
call IssuePointOrderById(new.me, new.orderId, new.orderX, new.orderY)
set Soldier.checkOrder = true
if .trainCount == 0 then
//If all the training is done, the periodic execution is stopped and bar removed
call .stop32()
call DestroyTextTag(.tag)
set .training = false
else
//Otherwise it starts a new bar
set .bar = BAR
set .filled = "|r"
set .trainProgress = 0.00
call SetTextTagText(.tag, BAR_COLOR+I2S(.trainCount)+" : "+.filled+.bar, 0.021)
endif
endif
else
//The training bar consists of two strings, a filled bar (.filled) and empty, original bar (.bar)
//Progress consists of extending the filled bar and shortening the original one
set .trainProgress = .trainProgress + .trainSpeed
if .trainProgress >= 1.00 then
set .trainProgress = 0.00
set .filled = SubString(.filled, 0, StringLength(.filled) - 2) + "||" + "|r"
set .bar = SubString(.bar, 0, StringLength(.bar) - 2)
call SetTextTagText(.tag, BAR_COLOR+I2S(.trainCount)+" : "+.filled+.bar, 0.021)
endif
endif
endmethod
//Modules for attaching instances and periodic iterations
implement AutoCreate
implement II32
implement II1
method queue takes nothing returns nothing
//Queueing a unit for training
set .trainCount = .trainCount + 1
if not .training then
//If there are no other units being trained, create the bar for it
set .training = true
set .tag = CreateTextTag()
set .bar = BAR
set .filled = "|r"
set .trainProgress = 0.00
call SetTextTagPos(.tag, GetUnitX(.me) - 100.0, GetUnitY(.me), GetUnitFlyHeight(.me) + 160.0)
call SetTextTagVisibility(.tag, GetLocalPlayer() == GetOwningPlayer(.me))
call .start32()
endif
call SetTextTagText(.tag, BAR_COLOR+I2S(.trainCount)+" : "+.filled+.bar, 0.021)
endmethod
method cancel takes nothing returns nothing
//Canceling a training unit
local player p
local integer id
if .training then
set p = GetOwningPlayer(.me)
set id = GetUnitTypeId(.me)
//Returning the resources back to player
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) + soldierGoldCost(id))
call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) + soldierLumberCost(id))
call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_USED, GetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_USED) - soldierFoodCost(id))
set p = null
set .trainCount = .trainCount - 1
if .trainCount == 0 then
//If that was the last unit, stop the periodic execution and remove the bar
call .stop32()
call DestroyTextTag(.tag)
set .training = false
else
//Otherwise start with next unit
set .bar = BAR
set .filled = "|r"
set .trainProgress = 0.00
call SetTextTagText(.tag, BAR_COLOR+I2S(.trainCount)+" : "+.filled+.bar, 0.021)
endif
endif
endmethod
method replace takes nothing returns nothing
//When the leader dies, a replacement has to be found
local Soldier newLeader = .first //The first available member is picked
if newLeader == 0 then //If the list is empty
//Stop any training and running methods and destroy the instance
if .training then
call .stop32()
call DestroyTextTag(.tag)
endif
call .stop1()
call .destroy()
else
//If there is an available member, then transform it into leading unit type
call transformSoldier(newLeader.me, GetUnitTypeId(.me))
//Assign the Leader instance to the new leader
set .me = newLeader.me
set .first = newLeader.next //The next member becomes the new first
call newLeader.remove() //Removing Soldier instance attached to the new leader
endif
endmethod
static method train takes nothing returns boolean
//Issuing training for a detachment
local thistype this
local integer id
local player p
//Checking if the casted ability is one of the reinforcing abilities
if isReinforce(GetSpellAbilityId()) then
set this = thistype[GetTriggerUnit()]
set id = GetUnitTypeId(.me)
set p = GetTriggerPlayer()
//Inspecting the requirements and whether they are fulfilled
if .memberCount + .trainCount < detachmentSize(id) then
if GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) >= soldierGoldCost(id) then
if GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) >= soldierLumberCost(id) then
if GetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_CAP) - GetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_USED) >= soldierFoodCost(id) then
//After all the checks, the resources are being deducted
call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD) - soldierGoldCost(id))
call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER) - soldierLumberCost(id))
call SetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_USED, GetPlayerState(p, PLAYER_STATE_RESOURCE_FOOD_USED) + soldierFoodCost(id))
//And a unit is queued for training
call .queue()
else
call SimError(p, ERROR_INSUFFICIENT_FOOD)
endif
else
call SimError(p, ERROR_INSUFFICIENT_LUMBER)
endif
else
call SimError(p, ERROR_INSUFFICIENT_GOLD)
endif
else
call SimError(p, ERROR_DETACHMENT_FULL)
endif
set p = null
elseif GetSpellAbilityId() == CANCEL then
//If the casted ability was cancel, call the cancel method
call thistype[GetTriggerUnit()].cancel()
endif
return false
endmethod
static method death takes nothing returns boolean
//If a dying unit is a leader or a soldier, corresponding procedures are performed
if isSoldier(GetTriggerUnit()) then
call Soldier[GetTriggerUnit()].remove()
elseif isLeader(GetTriggerUnit()) then
call thistype[GetTriggerUnit()].replace()
endif
return false
endmethod
static method order takes nothing returns boolean
//Mimicking leader's immediate orders
local thistype this
local Soldier soldier
local integer id
if isLeader(GetTriggerUnit()) then //If the issued unit is a leader
set this = thistype[GetTriggerUnit()]
if .memberCount > 0 then
set id = GetIssuedOrderId()
set soldier = .first
set Soldier.checkOrder = false
loop
exitwhen soldier == 0
//Issue the same order to every member
if IssueImmediateOrderById(soldier.me, id) then
//Issuing defend order alone interrupts previous orders so it has to be resumed
if id == OrderId("defend") or id == OrderId("undefend") then
call soldier.resumeOrder()
//Resuming order sets .checkOrder to true so it has to be set back to false
set Soldier.checkOrder = false
else
//If it wasn't a defend order, the standard procedure of saving issued order is carried out
set soldier.orderId = id
set soldier.orderPoint = false
set soldier.orderTarget = null
endif
endif
set soldier = soldier.next
endloop
set Soldier.checkOrder = true
endif
elseif isSoldier(GetTriggerUnit()) and Soldier.checkOrder then
//If the issued unit was soldier and the order did not come from the leader, the previous order is resumed
call Soldier[GetTriggerUnit()].resumeOrder()
endif
return false
endmethod
static method orderPoint takes nothing returns boolean
//Mimicking leader's point orders
local thistype this
local Soldier soldier
local integer id
local integer i
local integer rows
local real leaderx
local real leadery
local real facing
local real x
local real y
local real d
local real a
local integer c
if isLeader(GetTriggerUnit()) then
set this = thistype[GetTriggerUnit()]
if .memberCount > 0 then
set id = GetIssuedOrderId()
//Caching order coordinates and angle towards it
set leaderx = GetOrderPointX()
set leadery = GetOrderPointY()
set facing = Atan2(leadery - GetUnitY(.me), leaderx - GetUnitX(.me))
set soldier = .first
set rows = (.memberCount + (.spr - 1)) / .spr //Getting number of rows (.spr - soldiers per row)
set i = 1
set Soldier.checkOrder = false
loop
//Iterating every row
exitwhen i > rows
set x = i*.vDistance //Vertical distance to the row
//Getting the number of soldiers in current row
if i == rows then //If it's the last row
//Then the number needs to be calculated
set c = .memberCount - (rows - 1)*.spr
else
//If it's not last row, then the number is standard
set c = .spr
endif
set y = (c - 1)*.hDistance/2 //Distance from the first soldier's position in the row to the middle of the row
set d = SquareRoot(x*x + y*y) //Distance between the leader and the soldier's position
set a = Atan2(y, x) + facing + bj_PI //Angle between the leader and the soldier's position
//Coordinates of the first soldier in the row
set x = leaderx + d*Cos(a)
set y = leadery + d*Sin(a)
set a = facing + bj_PI/2 //Row angle, used for lining soldiers up
loop
//Iterating row's soldiers and their positions
set c = c - 1
//Saving issued order and giving it to the soldiers
set soldier.orderPoint = true
set soldier.orderX = x + c*.hDistance*Cos(a)
set soldier.orderY = y + c*.hDistance*Sin(a)
set soldier.orderTarget = null
if IssuePointOrderById(soldier.me, id, soldier.orderX, soldier.orderY) then
set soldier.orderId = id
else
call IssuePointOrderById(soldier.me, OrderId("smart"), soldier.orderX, soldier.orderY)
set soldier.orderId = OrderId("smart")
endif
set soldier = soldier.next
exitwhen c == 0
endloop
set i = i + 1
endloop
set Soldier.checkOrder = true
endif
elseif isSoldier(GetTriggerUnit()) and Soldier.checkOrder then
call Soldier[GetTriggerUnit()].resumeOrder()
endif
return false
endmethod
static method orderTarget takes nothing returns boolean
//Mimicking leader's target orders
local thistype this
local Soldier soldier
local integer id
local widget target
if isLeader(GetTriggerUnit()) then
set this = thistype[GetTriggerUnit()]
if .memberCount > 0 then
set id = GetIssuedOrderId()
set target = GetOrderTarget()
set soldier = .first
set Soldier.checkOrder = false
loop
exitwhen soldier == 0
//Saving leader's order and issuing it to members
set soldier.orderPoint = false
set soldier.orderTarget = target
if IssueTargetOrderById(soldier.me, id, target) then
set soldier.orderId = id
else
call IssueTargetOrderById(soldier.me, OrderId("smart"), target)
set soldier.orderId = OrderId("smart")
endif
set soldier = soldier.next
endloop
set Soldier.checkOrder = true
set target = null
endif
elseif isSoldier(GetTriggerUnit()) and Soldier.checkOrder then
call Soldier[GetTriggerUnit()].resumeOrder()
endif
return false
endmethod
static method onInit takes nothing returns nothing
static if LIBRARY_RegisterPlayerUnitEvent then
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.train)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.death)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function thistype.order)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function thistype.orderPoint)
call RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function thistype.orderTarget)
else
local trigger t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function thistype.train))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddCondition(t, Condition(function thistype.death))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerAddCondition(t, Condition(function thistype.order))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER)
call TriggerAddCondition(t, Condition(function thistype.orderPoint))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
call TriggerAddCondition(t, Condition(function thistype.orderTarget))
set t = null
endif
endmethod
endstruct
private module initModule
private static method onInit takes nothing returns nothing
//Initializing table and soldier linked list end
set detachmentData = TableArray[8]
set Soldier(0).next = 0
set Soldier(0).prev = 0
endmethod
endmodule
private struct initStruct
implement initModule
endstruct
endlibrary