• 🏆 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!

[vJASS] BuilderTrack

A system to get the worker who started constructing a building, and a bit more.

JASS:
library BuilderTrack /* v1.3 hiveworkshop.com/threads/buildertrack.290327/


    */ requires UnitDex       /* hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
 
   
    Information
    ¯¯¯¯¯¯¯¯¯¯¯
   
            Some powers to work with builders that are constructing/repairing.
         
         
    Applied method
    ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
 
        Default repair mechanics:
     
        There exist always 2 similar ways for repairing and for each race.
     
            Human:
                1) The unit casts "Repair" on the building (will order "repair" internaly)
                   When reaching the building the worker will cast spell "Repair".
                2) User "right clicks" (smart-order) on the building
                   When reaching the building the worker will cast spell "Repair".
                 
                    repair-order-id: 852024
                    Repair-spell-id: 'Ahrp'
                 
            Orc:
                1) The unit casts "Repair" on the building (will order "repair" internaly)
                   When reaching the building the worker will cast spell "Repair".
                2) User "right clicks" (smart-order) on the building
                   When reaching the building the worker will cast spell "Repair".
                 
                    repair-order-id: 852024
                    Repair-spell-id: 'Arep'
                 
            Night Elves:
                1) The unit casts "Renew" on the building (will order "renew" internaly)
                   When reaching the building the worker will cast spell "Renew".
                2) User "right clicks" (smart-order) on the building
                   When reaching the building the worker will cast spell "Renew".
                 
                    renew-order-id: 852161
                    Renew-spell-id: 'Aren'
                 
            Undead:
                1) The unit casts "Restore" on the building (will order "restoration" internaly)
                   When reaching the building the worker will cast spell "Restore".
                2) User "right clicks" (smart-order) on the building
                   When reaching the building the worker will cast spell "Restore".
                 
                    restoration-order-id: 852202
                    Restore-spell-id:     'Arst'
                 
 
        Default build mechanics:
     
            Human:

                1. Worker gets point order with id of BuildingType.
                2. When worker starts constructing the building it gets the "repair" order towards the building.
                3. When receiving the "repair" order it starts the spell "Repair" towards the building.
                 
            Orc:
         
                1. Worker gets point order with id of BuildingType.
                2. < no more orders >
             
            Night Elves:
         
                1. Worker gets point order with id of BuildingType.
                2. < no more orders >
             
            Undead:
         
                1. Worker gets point order with id of BuildingType.
                2. < no more orders >
             
-----------
       
            Only for human builder (peasants) there would exist a proper method to get
            the worker who starts constructing a building, because we could detect the first unit
            who starts casting "Repair" on the building. Though, for other races there are different
            mechanics, so it's sadly not applyable.
       
            So to get the main builder this system uses a naive approach, which is not water-proof,
            but which should work in 99,9% of cases. When a structure is started being built, it
            takes the clostest worker of the respective player, which has currently the required order id.
         
            Tracking for power building (multiple units building at same time), and tracking repairing workers should work always fine.
         
*/  

//! novjass
 //=================== --------- API --------- ====================

    struct Builder
 
        public static method GetMainBuilder takes unit building returns unit
            // who initiated the construction of a building
            // (Human-race!) when a worker stops constructung, and an other one
            // starts again, the new worker will count as initiator
         
        public static method GetBuilderGroup takes unit building returns group
            // returns group with units who are currently repairing/building
            // do not destroy this group
         
        public static method GetBuilderAmount takes unit building returns integer
            // how many builders are currently repairing/building
     
        public static method IsBuildingCurrentlyConstructed takes unit building returns boolean
            // won't return "true" if a building is only being repaired
 
 //! endnovjass

