- Joined
- Jul 18, 2010
- Messages
- 2,377
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.
V1.6
uses new Global Init, local for GroupSelectionMimic.DeathTrigger
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.
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.
V1.6
uses new Global Init, local for GroupSelectionMimic.DeathTrigger
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.
Lua:
--[[
V1.6
requires: Global Initialization 3.0.0.1 by Bribe https://www.hiveworkshop.com/threads/global-initialization.317099/#post-3535035
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
do
GroupSelectionMimic = {}
local deathTable
OnTrigInit(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
deathTable = GroupSelectionMimic.DeathTrigger
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 deathTable[unit] then
deathTable[unit].Counter = deathTable[unit].Counter + 1
else
--print("Create DeathTrigger")
local deathTrigger = CreateTrigger()
deathTable[unit] = {}
deathTable[unit].Counter = 1
deathTable[unit].Trigger = deathTrigger
deathTable[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 deathTable[unit] then return end --do nothing, if there is none
deathTable[unit].Counter = deathTable[unit].Counter - 1
if deathTable[unit].Counter < 1 then
--print("Destroy DeathTrigger")
TriggerRemoveAction( deathTable[unit].Trigger, deathTable[unit].TriggerAction)
DestroyTrigger(deathTable[unit].Trigger)
deathTable[unit].Trigger = nil
deathTable[unit].TriggerAction = nil
deathTable[unit].Counter = nil
deathTable[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
end
Last edited: