• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

TrainingDetection v3.1a

Info:

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.
Now it also provides funtion to get remaining time of a unit being trained.

Credits:


Code:
JASS:
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

Keywords:
Unit, UnitType, Building, Train, Detect, System, Table, IcemanBo, null
Contents

TrainingDetection (Map)

Reviews
10:58 2th May 2014 BPower: Code looks good to me. Approved. In my eyes you could spell all API functions in full i.e CountFinishedUps --> CountFinishedUpgrades. I changed version 0.9 to 1.0. It indicates this is working and ready to use. :)...
Top