struct Repair extends array
    public static constant integer HUMAN_SPELL           = 'Ahrp'
    public static constant integer HUMAN_ORDER           = 852024
    public static constant integer HUMAN_ORDER_ON        = 852025
    public static constant integer HUMAN_ORDER_OFF       = 852026
 
    public static constant integer ORC_SPELL             = 'Arep'
    public static constant integer ORC_ORDER             = 852024
    public static constant integer ORC_ORDER_ON          = 852025
    public static constant integer ORC_ORDER_OFF         = 852026
 
    public static constant integer NIGHT_ELVES_SPELL     = 'Aren'
    public static constant integer NIGHT_ELVES_ORDER     = 852161
    public static constant integer NIGHT_ELVES_ORDER_ON  = 852162
    public static constant integer NIGHT_ELVES_ORDER_OFF = 852163
 
    public static constant integer UNDEAD_SPELL          = 'Arst'
    public static constant integer UNDEAD_ORDER          = 852202
    public static constant integer UNDEAD_ORDER_ON       = 852203
    public static constant integer UNDEAD_ORDER_OFF      = 852204
 
    public static method IsRepairAbility takes integer id returns boolean
        return id == HUMAN_SPELL or id == ORC_SPELL or id == NIGHT_ELVES_SPELL or id == UNDEAD_SPELL
    endmethod
 
    public static method IsRepairOrder takes integer id returns boolean
        return id == HUMAN_ORDER or id == ORC_ORDER or id == NIGHT_ELVES_ORDER or id == UNDEAD_ORDER
    endmethod
 
    public static method IsRepairOrderOn takes integer id returns boolean
        return id == HUMAN_ORDER_ON or id == ORC_ORDER_ON or id == NIGHT_ELVES_ORDER_ON or id == UNDEAD_ORDER_ON
    endmethod
 
    public static method IsRepairOrderOff takes integer id returns boolean
        return id == HUMAN_ORDER_OFF or id == ORC_ORDER_OFF or id == NIGHT_ELVES_ORDER_OFF or id == UNDEAD_ORDER_OFF
    endmethod
endstruct
native UnitAlive            takes unit id                               returns boolean
struct Builder extends array
 
    private static constant integer SMART = 851971
    private static group Group = CreateGroup()

// building-related
    private static boolean array isConstructing
    private static integer array workerCount
    private static unit array mainBuilder
    private static group array builderGroup

