• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

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. :)...

Moderator

M

Moderator

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. :)


17:24, 11th Mar 2014
BPower:
I can see this beeing useful, however it is buggy at the moment.
You need more safety. Refer to my last post.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
@Dalvengyr: You don't have to null these.

Please follow the JPAG, you'll find the link in my signature. Get rid of that _
Index indicates UnitUserData or loop index. It causes confusion because you actually refer to the unit id.
I would prefer if you use triggercondition instead of triggeraction.
The code is easy to understand, still some documentation would be nice.
TrainDebug implies debug mode, which is also not true.
Unit Indexer is not required.
Hu? There is like no context between your system and an UnitIndexer.
 
you forgot to null them
unless they get not destroyed no need to null them :p

Please follow the JPAG, you'll find the link in my signature. Get rid of that _
Index indicates UnitUserData or loop index. It causes confusion because you actually refer to the unit id.
I would prefer if you use triggercondition instead of triggeraction.
The code is easy to understand, still some documentation would be nice.
TrainDebug implies debug mode, which is also not true.
Changed, and updated.

Hu? There is like no context between your system and an UnitIndexer.
That was a fail, lol. ^_^
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
Looks really useful, but i noted some bugs:
- "building is currently training X units" looks like more the number of units attempted to be trained when you train some units and then cancel them.
- when you train 7 units and then cancel them and click the building which was training them appears a weird number 12345678 or something.
It would be great if you fixed this :p:thumbs_up:
 
Looks really useful, but i noted some bugs:
- "building is currently training X units" looks like more the number of units attempted to be trained when you train some units and then cancel them.
- when you train 7 units and then cancel them and click the building which was training them appears a weird number 12345678 or something.
It would be great if you fixed this :p:thumbs_up:
2. Aw, thank you. You mean the if you cancel all training objects, then there is still the CurrentTrainedUnit not null? (bug) Is this also what you meant?. Thank you. Edit: I see now what you mean

1. Sorry what? Did not quizte understand. You're talkking about DisplayTrains, right?
DisplayTrains should display amount of trained objects, and then list them. (not always in correct order, but thats not so important)

Edit:

Bug should be fixed now. DisplayTrains takes now player as parameter. Updated.
 
Last edited:

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
1. yes. It's like... well lets say you order the building to train 3 units, then you cancel them. After you click the building, it says that it is currently trainning 3 objects, while it isn't, because you canceled them :p
 
^Thanks. Will make Table optional in next update.

Edit: mentioned bug when canceling researches. Will fix this soon.

Edit 2:

Updated.

Now optional usage of Bribe's table. jesus.. code looks more ugly with this optional, next time I will just use it. (sorry mods^^)

I know I could move my functions into the struct and change them to static methods, but there wont be any difference. Maybe I will do so in next update.
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
I've just done it: train 6 units, *[cancel 1, click on the building, then train another, click on the building again], repeat these 4 last steps*, then it should appear a message saying ObjectId: 0. I don't see how it is working properly :S
Edit: btw, correct me if I'm wrong:
JASS:
private struct A extends array
->
JASS:
private struct TD extends array
Shouldn't it have a proper name? like TD (train detection), just an example.
and shouldn't it be like this?
JASS:
//stuff
    private struct TD extends array
        static if LIBRARY_Table then
            static TableArray table
        else 
            static hashtable hash = InitHashtable()
        endif
    endstruct
//stuff
ok that's all, don't be mad at me xD :grin:
 
I've just done it: train 6 units, *[cancel 1, click on the building, then train another, click on the building again], repeat these 4 last steps*, then it should appear a message saying ObjectId: 0.
I testes as you explained and worked for me. :S

Which ObjectId is 0? There is current and last.

Current displays Id of object which is currently training.

If it finished training it gets automaticlly the "LastTrained".

Until no unit finished training ObjectId of last train is always 0.

Shouldn't it have a proper name? like TD (train detection), just an example.
and shouldn't it be like this?
JASS:
//stuff
    private struct TD extends array
        static if LIBRARY_Table then
            static TableArray table
        else 
            static hashtable hash = InitHashtable()
        endif
    endstruct
//stuff
Sorry, what? I only understood you suggest better name for struct.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
return FALSE --> return false

Making Table optional is somehow neat. Good Job

