1. Fill your cup and take your pick among the maps best suited for this year's Hive Cup. The 6th Melee Mapping Contest Poll is up!
    Dismiss Notice
  2. Shoot to thrill, play to kill. Sate your hunger with the 33rd Modeling Contest!
    Dismiss Notice
  3. Do you hear boss music? It's the 17th Mini Mapping Contest!
    Dismiss Notice
  4. 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.

[Lua] GetMainSelectedUnit

Discussion in 'Submissions' started by Tasyen, Jun 7, 2020.

  1. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,611
    Resources:
    21
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    4
    JASS:
    1
    Resources:
    21
    A System using the new Framechild natives (added in 1.32.6) to know the current main selected Unit.

    How this works?
    This System finds the index of the first shown yellow groupunitbutton background, this number is taken. Then the current selected units are enumed and ordered in the way the game displays them, this 2 combined are the mainselected unit.

    When one has the System code in your map use GetMainSelectedUnitEx() to get the mainselected unit.
    Code (Lua):

    do
        -- returns the local current main selected unit, using it in a sync gamestate relevant manner breaks the game.
        function GetMainSelectedUnitEx()
            return GetMainSelectedUnit(GetSelectedUnitIndex())
        end
       

        local containerFrame
        local frames = {}
        local group
        local units = {}
        local filter = Filter(function()
            local unit = GetFilterUnit()
            local prio = BlzGetUnitRealField(unit, UNIT_RF_PRIORITY)
            local found = false
            -- compare the current unit with allready found, to place it in the right slot
            for index, value in ipairs(units) do
                -- higher prio than this take it's slot
                if BlzGetUnitRealField(value, UNIT_RF_PRIORITY) < prio then
                    table.insert(units, index, unit)
                    found = true
                    break
                -- equal prio and better colisions Value
                elseif BlzGetUnitRealField(value, UNIT_RF_PRIORITY) == prio and GetUnitOrderValue(value) > GetUnitOrderValue(unit) then
                    table.insert( units, index, unit)
                    found = true
                    break
                end
            end
            -- not found add it at the end
            if not found then
                table.insert(units, unit)
            end

            unit = nil
            return false
        end)

       
        function GetSelectedUnitIndex()
            -- local player is in group selection?
            if BlzFrameIsVisible(containerFrame) then
                -- find the first visible yellow Background Frame
                for int = 0, #frames do
                    if BlzFrameIsVisible(frames[int]) then
                        return int
                    end          
                end
            end

            return nil
        end

        function GetUnitOrderValue(unit)
            --heroes use the handleId
            if IsUnitType(unit, UNIT_TYPE_HERO) then
                return GetHandleId(unit)
            else
            --units use unitCode
            return GetUnitTypeId(unit)
            end
        end


        function GetMainSelectedUnit(index)
            if index then
                GroupEnumUnitsSelected(group, GetLocalPlayer(), filter)
                local unit = units[index + 1]
                --clear table
                repeat until not table.remove(units)
                return unit
            else
                GroupEnumUnitsSelected(group, GetLocalPlayer(), nil)
                return FirstOfGroup(group)
            end
        end

        --init
        do
            local real = MarkGameStarted
          function MarkGameStarted()
                real()
            local console = BlzGetFrameByName("ConsoleUI", 0)
            local bottomUI = BlzFrameGetChild(console, 1)
            local groupframe = BlzFrameGetChild(bottomUI, 5)
            --globals
            containerFrame = BlzFrameGetChild(groupframe, 0)
            group = CreateGroup()
            -- give this frames a handleId
            for int = 0, BlzFrameGetChildrenCount(containerFrame) - 1 do
                local buttonContainer = BlzFrameGetChild(containerFrame, int)
                frames[int] = BlzFrameGetChild(buttonContainer, 0)
            end
          end
        end
     
    A example code: it prints the name of the current main Selected Unit once per second
    Code (Lua):
    --demo
        do
            local real = MarkGameStarted
          function MarkGameStarted()
                real()
            TimerStart(CreateTimer(), 1, true, function()
                local u = GetMainSelectedUnit(GetSelectedUnitIndex())
                if IsUnitType(u, UNIT_TYPE_HERO) then
                    print(GetUnitName(u), GetHeroProperName(u))
                else
                    print(GetUnitName(u))
                end
            end)
          end
        end
    end
    Edited: Replaced selfexecution with a less problematic approach
     
    Last edited: Sep 22, 2020
  2. Proshel_Doty

    Proshel_Doty

    Joined:
    May 5, 2013
    Messages:
    2
    Resources:
    0
    Resources:
    0
    Hi, @Tasyen, could you show what this code should look like on JASS?
     
  3. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,611
    Resources:
    21
    Tools:
    2
    Maps:
    3
    Spells:
    11
    Tutorials:
    4
    JASS:
    1
    Resources:
    21
    It could look somehow like this, haven't done so much testing with it.
    Code (vJASS):

    library GetMainSelectedUnit initializer init_function

    globals
        private framehandle containerFrame
        private framehandle array frames
        private group Group = CreateGroup()
        private unit array units
        private integer unitsCount = 0
        private filterfunc filter
    endglobals

    function GetUnitOrderValue takes unit u returns integer
        //heroes use the handleId
        if IsUnitType(u, UNIT_TYPE_HERO) then
            return GetHandleId(u)
        else
        //units use unitCode
            return GetUnitTypeId(u)
        endif
    endfunction

    private function FilterFunction takes nothing returns boolean
        local unit u = GetFilterUnit()
        local real prio = BlzGetUnitRealField(u, UNIT_RF_PRIORITY)
        local boolean found = false
        local integer loopA = 1
        local integer loopB = 0
        // compare the current u with allready found, to place it in the right slot
        loop
            exitwhen loopA > unitsCount
            if BlzGetUnitRealField(units[loopA], UNIT_RF_PRIORITY) < prio then
                set unitsCount = unitsCount + 1
                set loopB = unitsCount
                loop
                    exitwhen loopB <= loopA
                    set units[loopB] = units[loopB - 1]
                    set loopB = loopB - 1
                endloop
                set units[loopA] = u
                set found = true
                exitwhen true
            // equal prio and better colisions Value
            elseif BlzGetUnitRealField(units[loopA], UNIT_RF_PRIORITY) == prio and GetUnitOrderValue(units[loopA]) > GetUnitOrderValue(u) then
                set unitsCount = unitsCount + 1
                set loopB = unitsCount
                loop
                    exitwhen loopB <= loopA
                    set units[loopB] = units[loopB - 1]
                    set loopB = loopB - 1
                endloop
                set units[loopA] = u
                set found = true
                exitwhen true
            endif
            set loopA = loopA + 1
        endloop
       
        // not found add it at the end
        if not found then
            set unitsCount = unitsCount + 1
            set units[unitsCount] = u
        endif

        set u = null
        return false
    endfunction

        function GetSelectedUnitIndex takes nothing returns integer
            local integer i = 0
            // local player is in group selection?
            if BlzFrameIsVisible(containerFrame) then
                // find the first visible yellow Background Frame
                loop
                    exitwhen i > 11
                    if BlzFrameIsVisible(frames[i]) then
                        return i
                    endif
                    set i = i + 1
                endloop
            endif
            return -1
        endfunction  

        function GetMainSelectedUnit takes integer index returns unit
            if index >= 0 then
                call GroupEnumUnitsSelected(Group, GetLocalPlayer(), filter)
                set bj_groupRandomCurrentPick = units[index + 1]
                //clear table
                loop
                    exitwhen unitsCount <= 0
                    set units[unitsCount] = null
                    set unitsCount = unitsCount - 1
                endloop
                return bj_groupRandomCurrentPick
            else
                call GroupEnumUnitsSelected(Group, GetLocalPlayer(), null)
                return FirstOfGroup(Group)
            endif
        endfunction

        // returns the local current main selected unit, using it in a sync gamestate relevant manner breaks the game.
        function GetMainSelectedUnitEx takes nothing returns unit
            return GetMainSelectedUnit(GetSelectedUnitIndex())
        endfunction

        private function init_functionAt0s takes nothing returns nothing
            local integer i = 0
            local framehandle console = BlzGetFrameByName("ConsoleUI", 0)
            local framehandle bottomUI = BlzFrameGetChild(console, 1)
            local framehandle groupframe = BlzFrameGetChild(bottomUI, 5)
            local framehandle buttonContainer
            //globals
            set containerFrame = BlzFrameGetChild(groupframe, 0)
            set Group = CreateGroup()
            // give this frames a handleId
            loop
                exitwhen i >= BlzFrameGetChildrenCount(containerFrame) - 1
                set buttonContainer = BlzFrameGetChild(containerFrame, i)
                set frames[i] = BlzFrameGetChild(buttonContainer, 0)
                set i = i + 1
            endloop
            call DestroyTimer(GetExpiredTimer())
        endfunction

        private function demoFunction takes nothing returns nothing
            local unit u = GetMainSelectedUnitEx()
            if IsUnitType(u, UNIT_TYPE_HERO) then
                call BJDebugMsg(GetUnitName(u) + " " + GetHeroProperName(u))
            else
                call BJDebugMsg(GetUnitName(u))
            endif
            set u = null
        endfunction

        private function init_function takes nothing returns nothing
            set filter = Filter(function FilterFunction)
            call TimerStart(CreateTimer(), 0, false, function init_functionAt0s)
            //demo
            call TimerStart(CreateTimer(), 1, true, function demoFunction)
        endfunction
    endlibrary
     
     
  4. Proshel_Doty

    Proshel_Doty

    Joined:
    May 5, 2013
    Messages:
    2
    Resources:
    0
    Resources:
    0
    Thanks, man!)))
     
  5. Macadamia

    Macadamia

    Joined:
    Jan 30, 2020
    Messages:
    876
    Resources:
    0
    Resources:
    0
    Thanks for the updated Lua version, I am sure this will be very useful in many Custom UI situations !!!
     
  6. Beckx

    Beckx

    Joined:
    Dec 29, 2019
    Messages:
    4
    Resources:
    0
    Resources:
    0
    Dont use JASS man :( JASS == Obsolete