• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • 🏆 Hive's 6th HD Modeling Contest: Mechanical is now open! Design and model a mechanical creature, mechanized animal, a futuristic robotic being, or anything else your imagination can tinker with! 📅 Submissions close on June 30, 2024. Don't miss this opportunity to let your creativity shine! Enter now and show us your mechanical masterpiece! 🔗 Click here to enter!

Reselect

Introduction


Reselect makes deselection of your units less painful. It is a System for Warcraft 3 (Lua) V1.31.1 / (vjass) V1.29.2 or higher.
It aims to give a qol to selection. Reselecting your last selected group before selecting an other players unit becomes easy, just one click on the play ground.
Or when you use the right mouse click the last selected group will even be ordered to "smart"-move to that position.

The system works in multiplayer and for all player at the same time.

How to Install:


Copy Paste into your map the appropriated one, either from the map or taking it from the Code Section below.
Reselect vjass
Reselect Lua​

Code:


JASS:
library Reselect initializer Init
//ReselctV6
//Reselect saves the orderable Units a player had selected when deselecting them.
//When now pressing somewhere on the game screen, the saved deselected units will be reselected.
//UI clicks and another unit-selection won't trigger reselection.

//   function ReselectEnable takes boolean flag returns nothing
   // Disable/Enable Reselect Event Triggers

globals
  
   private group array Group         //Valid Reselect Targets
   private group array TempSave   //Possible Reselect Targets
   private unit array CurrentUnit   //Last Selected Unit of [playerId]
  
   private timer DeSelectTimer = CreateTimer()  
   private force DeselectForce = CreateForce()  
  
   private boolean array orderAsGroup           //(true) rightclick reselection order, will use formation for that player
   private boolean array Allowed                 //MouseClicks can Reselect, that will be calculated inside ActionDeSelectTimer
   private boolean array SelectNewShopTarget     //MouseClicks was meant to change shopping unit?
   
   private timer MouseTimer    = CreateTimer()   //This Timer allows to run Selection Events of a MouseClick which would execute Reselect, before reselecting.
   private force MouseForce    = CreateForce()   //That allows to select multiple other non controlable units before Reselection is applied. shop -> shop -> enemy -> press Some where -> reselect
   private unit array MouseUnit                 //CurrentUnit when Mouse Click
   private real array MouseX
   private real array MouseY
   private mousebuttontype array MouseButton

  
   public boolean array Enabled               //Enable Reselct for [playerId]; Reselect_Enabled = true, Reselect does only work for Users
   public trigger Select        = CreateTrigger()
   public trigger DeSelect    = CreateTrigger()
   public trigger Mouse        = CreateTrigger()
   public trigger Order        = CreateTrigger()
   public trigger ShopSell        = CreateTrigger()
  
endglobals

private function ActionSelect takes nothing returns nothing
   local player eventPlayer   = GetTriggerPlayer()
   local integer index        = GetPlayerId(eventPlayer)
   if Enabled[index] then
       set CurrentUnit[index] = GetTriggerUnit()
       if GetPlayerAlliance(GetOwningPlayer(CurrentUnit[index]), eventPlayer, ALLIANCE_SHARED_CONTROL) then
           set Allowed[index] = false
           call GroupClear(Group[index])
       endif
   endif
   set eventPlayer = null
endfunction