// builder-related
    private static boolean array isWorking
    readonly unit builder
    readonly unit building
    readonly integer buildingType
 
    private static method create takes unit u, unit b returns thistype
        local thistype this = GetUnitId(u)
        local integer id    = GetUnitId(b)
        set .builder        = u
        set .building       = b
        set .buildingType   = GetUnitTypeId(b)
        set isWorking[this] = true
        call GroupAddUnit(builderGroup[id], u)
        if isConstructing[id] and workerCount[id] == 0 then
            set mainBuilder[id] = u
        endif
        set workerCount[id] = workerCount[id] + 1
        return this
    endmethod
 
    method destroy takes nothing returns nothing
        local integer id = GetUnitId(.building)
        call GroupRemoveUnit(builderGroup[id], .builder)
        set workerCount[id] = workerCount[id] - 1
        if isConstructing[id] and .builder == mainBuilder[id] and workerCount[id] > 0 then
            set mainBuilder[id] = FirstOfGroup(builderGroup[id])
        endif
        set .builder          = null
        set .building         = null
        set .isWorking[this]  = false
    endmethod
 
    private static method onCast takes nothing returns boolean
        local unit builder    = GetTriggerUnit()
        local unit building   = GetSpellTargetUnit()
        local integer id      = GetUnitId(building)
        if Repair.IsRepairAbility(GetSpellAbilityId()) and not IsUnitInGroup(builder, builderGroup[id]) then
            //if TriggerEvaluate(FILTER_HANDLER) then
                call create(builder, building)
            //endif
        endif
        set builder = null
        set building = null
        return false
    endmethod
 
    // if unit gets ordered we remove the instance
    private static method onOrder takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local thistype this = GetUnitId(u)
        local integer order = GetIssuedOrderId()
     
        // allowed scenarios for not to destroy:
     
        // unit is ordered autorepairOn
        // unit is ordered autorepairOff while having a "smart" order (because of "smart" it will still continue repairing)
        // unit is ordered to cast a Repair Ability on the building
     
        if isWorking[this] and not Repair.IsRepairOrderOn(order) and not(Repair.IsRepairOrderOff(order) and GetUnitCurrentOrder(u) == SMART) /*
         */and not (GetOrderTarget() == this.building and Repair.IsRepairAbility(order) ) then
            call .destroy()
        endif
        set u = null
        return false
    endmethod
 
    // destroy enum units
    private static method callback takes nothing returns nothing
        call thistype(GetUnitId(GetEnumUnit())).destroy()
    endmethod
 
    private static method onRemove takes integer id returns nothing
        if builderGroup[id] != null then
            call ForGroup(builderGroup[id], function thistype.callback)
            call DestroyGroup(builderGroup[id])
            set builderGroup[id] = null
        elseif isWorking[id] then
            call thistype(id).destroy()
        endif
    endmethod
    private static method onDeindex takes nothing returns nothing
        call onRemove(GetIndexedUnitId())
    endmethod
    private static method onDeath takes nothing returns boolean
        call onRemove(GetUnitId(GetTriggerUnit()))
        return false
    endmethod
 
    private static method onIndex takes nothing returns nothing
        local integer id = GetIndexedUnitId()
        set mainBuilder[id] = null
        set isConstructing[id] = false
    endmethod
 
    private static integer orderId_s // s_ static
    private static method unitFilter takes nothing returns boolean
        local real orderId
        set bj_lastCreatedUnit = GetFilterUnit()
        set orderId = GetUnitCurrentOrder(bj_lastCreatedUnit)    // use slot orders 1-6
        return UnitAlive(bj_lastCreatedUnit) and orderId == orderId_s or (orderId >= 852008 and orderId <= 852013 )
    endmethod
 
    private static method onConstructStart takes nothing returns boolean
        local unit building = GetConstructingStructure()
        local unit builder = null
        local unit temp_unit
        local real orderId
        local unit fog
        local integer id = GetUnitId(building)
        local real x = GetUnitX(building)
        local real y = GetUnitY(building)
        local real dx
        local real dy
     
        local real distance_min = 9999
        local real distance_temp
     
        set orderId_s = GetUnitTypeId(building)
     
        set isConstructing[id] = true
        set builderGroup[id]   = CreateGroup()
        set workerCount[id]    = 0
        set mainBuilder[id] = null
     
    // night elves Gold Mine, just find nearest main house
        if GetUnitAbilityLevel(building, 'Aenc') > 0 then
         
            call GroupEnumUnitsOfPlayer(Group, GetTriggerPlayer(), null)
            loop
                set fog = FirstOfGroup(Group)
                exitwhen fog == null
             
                if GetUnitAbilityLevel(fog, 'Aent')  > 0 then
                    set dx = GetUnitX(fog) - x
                    set dy = GetUnitY(fog) - y
                    set distance_temp = SquareRoot(dx*dx + dy*dy)
                 
                    if distance_temp < distance_min then
                        set builder = fog
                    endif
                endif
             
                call GroupRemoveUnit(Group, fog)
            endloop
         
            call create(builder, building)
            set builder = null
            set building = null
            return false
        endif
       
        // find any (random) unit with current order id, because we wanna know it's type of Repair ability
        call GroupEnumUnitsOfPlayer(Group, GetTriggerPlayer(), Filter(function thistype.unitFilter))
        set temp_unit = FirstOfGroup(Group)
 
 
        if GetUnitAbilityLevel(temp_unit, Repair.HUMAN_SPELL) > 0 or GetUnitAbilityLevel(temp_unit, Repair.UNDEAD_SPELL) > 0 then
         
            // undead/human Repair -> just get closest
            loop
                set fog = FirstOfGroup(Group)
                exitwhen fog == null
             
                set dx = GetUnitX(fog) - x
                set dy = GetUnitY(fog) - y
                set distance_temp = SquareRoot(dx*dx + dy*dy)
             
                if distance_temp < distance_min then
                    set builder = fog
                endif
             
                call GroupRemoveUnit(Group, fog)
            endloop
     
        elseif GetUnitAbilityLevel(temp_unit, Repair.ORC_SPELL) > 0 or GetUnitAbilityLevel(temp_unit, Repair.NIGHT_ELVES_SPELL) > 0 then
         
            // orc/night elve Repair -> get closest hidden unit
            loop
                set fog = FirstOfGroup(Group)
                exitwhen fog == null
             
                if IsUnitHidden(fog) then
                    set dx = GetUnitX(fog) - x
                    set dy = GetUnitY(fog) - y
                    set distance_temp = SquareRoot(dx*dx + dy*dy)
                 
                    if distance_temp < distance_min then
                        set builder = fog
                    endif
                endif
             
                call GroupRemoveUnit(Group, fog)
            endloop
        endif
     
    // no luck yet, it means the building was casted via item slot (tiny building spells)
        if builder == null then
         
            loop
                set fog = FirstOfGroup(Group)
                exitwhen fog == null
             
                set orderId = GetUnitCurrentOrder(fog)
                if (orderId >= 852008 and orderId <= 852013 ) then
                    set dx = GetUnitX(fog) - x
                    set dy = GetUnitY(fog) - y
                    set distance_temp = SquareRoot(dx*dx + dy*dy)
                 
                    if distance_temp < distance_min then
                        set builder = fog
                    endif
                endif
             
                call GroupRemoveUnit(Group, fog)
            endloop
         
        endif
     
        call create(builder, building)
        set builder = null
        set building = null
        return false
    endmethod
 
    private static method onConstructFinish takes nothing returns boolean
        set isConstructing[GetUnitId(GetConstructedStructure())] = false
        return false
    endmethod
 
    implement optional BuilderEvent
 
    private static method onInit takes nothing returns nothing
        local trigger t1 = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local trigger t3 = CreateTrigger()
        local trigger t4 = CreateTrigger()
        local trigger t5 = CreateTrigger()
        local player p
        local integer i = 0
        loop
            exitwhen i == bj_MAX_PLAYER_SLOTS
            set p = Player(i)
            call TriggerRegisterPlayerUnitEvent(t5, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(t5, p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
            call TriggerRegisterPlayerUnitEvent(t5, p, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
         
            call TriggerRegisterPlayerUnitEvent(t1, p, EVENT_PLAYER_UNIT_CONSTRUCT_START, null)
            call TriggerRegisterPlayerUnitEvent(t2, p, EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, null)
         
            call TriggerRegisterPlayerUnitEvent(t3, p, EVENT_PLAYER_UNIT_SPELL_EFFECT, null)
         
            call TriggerRegisterPlayerUnitEvent(t4, p, EVENT_PLAYER_UNIT_DEATH, null)
            set i = i + 1
        endloop
     
        call TriggerAddCondition(t1, Condition(function thistype.onConstructStart))
        call TriggerAddCondition(t2, Condition(function thistype.onConstructFinish))
        call TriggerAddCondition(t3, Condition(function thistype.onCast))
        call TriggerAddCondition(t4, Condition(function thistype.onDeath))
        call TriggerAddCondition(t5, Condition(function thistype.onOrder))
        call OnUnitDeindex(function thistype.onDeindex)
        call OnUnitIndex(function thistype.onIndex)
    endmethod
 
    public static method GetMainBuilder takes unit building returns unit
        return mainBuilder[GetUnitId(building)]
    endmethod
 
    public static method GetBuilderAmount takes unit building returns integer
        return workerCount[GetUnitId(building)]
    endmethod
 
    public static method GetBuilderGroup takes unit building returns group
        return builderGroup[GetUnitId(building)]
    endmethod
 
    public static method IsBuildingCurrentlyConstructed takes unit building returns boolean
        return isConstructing[GetUnitId(building)]
    endmethod

endstruct
endlibrary
 

Attachments

  • Builder Track.w3x
    26.9 KB · Views: 105
  • BuilderTrack v1.1.w3x
    27.5 KB · Views: 85
  • BuilderTrack v1.2.w3x
    28.7 KB · Views: 96
  • BuilderTrack v1.3.w3x
    28.6 KB · Views: 107
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
Looks pretty good.

The Builder.getMainBuilder looks like it wants to be Building.GetMainBuilder|Worker perhaps?:
JASS:
function Trig_Get_Worker_Actions takes nothing returns nothing
    local unit building = GetTriggerUnit() // GetConstructingStructure()
    local unit worker = Builder.GetMainBuilder(GetTriggerUnit()) // <--
endfunction

function InitTrig_Get_Worker takes nothing returns nothing
    set gg_trg_Get_Worker = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Get_Worker, EVENT_PLAYER_UNIT_CONSTRUCT_START )
    call TriggerAddAction( gg_trg_Get_Worker, function Trig_Get_Worker_Actions )
endfunction

These 2 seem rather similar, you could merge them into 1 "on-death-remove" function?:
JASS:
private static method onDeindex takes nothing returns nothing
    local integer id = GetIndexedUnitId()
    if isConstructing[id] then
        call ForGroup(builderGroup[id], function thistype.callback2)
        call DestroyGroup(builderGroup[id])
        set builderGroup[id] = null
        set mainBuilder[id] = null
    elseif isWorking[id] then
        call thistype(id).destroy()
    endif
endmethod

private static method onDeath takes nothing returns boolean
    local integer id = GetUnitId(GetTriggerUnit())
    if isConstructing[id] then
        call ForGroup(builderGroup[id], function thistype.callback2)
        call DestroyGroup(builderGroup[id])
        set builderGroup[id] = null
    elseif isWorking[id] then
        call thistype(id).destroy()
    endif
    return false
endmethod

It seems that Builder.GetMainBuilder() returns null when trying to build a Tomb of Relics (undead shop).

You could probably come up with more descriptive names than callback and callback2 =).

PS: It seems Bannar has a similar (retrieving the worker) library here (I've never used/tested it though).

PS2: I wonder how many people have wanted to get a reference to the worker over the years and had to come up with their own work around for something blizzard could've "easily" exposed as an event response function.
 
Oh, man. Didn't see Bannar had something similar already.
The
Builder.[COLOR=color: #666666]getMainBuilder[/COLOR]
looks like it wants to be
Building.[COLOR=color: #666666]GetMainBuilder[/COLOR]|Worker
perhaps?:
What? you confuse me:> It is "GetMainBuilder" already.

It seems that
Builder.[COLOR=color: #666666]GetMainBuilder[/COLOR]()
returns null when trying to build a Tomb of Relics (undead shop).
what :( will test it, thanks.

Yes, it's kind of ridiculous. Native would be so much better.

Will split eventsystem from basic system, it will be more leightweight as standalone.

edit:

seems the range was not big enough for Tomb of Relics:s must increase it
 
Last edited:
Changed the approach a bit now, and for workers with orc repair, or night elves repair have now I think a 100% chance to be the correct unit.
For human repair units and undead repair units still a bit less.

Splitted events for now, I don't think they are too important at first. May work on it an other time.

Spell-based created buildings won't get properly detected. It only works for units with the respective repair ability, so usually workers.

I haven't tested Bannar's work, but there is a different method used.
He relys on the building (race) , and I rely on the builder (repair ability).
Will need to test it.

edit1:

- Bannar's periodicly keeps tracking distance of all instances, I only do it once when it starts construction.
- This does also allow to track repairing workers, not only building ones.
- His does not have safety when the initial worker dies, and an other one continues/finishes.
- I find repair ability method a bit more clearer

edit2:

Added gold mine support
Added tiny-ability spell support

I think all should work now.
 
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
A system to get the worker who started constructing a building, and a bit more.

Does that mean that Builder.GetMainBuilder(GetTriggerUnit()) returns null in an event handler for EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL and EVENT_PLAYER_UNIT_CONSTRUCT_FINISH? Or is it undefined/error to call it?

Maybe you should explicitly document that.

What? you confuse me:> It is "GetMainBuilder" already.
Building vs Builder ;P
 
No, it didn't work, good catch. Because I "nulled" it in my destroy/deindex functions.
Fixed it, it works now in all events for user, and also onDeindex, or so.

Same for the check if building is being constructed, it can be also be checked from anywhere.

So if it works always maybe no documentation needed.

Building vs Builder ;P
:vw_death: idk^^

edit: also in bannar's something like powerbuild is not supported. ^^
 
It's telling me:
Identifier redeclared: "order"
in this:
JASS:
// if unit gets ordered we remove the instance
    private static method onOrder takes nothing returns boolean
        local unit u = GetTriggerUnit()
        local thistype this = GetUnitId(u)
        local integer order = GetIssuedOrderId()
       
        // allowed scenarios for not to destroy:
       
        // unit is ordered autorepairOn
        // unit is ordered autorepairOff while having a "smart" order (because of "smart" it will still continue repairing)
        // unit is ordered to cast a Repair Ability on the building
       
        if isWorking[this] and not Repair.IsRepairOrderOn(order) and not(Repair.IsRepairOrderOff(order) and GetUnitCurrentOrder(u) == SMART) and not (GetOrderTarget() == this.building and Repair.IsRepairAbility(order) ) then
            call .destroy()
        endif
        set u = null
        return false
    endmethod
 
Oh, it told me where it was declared already:
JASS:
struct order[LastOrder___MAX_ORDERS] <-- this line
    unit    u
    integer id
    integer typ
    boolean fin
    widget  tar
    real    x
    real    y
    static method create takes unit ordered, integer ordid, integer ordtyp, widget target, real ordx, real ordy returns order
        local order   o   = order.allocate()
        local integer i   = LastOrder___ORDERS_TO_HOLD
        local integer hid = GetHandleId(ordered)
      
        set o.u   = ordered
        set o.id  = ordid
        set o.typ = ordtyp
        set o.fin = false
        set o.tar = target
        set o.x   = ordx
        set o.y   = ordy
        //Handle stored orders in the hashtable
        loop
            //We hold up to the constant ORDERS_TO_HOLD in the table
            exitwhen i == 1
            //Moves the N-1th order to the Nth slot, etc. except storing new last order
            if HaveSavedInteger(LastOrder___ht, hid, i-1) then
                if i == LastOrder___ORDERS_TO_HOLD and HaveSavedInteger(LastOrder___ht, hid, i) then
                    //Destroy lastmost order struct
                    call order.destroy(order(LoadInteger(LastOrder___ht, hid, i)))
                endif
                //Can only do this if the N-1th order exists
                call SaveInteger(LastOrder___ht, hid, i, LoadInteger(LastOrder___ht, hid, i-1))
            endif
            set i = i - 1
        endloop
        //Store the new order to the hashtable as 1th last order
        call SaveInteger(LastOrder___ht, hid, 1, integer(o))
        return o
    endmethod
endstruct

EDIT: I think my JNGP might be having an issue. It keeps finding redeclared identifiers from local variables and Struct names. Is that a thing that happens?
 
Last edited:
Aniki said, but I tried to compare
edit1:

- Bannar's periodicly keeps tracking distance of all instances, I only do it once when it starts construction.
- This does also allow to track repairing workers, not only building ones.
- His does not have safety when the initial worker dies, and an other one continues/finishes.
- I find repair ability method a bit more clearer
I see I lack of x/y check for a bit of extra safety, but I think I was also unsure why periodic check was needed, for example.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
The distance tracking is for another corner case where multiple, let's say, acolytes attempt to build the same building on the same map coord while being next to each other all the time.
With proper ESC/ STOP/ other shenanigans I could bug the system to chose the wrong worker for given building.
Troll-Brain convinced me to choose precision (100% > 99%) above overhead, and thus I have not yet found a situation where my system would be wrong.
For library of this type and purpose, the overhead is negligible.
 
Last edited:
Dunno. This approach seems more leight-weight, and also offers other functionality with tracking all builders that are repairing/constructing a building. Bannar's calcs seem to be more safe, though I would like to test it.. because I'm a bit unsure if it's really 100% correct, or not. But let's better post maybe at @Bannar 's thread, to decide if something needs to be adapted at his, or it makes no sense for my approach etc.
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
I've already explained why safety/ accuracy is needed in my snippet's thread and shortly here. My snippet is a work not only of my own, but also @Troll-Brain's. I've expanded on TB idea and validated more cases.
None of the math included in ConstructionEvent is made by mistake or could be easily avoided. If Blizzard's new additions simplify the detection, then code could be altered appropriately. Wasn't the case in 1.29 last time I've checked.

Whatmore, the only case left uncheck by ConstructionEvent, Entangled Goldmine, is still haunted by crit-error: https://us.battle.net/forums/en/bnet/topic/20764516363. No reply there, guess bug is still present in 1.30.
Remind yourself of ItemIndexer. There is no ItemIndexer lib in approved section solely because of lack of accuracy e.g., no method for retrieving items created from dying neutral monsters.

- One who sacrifices correctness for performance deserved neither
 
Never said maths you use is made by mistake or should be replaced. Yes, I could interpret your code (not now, but when more time), but I want to understand if your method is really 100% correct, and why - and if it can provide also other functinality which BuildTracker provides. Simply you saying you use more maths for corretness is ehm... yeh.

- One who sacrifices correctness for performance deserved neither
Good to know, thanks.
 
Top