However you have a lot of unnessesary code within the static if.
You could put only stuff inside the static if condition which is actually different. For instance in function TrainStart the static if should only cover these two lines.
JASS:
            static if LIBRARY_Table then
                set TD.table[CURRENTKEY][unitId] = trainId
                set TD.table[CURRENTKEY].string[unitId] = trainName
            else
                call SaveInteger(TD.hash, unitId, CURRENTKEY, trainId)
                call SaveStr(TD.hash, unitId, CURRENTKEY, trainName)
            endif
Do you realise that TrainOrder also stores a lot of crap. A LOT.
For example if someone hits "stop" with his unit. That will also be stored.
Since buildings can't oder such things it shouldn't be a big deal.

However if your building can order stop (A tower), then you are screwed because you will try to access TableArray instances which do not exist.
Why is that so? local integer trainCounter = TD.table[COUNTKEY][unitId] + 1 --> You happily increase this counter, but other orders than train-orders are also considered.

You can easily test this by placing a footman into you map and order stop a couple of times.
 
return FALSE --> return false

Making Table optional is somehow neat. Good Job

However you have a lot of unnessesary code within the static if.
You could put only stuff inside the static if condition which is actually different. For instance in function TrainStart the static if should only cover these two lines.
JASS:
            static if LIBRARY_Table then
                set TD.table[CURRENTKEY][unitId] = trainId
                set TD.table[CURRENTKEY].string[unitId] = trainName
            else
                call SaveInteger(TD.hash, unitId, CURRENTKEY, trainId)
                call SaveStr(TD.hash, unitId, CURRENTKEY, trainName)
            endif
Do you realise that TrainOrder also stores a lot of crap. A LOT.
For example if someone hits "stop" with his unit. That will also be stored.
Since buildings can't oder such things it shouldn't be a big deal.

However if your building can order stop (A tower), then you are screwed because you will try to access TableArray instances which do not exist.
Why is that so? local integer trainCounter = TD.table[COUNTKEY][unitId] + 1 --> You happily increase this counter, but other orders than train-orders are also considered.

You can easily test this by placing a footman into you map and order stop a couple of times.
I know there was lof of not needed code. I did it on purpose, because I did know 100% if I gonna let it at this state with optional table. Anyway, I changed it now.

Thank you very much for pointing on storage of unwanted orders. Now I check if there exists an ObjectName of the orderId, this should do it.

^This could also be the bug for Wrda. For myself I never could successfully let this bug occur, so idk for sure if it's fixed, I hope so..

Updated.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
  • I recommend you to mark Table as required, since there is no reason for user to use normal Hashtable instead of Table
    EDIT:
    forget it, I think as optional is better, so GUI ers no need to uselessly import Table
  • JASS:
        globals
            private   constant   integer     COUNT_KEY      =  8
            private   constant   integer     CURRENT_KEY    =  9
            private   constant   integer     LAST_KEY       =  10
            private   constant   integer     TRAIN_KEY      =  11
            private   constant   string      EMPTY_STRING   =  "Default string"
        endglobals
    warn user not to modify it, except if they are understand what they are doing :>
    especialy for EMPTY_STRING, I'm not sure about this bcs I'm not clearly remember it, but it must be "(Default string)" or "(Default String)", not "Default string". haven't tested it. it's important because it will causes bug if you mistyped it, I guess.
  • JASS:
    // check if train is no research
    // check if train is no unit
    I dunno understand what you are talking about :> but not a problem :grin:
  • May I ask about the useability of this system? to reveal what unit are enemies currently training? looks cheaty :xxd:
 
Next time maybe I really just make it not optional, but as it's done already, think won't remove it... it only has benefits. ;)

I didnt thought anyone would change anything in code, but maybe I really should write it in a comment, that there is nothing to config.

especialy for EMPTY_STRING , I'm not sure about this
Honestly me was neither, but I just tested it. And this was the text that always was displayed if I trtied to refer to an ObjectName which doesn't exist. So it's kind of safety not to detect unwanted orders.

JASS:
// check if train is no research
// check if train is no unit
I dunno understand what you are talking about :> but not a problem :grin
I use 3 events:
1. training
2. researching
3. upgrading

In most cases units are trained, so I first set my trainId = GetTrainedUnitType() But this will return 0 if it's no unit so, so better I check it.
Same with research. And if it's no research it has to be an upgrade, because I only use these 3 events.
There may be a better way todo so, teach me if there is. ;) I would only know to check for GetTriggerEventId(), but it would be less efficient as I would have more function calls if my first check is not matching.

May I ask about the useability of this system? to reveal what unit are enemies currently training? looks cheaty :xxd:
Yo, good question. I always thought that people may think it's for cheating... :S
But in past there sometimes were threads about exactly the question how to get the unit that is currently trained, and there was always the same answer: "impossible".