private function ActionTimerMouseForce takes nothing returns nothing
   local player p        = GetEnumPlayer()
   local integer index   = GetPlayerId(p)
   local effect eff
   local unit fog
   //Did this player select another Unit with this MousePress?
   if CurrentUnit[index] == MouseUnit[index] then
      if SelectNewShopTarget[index] then
         set SelectNewShopTarget[index] = false
         return
      endif
       //No new unit, Reselect!
       call ClearSelectionForPlayer(p)
      
       if MouseButton[index] == MOUSE_BUTTON_TYPE_RIGHT then //Right click -> Order
           set eff = AddSpecialEffect("UI\\Feedback\\Confirmation\\Confirmation.mdl", MouseX[index], MouseY[index])
           call BlzSetSpecialEffectColor( eff, 0, 255, 0 )
           if GetLocalPlayer() != p then
               call BlzSetSpecialEffectScale( eff, 0.00 )
           endif
           call DestroyEffect(eff)
           set eff = null
          
           if orderAsGroup[index] then //Order together?
               call GroupPointOrder( Group[index], "smart", MouseX[index], MouseY[index])
               loop
                   set fog = FirstOfGroup(Group[index])
                   exitwhen fog == null
                   call GroupRemoveUnit(Group[index], fog)
                   call SelectUnitAddForPlayer(fog, p)
               endloop
              
           else //Order one for one
               loop
                   set fog = FirstOfGroup(Group[index])
                   exitwhen fog == null
                   call GroupRemoveUnit(Group[index], fog)
                   call SelectUnitAddForPlayer(fog, p)
                   call IssuePointOrder(fog, "smart", MouseX[index], MouseY[index])
               endloop
           endif
       else //Not Right Click, Only Reselect
           loop
               set fog = FirstOfGroup(Group[index])
               exitwhen fog == null
               call GroupRemoveUnit(Group[index], fog)
               call SelectUnitAddForPlayer(fog, p)
           endloop
       endif      
   endif
   set p = null
endfunction

private function ActionTimerMouse takes nothing returns nothing
   call DisableTrigger(DeSelect)
   call DisableTrigger(Select)
  
   call ForForce(MouseForce, function ActionTimerMouseForce)
   call ForceClear(MouseForce)
  
   call EnableTrigger(DeSelect)
   call EnableTrigger(Select)
endfunction

private function ActionMouse takes nothing returns nothing
   local player p        = GetTriggerPlayer()
   local integer index   = GetPlayerId(p)
   local real x          = BlzGetTriggerPlayerMouseX()
   local real y          = BlzGetTriggerPlayerMouseY()
   if Enabled[index] and Allowed[index] and x != 0 and y != 0  then
           //Save Data of this MousePress
           set MouseX[index]        = x
           set MouseY[index]       = y
           set MouseButton[index]    = BlzGetTriggerPlayerMouseButton()
           call ForceAddPlayer(MouseForce, p)
           set MouseUnit[index]    = CurrentUnit[index]
           //Start a 0s timer, Allow the possible selection event of this mouse Press to run first.
           call TimerStart(MouseTimer,0, false, function ActionTimerMouse)
       //endif
   endif
   set p = null
endfunction

private function ActionDeselectForce takes nothing returns nothing
   local player p        = GetEnumPlayer()
   local integer index = GetPlayerId(p)
   if not IsUnitSelected(CurrentUnit[index], p) or not GetPlayerAlliance(GetOwningPlayer(CurrentUnit[index]), p, ALLIANCE_SHARED_CONTROL) then
       call GroupAddGroup(TempSave[index], Group[index])
       set Allowed[index] = true
   endif
   call GroupClear(TempSave[index])
   set p = null
endfunction

private function ActionDeselectTimer takes nothing returns nothing
   call ForForce(DeselectForce, function ActionDeselectForce)
   call ForceClear(DeselectForce)
endfunction

private function ActionDeSelect takes nothing returns nothing
   local player eventPlayer   = GetTriggerPlayer()
   local unit eventUnit       = GetTriggerUnit()
   local integer index        = GetPlayerId(eventPlayer)
   if Enabled[index] and GetPlayerAlliance(GetOwningPlayer(eventUnit), eventPlayer, ALLIANCE_SHARED_CONTROL) then
       call GroupAddUnit(TempSave[index], eventUnit)
       call ForceAddPlayer(DeselectForce, eventPlayer)
       call TimerStart(DeSelectTimer,0, false, function ActionDeselectTimer)
   endif  
   set eventPlayer   = null
   set eventUnit    = null
endfunction

