1. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  2. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  3. The Lich King demands your service! We've reached the 19th edition of the Icon Contest. Come along and make some chilling servants for the one true king.
    Dismiss Notice
  4. The 4th SFX Contest has started. Be sure to participate and have a fun factor in it.
    Dismiss Notice
  5. The poll for the 21st Terraining Contest is LIVE. Be sure to check out the entries and vote for one.
    Dismiss Notice
  6. The results are out! Check them out.
    Dismiss Notice
  7. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  8. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Techtree Contest #12 - Evolution

Discussion in 'Contest Archive' started by Spellbound, Apr 23, 2018.

Thread Status:
Not open for further replies.
  1. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,953
    Resources:
    16
    Icons:
    1
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    16
    They should be as it will be hard to test for balance otherwise.
     
  2. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,495
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    Gotcha. I will keep this in mind then.
     
  3. Abovegame

    Abovegame

    Joined:
    Jan 2, 2016
    Messages:
    464
    Resources:
    1
    Maps:
    1
    Resources:
    1
    So we should create dialog boxes or something when choosing a race?
     
  4. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,953
    Resources:
    16
    Icons:
    1
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    16
    That's one option. You can also use Custom Race System or some other method of your choice.
     
  5. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,320
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    This is quite the pain to develop, specially for this contest, but here is the system I'm developing:

    Code (vJASS):

    library CustomBuildSystem /*

        */
    requires /*
         
            Legend:
                #? -> optional resource
                #  -> required resource
             
            *||------------------------||*
            */
     DockingSystem           /*
                #? SmartTrack
                #? Table
             
                -> Spellbound
             
                link: https://www.hiveworkshop.com/threads/dockingsystem-v1-05-2.299959/#resource-78986
             
            *||------------------------||*
         
            *||------------------------||*
            */
     Table                   /*
                -> Bribe
             
                link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/
            *||------------------------||*
         
            *||------------------------||*
            */
     ListT                   /*
                # Table
                # Alloc
             
                -> Bannar
             
                link: https://www.hiveworkshop.com/threads/containers-list-t.249011/
            *||------------------------||*
     
            *||------------------------||*
            */
     Alloc                   /*
                -> Sevion
             
                link: https://hiveworkshop.com/threads/snippet-alloc.192348/
            *||------------------------||*
         
            *||------------------------||*
            */
     UnitDex                 /*
                -> TriggerHappy
             
                link: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
            *||------------------------||*
         
            *||------------------------||*
            */
     RegisterPlayerUnitEvent /*
                # RegisterNativeEvent
             
                -> Bannar
             
                link: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
            *||------------------------||*
         
            *||------------------------||*
            */
     UnitEventsEx            /*
                # UnitDex
                # RegisterPlayerUnitEvent
                #? ListT
                #? WorldBounds
             
                -> Spellbound
             
                link: https://www.hiveworkshop.com/threads/vjass-version-of-gui-unit-event.306289/#post-3270559
             
            *||------------------------||*
            */
     SimError                /*
                -> Vexorian
             
                link: http://www.wc3c.net/showthread.php?t=101260
            *||------------------------||*
         
         
            *||------------------------||*
            */
     BuilderTrace            /*
                # UnitDex
                # Table
                # OrderIndex
             
                -> MyPad
             
                link: Not yet defined...
        */

     
    native GetUnitGoldCost takes integer unitid returns integer
    native GetUnitWoodCost takes integer unitid returns integer

    globals
        private group copyGroup_tempGroup = null
    endglobals

    private function OnCopyGroup takes nothing returns nothing
        call GroupAddUnit(copyGroup_tempGroup, GetEnumUnit())
    endfunction

    private function CopyGroup takes group whichGroup, group tempGroup returns nothing
        set copyGroup_tempGroup = whichGroup

        call GroupClear(whichGroup)
        call ForGroup(tempGroup, function OnCopyGroup)
    endfunction

    static if DEBUG_MODE then

    private function printError takes string msg returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "|cffff0000Error:|r |cffffcc00" + msg + "|r")
    endfunction

    endif

    private module Initializer
        private static method onInit takes nothing returns nothing
            call thistype.init()
        endmethod  
    endmodule

    private struct BuildSystemEx extends array
        readonly static Table constructType                  = 0
        readonly static Table stationHolder                  = 0
        readonly static TableArray unitTypeLimit             = 0
     
        readonly static TableArray dataBase         = 0
     
        static method operator construct takes nothing returns integer
            return 1
        endmethod
     
        static method operator upgrade takes nothing returns integer
            return 2
        endmethod
     
        static method operator train takes nothing returns integer
            return 3
        endmethod
     
        static method getConstructType takes integer abilId returns integer
            return thistype.constructType.integer[abilId]
        endmethod
     
        static method increment takes player p, integer unitId returns nothing
            local integer pIndex = GetPlayerId(p) + 1
         
            set thistype.unitTypeLimit[pIndex].integer[unitId] = thistype.unitTypeLimit[pIndex].integer[unitId] + 1
            call SetPlayerTechMaxAllowed(p, unitId, thistype.unitTypeLimit[pIndex].integer[unitId])
        endmethod

        static method decrement takes player p, integer unitId returns nothing
            local integer pIndex = GetPlayerId(p) + 1
         
            set thistype.unitTypeLimit[pIndex].integer[unitId] = thistype.unitTypeLimit[pIndex].integer[unitId] - 1
            call SetPlayerTechMaxAllowed(p, unitId, thistype.unitTypeLimit[pIndex].integer[unitId])
        endmethod
     
        private static method init takes nothing returns nothing
            set thistype.stationHolder  = Table.create()
            set thistype.constructType  = Table.create()

            set thistype.dataBase       = TableArray[12]
            set thistype.unitTypeLimit  = TableArray[bj_MAX_PLAYER_SLOTS + 1]
        endmethod
     
        implement Initializer
    endstruct

    private struct SubBuildList extends array
        implement Alloc
     
        //  Ability is unique, so...
        private static Table abilityTable = 0
     
        readonly integer unitId
        readonly integer abilId
        readonly integer count
     
        readonly boolean consumesWorker
     
        static method getInstance takes integer abilId returns thistype
            return thistype.abilityTable.integer[abilId]
        endmethod
     
        static method create takes integer unitId, integer abilId, boolean consumesWorker, integer count returns thistype
            local thistype result = thistype.abilityTable.integer[abilId]
         
            if result == 0 then
                set result        = thistype.allocate()
             
                set result.unitId           = unitId
                set result.abilId           = abilId
                set result.count            = count
                set result.consumesWorker   =   consumesWorker
             
                set thistype.abilityTable.integer[abilId] = result
            endif
            return result
        endmethod
     
        private static method initVar takes nothing returns nothing
            set thistype.abilityTable = Table.create()
        endmethod
     
        private static method init takes nothing returns nothing
            call thistype.initVar()
        endmethod
     
        implement Initializer
    endstruct

    private struct BuildList extends array
        implement Alloc
     
        private static constant player NEUTRAL = Player(PLAYER_NEUTRAL_PASSIVE)
     
        private static Table holderTable            = 0
        private static Table subRefTable            = 0
     
        private static SubBuildList currentTemp     = 0
     
        private integer abilHolder
        private IntegerList subList
     
        static method create takes integer unitId, integer abilId, integer abilHolder, boolean consumesWorker, integer count returns thistype
            local thistype      result  = thistype.holderTable.integer[abilHolder]
            local SubBuildList  temp    = 0
         
            if result == 0 then
                set result              = thistype.allocate()
                set result.subList      = IntegerList.create()
                set result.abilHolder   = abilHolder
             
                set thistype.holderTable.integer[abilHolder] = result
            endif
         
            set temp = SubBuildList.getInstance(abilId)
            if temp == 0 then
                set temp = SubBuildList.create(unitId, abilId, consumesWorker, count)

                call result.subList.push(temp)
             
                set thistype.subRefTable.integer[temp] = result
            endif
         
            return result
        endmethod
     
        private static method onSpellCastHide takes nothing returns nothing
            local unit enum = GetEnumUnit()
         
            call SetUnitInvulnerable(enum, true)
         
            if thistype.currentTemp.consumesWorker then
                call SetUnitUseFood(enum, false)
            endif
         
            call PauseUnit(enum, true)
            call ShowUnit(enum, false)
         
            set enum = null
        endmethod
     
        private static method onSpellCastUndock takes nothing returns nothing
            call DockingSystem.undock(GetEnumUnit())
            call thistype.onSpellCastHide()
        endmethod
     
        private static method onSpellCast takes nothing returns nothing
            local unit station              = GetTriggerUnit()
            local unit plug
         
            local BuildSystem whichBuild  
         
            local SubBuildList temp         = SubBuildList.getInstance(GetSpellAbilityId())
            local thistype  handler         = thistype.subRefTable.integer[temp]
         
            local integer constructType     = BuildSystemEx.getConstructType(handler.abilHolder)
         
            local integer stationHandle     = GetHandleId(station)
            local integer stationId         = GetUnitTypeId(station)
         
            local integer plugHandle      
            local integer plugCount         = 0
         
            local IntegerListItem iter    
         
            local player stationOwner       = GetOwningPlayer(station)
         
            local trigger trig
            local trigger detector
         
            set whichBuild = BuildSystem(BuildSystemEx.stationHolder.integer[stationId])
         
            if constructType == BuildSystemEx.construct then
                set BuildSystemEx.dataBase[1].player[stationHandle] = stationOwner
                set BuildSystemEx.dataBase[2].real[stationHandle]   = GetUnitX(station)
                set BuildSystemEx.dataBase[3].real[stationHandle]   = GetUnitY(station)
                set BuildSystemEx.dataBase[4].group[stationHandle]  = CreateGroup()
             
                //  Get the number of available plugs...
                loop
                    exitwhen GetNumberOfOccupiedSockets(station) == 0 or (plugCount >= temp.count)
                 
                    set plug = GetSocketPlug(GetRandomSocketWithState(station, true))
                    call GroupAddUnit(BuildSystemEx.dataBase[4].group[stationHandle], plug)
                 
                    set plugCount = plugCount + 1
                endloop
             
                if plugCount < temp.count then
                    call SimError(stationOwner, "Construction of " + GetObjectName(temp.unitId) + " requires "/*
                                           */
    + "at least " + I2S(temp.count) + " " + GetObjectName(whichBuild.workerId))
                 
                    call DestroyGroup(BuildSystemEx.dataBase[4].group[stationHandle])
                 
                    call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[2].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[3].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[4].group.remove(stationHandle)
                 
                    set station = null
                    set plug = null
                 
                    return
                endif
             
                call ShowUnit(station, false)
                call SetUnitX(station, WorldBounds.minX)
                call SetUnitY(station, WorldBounds.minY)
                call SetUnitInvulnerable(station, true)
             
                /*
                set detector = CreateTrigger()
                call TriggerRegisterPlayerStateEvent(detector, stationOwner, PLAYER_STATE_RESOURCE_GOLD, NOT_EQUAL, GetPlayerState(stationOwner, PLAYER_STATE_RESOURCE_GOLD))
                call TriggerRegisterPlayerStateEvent(detector, stationOwner, PLAYER_STATE_RESOURCE_LUMBER, NOT_EQUAL, GetPlayerState(stationOwner, PLAYER_STATE_RESOURCE_LUMBER))
                */

             
                set plug = FirstOfGroup(BuildSystemEx.dataBase[4].group[stationHandle])
                call DockingSystem.undock(plug)
             
                call BuildSystemEx.increment(stationOwner, temp.unitId)
                if IssueBuildOrderById(plug, temp.unitId, BuildSystemEx.dataBase[2].real[stationHandle], BuildSystemEx.dataBase[3].real[stationHandle]) then              
                    set thistype.currentTemp = temp
                    call ForGroup(BuildSystemEx.dataBase[4].group[stationHandle], function thistype.onSpellCastUndock)
             
                    call ShowUnit(plug, true)
                    call PauseUnit(plug, false)
                 
                    //  Reissue the order...
                    // call IssueBuildOrderById(plug, temp.unitId, BuildSystemEx.dataBase[2].real[stationHandle], BuildSystemEx.dataBase[3].real[stationHandle])
                 
                    set plugHandle = GetHandleId(plug)
                 
                    //  Let the builder point at the station ...
                    set BuildSystemEx.dataBase[5].unit[plugHandle]      = station
                    set BuildSystemEx.dataBase[6].integer[plugHandle]   = temp
                 
                    call SetUnitX(plug, BuildSystemEx.dataBase[2].real[stationHandle])
                    call SetUnitY(plug, BuildSystemEx.dataBase[3].real[stationHandle])
                else
                    call BuildSystemEx.decrement(stationOwner, temp.unitId)
                    call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                 
                    loop
                        exitwhen plug == null
                     
                        call GroupRemoveUnit(BuildSystemEx.dataBase[4].group[stationHandle], plug)
                        set plug = FirstOfGroup(BuildSystemEx.dataBase[4].group[stationHandle])
                    endloop
                 
                    call SetUnitInvulnerable(station, false)
                    call SetUnitX(station, BuildSystemEx.dataBase[2].real[stationHandle])
                    call SetUnitY(station, BuildSystemEx.dataBase[3].real[stationHandle])
                    call ShowUnit(station, true)

                    call DestroyGroup(BuildSystemEx.dataBase[4].group[stationHandle])
                 
                    call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[2].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[3].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[4].group.remove(stationHandle)
                 
                    set station = null
                    set plug = null
                 
                    return
                endif
             
            elseif constructType == BuildSystemEx.upgrade then
                set BuildSystemEx.dataBase[7].player[stationHandle] = stationOwner
                set BuildSystemEx.dataBase[8].group[stationHandle]  = CreateGroup()
             
                //  Get the number of available plugs...
                loop
                    exitwhen GetNumberOfOccupiedSockets(station) == 0 or (plugCount >= temp.count)
                 
                    set plug = GetSocketPlug(GetRandomSocketWithState(station, true))
                    call GroupAddUnit(BuildSystemEx.dataBase[8].group[stationHandle], plug)
                 
                    set plugCount = plugCount + 1
                endloop
             
                if plugCount < temp.count then
                    call SimError(stationOwner, "Upgrading to " + GetObjectName(temp.unitId) + " requires "/*
                                           */
    + "at least " + I2S(temp.count) + " " + GetObjectName(whichBuild.workerId))
                 
                    call DestroyGroup(BuildSystemEx.dataBase[2].group[stationHandle])
                 
                    call BuildSystemEx.dataBase[7].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[8].group.remove(stationHandle)
                 
                    set station = null
                    set plug = null
                 
                    return
                endif
             
                call UnitRemoveAbility(station, handler.abilHolder)
                call UnitAddAbility(station, handler.abilHolder)
             
                call PauseUnit(station, true)
                call IssueImmediateOrderById(station, 851972)
                call PauseUnit(station, false)

                call BuildSystemEx.increment(stationOwner, temp.unitId)

                if IssueImmediateOrderById(station, temp.unitId) then
                    set thistype.currentTemp = temp
                    call ForGroup(BuildSystemEx.dataBase[8].group[stationHandle], function thistype.onSpellCastUndock)
                 
                    set BuildSystemEx.dataBase[9].integer[stationHandle] = temp
                    set BuildSystemEx.dataBase[10].integer[stationHandle] = stationId
                else
                    call BuildSystemEx.decrement(stationOwner, temp.unitId)
                 
                    loop
                        set plug = FirstOfGroup(BuildSystemEx.dataBase[8].group[stationHandle])
                        exitwhen plug == null
                                         
                        call GroupRemoveUnit(BuildSystemEx.dataBase[8].group[stationHandle], plug)
                    endloop
                 
                    call DestroyGroup(BuildSystemEx.dataBase[8].group[stationHandle])
                 
                    call BuildSystemEx.dataBase[7].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[8].group.remove(stationHandle)
                 
                    set station = null
                    set plug = null
                 
                    return
                endif
             
            elseif constructType == BuildSystemEx.train then
             
                /*
                set BuildSystemEx.dataBase[1].player[stationHandle] = stationOwner
                set BuildSystemEx.dataBase[2].group[stationHandle]  = CreateGroup()
             
                //  Get the number of available plugs...
                loop
                    exitwhen GetNumberOfOccupiedSockets(station) == 0 or (plugCount >= temp.count)
                 
                    set plug = GetSocketPlug(GetRandomSocketWithState(station, true))
                    call GroupAddUnit(BuildSystemEx.dataBase[2].group[stationHandle], plug)
                 
                    set plugCount = plugCount + 1
                endloop
             
                if plugCount < temp.count then
                    call SimError(stationOwner, "Upgrading to " + GetObjectName(temp.unitId) + " requires "/*
                                           */
    + "at least " + I2S(temp.count) + " " + GetObjectName(whichBuild.workerId))
                 
                    call DestroyGroup(BuildSystemEx.dataBase[2].group[stationHandle])
                 
                    call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[2].group.remove(stationHandle)
                 
                    set station = null
                    set plug = null
                 
                    return
                endif
             
                call UnitRemoveAbility(station, handler.abilHolder)
                call UnitAddAbility(station, handler.abilHolder)
             
                call PauseUnit(station, true)
                call IssueImmediateOrderById(station, 851972)
                call PauseUnit(station, false)

                call BuildSystemEx.increment(stationOwner, temp.unitId)

                if IssueImmediateOrderById(station, temp.unitId) then
                    set thistype.currentTemp = temp
                    call ForGroup(BuildSystemEx.dataBase[2].group[stationHandle], function thistype.onSpellCastUndock)
                 
                    set BuildSystemEx.dataBase[3].integer[stationHandle] = temp
                    set BuildSystemEx.dataBase[4].integer[stationHandle] = stationId
                else
                    call BuildSystemEx.decrement(stationOwner, temp.unitId)
                 
                    loop
                        set plug = FirstOfGroup(BuildSystemEx.dataBase[2].group[stationHandle])
                        exitwhen plug == null
                                         
                        call GroupRemoveUnit(BuildSystemEx.dataBase[2].group[stationHandle], plug)
                    endloop
                 
                    call DestroyGroup(BuildSystemEx.dataBase[2].group[stationHandle])
                 
                    call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[2].group.remove(stationHandle)
                 
                    set station = null
                    set plug = null
                 
                    return
                endif
                */
             
            endif
         
            set station = null
        endmethod
     
        private static method initVar takes nothing returns nothing
            set thistype.holderTable = Table.create()
            set thistype.subRefTable = Table.create()
        endmethod
     
        private static method initListener takes nothing returns nothing
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onSpellCast)
        endmethod
     
        private static method init takes nothing returns nothing
            call thistype.initVar()
            call thistype.initListener()
        endmethod
     
        implement Initializer
    endstruct

    struct BuildSystem extends array
        implement Alloc
     
        private static constant integer DEFAULT_WORKER_COUNT    = 4
     
        private static constant real    DEFAULT_WORKER_DIST     = 45.
        private static constant real    DEFAULT_WORKER_HEIGHT   = 30.
        private static constant real    DEFAULT_WORKER_RAD      = 0.
        private static constant real    DEFAULT_DETECT_DIST     = 250.
     
        private static constant boolean DEFAULT_CONSUMES_WORKER = true
     
        //  These store the unit types which have been passed into the BuildSystem
        private static Table unitTypeList       = 0
        //  This stores the owner of the unit whose unit type has been registered
        private static Table unitOwner          = 0
        //  This stores the flag of the unit whether it triggered a unit death
        //  event or not
        private static Table unitDeathFlag      = 0
        //  This stores the flag for all stations...
        private static Table stationFlag        = 0
     
        //  Main members. Can be read, but not written.
        readonly integer workerId
        readonly integer buildingId
     
        private trigger onPredock
        private trigger onDock
        private trigger onUndock
     
        private IntegerList constructList
        private IntegerList upgradeList
        private IntegerList trainList
     
        integer workerCount
     
        real    workerHeight
        real    workerDist
        real    workerStartRad
        real    workerDetectDist
     
        boolean workerConsumed
     
        static method create takes integer workerId, integer buildingId returns thistype
            local thistype result = BuildSystemEx.stationHolder.integer[buildingId]
         
            if result == 0 then
                set result                  = thistype.allocate()
             
                set result.workerId         = workerId
                set result.buildingId       = buildingId
             
                set result.constructList    = IntegerList.create()
                set result.upgradeList      = IntegerList.create()
                set result.trainList        = IntegerList.create()
             
                set result.workerCount      = DEFAULT_WORKER_COUNT
                set result.workerHeight     = DEFAULT_WORKER_HEIGHT
                set result.workerDist       = DEFAULT_WORKER_DIST
                set result.workerStartRad   = DEFAULT_WORKER_RAD
                set result.workerDetectDist = DEFAULT_DETECT_DIST
                set result.workerConsumed   = DEFAULT_CONSUMES_WORKER
             
                set result.onPredock        = CreateTrigger()
                set result.onDock           = CreateTrigger()
                set result.onUndock         = CreateTrigger()
             
                set BuildSystemEx.stationHolder.integer[buildingId] = result
            endif
         
            return result
        endmethod
     
        private static method precheckAbil takes integer abilHolder returns boolean
            return BuildSystemEx.constructType.integer.has(abilHolder)
        endmethod
     
        private static method addUnitType takes integer unitId returns nothing
            if unitId == 0 then
                debug call printError("thistype.addUnitType: Invalid unit type!")
                return
            endif
         
            if not thistype.unitTypeList.integer.has(unitId) then
                set thistype.unitTypeList.integer[unitId] = IntegerList(thistype.unitTypeList.integer[0]).push(unitId).last
            endif
        endmethod
     
        private static method isUnitPhysical takes unit u returns boolean
            return (not IsUnitType(u, UNIT_TYPE_SUMMONED)) and (not IsUnitIllusion(u))
        endmethod
     
        method addConstructAbil takes integer abilHolder returns nothing
            if thistype.precheckAbil(abilHolder) then
                debug call printError("thistype.addConstructAbil: Ability type was already defined.")
                return
            endif
         
            call this.constructList.push(abilHolder)
            set BuildSystemEx.constructType.integer[abilHolder] = BuildSystemEx.construct
        endmethod

        method addUpgradeAbil takes integer abilHolder returns nothing
            if thistype.precheckAbil(abilHolder) then
                debug call printError("thistype.addUpgradeAbil: Ability type was already defined.")
                return
            endif
         
            call this.upgradeList.push(abilHolder)
            set BuildSystemEx.constructType.integer[abilHolder] = BuildSystemEx.upgrade
        endmethod
     
        method addTrainAbil takes integer abilHolder returns nothing
            if thistype.precheckAbil(abilHolder) then
                debug call printError("thistype.addTrainAbil: Ability type was already defined.")
                return
            endif
         
            call this.trainList.push(abilHolder)
            set BuildSystemEx.constructType.integer[abilHolder] = BuildSystemEx.train
        endmethod
     
        method addConstructUnit takes integer unitId, integer abilId, integer abilHolder, integer count returns nothing
            if BuildSystemEx.constructType.integer[abilHolder] != BuildSystemEx.construct then
                debug call printError("thistype.addConstructUnit: Ability id is not of type construct.")
                return
            endif
            call BuildList.create(unitId, abilId, abilHolder, this.workerConsumed, count)
            call thistype.addUnitType(unitId)
        endmethod

        method addUpgradeUnit takes integer unitId, integer abilId, integer abilHolder, integer count returns nothing
            if BuildSystemEx.constructType.integer[abilHolder] != BuildSystemEx.upgrade then
                debug call printError("thistype.addUpgradeUnit: Ability id is not of type upgrade.")
                return
            endif
            call BuildList.create(unitId, abilId, abilHolder, this.workerConsumed, count)
            call thistype.addUnitType(unitId)
        endmethod
     
        method addTrainUnit takes integer unitId, integer abilId, integer abilHolder, integer count returns nothing
            if BuildSystemEx.constructType.integer[abilHolder] != BuildSystemEx.train then
                debug call printError("thistype.addTrainUnit: Ability id is not of type train.")
                return
            endif
            call BuildList.create(unitId, abilId, abilHolder, this.workerConsumed, count)
            call thistype.addUnitType(unitId)
        endmethod
     
        method setOnDockEvent takes integer dockEvent, code whichHandler returns nothing
            if dockEvent == EVENT_ON_PRE_DOCK then
                call DestroyTrigger(this.onPredock)
             
                set this.onPredock = CreateTrigger()
                call TriggerAddCondition(this.onPredock, Condition(whichHandler))
             
            elseif dockEvent == EVENT_ON_DOCK then
                call DestroyTrigger(this.onDock)
             
                set this.onDock = CreateTrigger()
                call TriggerAddCondition(this.onDock, Condition(whichHandler))
             
            elseif dockEvent == EVENT_ON_UNDOCK then
                call DestroyTrigger(this.onUndock)
             
                set this.onUndock = CreateTrigger()
                call TriggerAddCondition(this.onUndock, Condition(whichHandler))          
            debug else
                debug call printError("thistype.setOnDockEvent: Unrecognized dock event!")
            endif
        endmethod
     
        private method initStation takes unit station returns nothing
            local integer i             = 0
            local integer stationHandle = GetHandleId(station)
         
            local real pSlice           = 2*bj_PI/(this.workerCount)
            local real stationX         = GetUnitX(station)
            local real stationY         = GetUnitY(station)
         
            if not thistype.stationFlag.boolean.has(stationHandle) then
                set thistype.stationFlag.boolean[stationHandle] = false
             
                call DockingSystem.createStation(station, this.workerDetectDist)
                loop
                    exitwhen i >= this.workerCount
                 
                    call DockingSystem.addSocket(station, /*
                                                */
    stationX + this.workerDist*Cos(I2R(i)*pSlice + this.workerStartRad),/*
                                                */
    stationY + this.workerDist*Sin(I2R(i)*pSlice + this.workerStartRad),/*
                                                */
    this.workerHeight, (I2R(i)*pSlice + this.workerStartRad + (bj_PI/2))/*
                                                */
    * bj_RADTODEG)
                    set i = i + 1
                endloop
            endif
        endmethod
     
        private method removeStation takes unit station returns nothing
            local integer stationHandle = GetHandleId(station)
         
            if thistype.stationFlag.boolean.has(stationHandle) then
                call thistype.stationFlag.boolean.remove(stationHandle)
             
                call DockingSystem.terminateStation(station)
            endif
        endmethod
     
        /*
             -----------------------------------
            |   Handler methods                 |
             -----------------------------------
        */

     
        private static method onConstructStart takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit builder              = GetBuildingUnit()
            local unit station
         
            local player buildingOwner      = GetOwningPlayer(building)
         
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
            local integer builderHandle     = GetHandleId(builder)
         
            local SubBuildList  buildList   = BuildSystemEx.dataBase[6].integer[buildingId]
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
         
            //  If building is a station...
            if temp != 0 then
                call temp.removeStation(building)
            endif
         
            if BuildSystemEx.dataBase[5].unit.has(builderHandle) then
                //  Move data reference to the building currently being built...
                set station = BuildSystemEx.dataBase[5].unit[builderHandle]
                set BuildSystemEx.dataBase[5].unit[buildingHandle]      = station
                set BuildSystemEx.dataBase[6].integer[buildingHandle]   = BuildSystemEx.dataBase[6].integer[builderHandle]
             
                call BuildSystemEx.dataBase[5].unit.remove(builderHandle)
                call BuildSystemEx.dataBase[6].integer.remove(builderHandle)
             
                //  Correct the incremental value...
                call BuildSystemEx.decrement(buildingOwner, buildingId)
             
                if buildList.consumesWorker then
                    call UnitAddAbility(builder, 'Aloc')
                endif
            endif
         
            set builder  = null
            set building = null
        endmethod
     
        private static method onConstructCancel takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit builder              = GetBuildingUnit()
            local unit station
            local unit plug
         
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
         
            local integer builderHandle     = GetHandleId(builder)
         
            local integer stationHandle
         
            local real    stationX
            local real    stationY
         
            local player  stationOwner
            local group   stationGrp
         
            local trigger trig
         
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
            local SubBuildList  buildList   = 0
         
            //  Triggered by removal
            if GetTriggerEventId() == null then
                set building = GetIndexedUnit()
                set builder  = GetBuilder(building)
             
                set buildingId      = GetUnitTypeId(building)
                set buildingHandle  = GetHandleId(building)
             
                set builderHandle   = GetHandleId(builder)
            endif
         
            if BuildSystemEx.dataBase[5].unit.has(buildingHandle) then
                set station         = BuildSystemEx.dataBase[5].unit[buildingHandle]
             
                set stationHandle   = GetHandleId(station)
             
                set stationOwner    = BuildSystemEx.dataBase[1].player[stationHandle]
                set stationX        = BuildSystemEx.dataBase[2].real[stationHandle]
                set stationY        = BuildSystemEx.dataBase[3].real[stationHandle]
                set stationGrp      = BuildSystemEx.dataBase[4].group[stationHandle]
             
                set buildList       = BuildSystemEx.dataBase[6].integer[buildingHandle]
             
                call BuildSystemEx.dataBase[5].unit.remove(buildingHandle)
                call BuildSystemEx.dataBase[6].integer.remove(buildingHandle)
             
                if buildList.consumesWorker then
                    call UnitRemoveAbility(builder, 'Aloc')
                endif
             
                call SetUnitInvulnerable(station, false)
                call SetUnitX(station, stationX)
                call SetUnitY(station, stationY)
                call ShowUnit(station, true)
             
                set plug = FirstOfGroup(stationGrp)
                loop
                    exitwhen plug == null
                 
                    call ShowUnit(plug, true)
                    call SetUnitInvulnerable(plug, false)
                    call SetUnitPosition(plug, stationX, stationY)
                    call PauseUnit(plug, false)
                 
                    if GetLocalPlayer() == stationOwner then
                        call SelectUnit(plug, true)
                    endif
                 
                    if buildList.consumesWorker then
                        call SetUnitUseFood(plug, true)
                    endif
                 
                    call GroupRemoveUnit(stationGrp, plug)
                    set plug = FirstOfGroup(stationGrp)
                endloop
             
                call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                call BuildSystemEx.dataBase[2].real.remove(stationHandle)
                call BuildSystemEx.dataBase[3].real.remove(stationHandle)
                call BuildSystemEx.dataBase[4].group.remove(stationHandle)
             
                call DestroyGroup(stationGrp)
             
                set station    = null
                set stationGrp = null
            endif
         
            set builder  = null
            set building = null
        endmethod
     
        private static method onConstructFinish takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit builder              = GetBuildingUnit()
            local unit station              = null
            local unit plug
         
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
         
            local integer builderHandle     = GetHandleId(builder)
         
            local integer stationHandle
         
            local real    stationX
            local real    stationY
         
            local player  stationOwner
            local group   stationTempGrp    = null
            local group   stationGrp
                 
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
         
            local SubBuildList buildList    = 0
         
            if BuildSystemEx.dataBase[5].unit.has(buildingHandle) then
                set station         = BuildSystemEx.dataBase[5].unit[buildingHandle]
             
                set stationHandle   = GetHandleId(station)
             
                set stationOwner    = BuildSystemEx.dataBase[1].player[stationHandle]
                set stationX        = BuildSystemEx.dataBase[2].real[stationHandle]
                set stationY        = BuildSystemEx.dataBase[3].real[stationHandle]
                set stationGrp      = BuildSystemEx.dataBase[4].group[stationHandle]
             
                set buildList       = BuildSystemEx.dataBase[6].integer[stationHandle]
             
                set stationTempGrp  = CreateGroup()
                call CopyGroup(stationTempGrp, stationGrp)
            endif
         
            call thistype.onConstructCancel()
         
            if station != null then
                set plug = FirstOfGroup(stationTempGrp)
                loop
                    exitwhen plug == null
                 
                    call GroupRemoveUnit(stationTempGrp, plug)
                 
                    if buildList.consumesWorker then
                        call RemoveUnit(plug)
                    endif
                 
                    set plug = FirstOfGroup(stationTempGrp)
                endloop
             
                call RemoveUnit(station)
            endif
         
            if stationTempGrp != null then
                call DestroyGroup(stationTempGrp)
            endif
         
            if temp != 0 then
                call temp.initStation(building)
            endif
         
            set stationTempGrp  = null
            set station         = null
            set stationGrp      = null
         
            set builder         = null
            set building        = null
        endmethod
     
        /*
              ------------------
             |                  |
             |  Upgrades        |
             |                  |
              ------------------
        */

        private static method onUpgradeStart takes nothing returns nothing
            local unit building             = GetTriggerUnit()
         
            local player buildingOwner      = GetOwningPlayer(building)
         
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
         
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
             
            if temp != 0 then
                call DockingSystem.undockAll(building)
            endif
         
            if BuildSystemEx.dataBase[9].integer.has(buildingHandle) then
                //  Raise a flag telling the system that the unit is upgrading.
                set BuildSystemEx.dataBase[11].boolean[buildingHandle] = true
            endif
         
            set building = null
        endmethod
     
        private static method onUpgradeCancel takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit plug
         
            local real buildingX            = GetUnitX(building)
            local real buildingY            = GetUnitY(building)
         
            local group plugGroup
         
            local player buildingOwner      = GetOwningPlayer(building)
         
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
            local integer upgradeId        
         
            local SubBuildList buildList  
         
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
         
            if BuildSystemEx.dataBase[9].integer.has(buildingHandle) then
                set plugGroup   = BuildSystemEx.dataBase[8].group[buildingHandle]
                set buildList   = SubBuildList(BuildSystemEx.dataBase[9].integer[buildingHandle])
                set upgradeId   = buildList.unitId
             
                call BuildSystemEx.decrement(buildingOwner, upgradeId)
             
                //  Show the units...
                set plug = FirstOfGroup(plugGroup)
                loop
                    exitwhen plug == null
                 
                    call ShowUnit(plug, true)
                    call SetUnitInvulnerable(plug, false)
                    call SetUnitPosition(plug, buildingX, buildingY)
                    call PauseUnit(plug, false)
                 
                    if buildList.consumesWorker then
                        call SetUnitUseFood(plug, true)
                    endif
                 
                    call GroupRemoveUnit(plugGroup, plug)
                    set plug = FirstOfGroup(plugGroup)
                endloop
             
                call DestroyGroup(plugGroup)
             
                set plugGroup = null
             
                call BuildSystemEx.dataBase[7].player.remove(buildingHandle)
                call BuildSystemEx.dataBase[8].group.remove(buildingHandle)
                call BuildSystemEx.dataBase[9].integer.remove(buildingHandle)
                call BuildSystemEx.dataBase[10].integer.remove(buildingHandle)
                call BuildSystemEx.dataBase[11].boolean.remove(buildingHandle)
            endif
         
            set building = null
        endmethod
     
        private static method onUpgradeFinish takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit plug
         
            local real buildingX            = GetUnitX(building)
            local real buildingY            = GetUnitY(building)
         
            local group plugGroup
            local group plugGroupCopy       = null
         
            local player buildingOwner      = GetOwningPlayer(building)
         
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
            local integer upgradeId        
            local integer lastBuildingId  
         
            local SubBuildList buildList  
         
            local thistype temp            
         
            if BuildSystemEx.dataBase[3].integer.has(buildingHandle) then
                set plugGroup       = BuildSystemEx.dataBase[2].group[buildingHandle]
                set buildList       = SubBuildList(BuildSystemEx.dataBase[3].integer[buildingHandle])
                set upgradeId       = buildList.unitId
                set lastBuildingId  = BuildSystemEx.dataBase[4].integer[buildingHandle]
             
                set temp = BuildSystemEx.stationHolder.integer[lastBuildingId]
             
                set plugGroupCopy   = CreateGroup()
                call CopyGroup(plugGroupCopy, plugGroup)
             
                if temp != 0 then
                    call temp.removeStation(building)
                 
                    //  Outsource it to transformation.
                    /*
                    if thistype.unitTypeList.has(lastBuildingId) then
                        call BuildSystemEx.decrement(buildingOwner, lastBuildingId)
                    endif
                    */

                endif
             
                //  Counter the effects of decrement when calling onUpgradeCancel...
                call BuildSystemEx.increment(buildingOwner, buildingId)
            endif
         
            call thistype.onUpgradeCancel()
         
            set plug = FirstOfGroup(plugGroupCopy)
            loop
                exitwhen plug == null
             
                call GroupRemoveUnit(plugGroupCopy, plug)
             
                if buildList.consumesWorker then
                    call RemoveUnit(plug)
                endif
             
                set plug = FirstOfGroup(plugGroupCopy)
            endloop
         
            if plugGroupCopy != null then
                call DestroyGroup(plugGroupCopy)
            endif
         
            set plugGroupCopy = null
            set building = null
        endmethod
     
        private static method onUnitEnter takes nothing returns nothing
            local unit u             = GetIndexedUnit()
            local integer unitId     = GetUnitTypeId(u)
            local integer uHandle    = GetHandleId(u)
            local player uOwner      = GetOwningPlayer(u)
         
            local thistype inst      = BuildSystemEx.stationHolder.integer[unitId]
         
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
         
            //  Unit is a station.
            if inst != 0 then
                call inst.initStation(u)
            endif
         
            if thistype.unitTypeList.integer.has(unitId) then
                call BuildSystemEx.increment(uOwner, unitId)
                 
                set thistype.unitOwner.player[uHandle]      = uOwner
                set thistype.unitDeathFlag.boolean[uHandle] = false
            endif
         
            set u = null
        endmethod
     
        private static method onUnitExit takes nothing returns nothing
            local unit u            = GetIndexedUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
         
            local thistype inst     = BuildSystemEx.stationHolder.integer[unitId]
         
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
         
            if inst != 0 then
                call inst.removeStation(u)
            endif

            //  Unit must have undergone construction when it was removed...
            if BuildSystemEx.dataBase[5].unit.has(uHandle) then
                debug call BJDebugMsg("Unit was undergoing construction!")
                call thistype.onConstructCancel()
            endif
         
            if thistype.unitTypeList.integer.has(unitId) then
                if not thistype.unitDeathFlag.boolean[uHandle] then
                    call BuildSystemEx.decrement(uOwner, unitId)
                endif
             
                call thistype.unitOwner.player.remove(uHandle)
                call thistype.unitDeathFlag.boolean.remove(uHandle)
            endif
         
            set u = null
        endmethod
     
        private static method onUnitDeath takes nothing returns nothing
            local unit u            = GetTriggerUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
               
            local thistype inst      = BuildSystemEx.stationHolder.integer[unitId]

            if GetTriggerEventId() == null then
                //  Called on reincarnation
                set u = GetEventUnit()
            endif
         
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
         
            if inst != 0 then
                if GetNumberOfOccupiedSockets(u) != 0 then
                    call DockingSystem.undockAll(u)
                endif
            endif
         
            //  Unit must have undergone construction when it was destroyed...
            if BuildSystemEx.dataBase[5].unit.has(uHandle) then
                call thistype.onConstructCancel()
            endif
         
            if thistype.unitTypeList.integer.has(unitId) then
                if not thistype.unitDeathFlag.boolean[uHandle] then
                    call BuildSystemEx.decrement(uOwner, unitId)
                 
                    set thistype.unitDeathFlag.boolean[uHandle] = true              
                endif          
            endif
         
            set u = null
        endmethod
     
        private static method onUnitChangeOwner takes nothing returns nothing
            local unit u            = GetTriggerUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
         
            local thistype inst     = BuildSystemEx.stationHolder.integer[unitId]
         
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
         
            if inst != 0 then
                call DockingSystem.undockAll(u)
            endif
         
            if thistype.unitTypeList.integer.has(unitId) then
                call BuildSystemEx.decrement(thistype.unitOwner.player[uHandle], unitId)
                 
                set thistype.unitOwner.player[uHandle] = uOwner
             
                call BuildSystemEx.increment(thistype.unitOwner.player[uHandle], unitId)
            endif
         
            set u = null
        endmethod
     
        private static method onUnitRevive takes nothing returns nothing
            local unit u            = GetEventUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
         
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
         
            if thistype.unitTypeList.integer.has(unitId) then
                if thistype.unitDeathFlag.boolean[uHandle] then
                    call BuildSystemEx.increment(uOwner, unitId)
                 
                    set thistype.unitDeathFlag.boolean[uHandle] = false
                endif
            endif
         
            set u = null
        endmethod
     
        private static method onUnitTransform takes nothing returns nothing
            local unit u                = GetEventUnit()
         
            local integer prevUnitId    = GetEventTransformType()
            local integer unitId        = GetUnitTypeId(u)
            local integer uHandle       = GetHandleId(u)
            local player uOwner         = GetOwningPlayer(u)
         
            local thistype inst         = BuildSystemEx.stationHolder.integer[prevUnitId]
         
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
         
            if inst != 0 then
                call inst.removeStation(u)
            endif
         
            if thistype.unitTypeList.integer.has(GetEventTransformType()) then
                call BuildSystemEx.decrement(uOwner, GetEventTransformType())
            endif
             
            if thistype.unitTypeList.integer.has(unitId) then
                call BuildSystemEx.increment(uOwner, unitId)
            endif
         
            set u = null
        endmethod
     
        private static method onPreDockHandler takes nothing returns nothing
            local unit station          = GetStation()
            local unit plug             = GetPlug()
         
            local integer stationId     = GetUnitTypeId(station)
            local integer stationHandle = GetHandleId(station)
            local integer plugId        = GetUnitTypeId(plug)
            local integer plugHandle    = GetHandleId(plug)
         
            local player plugOwner      = GetOwningPlayer(plug)
         
            local thistype temp         = BuildSystemEx.stationHolder.integer[stationId]
         
            //  Nothing to do here...
            if (temp == 0) then
                // Don't interfere with other stations...
                set plug    = null
                set station = null
             
                return
            endif
         
            if (temp.workerId != plugId) then
                call InterruptDocking()
             
                set plug    = null
                set station = null
             
                return
            endif
                 
            if (GetOwningPlayer(plug) != GetOwningPlayer(station)) then
                call InterruptDocking()
                call SimError(plugOwner, "Cannot dock into a station owned by another player!")
             
                set plug    = null
                set station = null
            endif
         
            //  Unit is being upgraded. Don't allow docking!
            if BuildSystemEx.dataBase[7].boolean.has(stationHandle) then
                call InterruptDocking()
                call SimError(plugOwner, "Upgrade is in progress... you cannot dock at this moment.")
             
                set plug    = null
                set station = null
             
                return
            endif

            call ConditionalTriggerExecute(temp.onPredock)
         
            set plug    = null
            set station = null
        endmethod
     
        private static method onDockHandler takes nothing returns nothing
            local thistype temp = BuildSystemEx.stationHolder.integer[GetUnitTypeId(GetStation())]
         
            if temp != 0 then
                call ConditionalTriggerExecute(temp.onDock)
            endif
        endmethod
     
        private static method onUndockHandler takes nothing returns nothing
            local thistype temp = BuildSystemEx.stationHolder.integer[GetUnitTypeId(GetStation())]
         
            if temp != 0 then
                call ConditionalTriggerExecute(temp.onUndock)
            endif
        endmethod
     
        private static method initVar takes nothing returns nothing
            set thistype.unitTypeList   = Table.create()
            set thistype.unitOwner      = Table.create()
            set thistype.unitDeathFlag  = Table.create()
            set thistype.stationFlag    = Table.create()
         
            set thistype.unitTypeList.integer[0] = IntegerList.create()
        endmethod
     
        private static method initListener takes nothing returns nothing
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_START, function thistype.onConstructStart)
            //call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, function thistype.onConstructCancel)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.onConstructFinish)
         
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_START, function thistype.onUpgradeStart)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_CANCEL, function thistype.onUpgradeCancel)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_FINISH, function thistype.onUpgradeFinish)
         
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onUnitDeath)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CHANGE_OWNER, function thistype.onUnitChangeOwner)
         
            call RegisterUnitIndexEvent(Condition(function thistype.onUnitEnter), EVENT_UNIT_INDEX)
            call RegisterUnitIndexEvent(Condition(function thistype.onUnitExit), EVENT_UNIT_DEINDEX)
         
            call RegisterNativeEvent(EVENT_ON_REINCARNATION_START, function thistype.onUnitDeath)
         
            call RegisterNativeEvent(EVENT_ON_RESURRECTION, function thistype.onUnitRevive)
            call RegisterNativeEvent(EVENT_ON_REINCARNATION_FINISH, function thistype.onUnitRevive)
            call RegisterNativeEvent(EVENT_ON_TRANSFORM, function thistype.onUnitTransform)
         
            call RegisterNativeEvent(EVENT_ON_PRE_DOCK, function thistype.onPreDockHandler)
            call RegisterNativeEvent(EVENT_ON_DOCK, function thistype.onDockHandler)
            call RegisterNativeEvent(EVENT_ON_UNDOCK, function thistype.onUndockHandler)      
        endmethod
     
        private static method init takes nothing returns nothing
            call thistype.initVar()
            call thistype.initListener()
        endmethod
     
        static method onGameStart takes nothing returns nothing
            local integer i = 1
            local player  p = Player(i - 1)
            local IntegerListItem firstIter = IntegerList(thistype.unitTypeList.integer[0]).first
            local IntegerListItem iter      = firstIter
         
            loop
                loop
                    exitwhen iter == 0
                 
                    set BuildSystemEx.unitTypeLimit[i].integer[iter.data] = 0
                    call SetPlayerTechMaxAllowed(p, iter.data, BuildSystemEx.unitTypeLimit[i].integer[iter.data])
                 
                    set iter = iter.next
                endloop
                set iter = firstIter
             
                exitwhen i >= bj_MAX_PLAYER_SLOTS
             
                set p = Player(i)
                set i = i + 1
            endloop
        endmethod
     
        implement Initializer
    endstruct
     
    //  This function is required. Don't edit this. (functionally private)
    function InitTrig_CustomBuildSystem takes nothing returns nothing
        call ForForce(bj_FORCE_PLAYER[0], function BuildSystem.onGameStart)
    endfunction

    endlibrary
     



    As it is right now, there are a lot of bugs to iron out. I still have to cover another case before going all out with imports and spells.
     
    Last edited: Jun 22, 2018
  6. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    1,953
    Resources:
    16
    Icons:
    1
    Skins:
    5
    Spells:
    9
    JASS:
    1
    Resources:
    16
    Oh hey, UnitEventEx! xD

    I wasn't able to read through it all but, man, I'm very curious to see what you'll make for this contest.
     
  7. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,320
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    Oops, didn't realize I already had a second WIP.

    Now, here are some more WIP's (of course, all models found therein are not set in stone)

    WIP 3

    wip_2-1.png

    Units before creation of another unit...

    wip_2-2.png

    Units after creation of unit...
     
  8. xYours Trulyx

    xYours Trulyx

    Joined:
    Jan 9, 2016
    Messages:
    1,004
    Resources:
    0
    Resources:
    0
    not enuf memes

    sprite2.png
    2cp130.jpg


    Anyway, I've discovered yet ANOTHER bug with my race. Had something to do with the old Seatapper's regeneration ability. With the ability removed and its triggers do not, the triggers does not have a valid condition, thus it runs on every spell cast that happens in the game, making mages have more regeneration than it should. I fixed it and uploaded the new map.
     
  9. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,320
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    Quite the jolly banter, and I find the meme to be filled with the finest humors. :)

    If I post any more WIPS, I will stamp them here:

    WIP


    Libs

    CustomBuildSystem (Not much on how to use, so it's described here)
    Code (vJASS):

    library CustomBuildSystem /*

        */
    requires /*
           
            Legend:
                #? -> optional resource
                #  -> required resource
               
            *||------------------------||*
            */
     DockingSystem           /*
                #? SmartTrack
                #? Table
               
                -> Spellbound
               
                link: https://www.hiveworkshop.com/threads/dockingsystem-v1-05-2.299959/#resource-78986
               
            *||------------------------||*
           
            *||------------------------||*
            */
     Table                   /*
                -> Bribe
               
                link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/
            *||------------------------||*
           
            *||------------------------||*
            */
     ListT                   /*
                # Table
                # Alloc
               
                -> Bannar
               
                link: https://www.hiveworkshop.com/threads/containers-list-t.249011/
            *||------------------------||*
       
            *||------------------------||*
            */
     Alloc                   /*
                -> Sevion
               
                link: https://hiveworkshop.com/threads/snippet-alloc.192348/
            *||------------------------||*
           
            *||------------------------||*
            */
     UnitDex                 /*
                -> TriggerHappy
               
                link: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
            *||------------------------||*
           
            *||------------------------||*
            */
     RegisterPlayerUnitEvent /*
                # RegisterNativeEvent
               
                -> Bannar
               
                link: https://www.hiveworkshop.com/threads/snippet-registerevent-pack.250266/
            *||------------------------||*
           
            *||------------------------||*
            */
     UnitEventsEx            /*
                # UnitDex
                # RegisterPlayerUnitEvent
                #? ListT
                #? WorldBounds
               
                -> Spellbound
               
                link: https://www.hiveworkshop.com/threads/vjass-version-of-gui-unit-event.306289/#post-3270559
               
            *||------------------------||*
            */
     SimError                /*
                -> Vexorian
               
                link: http://www.wc3c.net/showthread.php?t=101260
            *||------------------------||*
           
           
            *||------------------------||*
            */
     BuilderTrace            /*
                # UnitDex
                # Table
                # OrderIndex
               
                -> MyPad
               
                link: Not yet defined...
        */

       
    native GetUnitGoldCost takes integer unitid returns integer
    native GetUnitWoodCost takes integer unitid returns integer

    //! runtextmacro DEFINE_LIST("", "PlayerList", "player")
    //! runtextmacro DEFINE_LIST("", "UnitGroupList", "group")

    globals
        private group copyGroup_tempGroup = null
    endglobals

    private function OnCopyGroup takes nothing returns nothing
        call GroupAddUnit(copyGroup_tempGroup, GetEnumUnit())
    endfunction

    private function CopyGroup takes group whichGroup, group tempGroup returns nothing
        set copyGroup_tempGroup = whichGroup

        call GroupClear(whichGroup)
        call ForGroup(tempGroup, function OnCopyGroup)
    endfunction

    static if DEBUG_MODE then

    private function printError takes string msg returns nothing
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "|cffff0000Error:|r |cffffcc00" + msg + "|r")
    endfunction

    endif

    private module Initializer
        private static method onInit takes nothing returns nothing
            call thistype.init()
        endmethod  
    endmodule

    private struct BuildSystemEx extends array
        readonly static Table constructType                  = 0
        readonly static Table stationHolder                  = 0
        readonly static TableArray unitTypeLimit             = 0
       
        readonly static TableArray dataBase         = 0
       
        static method operator construct takes nothing returns integer
            return 1
        endmethod
       
        static method operator upgrade takes nothing returns integer
            return 2
        endmethod
       
        static method operator train takes nothing returns integer
            return 3
        endmethod
       
        static method getConstructType takes integer abilId returns integer
            return thistype.constructType.integer[abilId]
        endmethod
       
        static method increment takes player p, integer unitId returns nothing
            local integer pIndex = GetPlayerId(p) + 1
           
            set thistype.unitTypeLimit[pIndex].integer[unitId] = thistype.unitTypeLimit[pIndex].integer[unitId] + 1
            call SetPlayerTechMaxAllowed(p, unitId, thistype.unitTypeLimit[pIndex].integer[unitId])
        endmethod

        static method decrement takes player p, integer unitId returns nothing
            local integer pIndex = GetPlayerId(p) + 1
           
            set thistype.unitTypeLimit[pIndex].integer[unitId] = thistype.unitTypeLimit[pIndex].integer[unitId] - 1
            call SetPlayerTechMaxAllowed(p, unitId, thistype.unitTypeLimit[pIndex].integer[unitId])
        endmethod
       
        private static method init takes nothing returns nothing
            set thistype.stationHolder  = Table.create()
            set thistype.constructType  = Table.create()

            set thistype.dataBase       = TableArray[15]
            set thistype.unitTypeLimit  = TableArray[bj_MAX_PLAYER_SLOTS + 1]
        endmethod
       
        implement Initializer
    endstruct

    private struct SubBuildList extends array
        implement Alloc
       
        //  Ability is unique, so...
        private static Table abilityTable = 0
       
        readonly integer unitId
        readonly integer abilId
        readonly integer count
       
        readonly boolean consumesWorker
       
        static method getInstance takes integer abilId returns thistype
            return thistype.abilityTable.integer[abilId]
        endmethod
       
        static method create takes integer unitId, integer abilId, boolean consumesWorker, integer count returns thistype
            local thistype result = thistype.abilityTable.integer[abilId]
           
            if result == 0 then
                set result        = thistype.allocate()
               
                set result.unitId           = unitId
                set result.abilId           = abilId
                set result.count            = count
                set result.consumesWorker   =   consumesWorker
               
                set thistype.abilityTable.integer[abilId] = result
            endif
            return result
        endmethod
       
        private static method initVar takes nothing returns nothing
            set thistype.abilityTable = Table.create()
        endmethod
       
        private static method init takes nothing returns nothing
            call thistype.initVar()
        endmethod
       
        implement Initializer
    endstruct

    private struct BuildList extends array
        implement Alloc
       
        private static constant player NEUTRAL = Player(PLAYER_NEUTRAL_PASSIVE)
       
        private static Table holderTable            = 0
        private static Table subRefTable            = 0
       
        private static SubBuildList currentTemp     = 0
       
        private integer abilHolder
        private IntegerList subList
       
        static method create takes integer unitId, integer abilId, integer abilHolder, boolean consumesWorker, integer count returns thistype
            local thistype      result  = thistype.holderTable.integer[abilHolder]
            local SubBuildList  temp    = 0
           
            if result == 0 then
                set result              = thistype.allocate()
                set result.subList      = IntegerList.create()
                set result.abilHolder   = abilHolder
               
                set thistype.holderTable.integer[abilHolder] = result
            endif
           
            set temp = SubBuildList.getInstance(abilId)
            if temp == 0 then
                set temp = SubBuildList.create(unitId, abilId, consumesWorker, count)

                call result.subList.push(temp)
               
                set thistype.subRefTable.integer[temp] = result
            endif
           
            return result
        endmethod
       
        private static method onSpellCastHide takes nothing returns nothing
            local unit enum = GetEnumUnit()
           
            call SetUnitInvulnerable(enum, true)
           
            if thistype.currentTemp.consumesWorker then
                call SetUnitUseFood(enum, false)
            endif
           
            call PauseUnit(enum, true)
            call ShowUnit(enum, false)
           
            set enum = null
        endmethod
       
        private static method onSpellCastUndock takes nothing returns nothing
            call DockingSystem.undock(GetEnumUnit())
            call thistype.onSpellCastHide()
        endmethod
       
        private static method onSpellCast takes nothing returns nothing
            local unit station              = GetTriggerUnit()
            local unit plug
           
            local BuildSystem whichBuild  
           
            local SubBuildList temp         = SubBuildList.getInstance(GetSpellAbilityId())
            local thistype  handler         = thistype.subRefTable.integer[temp]
           
            local TableArray   trainList
           
            local integer constructType     = BuildSystemEx.getConstructType(handler.abilHolder)
           
            local integer stationHandle     = GetHandleId(station)
            local integer stationId         = GetUnitTypeId(station)
           
            local integer plugHandle      
            local integer plugCount         = 0
           
            local IntegerListItem iter
            local IntegerListItem iter2
           
            local player stationOwner       = GetOwningPlayer(station)
           
            local group grp
           
            set whichBuild = BuildSystem(BuildSystemEx.stationHolder.integer[stationId])
           
            if constructType == BuildSystemEx.construct then
                set BuildSystemEx.dataBase[1].player[stationHandle] = stationOwner
                set BuildSystemEx.dataBase[2].real[stationHandle]   = GetUnitX(station)
                set BuildSystemEx.dataBase[3].real[stationHandle]   = GetUnitY(station)
                set BuildSystemEx.dataBase[4].group[stationHandle]  = CreateGroup()
               
                set grp = CreateGroup()
               
                //  Get the number of available plugs...
                loop              
                    exitwhen GetNumberOfOccupiedSockets(station) == 0
                   
                    set plug = GetSocketPlug(GetRandomSocketWithState(station, true))
                   
                    //  Remove the unit from the station
                    call DockingSystem.undock(plug)
                    call GroupAddUnit(grp, plug)
                   
                    if (plugCount < temp.count) then
                        call GroupAddUnit(BuildSystemEx.dataBase[4].group[stationHandle], plug)
                       
                        set plugCount = plugCount + 1
                    endif
                endloop
               
                if plugCount < temp.count then
                    call SimError(stationOwner, "Construction of " + GetObjectName(temp.unitId) + " requires "/*
                                           */
    + "at least " + I2S(temp.count) + " " + GetObjectName(whichBuild.workerId))
                   
                    //  Redock the units...
                    set plug = FirstOfGroup(grp)
                    loop
                        exitwhen plug == null
                       
                        call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                        call GroupRemoveUnit(grp, plug)
                       
                        set plug = FirstOfGroup(grp)
                    endloop
                   
                    call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[2].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[3].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[4].group.remove(stationHandle)
                   
                    call DestroyGroup(grp)
                    call DestroyGroup(BuildSystemEx.dataBase[4].group[stationHandle])
                   
                    set grp = null
                    set station = null
                    set plug = null
                   
                    return
                endif
               
                call ShowUnit(station, false)
                call SetUnitX(station, WorldBounds.minX)
                call SetUnitY(station, WorldBounds.minY)
                call SetUnitInvulnerable(station, true)
               
                set plug = FirstOfGroup(BuildSystemEx.dataBase[4].group[stationHandle])
               
                call BuildSystemEx.increment(stationOwner, temp.unitId)
                if IssueBuildOrderById(plug, temp.unitId, BuildSystemEx.dataBase[2].real[stationHandle], BuildSystemEx.dataBase[3].real[stationHandle]) then              
                    set thistype.currentTemp = temp
                    call ForGroup(BuildSystemEx.dataBase[4].group[stationHandle], function thistype.onSpellCastUndock)
               
                    call ShowUnit(plug, true)
                    call PauseUnit(plug, false)
                                   
                    set plugHandle = GetHandleId(plug)
                   
                    //  Let the builder point at the station ...
                    set BuildSystemEx.dataBase[5].unit[plugHandle]      = station
                    set BuildSystemEx.dataBase[6].integer[plugHandle]   = temp
                   
                    call SetUnitX(plug, BuildSystemEx.dataBase[2].real[stationHandle])
                    call SetUnitY(plug, BuildSystemEx.dataBase[3].real[stationHandle])
                   
                    //  The group is unnecessary at this point.
                    call DestroyGroup(grp)
                   
                    set grp = null
                else
                    call BuildSystemEx.decrement(stationOwner, temp.unitId)
                    call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                   
                    loop
                        exitwhen plug == null
                       
                        call GroupRemoveUnit(grp, plug)
                        set plug = FirstOfGroup(grp)
                       
                        call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                    endloop
                   
                    call SetUnitInvulnerable(station, false)
                    call SetUnitX(station, BuildSystemEx.dataBase[2].real[stationHandle])
                    call SetUnitY(station, BuildSystemEx.dataBase[3].real[stationHandle])
                    call ShowUnit(station, true)

                    call DestroyGroup(BuildSystemEx.dataBase[4].group[stationHandle])
                    call DestroyGroup(grp)
                   
                    call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[2].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[3].real.remove(stationHandle)
                    call BuildSystemEx.dataBase[4].group.remove(stationHandle)
                   
                    set grp = null
                    set station = null
                    set plug = null
                   
                    return
                endif
               
            elseif constructType == BuildSystemEx.upgrade then
                set BuildSystemEx.dataBase[7].player[stationHandle] = stationOwner
                set BuildSystemEx.dataBase[8].group[stationHandle]  = CreateGroup()
               
                set grp = BuildSystemEx.dataBase[8].group[stationHandle]
                //  Get the number of available plugs...
                loop
                    exitwhen GetNumberOfOccupiedSockets(station) == 0 or (plugCount >= temp.count)
                   
                    set plug = GetSocketPlug(GetRandomSocketWithState(station, true))
                    call DockingSystem.undock(plug)
                    call GroupAddUnit(BuildSystemEx.dataBase[8].group[stationHandle], plug)
                   
                    set plugCount = plugCount + 1
                endloop
               
                if plugCount < temp.count then
                    call SimError(stationOwner, "Upgrading to " + GetObjectName(temp.unitId) + " requires "/*
                                           */
    + "at least " + I2S(temp.count) + " " + GetObjectName(whichBuild.workerId))
                   
                    set plug = FirstOfGroup(grp)
                    loop
                        exitwhen plug == null
                       
                        call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                       
                        call GroupRemoveUnit(grp, plug)
                        set plug = FirstOfGroup(grp)
                    endloop
                   
                    call DestroyGroup(BuildSystemEx.dataBase[8].group[stationHandle])
                   
                    call BuildSystemEx.dataBase[7].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[8].group.remove(stationHandle)
                   
                    set grp = null
                    set station = null
                    set plug = null
                   
                    return
                endif
               
                call UnitRemoveAbility(station, handler.abilHolder)
                call UnitAddAbility(station, handler.abilHolder)
               

                call PauseUnit(station, true)
                call IssueImmediateOrderById(station, 851972)
                call PauseUnit(station, false)

                call BuildSystemEx.increment(stationOwner, temp.unitId)

                if IssueImmediateOrderById(station, temp.unitId) then
                    set thistype.currentTemp = temp
                    call ForGroup(BuildSystemEx.dataBase[8].group[stationHandle], function thistype.onSpellCastUndock)
                   
                    set BuildSystemEx.dataBase[9].integer[stationHandle] = temp
                    set BuildSystemEx.dataBase[10].integer[stationHandle] = stationId
                   
                    set grp = null
                else
                    call BuildSystemEx.decrement(stationOwner, temp.unitId)
                   
                    loop
                        exitwhen plug == null
                       
                        call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                       
                        call GroupRemoveUnit(grp, plug)
                        set plug = FirstOfGroup(grp)
                    endloop
                   
                    call DestroyGroup(grp)
                   
                    call BuildSystemEx.dataBase[7].player.remove(stationHandle)
                    call BuildSystemEx.dataBase[8].group.remove(stationHandle)
                   
                    set grp = null
                    set station = null
                    set plug = null
                   
                    return
                endif
               
            elseif constructType == BuildSystemEx.train then
               
                if not BuildSystemEx.dataBase[12].player.has(stationHandle) then
                    set BuildSystemEx.dataBase[12].player[stationHandle] = stationOwner
                    set BuildSystemEx.dataBase[13].integer[stationHandle] = 0
                endif
               
                set grp = CreateGroup()
                loop
                    exitwhen GetNumberOfOccupiedSockets(station) == 0 or (plugCount >= temp.count)
                   
                    set plug = GetSocketPlug(GetRandomSocketWithState(station, true))
                    call DockingSystem.undock(plug)
                    call GroupAddUnit(grp, plug)
                   
                    set plugCount = plugCount + 1
                endloop
               
                if plugCount < temp.count then
                    call SimError(stationOwner, "Upgrading to " + GetObjectName(temp.unitId) + " requires "/*
                                           */
    + "at least " + I2S(temp.count) + " " + GetObjectName(whichBuild.workerId))
                   
                    set plug = FirstOfGroup(grp)
                    loop
                        exitwhen plug == null
                       
                        call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                       
                        call GroupRemoveUnit(grp, plug)
                        set plug = FirstOfGroup(grp)
                    endloop
                   
                    call DestroyGroup(grp)
                   
                    if BuildSystemEx.dataBase[13].integer[stationHandle] == 0 then
                        call BuildSystemEx.dataBase[12].player.remove(stationHandle)
                        call BuildSystemEx.dataBase[13].integer.remove(stationHandle)
                    endif
                   
                    set grp = null
                    set station = null
                    set plug = null
                   
                    return
                endif
               
                call PauseUnit(station, true)
                call IssueImmediateOrderById(station, 851972)
                call PauseUnit(station, false)
               
                call UnitRemoveAbility(station, handler.abilHolder)
                call UnitAddAbility(station, handler.abilHolder)
               
                //  Using a counter mechanism
                call BlzUnitDisableAbility(station, handler.abilHolder, false, false)
                call BlzUnitHideAbility(station, handler.abilHolder, true)
               
                call BuildSystemEx.increment(stationOwner, temp.unitId)
               
                if IssueImmediateOrderById(station, temp.unitId) then
                    set thistype.currentTemp = temp
                    call ForGroup(grp, function thistype.onSpellCastUndock)
                                                 
                    if not BuildSystemEx.dataBase[14].integer.has(stationHandle) then                  
                        set trainList = TableArray[3]
                        set trainList[1].integer[0] = IntegerList.create()
                        set trainList[2].integer[0] = IntegerList.create()
                        set trainList[1].integer[-2] = IntegerList.create()
                       
                        set BuildSystemEx.dataBase[14].integer[stationHandle] = integer(trainList)
                    else
                        set trainList = BuildSystemEx.dataBase[14].integer[stationHandle]
                    endif
                   
                    //  Store the size().
                    set BuildSystemEx.dataBase[13].integer[stationHandle] = BuildSystemEx.dataBase[13].integer[stationHandle] + 1
                   
                    if not trainList[1].integer.has(temp.unitId) then
                        set trainList[1].integer[temp.unitId] = UnitGroupList.create()
                        set trainList[2].integer[temp.unitId] = temp
                       
                        call IntegerList(trainList[1].integer[0]).push(trainList[1].integer[temp.unitId])
                        call IntegerList(trainList[2].integer[0]).push(temp.unitId)
                    endif
                                   
                    call UnitGroupList(trainList[1].integer[temp.unitId]).push(grp)
                   
                    if IntegerList(trainList[1].integer[-2]).find(handler.abilHolder) == 0 then
                        call IntegerList(trainList[1].integer[-2]).push(handler.abilHolder)
                    endif
                   
                    set iter2 = IntegerList(trainList[1].integer[-2]).first
                    loop
                        exitwhen iter2 == 0
                       
                        set iter = thistype(thistype.holderTable.integer[iter2.data]).subList.first
                        loop
                            exitwhen iter == 0
                           
                            call BlzUnitDisableAbility(station, SubBuildList(iter.data).abilId, false, false)
                            call BlzUnitHideAbility(station, SubBuildList(iter.data).abilId, true)
                           
                            set iter = iter.next
                        endloop
                       
                        set iter2 = iter2.next
                    endloop
                   
                    set grp = null
                else
                    call BuildSystemEx.decrement(stationOwner, temp.unitId)
                   
                    static if DEBUG_MODE then
                        call BJDebugMsg("Insufficient resources.")
                    else
                        call SimError(stationOwner, "Insufficient resources.")
                    endif
                   
                    set plug = FirstOfGroup(grp)
                    loop
                        exitwhen plug == null
                       
                        call DockingSystem.dock(plug, GetClosestSocketWithState(station, plug, false))
                       
                        call GroupRemoveUnit(grp, plug)
                        set plug = FirstOfGroup(grp)
                    endloop
                   
                    call DestroyGroup(grp)
                   
                    if trainList[1].integer.has(-2) then
                        set iter2 = IntegerList(trainList[1].integer[-2]).first
                        loop
                            exitwhen iter2 == 0
                           
                            set iter = thistype(thistype.holderTable.integer[iter2.data]).subList.first
                            loop
                                exitwhen iter == 0
                               
                                call BlzUnitDisableAbility(station, SubBuildList(iter.data).abilId, false, false)
                                call BlzUnitHideAbility(station, SubBuildList(iter.data).abilId, true)
                               
                                set iter = iter.next
                            endloop
                           
                            set iter2 = iter2.next
                        endloop
                    endif
                   
                    if BuildSystemEx.dataBase[13].integer[stationHandle] == 0 then
                        if BuildSystemEx.dataBase[14].integer.has(stationHandle) then
                            set trainList = BuildSystemEx.dataBase[14].integer[stationHandle]
                           
                            if trainList[1].integer.has(0) then
                                call IntegerList(trainList[1].integer[0]).destroy()
                            endif
                            if trainList[2].integer.has(0) then
                                call IntegerList(trainList[2].integer[0]).destroy()
                            endif
                            if trainList[1].integer.has(-2) then
                                call IntegerList(trainList[1].integer[-2]).destroy()
                            endif
                           
                            call trainList.destroy()
                        endif
                       
                        call BuildSystemEx.dataBase[12].player.remove(stationHandle)
                        call BuildSystemEx.dataBase[13].integer.remove(stationHandle)
                        call BuildSystemEx.dataBase[14].integer.remove(stationHandle)
                    endif
                   
                    set grp = null
                    set station = null
                    set plug = null
                   
                    return
                endif
            endif
           
            set station = null
        endmethod
       
        private static method initVar takes nothing returns nothing
            set thistype.holderTable = Table.create()
            set thistype.subRefTable = Table.create()
        endmethod
       
        private static method initListener takes nothing returns nothing
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function thistype.onSpellCast)
        endmethod
       
        private static method init takes nothing returns nothing
            call thistype.initVar()
            call thistype.initListener()
        endmethod
       
        implement Initializer
    endstruct

    struct BuildSystem extends array
        implement Alloc
       
        private static constant integer DEFAULT_WORKER_COUNT    = 4
       
        private static constant real    DEFAULT_WORKER_DIST     = 45.
        private static constant real    DEFAULT_WORKER_HEIGHT   = 30.
        private static constant real    DEFAULT_WORKER_RAD      = 0.
        private static constant real    DEFAULT_DETECT_DIST     = 250.
       
        private static constant boolean DEFAULT_CONSUMES_WORKER = true
       
        //  These store the unit types which have been passed into the BuildSystem
        private static Table unitTypeList       = 0
        //  This stores the owner of the unit whose unit type has been registered
        private static Table unitOwner          = 0
        //  This stores the flag of the unit whether it triggered a unit death
        //  event or not
        private static Table unitDeathFlag      = 0
        //  This stores the flag for all stations...
        private static Table stationFlag        = 0
       
        //  Main members. Can be read, but not written.
        readonly integer workerId
        readonly integer buildingId
       
        private trigger onPredock
        private trigger onDock
        private trigger onUndock
       
        private IntegerList constructList
        private IntegerList upgradeList
        private IntegerList trainList
       
        integer workerCount
       
        real    workerHeight
        real    workerDist
        real    workerStartRad
        real    workerDetectDist
       
        boolean workerConsumed
       
        static method create takes integer workerId, integer buildingId returns thistype
            local thistype result = BuildSystemEx.stationHolder.integer[buildingId]
           
            if result == 0 then
                set result                  = thistype.allocate()
               
                set result.workerId         = workerId
                set result.buildingId       = buildingId
               
                set result.constructList    = IntegerList.create()
                set result.upgradeList      = IntegerList.create()
                set result.trainList        = IntegerList.create()
               
                set result.workerCount      = DEFAULT_WORKER_COUNT
                set result.workerHeight     = DEFAULT_WORKER_HEIGHT
                set result.workerDist       = DEFAULT_WORKER_DIST
                set result.workerStartRad   = DEFAULT_WORKER_RAD
                set result.workerDetectDist = DEFAULT_DETECT_DIST
                set result.workerConsumed   = DEFAULT_CONSUMES_WORKER
               
                set result.onPredock        = CreateTrigger()
                set result.onDock           = CreateTrigger()
                set result.onUndock         = CreateTrigger()
               
                set BuildSystemEx.stationHolder.integer[buildingId] = result
            endif
           
            return result
        endmethod
       
        private static method precheckAbil takes integer abilHolder returns boolean
            return BuildSystemEx.constructType.integer.has(abilHolder)
        endmethod
       
        private static method addUnitType takes integer unitId returns nothing
            if unitId == 0 then
                debug call printError("thistype.addUnitType: Invalid unit type!")
                return
            endif
           
            if not thistype.unitTypeList.integer.has(unitId) then
                set thistype.unitTypeList.integer[unitId] = IntegerList(thistype.unitTypeList.integer[0]).push(unitId).last
            endif
        endmethod
       
        private static method isUnitPhysical takes unit u returns boolean
            return (not IsUnitType(u, UNIT_TYPE_SUMMONED)) and (not IsUnitIllusion(u))
        endmethod
       
        method addConstructAbil takes integer abilHolder returns nothing
            if thistype.precheckAbil(abilHolder) then
                debug call printError("thistype.addConstructAbil: Ability type was already defined.")
                return
            endif
           
            call this.constructList.push(abilHolder)
            set BuildSystemEx.constructType.integer[abilHolder] = BuildSystemEx.construct
        endmethod

        method addUpgradeAbil takes integer abilHolder returns nothing
            if thistype.precheckAbil(abilHolder) then
                debug call printError("thistype.addUpgradeAbil: Ability type was already defined.")
                return
            endif
           
            call this.upgradeList.push(abilHolder)
            set BuildSystemEx.constructType.integer[abilHolder] = BuildSystemEx.upgrade
        endmethod
       
        method addTrainAbil takes integer abilHolder returns nothing
            if thistype.precheckAbil(abilHolder) then
                debug call printError("thistype.addTrainAbil: Ability type was already defined.")
                return
            endif
           
            call this.trainList.push(abilHolder)
            set BuildSystemEx.constructType.integer[abilHolder] = BuildSystemEx.train
        endmethod
       
        method addConstructUnit takes integer unitId, integer abilId, integer abilHolder, integer count returns nothing
            if BuildSystemEx.constructType.integer[abilHolder] != BuildSystemEx.construct then
                debug call printError("thistype.addConstructUnit: Ability id is not of type construct.")
                return
            endif
            call BuildList.create(unitId, abilId, abilHolder, this.workerConsumed, count)
            call thistype.addUnitType(unitId)
        endmethod

        method addUpgradeUnit takes integer unitId, integer abilId, integer abilHolder, integer count returns nothing
            if BuildSystemEx.constructType.integer[abilHolder] != BuildSystemEx.upgrade then
                debug call printError("thistype.addUpgradeUnit: Ability id is not of type upgrade.")
                return
            endif
            call BuildList.create(unitId, abilId, abilHolder, this.workerConsumed, count)
            call thistype.addUnitType(unitId)
        endmethod
       
        method addTrainUnit takes integer unitId, integer abilId, integer abilHolder, integer count returns nothing
            if BuildSystemEx.constructType.integer[abilHolder] != BuildSystemEx.train then
                debug call printError("thistype.addTrainUnit: Ability id is not of type train.")
                return
            endif
            call BuildList.create(unitId, abilId, abilHolder, this.workerConsumed, count)
            call thistype.addUnitType(unitId)
        endmethod
       
        method setOnDockEvent takes integer dockEvent, code whichHandler returns nothing
            if dockEvent == EVENT_ON_PRE_DOCK then
                call DestroyTrigger(this.onPredock)
               
                set this.onPredock = CreateTrigger()
                call TriggerAddCondition(this.onPredock, Condition(whichHandler))
               
            elseif dockEvent == EVENT_ON_DOCK then
                call DestroyTrigger(this.onDock)
               
                set this.onDock = CreateTrigger()
                call TriggerAddCondition(this.onDock, Condition(whichHandler))
               
            elseif dockEvent == EVENT_ON_UNDOCK then
                call DestroyTrigger(this.onUndock)
               
                set this.onUndock = CreateTrigger()
                call TriggerAddCondition(this.onUndock, Condition(whichHandler))          
            debug else
                debug call printError("thistype.setOnDockEvent: Unrecognized dock event!")
            endif
        endmethod
       
        private method initStation takes unit station returns nothing
            local integer i             = 0
            local integer stationHandle = GetHandleId(station)
           
            local real pSlice           = 2*bj_PI/(this.workerCount)
            local real stationX         = GetUnitX(station)
            local real stationY         = GetUnitY(station)
           
            if not thistype.stationFlag.boolean.has(stationHandle) then
                set thistype.stationFlag.boolean[stationHandle] = false
               
                call DockingSystem.createStation(station, this.workerDetectDist)
                loop
                    exitwhen i >= this.workerCount
                   
                    call DockingSystem.addSocket(station, /*
                                                */
    stationX + this.workerDist*Cos(I2R(i)*pSlice + this.workerStartRad),/*
                                                */
    stationY + this.workerDist*Sin(I2R(i)*pSlice + this.workerStartRad),/*
                                                */
    this.workerHeight, (I2R(i)*pSlice + this.workerStartRad + (bj_PI/2))/*
                                                */
    * bj_RADTODEG)
                    set i = i + 1
                endloop
            endif
        endmethod
       
        private method removeStation takes unit station returns nothing
            local integer stationHandle = GetHandleId(station)
           
            if thistype.stationFlag.boolean.has(stationHandle) then
                call thistype.stationFlag.boolean.remove(stationHandle)
               
                call DockingSystem.terminateStation(station)
            endif
        endmethod
       
        /*
             -----------------------------------
            |   Handler methods                 |
             -----------------------------------
        */

       
        /*
              ------------------
             |                  |
             |  Constructs      |
             |                  |
              ------------------
        */

        private static method onConstructStart takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit builder              = GetBuildingUnit()
            local unit station
           
            local player buildingOwner      = GetOwningPlayer(building)
           
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
            local integer builderHandle     = GetHandleId(builder)
           
            local SubBuildList  buildList   = BuildSystemEx.dataBase[6].integer[buildingId]
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
           
            //  If building is a station...
            if temp != 0 then
                call temp.removeStation(building)
            endif
           
            if BuildSystemEx.dataBase[5].unit.has(builderHandle) then
                //  Move data reference to the building currently being built...
                set station = BuildSystemEx.dataBase[5].unit[builderHandle]
                set BuildSystemEx.dataBase[5].unit[buildingHandle]      = station
                set BuildSystemEx.dataBase[6].integer[buildingHandle]   = BuildSystemEx.dataBase[6].integer[builderHandle]
               
                call BuildSystemEx.dataBase[5].unit.remove(builderHandle)
                call BuildSystemEx.dataBase[6].integer.remove(builderHandle)
               
                //  Correct the incremental value...
                call BuildSystemEx.decrement(buildingOwner, buildingId)
               
                if buildList.consumesWorker then
                    call UnitAddAbility(builder, 'Aloc')
                endif
            endif
           
            set builder  = null
            set building = null
        endmethod
       
        private static method onConstructCancel takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit builder              = GetBuildingUnit()
            local unit station
            local unit plug
           
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
           
            local integer builderHandle     = GetHandleId(builder)
           
            local integer stationHandle
           
            local real    stationX
            local real    stationY
           
            local player  stationOwner
            local group   stationGrp
           
            local trigger trig
           
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
            local SubBuildList  buildList   = 0
           
            //  Triggered by removal
            if GetTriggerEventId() == null then
                set building = GetIndexedUnit()
                set builder  = GetBuilder(building)
               
                set buildingId      = GetUnitTypeId(building)
                set buildingHandle  = GetHandleId(building)
               
                set builderHandle   = GetHandleId(builder)
            endif
           
            if BuildSystemEx.dataBase[5].unit.has(buildingHandle) then
                set station         = BuildSystemEx.dataBase[5].unit[buildingHandle]
               
                set stationHandle   = GetHandleId(station)
               
                set stationOwner    = BuildSystemEx.dataBase[1].player[stationHandle]
                set stationX        = BuildSystemEx.dataBase[2].real[stationHandle]
                set stationY        = BuildSystemEx.dataBase[3].real[stationHandle]
                set stationGrp      = BuildSystemEx.dataBase[4].group[stationHandle]
               
                set buildList       = BuildSystemEx.dataBase[6].integer[buildingHandle]
               
                call BuildSystemEx.dataBase[5].unit.remove(buildingHandle)
                call BuildSystemEx.dataBase[6].integer.remove(buildingHandle)
               
                if buildList.consumesWorker then
                    call UnitRemoveAbility(builder, 'Aloc')
                endif
               
                call SetUnitInvulnerable(station, false)
                call SetUnitX(station, stationX)
                call SetUnitY(station, stationY)
                call ShowUnit(station, true)
               
                set plug = FirstOfGroup(stationGrp)
                loop
                    exitwhen plug == null
                   
                    call ShowUnit(plug, true)
                    call SetUnitInvulnerable(plug, false)
                    call SetUnitPosition(plug, stationX, stationY)
                    call PauseUnit(plug, false)
                   
                    if GetLocalPlayer() == stationOwner then
                        call SelectUnit(plug, true)
                    endif
                   
                    if buildList.consumesWorker then
                        call SetUnitUseFood(plug, true)
                    endif
                   
                    call GroupRemoveUnit(stationGrp, plug)
                    set plug = FirstOfGroup(stationGrp)
                endloop
               
                call BuildSystemEx.dataBase[1].player.remove(stationHandle)
                call BuildSystemEx.dataBase[2].real.remove(stationHandle)
                call BuildSystemEx.dataBase[3].real.remove(stationHandle)
                call BuildSystemEx.dataBase[4].group.remove(stationHandle)
               
                call DestroyGroup(stationGrp)
               
                set station    = null
                set stationGrp = null
            endif
           
            set builder  = null
            set building = null
        endmethod
       
        private static method onConstructFinish takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit builder              = GetBuildingUnit()
            local unit station              = null
            local unit plug
           
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
           
            local integer builderHandle     = GetHandleId(builder)
           
            local integer stationHandle
           
            local real    stationX
            local real    stationY
           
            local player  stationOwner
            local group   stationTempGrp    = null
            local group   stationGrp
                   
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
           
            local SubBuildList buildList    = 0
           
            if BuildSystemEx.dataBase[5].unit.has(buildingHandle) then
                set station         = BuildSystemEx.dataBase[5].unit[buildingHandle]
               
                set stationHandle   = GetHandleId(station)
               
                set stationOwner    = BuildSystemEx.dataBase[1].player[stationHandle]
                set stationX        = BuildSystemEx.dataBase[2].real[stationHandle]
                set stationY        = BuildSystemEx.dataBase[3].real[stationHandle]
                set stationGrp      = BuildSystemEx.dataBase[4].group[stationHandle]
               
                set buildList       = BuildSystemEx.dataBase[6].integer[stationHandle]
               
                set stationTempGrp  = CreateGroup()
                call CopyGroup(stationTempGrp, stationGrp)
            endif
           
            call thistype.onConstructCancel()
           
            if station != null then
                set plug = FirstOfGroup(stationTempGrp)
                loop
                    exitwhen plug == null
                   
                    call GroupRemoveUnit(stationTempGrp, plug)
                   
                    if buildList.consumesWorker then
                        call RemoveUnit(plug)
                    endif
                   
                    set plug = FirstOfGroup(stationTempGrp)
                endloop
               
                call RemoveUnit(station)
            endif
           
            if stationTempGrp != null then
                call DestroyGroup(stationTempGrp)
            endif
           
            if temp != 0 then
                call temp.initStation(building)
            endif
           
            set stationTempGrp  = null
            set station         = null
            set stationGrp      = null
           
            set builder         = null
            set building        = null
        endmethod
       
        /*
              ------------------
             |                  |
             |  Upgrades        |
             |                  |
              ------------------
        */

        private static method onUpgradeStart takes nothing returns nothing
            local unit building             = GetTriggerUnit()
           
            local player buildingOwner      = GetOwningPlayer(building)
           
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
           
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
               
            if temp != 0 then
                call DockingSystem.undockAll(building)
            endif
           
            if BuildSystemEx.dataBase[9].integer.has(buildingHandle) then
                //  Raise a flag telling the system that the unit is upgrading.
                set BuildSystemEx.dataBase[11].boolean[buildingHandle] = true
            endif
           
            set building = null
        endmethod
       
        private static method onUpgradeCancel takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit plug
           
            local real buildingX            = GetUnitX(building)
            local real buildingY            = GetUnitY(building)
           
            local group plugGroup
           
            local player buildingOwner      = GetOwningPlayer(building)
           
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
            local integer upgradeId        
           
            local SubBuildList buildList  
           
            local thistype temp             = BuildSystemEx.stationHolder.integer[buildingId]
           
            if GetTriggerEventId() == null then
                set building        = GetIndexedUnit()
               
                set buildingX       = GetUnitX(building)
                set buildingY       = GetUnitY(building)
               
                set buildingOwner   = GetOwningPlayer(building)
               
                set buildingId      = GetUnitTypeId(building)
                set buildingHandle  = GetHandleId(building)
            endif
           
            if BuildSystemEx.dataBase[9].integer.has(buildingHandle) then
                set plugGroup   = BuildSystemEx.dataBase[8].group[buildingHandle]
                set buildList   = SubBuildList(BuildSystemEx.dataBase[9].integer[buildingHandle])
                set upgradeId   = buildList.unitId
               
                call BuildSystemEx.decrement(buildingOwner, upgradeId)
               
                //  Show the units...
                set plug = FirstOfGroup(plugGroup)
                loop
                    exitwhen plug == null
                   
                    call ShowUnit(plug, true)
                    call SetUnitInvulnerable(plug, false)
                    call SetUnitPosition(plug, buildingX, buildingY)
                    call PauseUnit(plug, false)
                   
                    if buildList.consumesWorker then
                        call SetUnitUseFood(plug, true)
                    endif
                   
                    call GroupRemoveUnit(plugGroup, plug)
                    set plug = FirstOfGroup(plugGroup)
                endloop
               
                call DestroyGroup(plugGroup)
               
                set plugGroup = null
               
                call BuildSystemEx.dataBase[7].player.remove(buildingHandle)
                call BuildSystemEx.dataBase[8].group.remove(buildingHandle)
                call BuildSystemEx.dataBase[9].integer.remove(buildingHandle)
                call BuildSystemEx.dataBase[10].integer.remove(buildingHandle)
                call BuildSystemEx.dataBase[11].boolean.remove(buildingHandle)
            endif
           
            set building = null
        endmethod
       
        private static method onUpgradeFinish takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit plug
           
            local real buildingX            = GetUnitX(building)
            local real buildingY            = GetUnitY(building)
           
            local group plugGroup
            local group plugGroupCopy       = null
           
            local player buildingOwner      = GetOwningPlayer(building)
           
            local integer buildingId        = GetUnitTypeId(building)
            local integer buildingHandle    = GetHandleId(building)
            local integer upgradeId        
            local integer lastBuildingId  
           
            local SubBuildList buildList  
           
            local thistype temp            
           
            if BuildSystemEx.dataBase[3].integer.has(buildingHandle) then
                set plugGroup       = BuildSystemEx.dataBase[2].group[buildingHandle]
                set buildList       = SubBuildList(BuildSystemEx.dataBase[3].integer[buildingHandle])
                set upgradeId       = buildList.unitId
                set lastBuildingId  = BuildSystemEx.dataBase[4].integer[buildingHandle]
               
                set temp = BuildSystemEx.stationHolder.integer[lastBuildingId]
               
                set plugGroupCopy   = CreateGroup()
                call CopyGroup(plugGroupCopy, plugGroup)
               
                if temp != 0 then
                    /*
                    call temp.removeStation(building)
                   
                    //  Outsource it to transformation.
                    if thistype.unitTypeList.has(lastBuildingId) then
                        call BuildSystemEx.decrement(buildingOwner, lastBuildingId)
                    endif
                    */

                endif
               
                //  Counter the effects of decrement when calling onUpgradeCancel...
                call BuildSystemEx.increment(buildingOwner, buildingId)
            endif
           
            call thistype.onUpgradeCancel()
           
            set plug = FirstOfGroup(plugGroupCopy)
            loop
                exitwhen plug == null
               
                call GroupRemoveUnit(plugGroupCopy, plug)
               
                if buildList.consumesWorker then
                    call RemoveUnit(plug)
                endif
               
                set plug = FirstOfGroup(plugGroupCopy)
            endloop
           
            if plugGroupCopy != null then
                call DestroyGroup(plugGroupCopy)
            endif
           
            set plugGroupCopy = null
            set building = null
        endmethod
       
        /*
              ------------------
             |                  |
             |  Training        |
             |                  |
              ------------------
        */

        private static method onTrainCancel takes nothing returns nothing
            local unit building
            local unit plug
           
            local integer buildingHandle
            local integer buildingId
            local integer trainedType       = GetTrainedUnitType()
           
            local real buildingX
            local real buildingY
           
            local group buildGroup
           
            local TableArray buildTable
           
            local SubBuildList buildList
           
            local player buildingOwner    
           
            local thistype inst            
           
            set building = GetTriggerUnit()
           
            set buildingId      = GetUnitTypeId(building)
            set buildingHandle  = GetHandleId(building)
            set buildingOwner   = GetOwningPlayer(building)
           
            set buildingX       = GetUnitX(building)
            set buildingY       = GetUnitY(building)
           
            //  If there are units to be trained...
            if BuildSystemEx.dataBase[14].integer.has(buildingHandle) then
                set buildTable = BuildSystemEx.dataBase[14].integer[buildingHandle]
                set buildGroup = UnitGroupList(buildTable[1].integer[trainedType]).first.data
                set buildList  = SubBuildList(buildTable[2].integer[trainedType])
               
                call UnitGroupList(buildTable[1].integer[trainedType]).removeElem(buildGroup)
               
                call BuildSystemEx.decrement(buildingOwner, trainedType)
               
                //  Show the units...
                set plug = FirstOfGroup(buildGroup)
                loop
                    exitwhen plug == null
                   
                    call ShowUnit(plug, true)
                    call SetUnitInvulnerable(plug, false)
                    call SetUnitPosition(plug, buildingX, buildingY)
                    call PauseUnit(plug, false)
                   
                    if buildList.consumesWorker then
                        call SetUnitUseFood(plug, true)
                    endif
                   
                    call GroupRemoveUnit(buildGroup, plug)
                    set plug = FirstOfGroup(buildGroup)
                endloop
               
                call DestroyGroup(buildGroup)
               
                if UnitGroupList(buildTable[1].integer[trainedType]).size() == 0 then
                    call IntegerList(buildTable[1].integer[0]).removeElem(buildTable[1].integer[trainedType])
                    call IntegerList(buildTable[2].integer[0]).removeElem(trainedType)
                   
                    call buildTable[2].integer.remove(trainedType)
                endif
               
                set BuildSystemEx.dataBase[13].integer[buildingHandle] = BuildSystemEx.dataBase[13].integer[buildingHandle] - 1
               
                set buildGroup = null
            endif
        endmethod
       
        private static method onTrainFinish takes nothing returns nothing
            local unit building             = GetTriggerUnit()
            local unit plug
           
            local integer buildingHandle
            local integer buildingId
            local integer trainTypeId       = GetTrainedUnitType()
           
            local group buildGroup
           
            local TableArray trainTable
           
            local SubBuildList buildList
           
            local player buildingOwner    
           
            local thistype inst            
           
            local IntegerListItem iter
           
            if not thistype.unitTypeList.integer.has(trainTypeId) then
                set building = null
                return
            endif
           
            set buildingId      = GetUnitTypeId(building)
            set buildingHandle  = GetHandleId(building)
            set buildingOwner   = GetOwningPlayer(building)
           
            set buildGroup      = CreateGroup()
           
            set trainTable      = BuildSystemEx.dataBase[14].integer[buildingHandle]
            set buildList       = trainTable[2].integer[trainTypeId]

            //  Copy the group first, since it will get destroyed on thistype.onTrainCancel
            call CopyGroup(buildGroup, UnitGroupList(trainTable[1].integer[trainTypeId]).first.data)
           
            //  Negation of the decrement call is on onEnter
            call thistype.onTrainCancel()

            set plug = FirstOfGroup(buildGroup)
            loop
                exitwhen plug == null
               
                call GroupRemoveUnit(buildGroup, plug)
               
                if buildList.consumesWorker then
                    call RemoveUnit(plug)
                endif
               
                set plug = FirstOfGroup(buildGroup)
            endloop
           
            call DestroyGroup(buildGroup)
            set buildGroup = null
           
            if BuildSystemEx.dataBase[13].integer[buildingHandle] == 0 then                  
                set iter = IntegerList(trainTable[1].integer[-2]).first
                loop
                    exitwhen iter == 0
                   
                    call UnitRemoveAbility(building, iter.data)
                    call UnitAddAbility(building, iter.data)
                   
                    set iter = iter.next
                endloop
               
                call IntegerList(trainTable[1].integer[0]).destroy()
                call IntegerList(trainTable[2].integer[0]).destroy()
                call IntegerList(trainTable[1].integer[-2]).destroy()
               
                call trainTable.destroy()
               
                call BuildSystemEx.dataBase[12].player.remove(buildingHandle)
                call BuildSystemEx.dataBase[13].integer.remove(buildingHandle)
                call BuildSystemEx.dataBase[14].integer.remove(buildingHandle)          
            endif
        endmethod
       
        private static method onTrainDeath takes unit building returns nothing
            local unit plug
           
            local integer buildingId                = GetUnitTypeId(building)
            local integer buildingHandle            = GetHandleId(building)
            local player buildingOwner              = GetOwningPlayer(building)
           
            local real buildingX                           = GetUnitX(building)
            local real buildingY                           = GetUnitY(building)
           
            local group trainGroup
           
            local SubBuildList buildList
           
            local TableArray trainTable
           
            local IntegerListItem iter
            local IntegerListItem iter2
           
            local UnitGroupListItem iterGrp
           
            set trainTable  = BuildSystemEx.dataBase[14].integer[buildingHandle]
            set iter        = IntegerList(trainTable[1].integer[0]).first
            set iter2       = IntegerList(trainTable[2].integer[0]).first
           
            loop
                exitwhen iter == 0
               
                set iterGrp = UnitGroupList(iter.data).first
                set buildList = SubBuildList(trainTable[2].integer[iter2.data])
               
                loop
                    exitwhen iterGrp == 0
                   
                    set trainGroup = iterGrp.data
                    call BuildSystemEx.decrement(buildingOwner, buildList.unitId)
                   
                    loop
                        set plug = FirstOfGroup(trainGroup)
                        exitwhen plug == null
                       
                        call ShowUnit(plug, true)
                        call SetUnitInvulnerable(plug, false)
                        call SetUnitPosition(plug, buildingX, buildingY)
                        call PauseUnit(plug, false)
                       
                        if buildList.consumesWorker then
                            call SetUnitUseFood(plug, true)
                        endif
                       
                        call GroupRemoveUnit(trainGroup, plug)
                    endloop
                   
                    call DestroyGroup(trainGroup)
               
                    set BuildSystemEx.dataBase[13].integer[buildingHandle] = BuildSystemEx.dataBase[13].integer[buildingHandle] - 1
               
                    set iterGrp = iterGrp.next
                endloop
                           
                set iter    = iter.next
                set iter2   = iter2.next
            endloop
       
            if GetTriggerEventId() != null then
                set iter = IntegerList(trainTable[1].integer[-2]).first
                call PauseUnit(building, true)
                loop
                    exitwhen iter == 0
                   
                    call UnitRemoveAbility(building, iter.data)
                    call UnitAddAbility(building, iter.data)
                   
                    set iter = iter.next
                endloop
                call PauseUnit(building, false)
            endif
           
            call IntegerList(trainTable[1].integer[0]).destroy()
            call IntegerList(trainTable[2].integer[0]).destroy()
            call IntegerList(trainTable[1].integer[-2]).destroy()

            call trainTable.destroy()
           
            call BuildSystemEx.dataBase[12].player.remove(buildingHandle)
            call BuildSystemEx.dataBase[13].integer.remove(buildingHandle)
            call BuildSystemEx.dataBase[14].integer.remove(buildingHandle)
        endmethod
       
        private static method onUnitEnter takes nothing returns nothing
            local unit u             = GetIndexedUnit()
            local integer unitId     = GetUnitTypeId(u)
            local integer uHandle    = GetHandleId(u)
            local player uOwner      = GetOwningPlayer(u)
           
            local thistype inst      = BuildSystemEx.stationHolder.integer[unitId]
           
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
           
            //  Unit is a station.
            if inst != 0 then
                call inst.initStation(u)
            endif
           
            if thistype.unitTypeList.integer.has(unitId) then
                call BuildSystemEx.increment(uOwner, unitId)
                   
                set thistype.unitOwner.player[uHandle]      = uOwner
                set thistype.unitDeathFlag.boolean[uHandle] = false
            endif
           
            set u = null
        endmethod
       
        private static method onUnitExit takes nothing returns nothing
            local unit u            = GetIndexedUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
           
            local thistype inst     = BuildSystemEx.stationHolder.integer[unitId]
           
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
           
            if inst != 0 then
                call inst.removeStation(u)
            endif

            //  Unit must have undergone construction when it was removed...
            if BuildSystemEx.dataBase[5].unit.has(uHandle) then
                call thistype.onConstructCancel()
            endif
           
            //  Unit must have undergone upgrade when it was removed...
            if BuildSystemEx.dataBase[9].integer.has(uHandle) then
                call thistype.onUpgradeCancel()
            endif
           
            //  Unit must have trained some units...
            if BuildSystemEx.dataBase[13].integer.has(uHandle) then
                call thistype.onTrainDeath(u)
            endif
           
            if thistype.unitTypeList.integer.has(unitId) then
                if not thistype.unitDeathFlag.boolean[uHandle] then
                    call BuildSystemEx.decrement(uOwner, unitId)
                endif
               
                call thistype.unitOwner.player.remove(uHandle)
                call thistype.unitDeathFlag.boolean.remove(uHandle)
            endif
           
            set u = null
        endmethod
       
        private static method onUnitDeath takes nothing returns nothing
            local unit u            = GetTriggerUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
                 
            local thistype inst      = BuildSystemEx.stationHolder.integer[unitId]

            if GetTriggerEventId() == null then
                //  Called on reincarnation
                set u = GetEventUnit()
            endif
           
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
           
            if inst != 0 then
                if GetNumberOfOccupiedSockets(u) != 0 then
                    call DockingSystem.undockAll(u)
                endif
            endif
           
            //  Unit must have undergone construction when it was destroyed...
            if BuildSystemEx.dataBase[5].unit.has(uHandle) then
                call thistype.onConstructCancel()
            endif
           
            //  Unit must have trained some units beforehand...
            if BuildSystemEx.dataBase[13].integer.has(uHandle) then
                call thistype.onTrainDeath(u)
            endif
           
            if thistype.unitTypeList.integer.has(unitId) then
                if not thistype.unitDeathFlag.boolean[uHandle] then
                    call BuildSystemEx.decrement(uOwner, unitId)
                   
                    set thistype.unitDeathFlag.boolean[uHandle] = true              
                endif          
            endif
           
            set u = null
        endmethod
       
        private static method onUnitChangeOwner takes nothing returns nothing
            local unit u            = GetTriggerUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
           
            local thistype inst     = BuildSystemEx.stationHolder.integer[unitId]
           
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
           
            if inst != 0 then
                call DockingSystem.undockAll(u)
            endif
           
            if thistype.unitTypeList.integer.has(unitId) then
                call BuildSystemEx.decrement(thistype.unitOwner.player[uHandle], unitId)
                   
                set thistype.unitOwner.player[uHandle] = uOwner
               
                call BuildSystemEx.increment(thistype.unitOwner.player[uHandle], unitId)
            endif
           
            set u = null
        endmethod
       
        private static method onUnitRevive takes nothing returns nothing
            local unit u            = GetEventUnit()
            local integer unitId    = GetUnitTypeId(u)
            local integer uHandle   = GetHandleId(u)
            local player uOwner     = GetOwningPlayer(u)
           
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
           
            if thistype.unitTypeList.integer.has(unitId) then
                if thistype.unitDeathFlag.boolean[uHandle] then
                    call BuildSystemEx.increment(uOwner, unitId)
                   
                    set thistype.unitDeathFlag.boolean[uHandle] = false
                endif
            endif
           
            set u = null
        endmethod
       
        private static method onUnitTransform takes nothing returns nothing
            local unit u                = GetEventUnit()
           
            local integer prevUnitId    = GetEventTransformType()
            local integer unitId        = GetUnitTypeId(u)
            local integer uHandle       = GetHandleId(u)
            local player uOwner         = GetOwningPlayer(u)
           
            local thistype inst         = BuildSystemEx.stationHolder.integer[prevUnitId]
           
            if not thistype.isUnitPhysical(u) then
                set u = null
                return
            endif
           
            if inst != 0 then
                call inst.removeStation(u)
            endif
           
            set inst = BuildSystemEx.stationHolder.integer[unitId]
           
            if inst != 0 then
                call inst.initStation(u)
            endif
           
            if thistype.unitTypeList.integer.has(GetEventTransformType()) then
                call BuildSystemEx.decrement(uOwner, GetEventTransformType())
            endif
               
            if thistype.unitTypeList.integer.has(unitId) then
                call BuildSystemEx.increment(uOwner, unitId)
            endif
           
            set u = null
        endmethod
       
        private static method onPreDockHandler takes nothing returns nothing
            local unit station          = GetStation()
            local unit plug             = GetPlug()
           
            local integer stationId     = GetUnitTypeId(station)
            local integer stationHandle = GetHandleId(station)
            local integer plugId        = GetUnitTypeId(plug)
            local integer plugHandle    = GetHandleId(plug)
           
            local player plugOwner      = GetOwningPlayer(plug)
           
            local thistype temp         = BuildSystemEx.stationHolder.integer[stationId]
           
            //  Nothing to do here...
            if (temp == 0) then
                // Don't interfere with other stations...
                set plug    = null
                set station = null
               
                return
            endif
           
            if (temp.workerId != plugId) then
                call InterruptDocking()
               
                set plug    = null
                set station = null
               
                return
            endif
                   
            if (GetOwningPlayer(plug) != GetOwningPlayer(station)) then
                call InterruptDocking()
                call SimError(plugOwner, "Cannot dock into a station owned by another player!")
               
                set plug    = null
                set station = null
            endif
           
            //  Unit is being upgraded. Don't allow docking!
            if BuildSystemEx.dataBase[7].player.has(stationHandle) then
                call InterruptDocking()
                call SimError(plugOwner, "Upgrade is in progress... you cannot dock at this moment.")
               
                set plug    = null
                set station = null
               
                return
            endif

            call ConditionalTriggerExecute(temp.onPredock)
           
            set plug    = null
            set station = null
        endmethod
       
        private static method onDockHandler takes nothing returns nothing
            local thistype temp = BuildSystemEx.stationHolder.integer[GetUnitTypeId(GetStation())]
           
            if temp != 0 then
                call ConditionalTriggerExecute(temp.onDock)
            endif
        endmethod
       
        private static method onUndockHandler takes nothing returns nothing
            local thistype temp = BuildSystemEx.stationHolder.integer[GetUnitTypeId(GetStation())]
           
            if temp != 0 then
                call ConditionalTriggerExecute(temp.onUndock)
            endif
        endmethod
       
        private static method initVar takes nothing returns nothing
            set thistype.unitTypeList   = Table.create()
            set thistype.unitOwner      = Table.create()
            set thistype.unitDeathFlag  = Table.create()
            set thistype.stationFlag    = Table.create()
           
            set thistype.unitTypeList.integer[0] = IntegerList.create()
        endmethod
       
        private static method initListener takes nothing returns nothing
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_START, function thistype.onConstructStart)
            //call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL, function thistype.onConstructCancel)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CONSTRUCT_FINISH, function thistype.onConstructFinish)
           
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_START, function thistype.onUpgradeStart)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_CANCEL, function thistype.onUpgradeCancel)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_UPGRADE_FINISH, function thistype.onUpgradeFinish)
           
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_TRAIN_CANCEL, function thistype.onTrainCancel)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_TRAIN_FINISH, function thistype.onTrainFinish)
           
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function thistype.onUnitDeath)
            call RegisterAnyPlayerUnitEvent(EVENT_PLAYER_UNIT_CHANGE_OWNER, function thistype.onUnitChangeOwner)
           
            call RegisterUnitIndexEvent(Condition(function thistype.onUnitEnter), EVENT_UNIT_INDEX)
            call RegisterUnitIndexEvent(Condition(function thistype.onUnitExit), EVENT_UNIT_DEINDEX)
           
            call RegisterNativeEvent(EVENT_ON_REINCARNATION_START, function thistype.onUnitDeath)
           
            call RegisterNativeEvent(EVENT_ON_RESURRECTION, function thistype.onUnitRevive)
            call RegisterNativeEvent(EVENT_ON_REINCARNATION_FINISH, function thistype.onUnitRevive)
            call RegisterNativeEvent(EVENT_ON_TRANSFORM, function thistype.onUnitTransform)
           
            call RegisterNativeEvent(EVENT_ON_PRE_DOCK, function thistype.onPreDockHandler)
            call RegisterNativeEvent(EVENT_ON_DOCK, function thistype.onDockHandler)
            call RegisterNativeEvent(EVENT_ON_UNDOCK, function thistype.onUndockHandler)      
        endmethod
       
        private static method init takes nothing returns nothing
            call thistype.initVar()
            call thistype.initListener()
        endmethod
       
        static method onGameStart takes nothing returns nothing
            local integer i = 1
            local player  p = Player(i - 1)
            local IntegerListItem firstIter = IntegerList(thistype.unitTypeList.integer[0]).first
            local IntegerListItem iter      = firstIter
           
            loop
                loop
                    exitwhen iter == 0
                   
                    set BuildSystemEx.unitTypeLimit[i].integer[iter.data] = 0
                    call SetPlayerTechMaxAllowed(p, iter.data, BuildSystemEx.unitTypeLimit[i].integer[iter.data])
                   
                    set iter = iter.next
                endloop
                set iter = firstIter
               
                exitwhen i >= bj_MAX_PLAYER_SLOTS
               
                set p = Player(i)
                set i = i + 1
            endloop
        endmethod
       
        implement Initializer
    endstruct
       
    //  This function is required. Don't edit this. (functionally private)
    function InitTrig_CustomBuildSystem takes nothing returns nothing
        call ForForce(bj_FORCE_PLAYER[0], function BuildSystem.onGameStart)
    endfunction

    endlibrary
     


    Look at the create method in BuildSystem as it is the only public struct.

    All public methods are public there.

    BuilderTrace (Requirement for CustomBuildSystem)
    Code (vJASS):

    library BuilderTrace /*

        */
    requires /*
       
            *||------------||*
            */
    UnitDex      /*
                -> TriggerHappy
                   
                link: https://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
                   
                #? WorldBounds
                #? GroupUtils
            *||------------||*
           
            *||------------||*  
            */
    Table   /*  
                -> Bribe
                   
                link: https://www.hiveworkshop.com/threads/snippet-new-table.188084/#post-1835068
            *||------------||*
               
            *||------------||*
            */
    OrderMatrix  /*
                -> MyPad
                   
                link: Not yet included, will do so in the future.
                   
                # Table
               
            */
    optional TimerUtils /*
                -> Preferably by Magtheridon96, Bribe, and Vexorian.
               
            UnitDex -> Uses GetUnitId instead of GetHandleId, which is nicer in performance
            overall.
           
            Table   -> Makes attaching data to stuff a breeze.
           
            OrderMatrix ->   Stores the relevant data of an issued order in an issued order event.
           
            optional TimerUtils -> Only attach data to timers, which will clear out the first
            builder of the building.

            ----------------------------
            |Legend:                    |
            |    - #     -> Requirement |
            |    - #?    -> Optional    |
            ----------------------------
           
            ------------------------------------
            |   BuilderTrace                    |
            |   -   By MyPad                    |
            ------------------------------------
           
            Track the first builder of any building.
           
            -------------------------------------------------
                Approach:
            -------------------------------------------------
           
            Simply enumerate units with the appropriate order id in order to get the builder.
            From there, filter out the closest unit with that order id.
           
            This runs on a CONSTRUCT_START event, so we are assured that buildings created
            via CreateUnit or by training don't get included by mistake.
           
            -------------------------------------------------
                Functions:
            -------------------------------------------------
           
            function GetBuilder(unit whichBuilding) returns unit
                - Returns the first builder of the building. Will return null if the unit
                hasn't fired the CONSTRUCT_START event or 0 seconds after CONSTRUCT_FINISH,
                if the unit has fired said event.
               
            function GetBuildingUnit() returns unit
                - A wrapper function for GetBuilder(GetTriggerUnit()) which behaves like a
                native of sorts.
        */


    private keyword BuilderTraceM

    private struct BuilderTrace extends array
        private static Table      isIdBuilding = 0
        private static TableArray builderTable = 0
       
    static if not LIBRARY_TimerUtils then
        private static Table newTimerData = 0
    endif

        private static group enum         = null
        private static integer buildingId = 0
       
        private static method getUnitDist takes unit u1, unit u2 returns real
            return SquareRoot( (GetUnitX(u1) - GetUnitX(u2))*(GetUnitX(u1) - GetUnitX(u2)) + (GetUnitY(u1) - GetUnitY(u2))*(GetUnitY(u1) - GetUnitY(u2)) )
        endmethod
       
        private static method isBuildingId takes unit u returns boolean
            local integer uId = GetUnitTypeId(u)
           
            if not isIdBuilding.boolean.has(uId) then
                set isIdBuilding.boolean[uId] = IsUnitType(u, UNIT_TYPE_STRUCTURE)
            endif
            return isIdBuilding.boolean[uId]
        endmethod
       
        private static method onOrderMatchMisc takes unit u returns boolean
            return (852013 >= GetUnitCurrentOrder(u) and GetUnitCurrentOrder(u) >= 852008) or GetUnitCurrentOrder(u) == 852147
        endmethod
       
        private static method onOrderMatch takes nothing returns boolean
            local unit filterUnit = GetFilterUnit()
            local boolean result  = (GetUnitCurrentOrder(filterUnit) == buildingId) or onOrderMatchMisc(filterUnit)
           
            set filterUnit = null
            return result
        endmethod
       
        private static method onEnter takes nothing returns nothing
            local unit u            = GetTriggerUnit()
            local unit iter         = null
            local unit builder      = null
           
            local real dist         = 0.
            local real minDist      = 999999.
           
            local real orderDist    = 0.
            local real minOrderDist = 999999.
           
            local real cx           = GetUnitX(u)
            local real cy           = GetUnitY(u)
           
            if thistype.isBuildingId(u) then
                set buildingId = GetUnitTypeId(u)
               
                call GroupEnumUnitsOfPlayer(thistype.enum, GetOwningPlayer(u), Filter(function thistype.onOrderMatch))
                loop
                    set iter = FirstOfGroup(thistype.enum)
                    exitwhen iter == null
                   
                    set dist = thistype.getUnitDist(u, iter)
                    if dist < minDist then
                        set orderDist = SquareRoot((cx - OrderMatrix[iter].getTargetX())*(cx - OrderMatrix[iter].getTargetX()) + (cy - OrderMatrix[iter].getTargetY())*(cy - OrderMatrix[iter].getTargetY()))
                        if orderDist < minOrderDist then
                            set builder = iter
                            set minDist = dist
                            set minOrderDist = orderDist
                           
                            exitwhen minOrderDist <= 0
                        endif
                    endif
                   
                    call GroupRemoveUnit(thistype.enum, iter)
                endloop
            endif
           
            set thistype.builderTable[1].unit[GetUnitId(u)] = builder
                   
            set builder = null
            set u       = null
        endmethod
       
        private static method onClearData takes nothing returns nothing
            local timer t       = GetExpiredTimer()
            local integer data
           
        static if LIBRARY_TimerUtils then
            set data = ReleaseTimer(t)
        else
            set data = thistype.newTimerData.integer[GetHandleId(t)]
           
            call thistype.newTimerData.integer.remove(GetHandleId(t))
            call DestroyTimer(t)
        endif
           
            call thistype.builderTable[1].unit.remove(data)
            call thistype.builderTable[2].boolean.remove(data)
           
            set t = null
        endmethod
       
        private static method onCancel takes nothing returns nothing
            local unit u    = GetTriggerUnit()
            local timer t
           
            if (GetTriggerEventId() != EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL) and (GetTriggerEventId() != EVENT_PLAYER_UNIT_CONSTRUCT_FINISH) then
                set u = GetIndexedUnit()
            endif
           
        static if LIBRARY_TimerUtils then
            if not thistype.builderTable[2].boolean.has(GetUnitId(u)) then
                set t = NewTimerEx(GetUnitId(u))
            endif
        else
            set t = CreateTimer()
            set thistype.newTimerData.integer[GetHandleId(t)] = GetUnitId(u)      
        endif
           
            if not thistype.builderTable[2].boolean[GetUnitId(u)] then
                call TimerStart(t, 0., false, function thistype.onClearData)
                set thistype.builderTable[2].boolean[GetUnitId(u)] = true
            endif
           
            set t = null
            set u = null
        endmethod
       
        private static method onFinish takes nothing returns nothing
            call thistype.onCancel()
        endmethod
       
        private static method initVars takes nothing returns nothing
            set thistype.isIdBuilding = Table.create()
            set thistype.builderTable = TableArray[3]
           
        static if not LIBRARY_TimerUtils then
            set thistype.newTimerData = Table.create()
        endif
       
            set thistype.enum         = CreateGroup()
        endmethod
       
        private static method initHandlers takes nothing returns nothing
            local trigger array t
           
            set t[0] = CreateTrigger()
            set t[1] = CreateTrigger()
            set t[2] = CreateTrigger()
           
            call TriggerRegisterAnyUnitEventBJ(t[1], EVENT_PLAYER_UNIT_CONSTRUCT_START)
            call TriggerRegisterAnyUnitEventBJ(t[0], EVENT_PLAYER_UNIT_CONSTRUCT_CANCEL)
            call TriggerRegisterAnyUnitEventBJ(t[2], EVENT_PLAYER_UNIT_CONSTRUCT_FINISH)
           
            call TriggerRegisterUnitIndexEvent(t[0], EVENT_UNIT_DEINDEX)
           
            call TriggerAddCondition(t[1], function thistype.onEnter)
            call TriggerAddCondition(t[0], function thistype.onCancel)
            call TriggerAddCondition(t[2], function thistype.onFinish)
           
            set t[2] = null
            set t[1] = null
            set t[0] = null
        endmethod
       
        private static method init takes nothing returns nothing
            call initVars()      
            call initHandlers()
        endmethod
       
        static method getBuilder takes unit u returns unit
            return thistype.builderTable[1].unit[GetUnitId(u)]
        endmethod
       
        implement BuilderTraceM
    endstruct

    private module BuilderTraceM
        private static method onInit takes nothing returns nothing
            call init()
        endmethod
    endmodule

    function GetBuilder takes unit building returns unit
        return BuilderTrace.getBuilder(building)
    endfunction

    function GetBuildingUnit takes nothing returns unit
        return GetBuilder(GetTriggerUnit())
    endfunction

    endlibrary
     


    OrderMatrix (Actually, it is already up and running here, so I'll just leave the link)
    Link: Here
     
    Last edited: Jun 23, 2018
  10. Kyrbi0

    Kyrbi0

    Joined:
    Jul 29, 2008
    Messages:
    7,899
    Resources:
    1
    Models:
    1
    Resources:
    1
    Continue The Memes

    Never Stop The Memes of Production
     
  11. xYours Trulyx

    xYours Trulyx

    Joined:
    Jan 9, 2016
    Messages:
    1,004
    Resources:
    0
    Resources:
    0
    I hope this will be the last time I update my race. Made a few changes to the units' abilities.

    Now whip me up with some of your WIPs so I don't feel like a loner.
     
  12. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,320
    Resources:
    7
    Models:
    1
    Icons:
    2
    Spells:
    3
    JASS:
    1
    Resources:
    7
    I have been working on the script for some time. If you like, see my previous post on which WIPS you would like to see.

    I got a lot of libraries coming up there. They are functional.
     
    Last edited: Jun 23, 2018
  13. EternalOne

    EternalOne

    Joined:
    Feb 10, 2016
    Messages:
    49
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Tell you what, I'll drop a third WIP screenshot. Here's a somewhat-battered army of Shadefrost Nerubians fighting against their natural enemy, the Undead.
    Stingers, Injectors, and a pair of Heroes.
    Warcraft III 6_23_2018 10_43_41 PM.png
     
  14. Kyrbi0

    Kyrbi0

    Joined:
    Jul 29, 2008
    Messages:
    7,899
    Resources:
    1
    Models:
    1
    Resources:
    1
    Interesting interesting! Spider-people are best-people.
     
  15. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,495
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    So it looks like Direfury and I are droppin' out. I'm totally not diggin' the theme :p hopefully #13 is better!
     
  16. Retera

    Retera

    Tool Reviewer

    Joined:
    Apr 19, 2008
    Messages:
    818
    Resources:
    25
    Models:
    17
    Tools:
    2
    Maps:
    6
    Resources:
    25
    Seems unfortunate to lose people. I've been working with Spellbound, and he's ahead of me in terms of team work done and progress, but I've started getting things done to contribute on the weekends.

    Ya gotta look inside yourself. (Isn't that a quote from somewhere?)
     
  17. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,620
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    I'll probably join in within a few days, just have some uni stuff to tend to.
     
  18. EternalOne

    EternalOne

    Joined:
    Feb 10, 2016
    Messages:
    49
    Resources:
    1
    Maps:
    1
    Resources:
    1
    Well, here's my first draft:
    Banner.png
    When Azjol-Nerub fell to the Undead Scourge, a select group of Nerubians chose, rather than defending their home to the death, to flee past the Icecrown Glacier. There, in the shadows of the icy mountains, they carved out a series of tunnels in the Shadefrost Ice Sheet, eventually burrowing even further down into the rocky depths. With their technology and craftsmanship so far behind them, these Nerubians realized that they would need to change their way of life in order to stand a chance at revenge. Substituting their flesh for their masonry, they began to experiment and alter their own physiology, allowing them to breed at even greater paces than before, and even mutating themselves into more monstrous and animalistic forms. Capturing and breeding a nest of Arachnathids to round out their forces, these new Shadefrost Nerubians have at last surfaced and begun spreading throughout the world, determined to drive all who stand before them, especially the hated Undead, to total extinction.
    Using the Shadefrost Nerubians


    • The Shadefrost Nerubians use only six structures:
      1. Bastion (main base with 2 upgraded forms, and also standard upgrades)
      2. Ziggurat (food source, upgrades into 2 different towers)
      3. Altar of Shadefrost (makes heroes)
      4. Reliquary (item shop and lumber dropoff)
      5. Sculptuary (enables spellcasters and upgrades them)
      6. Hatchery (creates Arachnathid eggs, has upgrades)
      Aside from the Altar and Hatchery, you won't use structures to build your army. Instead, the majority of your units will either be laid in an egg by one of your units, or evolve in a cocoon from one of your units. You can easily tell the eggs and cocoons apart by their color.
      The basic units of your army evolve into each other in a linear fashion. Some of the more specialized troops will either branch off of that line, or be acquired in a different way.
      The Ziggurat and Bastion are summoned by your basic Melee and Ranged unit, respectively. The other structures will require you to sacrifice one of your worker units.

    • Tier 1 Units

      Collector.png Vanguard.png Stinger.png
      Laid by Vanguard Evolves from Collector Evolves from Vanguard
      Total cost: 75 gold 1 food Total cost: 140 gold 2 food Total cost: 200 gold 35 lumber 3 food
      The basic worker unit of the Shadefrost Nerubians.
      • Evolve to Vanguard: Spend 65 gold to evolve this unit into a Vanguard.
      • Summon Foundation: Places a foundation at a target location. Once the foundation is placed, this unit can be sacrificed to summon a structure into the foundation.
      • Restore: Repairs mechanical units and structures. Costs resources.
      • Gather: Mines gold from gold mines. Can't harvest lumber.
      Primary troop and lumber harvester.
      • Lay Collector: Spend 75 gold to lay an egg that hatches into a Collector.
      • Evolve to Stinger: Spend 60 gold and 35 lumber to evolve this unit into a Stinger.
      • Evolve to Bombarder: Spend 75 gold and 40 lumber to evolve this unit into a Bombarder. Requires you to upgrade your Bastion into a Bulwark first.
      • Summon Ziggurat: Spend 125 gold and 50 lumber to summon a Ziggurat at a target location. Ziggurats provide 8 food, and can be upgraded into towers.
      • Gather: Harvests lumber from trees. This harvesting is at double speed, like how Ghouls do it.
      Basic ranged troop.
      • Lay Corruptor: Spend 130 gold and 20 lumber to lay an egg that hatches into a Corruptor. Requires a Sculptuary.
      • Evolve to Brood Queen: Spend 80 gold and 20 lumber to evolve this unit into a Brood Queen. Requires a Hatchery.
      • Summon Bastion: Spend 385 gold and 185 lumber to summon a Bastion at a target location. Bastions act as both Town Hall and Blacksmith for Shadefrost Nerubians, allowing resources to be dropped off and core upgrades to be completed.
      • Poison Sting: Deals minor damage over time to organic enemies.
      • Web: Binds air enemies to the ground. Requires a research from the Hatchery.

      Tier 2 Units (Upgrade Bastion to Bulwark)

      Bombarder.png Corruptor.png Fireshaper.png
      Evolves from Vanguard Laid by Stinger Evolves from Corruptor
      Total cost: 215 gold 40 lumber 3 food Total cost: 130 gold 20 lumber 2 food Total cost: 180 gold 70 lumber 3 food
      Basic flying unit with a ranged attack.
      Primary spellcaster.
      • Parasite: Deals 5 damage per second. If a unit dies with this buff, it spawns a Scarab.
      • Shadow Blend: Turns a unit invisible, while slowing and regenerating it. The unit becomes visible while attacking or spellcasting, but only for a moment. Requires Adept training.
      • Merge: Permanently combines with an Arachnathid to form an Injector. Requires Master training.
      • Evolve to Fireshaper: Spend 50 gold and 50 lumber to evolve this unit into a Fireshaper. Requires a Reliquary.
      Long range siege unit with splash damage. Can't attack at close range.
      • Permanent Immolation: Burns nearby enemies for 10 damage per second.
      Brood Queen.png Seer.png Arachnathid.png
      Evolves from Stinger Laid by Brood Queen Laid by Hatchery
      Total cost: 280 gold 55 lumber 4 food Total cost: 160 gold 20 lumber 2 food Total cost: 200 gold 80 lumber 3 food
      Tactical ranged unit that can be upgraded to serve a siege role.
      • Lay Seer: Spend 160 gold and 20 lumber to lay an egg that hatches into a Seer. Requires a Sculptuary.
      • Brood Spawn: Summons 2 Scarabs. These Scarabs can help divert damage from your more important units.
      • Poison Sting: Deals minor damage over time to organic enemies.
      Support spellcaster.
      • Inner Frost: Increases a friendly unit's attack damage, and regenerates their mana when nearby units cast spells.
      • Spell Shield: Removes negative buffs from friendly units, and heals them by 25 per buff. Also blocks the next spell cast on those units, but this shield only lasts 20 seconds. Requires Adept training.
      • Liquid Energy: Speeds up friendly units, but disables their abilities. Requires Master training.
      • True Sight: Can see invisible and burrowed units.
      Armored short-range unit. Its eggs are laid by the Hatchery, instead of by a Shadefrost Nerubian unit.
      • Noxious Blast: Deals damage over time to a target and nearby enemies.
      • Burrow: Digs underground, becoming invisible and regenerating faster. Requires a research from the Hatchery.
      • Merge: Permanently combines with a Corruptor to become an Injector. Requires Corruptor Master Training.

      Tier 3 Units (Upgrade Bulwark to Zacuali)

      Injector.png Sky Palace.png
      Merge Corruptor and Arachnathid Built by Collector
      Total cost: 330 gold 100 lumber 5 food Total cost: 375 gold 175 lumber 8 food
      Fast and heavily armored melee attacker. Complicated to make, but superior in ground combat.
      • Noxious Blast: Deals damage over time to a target and nearby enemies, and slows them.
      • Burrow: Digs underground, becoming invisible and regenerating faster. Requires a research from the Hatchery.
      Heavily armored anti-air structure with a much weaker attack versus ground units.
      • Genocide Gas: Damages all units, friend or foe, underneath this unit. Ignores spell immunity. Requires a research from the Sculptuary.



    • Heroes.png
      Warrior Hero, skilled at defending allied units and absorbing damage.
      Mystical Hero, counterpart to the Undead Lich.
      Cunning Hero, adept at manipulating large battles.
      Vicious Hero, capable of quickly killing groups of enemies and chasing down important targets.
      Ice Fissure.png Ice Fissure
      Warp Bolt.png Warp Bolt
      Scream.png Scream
      Pursuit.png Pursuit
      Can be cast two different ways:
      Target a point to create an arc of ice, dealing damage to all enemy ground units within.
      Target an enemy ground unit to create a cross of ice, dealing double damage to the target and half damage to all other enemy ground units within.
      Either way, all units damaged by this spell have their attack damage reduced for 10 seconds.
      • Level 1 - 50 base damage, 10% damage reduction.
      • Level 2 - 100 base damage, 20% damage reduction.
      • Level 3 - 150 base damage, 30% damage reduction.
      Short-range blast of darkness that reduces the movement and attack speed of the target and all nearby hostile units.
      • Level 1 - Slows movement speed by 30% and attack speed by 30% for 10 seconds.
      • Level 2 - Slows movement speed by 40% and attack speed by 45% for 12 seconds.
      • Level 3 - Slows movement speed by 50% and attack speed by 60% for 14 seconds.
      The Venom Widow lets loose a powerful blast of sound in a target area. For 10 seconds, friendly units in the area gain armor and life regeneration, while hostile units are slowed.
      • Level 1 - 3 armor and 5 life per second, 30% attack rate.
      • Level 2 - 4 armor and 7 life per second, 40% attack rate.
      • Level 3 - 5 armor and 10 life per second, 50% attack rate.
      The Predator digs into the ground and tunnels towards a target, damaging enemy ground units in the way and pushing them aside. If the Predator hits an enemy ground unit head-on at the end of the tunnel, that unit takes extra damage and is stunned for 3 seconds.
      • Level 1 - 25 damage during the tunnel, 75 damage at the end.
      • Level 2 - 50 damage during the tunnel, 150 damage at the end.
      • Level 3 - 75 damage during the tunnel, 225 damage at the end.
      Coiling Darkness.png Coiling Darkness
      Torment.png Torment
      Venom Stream.png Venom Stream
      Crimson Energy.png Crimson Energy
      Teleports friendly units in the target area to the Guardian one by one, healing them in the process. The amount of healing increases with each teleport. Each unit teleported costs an extra 25 mana, and the spell will stop if the Guardian runs out of mana.
      • Level 1 - First heal is for 50 life, adds 10 each time.
      • Level 2 - First heal is for 80 life, adds 20 each time.
      • Level 3 - First heal is for 130 life, adds 30 each time.
      Strikes a target unit with a bolt of dark lightning, dealing damage based on the number of debuffs the target has.
      If the target has a debuff that stuns, slows, or snares it, nearby hostile units will flee in fear, unable to fight.
      • Level 1 - 100 base damage, 50 per debuff. Fear lasts for 5 seconds.
      • Level 2 - 150 base damage, 75 per debuff. Fear lasts for 7 seconds.
      • Level 3 - 200 base damage, 100 per debuff. Fear lasts for 10 seconds.
      Periodically causes the Venom Widow's attack to unleash a blast of venom at enemy units in a line that deals 20 initial damage, and 5 damage per second over 5 seconds. Drains mana for each blast.
      • Level 1 - 5 second cooldown.
      • Level 2 - 4 second cooldown.
      • Level 3 - 3 second cooldown.
      Increases the Hero's attack rate for a short amount of time. If the Hero kills an enemy unit while this spell is in effect, its cooldown will be reset.
      • Level 1 - 40% bonus for 8 seconds.
      • Level 2 - 100% bonus for 10 seconds.
      • Level 3 - 175% bonus for 12 seconds.
      Inciting Aura.png Inciting Aura
      Soul Haze Aura.png Soul Haze Aura
      Mana Bleed.png Mana Bleed
      Draining Whirl.png Draining Whirl
      Increases the life regeneration rate of nearby friendly units. When an enemy attacks a unit under the effect of this aura, they have a chance to be forced to attack the Guardian.
      • Level 1 - 5% chance to taunt, minor regeneration bonus.
      • Level 2 - 10% chance to taunt, moderate regeneration bonus.
      • Level 3 - 15% chance to taunt, greater regeneration bonus.
      Increases the mana regeneration of nearby friendly units when nearby friendly units are being attacked. When a nearby friendly unit dies, the Dark Elder will have the cooldown of one of his abilities reset.
      • Level 1 - Lesser mana regeneration.
      • Level 2 - Moderate mana regeneration.
      • Level 3 - Greater mana regeneration.
      When the Hero is attacked by a unit with mana, a small amount of mana will be transferred from that unit to the Hero. Also increases the Hero's Intelligence.
      • Level 1 - Transfers 2 mana, 3 bonus Intelligence.
      • Level 2 - Transfers 4 mana, 4 bonus Intelligence.
      • Level 3 - Transfers 6 mana, 5 bonus Intelligence.
      Gives a 15% chance with each attack to deal damage to all nearby ground enemies. Heals the Predator for half of the damage dealt to non-mechanical units.
      • Level 1 - 25 damage.
      • Level 2 - 60 damage.
      • Level 3 - 100 damage.
      Summon Ice Titan.png Summon Ice Titan
      Abyss Well.png Abyss Well
      Terminal Injection.png Terminal Injection
      Blood Storm.png Blood Storm
      Summons a powerful Ice Titan. The Ice Titan is immune to magic, has Bash, and deals Siege damage. It can also use its mana supply to heal your Heroes and restore their mana.
      Lasts 120 seconds.

      If the Guardian has Inciting Aura, and the Ice Titan is standing in it, units that attack the Guardian have a chance to be taunted by the Ice Titan.
      Creates a sphere of darkness that damages everything based on how close it is to the center. Units that get too close are pulled in.
      The sphere and its area grow as more damage is dealt, and it will slowly move towards the Dark Elder. Be careful not to channel the spell for so long that the caster is killed!
      Injects one of your ground units with a powerful and dangerous stimulant. The stimulant increases the unit's attack rate by 75%, makes it move faster, causes it to regenerate 10 life per second, and makes it immune to magic for 1 minute. When the stimulant wears off, the unit dies instantly.
      Cannot be used on Heroes or Mechanical units.
      Unleashes a whirling storm of blood towards a target point. The storm's target point can be changed during the spell's duration. After reaching its target, the storm will move towards the Predator until it is re-targeted. Enemy units caught in the storm will take 100 damage per second.
      Lasts 12 seconds.

    • The basics

      • The total amount of time that it takes to make a Shadefrost Nerubian unit is longer than normal. However, you can turn this around through clever micromanagement. For instance, to get a Vanguard faster, you can have a Vanguard lay a Collector egg, then immediately have one of your Collectors evolve into a Vanguard. Now, instead of a 27 second turn-around time, you get your Vanguard in 7 seconds and replace your Collector in 20 seconds!
      • Time your egg-laying and evolution around your battles! Before going into battle, have your units lay eggs to replenish the forces you will lose. After battle, have your injured units evolve, and the resulting units will be at full health. If you've researched Resilient Shells, you can do some of this mid-battle, since your normally Unarmored eggs and cocoons will get Fortified armor.
      • Usually, the Shadefrost Nerubians want their Heroes close to the front line, taking damage in place of the regular units. Be sure to keep your Heroes stocked with healing items!
      • If you wall off part of your base using your Ziggurats, be sure to summon your Altar of Shadefrost and your Hatchery outside of the wall. Otherwise, the units they produce may become trapped.

      Vanguard

      • Vanguards are the real key unit to maintaining your army, even if you're not putting them on the front lines. Keep on laying more Collectors, and keep on evolving those Collectors into Vanguards, to ensure you have a steady supply of troops.
      • You usually don't want Vanguards in your army during Tier 2, since Arachnathids and Brood Queens can handle the front better. At Tier 3, on the other hand, the Vicious Claws upgrade makes them relevant again.

      Stinger

      • Be choosy about when you have Web turned on. If you have Sky Palaces in the back, it's better not to Web weaker flying units, since the Palaces can obliterate them in the air. You might still want to Web enemy Frost Wyrms and Dragonhawk Riders, since they are strong against Sky Palaces.
      • If your opponent isn't using Mechanical units, a hit-and-run strategy can help you get the most out of Poison Sting, especially once you've upgraded it to slow enemies.

      Bombarder

      • Bombarders take a while to hatch, and they're most effective when you have a lot of them, so maintaining a large supply of Vanguards to transition into them is essential.
      • Bombarders attack slowly, so watch your timing if you're doing a hit-and-run with them.

      Corruptor

      • Shadow Blend is a very useful tactical spell, especially for units that are slow and vulnerable. Using it on your Fireshapers can make your siege attacks very difficult for enemies to deal with, for instance. Don't waste Shadow Blend on cheap units, because it isn't worth it.
      • Keep in mind that Merge takes 100 Mana to use. If it all gets spent on Parasites, you might have a hard time transitioning into Injectors. You can mitigate this problem by getting the Magenesis upgrade.

      Fireshaper

      • Permanent Immolation might not seem very useful for a siege unit, but it can help prevent the Fireshaper from being picked off by enemy melee units, if you think fast. Use your other troops to pin the melee units close to the Fireshaper when they move in to attack it, then use the added damage to focus them down.

      Brood Queen

      • Brood Spawn might be on Autocast, but sometimes you can time your Scarabs better by casting it manually.
      • Scarabs will chase enemies down, but eventually they will come back to the place they were summoned. Since they're expendable, it's worth taking a moment to select them all and order them to attack, so they won't return to you at an inopportune moment.
      • The Ram upgrade can let you use Brood Queens as a siege unit, but this will backfire against Orcs with the Spiked Barricades upgrade. If you still must use them this way, try having just one Brood Queen ram the building while a bunch of your ranged units (such as Fireshapers) hit it from safety.

      Seer

      • It's usually better to cast Spell Shield on your units right before battle, even if you miss out on some healing this way. Your opponent will have a harder time picking targets that don't have a shield amidst the crowd.
      • Inner Frost is useful and all, but try to save some mana for Liquid Energy! Liquid Energy is especially useful for empowering units that don't rely on abilities, such as Vanguards and Scarabs.

      Arachnathid

      • Hit your enemies early with Noxious Blast, so that the damage will have as much time as possible to pile up.
      • Despite having a ranged attack, Arachnathids work great on the front line. Their armor is high, and if they take too much damage, you can just Burrow them and let them regenerate in safety.

      Injector

      • Injectors might be a pain to get, but make no mistake: they are the true masters of melee combat, especially once the Vicious Claws research is complete. Hit the enemy with the improved version of Noxious Blast to keep them from escaping, then just let their attacks do the rest.
      • Burrow is even more important for Injectors, since that's a more expensive unit that you're rescuing. Keep an eye on their life, and don't be afraid to pull them out of combat if things are looking dicey.

      Sky Palace

      • This is the only Mechanical unit that the Shadefrost Nerubians use, and it's worth repairing them if they get battered. Each Palace is a big investment on your part. If you're a StarCraft player, think of them as your Battlecruisers.
      • Sky Palaces are the only flying unit that has Heavy armor, which means that traditional anti-air isn't all that effective against them. If your opponent tries to focus-fire a Sky Palace with Archers or something, fly right on top of them and use Genocide Gas. You should survive long enough to destroy them.



    This is the second version of the Shadefrost Nerubians, and the map file is attached. I don't have a replay for it right this second, but I can create another if there's demand for it. This is most likely my final version, unless something unforseen comes up.
    I'm working on a campaign-style map that teaches how to play as the Shadefrost Nerubians, but it won't be done by the end date. It's not part of the contest, anyway, but it might be fun for some.

    Update:
    Credits

    alfredx_sotn:
    Chaos Spider Model
    Nerubian Egg Icon
    Nerubian Egg Model
    Nerubian Stinger Model
    AlienAtSystem:
    Circle Buffs Model
    Alok:
    Nerubian Worker Model
    -Berz-:
    Frost Spell Icon
    Nether Bolt Icon
    Blood Raven:
    Black Pendant Icon
    Burning_Dragoon5:
    Ice Elemental Model
    Nerubian Flying Bombarder Model
    Callahan:
    Nerubian Guard Model
    Nerubian Widow Model
    Radioactive Cloud Model
    CRAZYRUSSIAN:
    Blood Pool Icon
    Frost Shock Icon
    CreatorD3292:
    Energy Shield Model
    crl:
    Blood Cloud Model
    Daelin:
    Frozen Mana Model
    dansaDisco:
    Water Stuff Icon
    Darkfang:
    Arcane Blast Icon
    Hatchery Egg Icon
    Monstrous Armor and Nerubian Chitin Icons (co-credit Blizzard)
    Volatile Waters Icon (co-credit Blizzard)
    D.ee:
    Mana Breathe Icon
    dhguardianes:
    Green Ring Model
    flying sheep:
    Spider Strength 2 Icon
    Ginufe:
    Poison Dagger Icon
    -Grendel:
    Crypt Mother Icon
    Crypt Mother Model
    Nerubian Elder Model
    Nerubian Infector Model
    Nerubian Youngling Model
    Hellx-Magnus:
    Eye Of Shadows Icon
    Nerubian Scepter Icon
    Plague Nova Icon
    Hermit:
    Empathic Bond Model (co-credit Blizzard)
    infrenus:
    Plague Spreader Icon and Model
    JesusHipster:
    Venomous Gale Model and Buff Model
    JetFangInferno:
    Aqua Spike Model
    Blood Tornado Model
    Dark Lightning Model
    JollyD:
    Green Stone Icon
    kellym0:
    Acid Spew Model
    KelThuzad:
    Sonic Blast Icon
    Kenntaur:
    Blood Beetle Model
    Kino:
    Instant Whirl Model
    -Kobas-:
    Mana Orb Icon
    kola:
    Arcane Bolt Icon
    Lelling:
    Noxious Fumes Icon
    Marcos DAB:
    Conjuring Webs Icon
    Scaled Egg Icon
    MatiS:
    Nerubian Building Pack
    Muoteck:
    Nerubian Widow Icon
    NFWar:
    Acid Bottle Icon
    NightSkyAurora:
    Cosmic Insperation (sic) Model
    Nudl9:
    Tremor Icon
    OgeRfaCes:
    Purple Orb Icon
    Paladon:
    Blind Eye Icon
    Palaslayer:
    Blade Storm Icon
    PeeKay:
    Acid Stinger Icon
    Law of Blood Icon
    Law of Blood Model
    Magic Shield Icon
    Nerubian Elder Icon
    Punish Icon
    Punish Model
    Tiger Strike Icon
    Viper Icon
    Pyritie:
    Darkness High and Uber Models
    s4nji:
    Ice Incinerate Buff Model
    Sellenisko:
    Dark Elf Aura Icon
    Dark Elf Aura Model
    The Panda:
    Ice Fissure Icon
    Nerubian Icon Pack
    Saturation Icon
    Sparkle Icon
    Thrikodius:
    Warp Dark Caster Model
    Warp Dark Target Model
    UgoUgo:
    Orb of Corruption Model
    Water Enchant and Buff Models
    valkemiere:
    Ice Slam Model
    VaLkYroN:
    Total Darkness Icon
    Vortigon:
    Dark Orb Model
    WaremDarkslayer:
    Watery Fog Model
    WILL THE ALMIGHTY:
    Black Hole Model
    Windrunner29:
    Nerubian Building Icon Pack
    xyzier_24:
    Void Skull Aura Model

    The rest is either made by me or Blizzard.

    Update 2:
    8/7/2018 Tweaks

    Shadefrost Foundations will now vanish if the Collector that made them is killed before it can be sacrificed.
    If you try to select a Shadefrost Foundation, it will select the Collector that made it instead.
    Made some balance changes:
    • Arachnathid and Injector upgrades nerfed. Burrow takes longer to research, and Enhanced Metabolism doesn't add as much mana regen.
    • Bombarders deal piercing damage, have a faster attack, and have a few more HP.
    • Brood Queen deals less damage with her ranged attack, and no longer regenerates faster than other units.
    • Fireshaper attacks faster.
    • Stinger attacks more slowly and does less damage.
    • Vanguard has an extra point of armor.
    • Heroes other than Dark Elder have less base attack speed.
    • Predator deals slightly less damage with each attack.
    • Venom Widow and Guardian start with slightly higher attributes.
     

    Attached Files:

    Last edited: Aug 8, 2018
  19. Kyrbi0

    Kyrbi0

    Joined:
    Jul 29, 2008
    Messages:
    7,899
    Resources:
    1
    Models:
    1
    Resources:
    1
    I really like your presentation, but even the desktop version of the site doesn't show the full images; they are too big (maybe?).
     
  20. EternalOne

    EternalOne

    Joined:
    Feb 10, 2016
    Messages:
    49
    Resources:
    1
    Maps:
    1
    Resources:
    1
    I'm looking at it from my desktop right now, and I can see the full images. I cropped most of them down to help them fit on the page. The unit images are supposed to be 256x256 squares, and that's how I see them. If you're seeing something different, maybe take a screenshot and I can try to figure out what's amiss.
     
Thread Status:
Not open for further replies.