And then I needed it one day for a side project. For example here I use it for auto-training. After a building finishes training a unit, I order it after a short timeout to train LastTrainedUnitId.
Also for example if you reach food limit, your building will stop training. So, whenever a unit dies, I check if CountCurrentTrains equals 0. If yes I order building to train units again, and so on ...

Thank you for your attention. :csmile:
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I recommend you to mark Table as required, since there is no reason for user to use normal Hashtable instead of Table
EDIT:
forget it, I think as optional is better, so GUI ers no need to uselessly import Table
have you noticed that I updated the post? Table as optional is cool..

Yo, good question. I always thought that people may think it's for cheating... :S
I also found this useful on melee map to check what unit is allied player currently training, then coder can display it using multiboard. so there is no more question about useability ^^ good job

my suggestion:
if possible, how about adding CheckCurrentResearch/Upgrade functionality?
 

Wrda

Spell Reviewer
Level 25
Joined
Nov 18, 2012
Messages
1,864
I also found this useful on melee map to check what unit is allied player currently training, then coder can display it using multiboard. so there is no more question about useability ^^ good job

You actually don't need this system to check what unit is allied player currently training, there's function to do that when you click on an allied building, it shows you the units that are being trained.
 
You should explain what this would be used for, so that noobs like me can begin to learn tables. Why would I want to save the value to a table?
Ok, I will try to explain my personal usage... I use it in a footy map:

It allows me to auto-train several unit types with same building. (most systems only support only one UnitType/Building)

Also, once the food limit is reached, training is logically stopped. So when ever one of my units die, I check if my building is already training a unit or not. I do this with CountCurrentTrains
If not I would like to continue the training automatically. For this I use GetLastTrainId

And there was several times the question in forum how to detect which unit is currently trained by a building. Here you simply can use GetCurrentTrainId and GetCurrentTrainName to display it.

I don't think someone will take usage of all API (and I know that's not the best for a system also to have not 100% necessarily functions), but as it's still readable and is not overflooded so I think it's acceptable. :csmile:
 
Last edited:

Deleted member 219079

D

Deleted member 219079

What are the disadvantages of not using table?
 
You can't have more than 256 hashtables.
If you don't use table then the system creates a new hashtable only for this system.
If you use table, the "table-system" creates one hashtable and shares it to all systems that uses table. (so not necessarily a new hashtable is created each time)

So if you are creating a lot of hashtables for you map then it's recommended to use table.
 
Last edited:
No, and I'm not sure there is something to read training time.
Maybe one approach is that the user defines every training time
for unit types. The data would get saved in a table, and the training system could read it out.
If you need it for a map, I suggest that there should be written an extra seperated system for it that can handle it as extension.
 
Last edited:
Thanks to masta_orc for PMing me about a crash bug when training a hero.

The bug won't occur anymore.

It is because:
native GetUnitBuildTime takes integer unitid returns integer
... only works for normal units. Not for heroes.

Now my function to get remaining build time will just return 0 for heroes.

Thanks to Killcide for helping in fixing it.
 
Level 6
Joined
Oct 31, 2015
Messages
95
I've downloaded the latest version of this system right now and noticed that when you order two or more units/upgrades and then cancel a unit/upgrade which is not the one currently being trained/researched the system apparently loses track over the remaining time of the unit/upgrade being trained/researched. All other data remain, except for "spawn in:". Everything comes back to normal once the actual unit/upgrade is finished or cancelled.
 
Updated.

With gettting correct order in object queue and using GetGetCleanTraningQueue takes unit building returns nothing, all objects got canceled and re-ordered. This might be wrong, if user wants not to cancel first object in training queue, as it would need to start the training process from begining after an re-order.

So there's an extra parameter now, if currently trained object should not be touched:
GetCleanTraningQueue takes unit building, boolean keepFirst returns nothing
 
Level 3
Joined
Aug 25, 2012
Messages
30
I just found an issue with this - if you cancel something using the "Cancel" button (ESC shortcut), then it does not get registered correctly. My case: I am training one unit, cancel it using ESC, GetTrainingQueueSize still returns 1.
 
Level 10
Joined
Sep 25, 2013
Messages
521
Thanks for this system, very useful and super easy to install. Just one question, how do i disable the information display which tells you what units you are training and how many etc?

EDIT: I just disabled the "unit select" trigger which seems to have done the trick
 
Last edited:
Top