private function ActionOrder takes nothing returns nothing
   local integer index   = GetPlayerId(GetOwningPlayer(GetOrderTargetUnit()))
   local integer orderId = GetIssuedOrderId()
   local string orderString = OrderId2String(orderId)
   local integer orderedUnitCode = GetUnitTypeId(GetTriggerUnit())
   if orderString == "neutralinteract" or (orderString == "smart" and not IsUnitIdType(orderedUnitCode, UNIT_TYPE_MELEE_ATTACKER ) and not IsUnitIdType(orderedUnitCode, UNIT_TYPE_RANGED_ATTACKER) and GetUnitDefaultMoveSpeed(GetTriggerUnit()) == 0) then
   // rightClick with shop gives smart order. Imo shops can not attack nor can move.
      set SelectNewShopTarget[index] = true
   endif
endfunction
private function ActionShopSell takes nothing returns nothing
    local integer index   = GetPlayerId(GetOwningPlayer(GetBuyingUnit()))
    set SelectNewShopTarget[index] = true
 endfunction


private function InitPlayers takes nothing returns nothing
   local player p      = GetEnumPlayer()
   local integer index = GetPlayerId(p)
   if GetPlayerController(p) == MAP_CONTROL_USER then
       set Enabled[index] = true
       set Group[index]        = CreateGroup()
       set TempSave[index]     = CreateGroup()
       set orderAsGroup[index] = true
       call TriggerRegisterPlayerEvent(Mouse, p, EVENT_PLAYER_MOUSE_UP)
       call TriggerRegisterPlayerUnitEvent(Select, p, EVENT_PLAYER_UNIT_SELECTED, null)
       call TriggerRegisterPlayerUnitEvent(DeSelect, p, EVENT_PLAYER_UNIT_DESELECTED, null)
   endif  
   set p = null
endfunction

function ReselectEnable takes boolean flag returns nothing
   if flag then
       call EnableTrigger(Select)
       call EnableTrigger(Mouse)
       call EnableTrigger(DeSelect)
       call EnableTrigger(Order)
   else
       call DisableTrigger(Select)
       call DisableTrigger(Mouse)
       call DisableTrigger(DeSelect)
       call DisableTrigger(Order)
   endif
endfunction

