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 poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  4. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  5. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  6. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  7. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  8. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    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.

[Lua] GroupSelectionMimic

Discussion in 'Submissions' started by Tasyen, Aug 28, 2019.

  1. Tasyen

    Tasyen

    Joined:
    Jul 18, 2010
    Messages:
    1,263
    Resources:
    16
    Tools:
    2
    Maps:
    2
    Spells:
    7
    Tutorials:
    4
    JASS:
    1
    Resources:
    16
    Not tested enough: losing selection by remove unit, death or loading did bug the System.
    This should be fixed with V1.1

    Can Still Bug out, if one uses the SoulStone ability, Through that can be fixed by Hand by calling GroupSelectionMimic.RemoveUnit(GetSpellTargetUnit()).

    Fails to detect focus change done by left cklicking an current selected unit in the Group selection Frame , the one at the bottom.
    Also Fails to detect losing selection when reincarnation triggers.

    V1.2:
    Does now filter tab clicks when 1 or no unit is selected to Prevent game freeze by endless loop.

    V1.3:
    This FirstHandle Caching was bullshit and somehow worked on the test map. In reality non heroes use their unitCode like 'hfoo', while heroes use GetHandleId, If prio values colide.

    V1.4:
    Creates now Life Drops below equal to 0.45 Life triggers on selection, on deselection destroys them. That way reincarnation is also detected. The any death Event was removed.
    Functions names are now all in camelCase, while Other table Values start with an UperCase.

    V1.5
    requries now Global Initialization by Bribe.

    I wrote this system to know which unit is currently the main focused unit. I needed this feature to display the Name/Icon/hp/mana/exp for the main focused unit for custom UI which still support groupselection.

    Code (Lua):

    --[[
    V1.5
    requires: Global Initialization 1.2 by Bribe  
    GroupSelectionMimic is an system that mimics warcraft 3 group selection order.
    That is done so one knows which unit is in current main focus when having multiple units selected.

    This is an synced resource hence it might work wrong when having bad latency Through that would have to be tested.

    One can get the current focuse selected unit with
    function GroupSelectionMimic.getFocusUnit(player)
    --]]


    --hook into remove and showUnit
    --RemoveUnit and ShowUnit do not throw an deselection event hence hook in.
    do
        local realShowUnit = ShowUnit
        function ShowUnit(whichUnit, show)
            if not show then
                GroupSelectionMimic.removeUnit(whichUnit)
            end
            realShowUnit(whichUnit,show)
        end
        local realRemoveUnit = RemoveUnit
        function RemoveUnit(whichUnit)
            GroupSelectionMimic.removeUnit(whichUnit)
            realRemoveUnit(whichUnit)
        end
    end

    GroupSelectionMimic = {}
    onTriggerInit(function()
        GroupSelectionMimic.SelectedUnits = {}
        GroupSelectionMimic.DeselectionTrigger = CreateTrigger()
        GroupSelectionMimic.KeyTrigger = CreateTrigger() --pressing tab
        GroupSelectionMimic.LoadedTrigger = CreateTrigger() --needed when units can be loaded into transporters
        GroupSelectionMimic.SelectionTrigger = CreateTrigger()
        GroupSelectionMimic.DeathTrigger = {} --death, reincarnation
        GroupSelectionMimic.SummonTrigger = CreateTrigger() --needed for storm earth fire

        --add arrays for players and add Events
        ForForce(bj_FORCE_ALL_PLAYERS, function()
            local player = GetEnumPlayer()
            GroupSelectionMimic.SelectedUnits[player] = {Selected = 1, Running = false}
            BlzTriggerRegisterPlayerKeyEvent(GroupSelectionMimic.KeyTrigger, player, OSKEY_TAB, 0, true)
            BlzTriggerRegisterPlayerKeyEvent(GroupSelectionMimic.KeyTrigger, player, OSKEY_TAB, 1, true) --shift
            BlzTriggerRegisterPlayerKeyEvent(GroupSelectionMimic.KeyTrigger, player, OSKEY_TAB, 2, true) --ctrl
            BlzTriggerRegisterPlayerKeyEvent(GroupSelectionMimic.KeyTrigger, player, OSKEY_TAB, 3, true) --shift + ctrl
            TriggerRegisterPlayerUnitEvent(GroupSelectionMimic.SelectionTrigger, player, EVENT_PLAYER_UNIT_SELECTED, nil)
            TriggerRegisterPlayerUnitEvent(GroupSelectionMimic.DeselectionTrigger, player, EVENT_PLAYER_UNIT_DESELECTED, nil)
        end)
        TriggerRegisterAnyUnitEventBJ(GroupSelectionMimic.LoadedTrigger, EVENT_PLAYER_UNIT_LOADED)
        TriggerRegisterAnyUnitEventBJ(GroupSelectionMimic.SummonTrigger, EVENT_PLAYER_UNIT_SUMMON)
       

        TriggerAddAction(GroupSelectionMimic.KeyTrigger, function()
            local player = GetTriggerPlayer()
            local playerData = GroupSelectionMimic.SelectedUnits[player]    
            if #playerData <= 1 then return end --do nothing, if only one or less is selected
            local currentUnitCode = GetUnitTypeId(playerData[playerData.Selected])
                   
            if BlzBitAnd(BlzGetTriggerPlayerMetaKey(), 1) == 1 then
                if not IsUnitType(playerData[playerData.Selected], UNIT_TYPE_HERO) then
                    --jump to prev group
                    while (GetUnitTypeId(playerData[playerData.Selected]) == currentUnitCode)
                    do
                        playerData.Selected = playerData.Selected - 1
                    end
                else
                    playerData.Selected = playerData.Selected - 1
                end
            else
                if not IsUnitType(playerData[playerData.Selected], UNIT_TYPE_HERO) then
                    --jump to next group
                    while (GetUnitTypeId(playerData[playerData.Selected]) == currentUnitCode)
                    do
                        playerData.Selected = playerData.Selected + 1
                    end
                else
                    playerData.Selected = playerData.Selected + 1
                end
            end
       
            --overflow
            if playerData.Selected > #playerData then playerData.Selected = 1 end
            if playerData.Selected < 1 then playerData.Selected = #playerData end
       
            --debug display the new focus unit
            --print(playerData.Selected, GetUnitName(playerData[playerData.Selected]))
        end)

        TriggerAddAction(GroupSelectionMimic.SelectionTrigger, function()
            --print("Select")
            GroupSelectionMimic.addUnitToPlayer(GetTriggerPlayer(), GetTriggerUnit())
        end)
       
        TriggerAddAction(GroupSelectionMimic.DeselectionTrigger, function()
            local player = GetTriggerPlayer()
            local playerData = GroupSelectionMimic.SelectedUnits[player]
            local triggerUnit = GetTriggerUnit()  
            GroupSelectionMimic.removeUnitFromPlayerData(playerData, triggerUnit)
        end)

        TriggerAddAction(GroupSelectionMimic.LoadedTrigger, function()
            GroupSelectionMimic.removeUnit(GetTriggerUnit())
        end)

        TriggerAddAction(GroupSelectionMimic.SummonTrigger, function()
            if GetUnitCurrentOrder(GetSummoningUnit()) == OrderId("elementalfury") then
                GroupSelectionMimic.removeUnit(GetSummoningUnit())
                GroupSelectionMimic.addUnitToPlayer(GetTriggerPlayer(), GetSummonedUnit(), true)
            end
        end)
    end)

    function GroupSelectionMimic.getFocusUnit(player)
        local playerData = GroupSelectionMimic.SelectedUnits[player]
        return playerData[playerData.Selected]
    end

    function GroupSelectionMimic.debugPrint()
        local playerData = GroupSelectionMimic.SelectedUnits[Player(0)]
        print("Selected: ",playerData.Selected, GetUnitName(playerData[playerData.Selected]))
        for key, value in ipairs(playerData)
        do  
            print(key, GetUnitName(value), BlzGetUnitRealField(value, UNIT_RF_PRIORITY), GroupSelectionMimic.getOrderValue(value))
        end
    end

    function GroupSelectionMimic.getOrderValue(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 GroupSelectionMimic.createDeathDetect(unit)
        if GroupSelectionMimic.DeathTrigger[unit] then
            GroupSelectionMimic.DeathTrigger[unit].Counter = GroupSelectionMimic.DeathTrigger[unit].Counter + 1
        else
            --print("Create DeathTrigger")
            local deathTrigger = CreateTrigger()
            GroupSelectionMimic.DeathTrigger[unit] = {}
            GroupSelectionMimic.DeathTrigger[unit].Counter = 1
            GroupSelectionMimic.DeathTrigger[unit].Trigger = deathTrigger
            GroupSelectionMimic.DeathTrigger[unit].TriggerAction = TriggerAddAction(deathTrigger, function()
                GroupSelectionMimic.removeUnit(GetTriggerUnit())
            end)
            TriggerRegisterUnitLifeEvent(deathTrigger, unit, LESS_THAN_OR_EQUAL, 0.45)
        end
    end
    function GroupSelectionMimic.destroyDeathDetect(unit)
        if not GroupSelectionMimic.DeathTrigger[unit] then return end --do nothing, if there is none
        GroupSelectionMimic.DeathTrigger[unit].Counter = GroupSelectionMimic.DeathTrigger[unit].Counter - 1
        if GroupSelectionMimic.DeathTrigger[unit].Counter < 1 then
            --print("Destroy DeathTrigger")
            TriggerRemoveAction( GroupSelectionMimic.DeathTrigger[unit].Trigger,  GroupSelectionMimic.DeathTrigger[unit].TriggerAction)
            DestroyTrigger(GroupSelectionMimic.DeathTrigger[unit].Trigger)
            GroupSelectionMimic.DeathTrigger[unit].Trigger = nil
            GroupSelectionMimic.DeathTrigger[unit].TriggerAction = nil
            GroupSelectionMimic.DeathTrigger[unit].Counter = nil
            GroupSelectionMimic.DeathTrigger[unit] = nil
        end
    end

    function GroupSelectionMimic.removeUnitFromPlayerData(playerData, unit)
        local unitCode = GetUnitTypeId(unit)
        for key, value in ipairs(playerData)
        do  
            if value == unit then
                --update focus when an unit was removed that has an lower index then the focused unit
                if key == playerData.Selected then
                    if IsUnitType(value, UNIT_TYPE_HERO) then
                        playerData.Selected = 1
                    --have more of that unit?
                    elseif GetUnitTypeId(playerData[playerData.Selected + 1]) == unitCode or GetUnitTypeId(playerData[playerData.Selected - 1]) == unitCode then
                        --do nothing
                    else
                        playerData.Selected = 1
                    end
                elseif key < playerData.Selected then playerData.Selected = playerData.Selected - 1 end
                --pull down the selected index, do not drop below 1
                table.remove(playerData, key)
                GroupSelectionMimic.destroyDeathDetect(value)
                --when the last unit was loaded in and there is an transporter the transporter gets focus without a selection
                if #playerData == 0 and GetTransportUnit() then table.insert(playerData, GetTransportUnit())
                elseif playerData.Selected > #playerData then playerData.Selected = math.max(#playerData,1) end
                break
            end
        end
    end
    function GroupSelectionMimic.removeUnit(unit)
        ForForce(bj_FORCE_ALL_PLAYERS, function()
            local player = GetEnumPlayer()
            local playerData = GroupSelectionMimic.SelectedUnits[player]
            GroupSelectionMimic.removeUnitFromPlayerData(playerData, unit)
        end)
       
    end

    function GroupSelectionMimic.addUnitToPlayer(player, unit, ignoreMultiAdd)
        local playerData = GroupSelectionMimic.SelectedUnits[player]
        local triggerUnitCode = GetUnitTypeId(unit)
        local triggerUnitPrio = BlzGetUnitRealField(unit, UNIT_RF_PRIORITY)

        if not ignoreMultiAdd then
            --start a 0s timer when it not runs already.
            --when another selection happens in that time, then the focus unit is reseted to the first unit.
            --This is done to correct multigroup selection with mouse clicking + drag or shift + double clicking
            if not playerData.Running then
                playerData.Running = true
                TimerStart(CreateTimer(), 0, false, function()
                    playerData.Running = false
                    DestroyTimer(GetExpiredTimer())
                end)
            else
                playerData.Selected = 1
            end
        end
       
            --contains this unit already?
        for key, value in ipairs(playerData)
        do  
            if value == unit then
                return
            end
        end
       
        local added = false
        --Add the unit where it should be prio wise.

        --print("Add",GetUnitName(unit), GroupSelectionMimic.getOrderValue(triggerUnitCode), triggerUnitPrio)
        for key, value in ipairs(playerData)
        do

            --same prio and trigger units handle is smaller then take values place.
            if BlzGetUnitRealField(value, UNIT_RF_PRIORITY) == triggerUnitPrio and GroupSelectionMimic.getOrderValue(value) > GroupSelectionMimic.getOrderValue(unit) then
                --print("Replace Key",key)
                table.insert( playerData, key, unit)
                added = true
                GroupSelectionMimic.createDeathDetect(unit)

                --update the focus index when this unit was added into an lower index
                if key <= playerData.Selected then playerData.Selected = playerData.Selected + 1 end
                break            
            elseif BlzGetUnitRealField(value, UNIT_RF_PRIORITY) < triggerUnitPrio then
                table.insert( playerData, key, unit)
                added = true
                GroupSelectionMimic.createDeathDetect(unit)

                --update the focus index when this unit was added into an lower index
                if key <= playerData.Selected then playerData.Selected = playerData.Selected + 1 end
                break
            end
        end
        --not added yet?
        if not added then
            --add it at the end
            table.insert(playerData, unit)
            GroupSelectionMimic.createDeathDetect(unit)
        end
    end
     
     
    Last edited: Sep 20, 2019 at 10:53 AM