library TrainingDetection initializer Init/* v3.1a -- www.hiveworkshop.com/threads/trainingdetection.248978/
Information
The system provides information about objects being trained.
It also allows you to loop through the current training queue,
and saves the amount object types that were entirely trained by a unit.
Objects are: Units, Researches/Techs, Upgrades
*/ requires /*
*/ TimerUtils /* www.wc3c.net/showthread.php?t=101322
*/ Table /* www.hiveworkshop.com/forums/jass-resources-412/snippet-new-table-188084/
API
*/
//! novjass
// Last & Current getters
GetCurrentTrainedObjectId takes unit whichbuilding returns integer
GetLastTrainedObjectId takes unit whichbuilding returns integer
GetBuildTimeRemaining takes unit whichbuilding returns real
// It will return the remaining time for a unit being trained.
// Sadly the native only works for normal units, not for heroes.
// For anything but a normal unit it will return 0.
// Queue Getters
// Attention!
// The slot number might not be identical with the true position in order-queue.
// Only the size number is identical. But the true slot positon might change.
// With knowing the size you might loop through the queue each get each element.
GetTrainingQueueSize takes unit whichbuilding returns integer
GetTrainedObjectId takes unit whichbuilding, integer whichslot returns
ReOrderTrainingQueue takes unit building returns nothing
// ReOrder will internally stop and start trainings to simulate cancel at front,
// be aware of that. After cleaning the queue, all units will have correct position in queue.
// (works with GetCleanTrainingQueue, see later)
CancelTrainingBack takes unit building returns nothing
// Back works same as using order native with CANCEL-id
CancelTrainingFront takes unit building returns nothing
// Front will internally stop and start trainings to simulate cancel at front, be aware of that.
// Count objects
CountFinishedUnits takes unit whichbuilding returns integer
CountFinishedTechs takes unit whichbuilding returns integer
CountFinishedUpgrades takes unit whichbuilding returns integer
// A bit advanced:
GetCleanTraningQueue takes unit building, boolean keepFirst returns nothing
// After calling it the building will have all traings canceled.
// set keepFirst "true" to not having currently object training canceled. This is an
// exception, because first element does not need to get canceled to retrieve
// the correct object ID.
// After calling, you will have access to:
integer array TrainingDetection_Queue[]
// which will hold correct order of trained objects. Loop from 1 to .. until Queue[i] == 0.
// to access all elements. The array should be used to re-order the building's training.
// It does not matter if you keepFirst, or not, Queue[1] is always the first object in training.
// ==> you may use this tequnique to only re-order certain objects, for example,
// how it's used inside function CancelTrainingFront, which does not keep first, and then
// starts at Queue[2] to re-order training the objects.
//! endnovjass
// ============================= End API ============================= //
native GetUnitBuildTime takes integer unitid returns integer
private struct TimeData
readonly boolean allocated
readonly timer clock
private static key k
private static Table table = k
method destroy takes nothing returns nothing
if .allocated then
// for safety we let expire the timer like instantly
call TimerStart(.clock, 0, false, null)
call ReleaseTimer(.clock)
call .deallocate()
set .allocated = false
endif
endmethod
static method create takes unit building, integer unitId returns thistype
local thistype this = thistype.allocate()
set .allocated = true
set table[GetHandleId(building)] = this
call TimerStart(NewTimerEx(this), GetUnitBuildTime(unitId), false, null)
return this
endmethod
static method operator [] takes unit building returns thistype
return table[GetHandleId(building)]
endmethod
endstruct
globals
private TableArray table
// First 7 slots are rserved for training units.
private constant integer COUNT_KEY = 8 // How many objects are currently in queue.
private constant integer CURRENT_KEY = 9 // Currently trained object.
private constant integer LAST_KEY = 10 // Last Trained object.
private constant integer TRAIN_UNITS_KEY = 11 // Amount of trained units.
private constant integer TRAIN_UPGRADES_KEY = 12 // Amount of made upgrads.
private constant integer TRAIN_TECHS_KEY = 13 // Amount of made researches.
private constant integer LAST_CANCEL_KEY = 14
private constant integer MAX_KEY = 14
private constant string EMPTY_STRING = "Default string" // This is an indicator for an invalid ObjectName.
private constant integer ORDER_CANCEL = 851976
endglobals
// QueueClean will clean the queue from an object that was
// canceled or finished training.
private function QueueClean takes unit building, integer trainId returns nothing
local integer unitId = GetHandleId(building)
local integer i = 0
local integer trainCounter = table[COUNT_KEY][unitId]
set table[LAST_CANCEL_KEY][unitId] = trainId
set table[COUNT_KEY][unitId] = (trainCounter - 1)
if (trainCounter == 1) then // No loop needed if only 1 trained object exists.
set table[CURRENT_KEY][unitId] = 0
set table[1][unitId] = 0
else
loop // Loop thorugh queue to get correct object
set i = (i + 1)
if (table[i][unitId] == trainId) then
if (i != trainCounter) then
// we move last object from queue to fill the gap
set table[i][unitId] = table[trainCounter][unitId]
endif
set table[trainCounter][unitId] = 0
exitwhen(true)
endif
endloop
endif
endfunction
// Building gets order to train an object.
private function TrainOrder takes nothing returns boolean
local integer trainId = GetIssuedOrderId()
local integer unitId
local integer trainCounter
if (GetObjectName(trainId) != EMPTY_STRING and GetObjectName(trainId) != null) then
set unitId = GetHandleId(GetTriggerUnit())
set trainCounter = table[COUNT_KEY][unitId] + 1
set table[COUNT_KEY][unitId] = trainCounter
set table[trainCounter][unitId] = trainId
endif
return false
endfunction
// Building starts to train an object.
private function TrainStart takes nothing returns boolean
local unit u = GetTriggerUnit()
local integer unitId = GetHandleId(u)
local integer trainId = GetTrainedUnitType()
call TimeData[u].destroy()
if (trainId == 0) then // Check if object is no unit.
set trainId = GetResearched()
if (trainId == 0) then // Check if object is no tech.
set trainId = GetUnitTypeId(u) // -> Object is an upgrade.
endif
elseif not IsHeroUnitId(trainId) then
// Start timer for training unit
call TimeData.create(u, trainId)
endif
set table[CURRENT_KEY][unitId] = trainId
set u = null
return false
endfunction
// Building cansels training for a object in queue.
private function TrainCancel takes nothing returns boolean
local integer i = GetTrainedUnitType()
local unit u = GetTriggerUnit()
if (i == 0) then // Check if canceled object is an unit or a research, no need to check for upgrade in here.
call QueueClean(u, GetResearched())
else
call QueueClean(u, i)
if not IsHeroUnitId(i) and table[COUNT_KEY][GetHandleId(u)] == 0 then
// deallocate TimeData
call TimeData[u].destroy()
endif
endif
set u = null
return false
endfunction
// Building finished training an objec.t
private function TrainFinish takes nothing returns boolean
local unit building = GetTriggerUnit()
local integer unitId = GetHandleId(building)
local integer trainId = GetTrainedUnitType()
if (trainId == 0) then // Check if object was no unit.
set trainId = GetResearched()
if (trainId == 0) then // Check if object was no research.
set trainId = GetUnitTypeId(building) // -> Object was an upgrade
set table[TRAIN_UPGRADES_KEY][unitId] = table[TRAIN_UPGRADES_KEY][unitId] + 1
else
set table[TRAIN_TECHS_KEY][unitId] = table[TRAIN_TECHS_KEY][unitId] + 1
endif
else
set table[TRAIN_UNITS_KEY][unitId] = table[TRAIN_UNITS_KEY][unitId] + 1
if not IsHeroUnitId(trainId) then
call TimeData[building].destroy()
endif
endif
set table[LAST_KEY][unitId] = trainId
set table[CURRENT_KEY][unitId] = 0
call QueueClean(building, trainId)
set building = null
return false
endfunction
private function Init takes nothing returns nothing
local trigger trigger_order = CreateTrigger()
local trigger trigger_start = CreateTrigger()
local trigger trigger_cancel = CreateTrigger()
local trigger trigger_finish = CreateTrigger()
local player p
local integer i = 0
set table = TableArray[MAX_KEY+1]
loop
exitwhen i > bj_MAX_PLAYERS
set p = Player(i)
call TriggerRegisterPlayerUnitEvent(trigger_order, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
call TriggerRegisterPlayerUnitEvent(trigger_start, p, EVENT_PLAYER_UNIT_RESEARCH_START, null)
call TriggerRegisterPlayerUnitEvent(trigger_start, p, EVENT_PLAYER_UNIT_TRAIN_START, null)
call TriggerRegisterPlayerUnitEvent(trigger_start, p, EVENT_PLAYER_UNIT_UPGRADE_START, null)
call TriggerRegisterPlayerUnitEvent(trigger_cancel, p, EVENT_PLAYER_UNIT_TRAIN_CANCEL, null)
call TriggerRegisterPlayerUnitEvent(trigger_cancel, p, EVENT_PLAYER_UNIT_RESEARCH_CANCEL, null)
call TriggerRegisterPlayerUnitEvent(trigger_cancel, p, EVENT_PLAYER_UNIT_UPGRADE_CANCEL, null)
call TriggerRegisterPlayerUnitEvent(trigger_finish, p, EVENT_PLAYER_UNIT_TRAIN_FINISH, null)
call TriggerRegisterPlayerUnitEvent(trigger_finish, p, EVENT_PLAYER_UNIT_RESEARCH_FINISH, null)
call TriggerRegisterPlayerUnitEvent(trigger_finish, p, EVENT_PLAYER_UNIT_UPGRADE_FINISH, null)
set i = i + 1
endloop
call TriggerAddCondition(trigger_order, Condition(function TrainOrder))
call TriggerAddCondition(trigger_start, Condition(function TrainStart))
call TriggerAddCondition(trigger_finish, Condition(function TrainFinish))
call TriggerAddCondition(trigger_cancel, Condition(function TrainCancel))
endfunction
function CountFinishedUpgrades takes unit building returns integer
return table[TRAIN_UPGRADES_KEY][GetHandleId(building)]
endfunction
function CountFinishedUnits takes unit building returns integer
return table[TRAIN_UNITS_KEY][GetHandleId(building)]
endfunction
function CountFinishedTechs takes unit building returns integer
return table[TRAIN_TECHS_KEY][GetHandleId(building)]
endfunction
function GetTrainingQueueSize takes unit building returns integer
return table[COUNT_KEY][GetHandleId(building)]
endfunction
function GetTrainedObjectId takes unit building, integer slot returns integer
if (slot > 0) and (slot < 8) then
return table[slot][GetHandleId(building)]
else
return 0
endif
endfunction
function GetLastTrainedObjectId takes unit building returns integer
return table[LAST_KEY][GetHandleId(building)]
endfunction
function GetCurrentTrainedObjectId takes unit building returns integer
return table[CURRENT_KEY][GetHandleId(building)]
endfunction
function GetUnitBuildTimeRemaining takes unit building returns real
local TimeData this = TimeData[building]
if this.allocated then
return TimerGetRemaining(this.clock)
else
return 0.
endif
endfunction
private function GetLastCanceledObjectId takes unit building returns integer
return table[LAST_CANCEL_KEY][GetHandleId(building)]
endfunction
globals
public integer array Queue[10]
endglobals
function GetCleanTraningQueue takes unit building, boolean keepFirst returns nothing
local integer i
local integer lowerBound
local integer size = GetTrainingQueueSize(building)
if size < 1 or (size == 1 and keepFirst) then
return
endif
if keepFirst then
set lowerBound = 2
set Queue[1] = GetCurrentTrainedObjectId(building)
else
set lowerBound = 1
endif
set i = size
loop
exitwhen i < lowerBound
call IssueImmediateOrderById(building, ORDER_CANCEL)
set Queue[i] = GetLastCanceledObjectId(building)
set i = i - 1
endloop
set Queue[size + 1] = 0
endfunction
private function ReOrderTrainingQueue_p takes unit building, boolean keepFirst returns nothing
local integer i
local integer size = GetTrainingQueueSize(building)
if size < 1 or (size == 1 and keepFirst) then
return
endif
call GetCleanTraningQueue(building, keepFirst)
if keepFirst then
set i = 1
else
set i = 2
endif
loop
exitwhen i > size
call IssueImmediateOrderById(building, Queue[i])
set i = i + 1
endloop
endfunction
function ReOrderTrainingQueue takes unit building returns nothing
call ReOrderTrainingQueue_p(building, true)
endfunction
function CancelTrainingBack takes unit building returns nothing
call IssueImmediateOrderById(building, ORDER_CANCEL)
endfunction
function CancelTrainingFront takes unit building returns nothing
call ReOrderTrainingQueue_p(building, false)
endfunction
endlibrary