private function Init takes nothing returns nothing
   call TriggerAddAction(Select, function ActionSelect)
   call TriggerAddAction(DeSelect, function ActionDeSelect)
   call TriggerAddAction(Mouse, function ActionMouse)
   call TriggerAddAction(Order, function ActionOrder)
   call TriggerAddAction(ShopSell, function ActionShopSell)
   
   call ForForce(bj_FORCE_ALL_PLAYERS, function InitPlayers)
   call TriggerRegisterAnyUnitEventBJ(Order, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
   call TriggerRegisterAnyUnitEventBJ(ShopSell, EVENT_PLAYER_UNIT_SELL_ITEM)
   call TriggerRegisterAnyUnitEventBJ(ShopSell, EVENT_PLAYER_UNIT_SELL)
endfunction
endlibrary
Lua:
Reselect = {
    AutoRun = true --(true) will create Itself at 0s, (false) you need to Reselect.Init()
}
--ReselctV6
--Reselect saves the orderable Units a player had selected when deselecting them.
--When now pressing somewhere on the game screen, the saved deselected units will be reselected.
--UI clicks and another unit-selection won't trigger reselection.

--   function Reselect.Enable(flag)
   -- Disable/Enable Reselect Event Triggers for everyone
Reselect.Init = function()
    Reselect.Group         = {}   --Valid Reselect Targets
    Reselect.TempSave      = {}   --Possible Reselect Targets
    Reselect.CurrentUnit   = {}   --Last Selected Unit of [playerId]
    Reselect.DeSelectTimer = CreateTimer()
    Reselect.DeselectForce = CreateForce()  
    Reselect.orderAsGroup  = __jarray(true)          --(true) rightclick reselection order, will use formation for that player
    Reselect.Allowed     = {}              --MouseClicks can Reselect, that will be calculated inside ActionDeSelectTimer
    Reselect.MouseTimer  = CreateTimer()   --This Timer allows to run Selection Events of a MouseClick which would execute Reselect, before reselecting.
    Reselect.MouseForce  = CreateForce()   --That allows to select multiple other non controlable units before Reselection is applied. shop -> shop -> enemy -> press Some where -> reselect
    Reselect.MouseUnit   = {}              --Reselect.CurrentUnit when Mouse Click
    Reselect.MouseX      = __jarray(0.0)
    Reselect.MouseY      = __jarray(0.0)
    Reselect.MouseButton = {}
  
    Reselect.Enabled     = __jarray(true)               --Enable Reselect for [playerId]; Reselect_Enabled = true, Reselect does only work for Users
    Reselect.Select      = CreateTrigger()
    Reselect.DeSelect    = CreateTrigger()
    Reselect.Mouse       = CreateTrigger()
    Reselect.Order       = CreateTrigger()
    Reselect.ShopSell    = CreateTrigger()
    Reselect.SelectNewShopTarget = {}            --MouseClicks was meant to change shopping unit?

    TriggerAddAction(Reselect.Select, function()
        local eventPlayer   = GetTriggerPlayer()
        local index        = GetPlayerId(eventPlayer)
        if Reselect.Enabled[index] then
            Reselect.CurrentUnit[index] = GetTriggerUnit()
            if GetPlayerAlliance(GetOwningPlayer(Reselect.CurrentUnit[index]), eventPlayer, ALLIANCE_SHARED_CONTROL) then
                Reselect.Allowed[index] = false
                GroupClear(Reselect.Group[index])
            end
        end
    end)
    TriggerAddAction(Reselect.DeSelect, function()
        local eventPlayer   = GetTriggerPlayer()
        local eventUnit       = GetTriggerUnit()
        local index        = GetPlayerId(eventPlayer)
        if Reselect.Enabled[index] and GetPlayerAlliance(GetOwningPlayer(eventUnit), eventPlayer, ALLIANCE_SHARED_CONTROL) then
            GroupAddUnit(Reselect.TempSave[index], eventUnit)
            ForceAddPlayer(Reselect.DeselectForce, eventPlayer)
            TimerStart(Reselect.DeSelectTimer,0, false, Reselect.ActionDeselectTimer)
        end 
    end)
    TriggerAddAction(Reselect.Mouse, function()
        local p        = GetTriggerPlayer()
        local index   = GetPlayerId(p)
        local x          = BlzGetTriggerPlayerMouseX()
        local y          = BlzGetTriggerPlayerMouseY()
        -- coords 0/0 are outside of world Frame
        if Reselect.Enabled[index] and Reselect.Allowed[index] and x ~= 0 and y ~= 0  then
            --Save Data of this MousePress
            Reselect.MouseX[index]        = x
            Reselect.MouseY[index]       = y
            Reselect.MouseButton[index]    = BlzGetTriggerPlayerMouseButton()
            ForceAddPlayer(Reselect.MouseForce, p)
            Reselect.MouseUnit[index]    = Reselect.CurrentUnit[index]
            --Start a 0s timer, Allow the possible selection event of this mouse Press to run first.
            TimerStart(Reselect.MouseTimer,0, false, Reselect.ActionTimerMouse)
        --endif
        end
    end)

    TriggerAddAction(Reselect.Order, function()
        local p        = GetTriggerPlayer()
        local index   = GetPlayerId(GetOwningPlayer(GetOrderTargetUnit()))
        local orderId = GetIssuedOrderId()
        local orderString = OrderId2String(orderId)
        local orderedUnitCode = GetUnitTypeId(GetTriggerUnit())
        if orderString == "neutralinteract"
        -- rightClick with shop gives smart order. Imo shops can not attack nor can move.
        or (orderString == "smart" and not IsUnitIdType(orderedUnitCode, UNIT_TYPE_MELEE_ATTACKER ) and not IsUnitIdType(orderedUnitCode, UNIT_TYPE_RANGED_ATTACKER) and GetUnitDefaultMoveSpeed(GetTriggerUnit()) == 0)
        then
            Reselect.SelectNewShopTarget[index] = true
        end
    end)
    TriggerAddAction(Reselect.ShopSell, function()
        local p       = GetOwningPlayer(GetBuyingUnit())
        local index   = GetPlayerId(p)
        Reselect.SelectNewShopTarget[index] = true
    end)
    
    ForForce(bj_FORCE_ALL_PLAYERS, function()
        local p      = GetEnumPlayer()
        local index = GetPlayerId(p)
        if GetPlayerController(p) == MAP_CONTROL_USER then
            Reselect.Group[index]        = CreateGroup()
            Reselect.TempSave[index]     = CreateGroup()
            TriggerRegisterPlayerEvent(Reselect.Mouse, p, EVENT_PLAYER_MOUSE_UP)
            TriggerRegisterPlayerUnitEvent(Reselect.Select, p, EVENT_PLAYER_UNIT_SELECTED, null)
            TriggerRegisterPlayerUnitEvent(Reselect.DeSelect, p, EVENT_PLAYER_UNIT_DESELECTED, null)
        end 
    end)
    TriggerRegisterAnyUnitEventBJ(Reselect.Order, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER)
    TriggerRegisterAnyUnitEventBJ(Reselect.ShopSell, EVENT_PLAYER_UNIT_SELL_ITEM)
    TriggerRegisterAnyUnitEventBJ(Reselect.ShopSell, EVENT_PLAYER_UNIT_SELL)
end

function Reselect.ActionTimerMouseForce()
    local p        = GetEnumPlayer()
    local index   = GetPlayerId(p)
    local eff
    local fog
    --Did this player select another Unit with this MousePress?
    if Reselect.CurrentUnit[index] == Reselect.MouseUnit[index] then
        if Reselect.SelectNewShopTarget[index] then Reselect.SelectNewShopTarget[index] = false return end

        --No new unit, Reselect!
        ClearSelectionForPlayer(p)
       
        if Reselect.MouseButton[index] == MOUSE_BUTTON_TYPE_RIGHT then --Right click -> Order
            eff = AddSpecialEffect("UI\\Feedback\\Confirmation\\Confirmation.mdl", Reselect.MouseX[index], Reselect.MouseY[index])
            BlzSetSpecialEffectColor( eff, 0, 255, 0 )
            if GetLocalPlayer() ~= p then
                BlzSetSpecialEffectScale( eff, 0.00 )
            end
            DestroyEffect(eff)
            eff = nil
           
            if Reselect.orderAsGroup[index] then --Order together?
                GroupPointOrder( Reselect.Group[index], "smart", Reselect.MouseX[index], Reselect.MouseY[index])
                while true do
                    fog = FirstOfGroup(Reselect.Group[index])
                    if not fog then break end
                    GroupRemoveUnit(Reselect.Group[index], fog)
                     SelectUnitAddForPlayer(fog, p)
                end
               
            else --Order one for one
                while true do
                    fog = FirstOfGroup(Reselect.Group[index])
                    if not fog then break end
                    GroupRemoveUnit(Reselect.Group[index], fog)
                    SelectUnitAddForPlayer(fog, p)
                    IssuePointOrder(fog, "smart", Reselect.MouseX[index], Reselect.MouseY[index])
                end
            end
        else --Not Right Click, Only Reselect
            while true do
                fog = FirstOfGroup(Reselect.Group[index])
                if not fog then break end
                 GroupRemoveUnit(Reselect.Group[index], fog)
                 SelectUnitAddForPlayer(fog, p)
            end
        end      
    end
end

function Reselect.ActionTimerMouse()
    DisableTrigger(Reselect.DeSelect)
    DisableTrigger(Reselect.Select)
   
    ForForce(Reselect.MouseForce, Reselect.ActionTimerMouseForce)
    ForceClear(Reselect.MouseForce)
   
    EnableTrigger(Reselect.DeSelect)
    EnableTrigger(Reselect.Select)
    
end

function Reselect.ActionDeselectForce()
    local p        = GetEnumPlayer()
    local index = GetPlayerId(p)
    if not IsUnitSelected(Reselect.CurrentUnit[index], p) or not GetPlayerAlliance(GetOwningPlayer(Reselect.CurrentUnit[index]), p, ALLIANCE_SHARED_CONTROL) then
        GroupAddGroup(Reselect.TempSave[index], Reselect.Group[index])
        Reselect.Allowed[index] = true
    end
    GroupClear(Reselect.TempSave[index])
end

function Reselect.ActionDeselectTimer()
    ForForce(Reselect.DeselectForce, Reselect.ActionDeselectForce)
    ForceClear(Reselect.DeselectForce)
end

function Reselect.Enable(flag)
    if flag then
        EnableTrigger(Reselect.Select)
        EnableTrigger(Reselect.Mouse)
        EnableTrigger(Reselect.DeSelect)
        EnableTrigger(Reselect.Order)
    else
        DisableTrigger(Reselect.Select)
        DisableTrigger(Reselect.Mouse)
        DisableTrigger(Reselect.DeSelect)
        DisableTrigger(Reselect.Order)
    end
end
if Reselect.AutoRun then
    if OnInit then -- Total Initialization v5.2.0.1 by Bribe
        OnInit.final(Reselect.Init())
    else -- without
        local real = MarkGameStarted
        function MarkGameStarted()
            real()
            Reselect.Init()
        end
    end
end

Changelog


Changes:
V6
Reselect triggers on Mouse Release
Reselect will not trigger when succesfully buying item/units anymore.​
V5
+Lua
Support NewTarget Shop
Migrate from Code Archive
V4:
added ReselectEnable a function to dis/enable Reselect Event Triggers.
added whitespace
ForGroup -> FoG loop
V3:
Added OrderAsGroup for each player, (true) -> right click uses Formation.
Fixed a bug: reselecting to many units.
V2:
(currently not gain) Formating adjusted
added some missing "set p = null"

Tags: QoL​
Contents

ReselectV6 (Map)

Reviews
Ralle
This was approved in the old Code section by @Bribe: This is an absolutely solid vJass resource, and is something that a lot of maps could benefit from. Very innovative, as usual. Sorry it took so long to get to. Approved now.
Level 5
Joined
Oct 31, 2011
Messages
76
Another very useful system.

Is there a way to disable this function when I use the left mouse button?

Example: when I go to buy an item in the shop it selects my last selected units.

I'm using the vjass version
 
Last edited:
yes, you can enable/disable the system for specific players using the Reselect_Enabled boolean array.
set Reselect_Enabled[playerId] = true
set Reselect_Reselect[0] = false

the playerId uses jass indexing: [0] is player red, [1] blue.

oh no, Reforged expanded world frame, so pressing on command buttons has not coords 0/0. This case was filtered for v1.31 :(

Sounds like i need a better filter/approach that works for reforged.

Edit: Updated to V6)
When a player buys an item or hires an unit. Then Reselect will not trigger. Failing the buying will still trigger Reselect.
Reselect now triggers on mouse up instead of mouse down.
 
Last edited:
Level 5
Joined
Oct 31, 2011
Messages
76
yes, you can enable/disable the system for specific players using the Reselect_Enabled boolean array.
set Reselect_Enabled[playerId] = true
set Reselect_Reselect[0] = false

the playerId uses jass indexing: [0] is player red, [1] blue.

oh no, Reforged expanded world frame, so pressing on command buttons has not coords 0/0. This case was filtered for v1.31 :(

Sounds like i need a better filter/approach that works for reforged.

Edit: Updated to V6)
When a player buys an item or hires an unit. Then Reselect will not trigger. Failing the buying will still trigger Reselect.
Reselect now triggers on mouse up instead of mouse down.
It's improved a lot, it doesn't disable the left mouse button click, but it's much better for buying items.

Thanks!
 
Top