• Check out the results of the Techtree Contest #19!
  • Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.
  • Create a void inspired texture for Warcraft 3 and enter Hive's 34th Texturing Contest: Void! Click here to enter!
  • The Hive's 22nd Icon Contest: Creep Abilities is now concluded, time to vote for your favourite set of icons! Click here to vote!

HeroSelector

This bundle is marked as pending. It has not been reviewed by a staff member yet.

Introduction


HeroSelector is a Frame-UI System to pick a hero. Provides a vjass and Lua version. It features Random, Picking, Baning, unique/limited Picks, Random-Only "Picks", Swap, simple Pick Requirement, Categories. Picking/Banning can be enabled or disabled for a subgroup of Players at a wanted moment. The baned heroes can be baned instantly or wait until the next Picking.
A Pick Requirement enables a Hero only for a Player, a TeamNr, a force, a Race or for a TechLevel/UnitCode-count. But each option can have only one requirement. The button is still visible for all Players.

Category can be clicked to have Heros not having that category being displayed less visible. One should use power 2 numbers when Setting/adding categories to selection Options. All added Options get on Default the category melee or ranged or None if the can't attack on default.

The Selection pool is displayed in one page in rows and cols the amount of cols and rows can be changed inside HeroSelectors Setup which is at the top of HeroSelector after the API comments. The space between 2 rows and 2 Buttons can be changed as well as the button size.

HeroSelector Trigger Files


A short overview of the Trigger Editor files
HeroSelectors features are split into 3/(4 jass) Trigger Editor Files.
HeroSelector is the main one the big box with the hero icons and categories.
Teamviewer manages the herocards that show current preview.
HeroInfo shows the info for the current selected preview.
Each of them has a setup part at the top (globals vjass + some functions)

(vjass) HeroSelectorAction is the place for manual (v)jass writers to change the default behaviour of HeroSelectorEvents and the initial selectable options setup

How to use the Selection grid?


Each hero can only be once in the grid. When using HeroSelector.addUnit it will add a new slot. There are HeroSelector.ButtonColCount*HeroSelector.ButtonRowCount slots.
3 rows with 4 cols would result into:

Code:
01,  02,  03,  04,
05,  06,  07,  08,
09,  10,  11,  12,

When you want to leave fields in the grid empty use HeroSelector.addUnit(0) or HeroSelector.addUnit().
There is a GUI setup which works with indexes, not set indexes will be empty fields.

Hook into


Offers 4 functions one can hook into to add custom Code without having to touch the System Code. By doing that one can add extra Features. Like shown with TeamView and HeroInfo which are not part of the core System but in the same folder. Although this functions have in this upload some content which I think is useful to have. They are below HeroSelector Setup.
Lua:
function HeroSelector.unitCreated(player, unit, isRandom)
--    this function is called when an unit is picked, add here you actions that have to be done for the picked unit
function HeroSelector.buttonSelected(player, unitCode)
--    this function is called when an player selects an button, this is not the picking.
function HeroSelector.unitBaned(player, unitCode)
--    this function is called when a player bans an unitCode.
function HeroSelector.initHeroes()
--    this function will be called before anything is created, when not using GUI to setup data you could add the selectable heroes here.
function HeroSelector.repick(unit[, player])
-- if you hook/overwritte this, make sure you run it.

How to Install:


You map has to be in Lua-Mode (Lua mode).
jass requires vjass enabled
Export and Import
war3mapImported\HeroSelector.fdf
war3mapImported\HeroSelector.toc
war3mapImported\HeroSelectorBan.mdx
WHEN USING THE EXPORT ALL BUTTON, IT CAN HAPPEN THAT THE CONTENT OF the fdf and toc FILES SWAP
Copy the trigger Folder "HeroSelector" into your map.

API:


Lua:
--[[
HeroSelector V2.2d by Tasyen

A UI System to pick a Hero/UnitCode also supports ban and requirments
requires TasFrameList & TasWindow
]]
HeroSelector = {}
HeroSelector.TocPath                = "war3mapImported\\HeroSelector.toc" --ex/import also "HeroSelector.fdf"
HeroSelector.ParentFunc             = function() return BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0) end
--Box
HeroSelector.BoxFrameName           = "HeroSelectorRaceBox" --this is the background box being created
HeroSelector.BackgroundTexture      = ""  -- if this is not "" it displays it over the box background
HeroSelector.BackgroundTiles        = 1 -- 0=streched  1=tileMode
HeroSelector.BoxPosX                = 0.4
HeroSelector.BoxPosY                = 0.55
HeroSelector.BoxPosPoint            = FRAMEPOINT_TOP
HeroSelector.AutoShow               = true --(true) shows the box and the Selection at 0.0 for all players
HeroSelector.AutoCreate             = true --(true) create itself when gameStarted. (false) you need to HeroSelector.initHeroes() HeroSelector.initFrames()
--Unique Picks

HeroSelector.UnitCount              = 999 --each hero is in total allowed to be picked this amount of times (includes random, repicking allows a hero again).
HeroSelector.UnitCountPerTeam       = 1 --Each Team is allowed to pick this amount of each unitType
HeroSelector.ToManyTooltip          = "OUTOFSTOCKTOOLTIP"
-- HeroSelector.PickAbleHeroes is a Way to define pickable Units without touching HeroSelector.initHeroes
-- pickable options expects one string like "Hamg,Hmkg,Hpal,Hblm". It can contain 0 to have empty slots "Hamg,0,Hpal,0,Hblm"
-- all melee heroes "Hamg,Hmkg,Hpal,Hblm,Obla,Ofar,Otch,Oshd,Udea,Ulic,Udre,Ucrl,Edem,Ekee,Emoo,Ewar,Nngs,Nbrn,Npbm,Nplh,Nbst,Nfir,Ntin,Nalc"
HeroSelector.PickAbleHeroes         = ""
HeroSelector.EnableGUICode          = true  -- enables the gui parser and throwing the picked event. if you don't use either you should set this to false.
HeroSelector.GUIParserLastIndex     = 99  -- until which array index the GUI parser reads the parser drops all 0 after the last slot with a valid number
--Ban
HeroSelector.DelayBanUntilPick      = false --(true) baning will not be applied instantly, instead it is applied when HeroSelector.enablePick is called the next time.
--Category
HeroSelector.CategoryData = {
    --Icon path, tooltip Text (tries to localize), HeroCdes "Hmkg,Hpal"
    {"ReplaceableTextures\\CommandButtons\\BTNSteelMelee", "MELEE"},                 --1, automatic detected when adding an unit
    {"ReplaceableTextures\\CommandButtons\\BTNHumanMissileUpOne", "Ranged"},         --2, automatic detected when adding an unit
    {"ReplaceableTextures\\CommandButtons\\BTNGauntletsOfOgrePower", "STRENGTH"},    --4
    {"ReplaceableTextures\\CommandButtons\\BTNSlippersOfAgility", "AGILITY"},        --8
    {"ReplaceableTextures\\CommandButtons\\BTNMantleOfIntelligence", "INTELLECT"},   --16
}

HeroSelector.CategoryAffectRandom   = true  --(false) random will not care about selected category
HeroSelector.CategorySize           = 0.02  --the size of the Category Button
HeroSelector.CategorySpaceX         = 0.0008 --space between 2 category Buttons, it is meant to need only one line of Categoryy Buttons.
HeroSelector.CategoryFilteredAlpha  = 45     -- Alpha value of Heroes being filtered by unselected categories
HeroSelector.CategoryAutoDetect     = true  -- try to set category of added options automatic
HeroSelector.CategoryAutoDetectHero = true  -- (heavy performance wise) create and remove added Heroes to setup the Category for the primary Attribute Str(4) Agi(8) Int(16)
HeroSelector.CategorySingleSelect   = __jarray(true)  -- (true) clicking category will unselect others, you need this false when you want to have multiselect
HeroSelector.PlayerSelectModeExclusive = __jarray(false) -- (true) hero needs to fit all categories selected (false) needs to fit 1 category

--Indicator
HeroSelector.IndicatorPathPick      = "UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdl" --this model is used by the indicator during picking
HeroSelector.IndicatorPathBan       = "war3mapImported\\HeroSelectorBan.mdl" --this model is used by the indicator during baning
--Grid
HeroSelector.SpaceBetweenX          = 0.008 --space between 2 buttons in one row
HeroSelector.SpaceBetweenY          = 0.008 --space between 2 rows
HeroSelector.ButtonColCount         = 4 --amount of buttons in one row
HeroSelector.ButtonRowCount         = 4 --amount of rows
HeroSelector.ScrollRowCount         = 1 -- How many rows to Scroll at once?
--Button
HeroSelector.ButtonSize             = 0.036 --size of each button
HeroSelector.ButtonBlendAll         = false --(true) when a hero icon uses transparenzy
HeroSelector.EmptyButtonPath        = "UI\\Widgets\\EscMenu\\Human\\blank-background.blp"
HeroSelector.HideEmptyButtons       = true
--Ban Button
HeroSelector.BanButtonTextPrefix    = "|cffcf2084" --Prefix Text for the Ban Button
HeroSelector.BanButtonText          = "CHAT_ACTION_BAN" 
HeroSelector.BanButtonTooltipText   = "CHAT_ACTION_BAN" --over the ban button
HeroSelector.BanButtonSizeX         = 0.085
HeroSelector.BanButtonSizeY         = 0.03
HeroSelector.BanTooltip             = "DISALLOWED" -- over the hero button
HeroSelector.BanIgnoreRequirment    = true -- (true) Ban is not restricted by Requirments
--BanDone Button
HeroSelector.BanDoneButtonTextPrefix    = "|cffcf2084" --Prefix Text for the Ban Button
HeroSelector.BanDoneButtonText          = "QUESTACCEPT" --tries to get a Localized String
HeroSelector.BanDoneButtonTooltipText   = "I am done with Banning" --tries to get a Localized String
HeroSelector.BanDoneButtonSizeX         = 0.085
HeroSelector.BanDoneButtonSizeY         = 0.03
HeroSelector.BanDoneButtonIsShown       = true --show this button in the Ban Phase
--BanMany Button
HeroSelector.BanManyButtonTextPrefix    = "|cffcf2084" --Prefix Text for the Ban Button
HeroSelector.BanManyButtonTooltipText    = "String_BanManyButtonTooltip" --Prefix Text for the Ban Button
HeroSelector.BanManyButtonText          = "Massen Bann" --tries to get a Localized String
HeroSelector.BanManyButtonSizeX         = 0.085
HeroSelector.BanManyButtonSizeY         = 0.03
HeroSelector.BanManyBannRemain          = __jarray(0) -- how many bans this button does? write a number in __jarray(x) BanMany bans random heroes of current category
HeroSelector.BanManyButtonIsShown       = false --show this button in the Ban Phase
--Accept Button
HeroSelector.AcceptButtonTextPrefix = ""
HeroSelector.AcceptButtonText       = "ACCEPT"
HeroSelector.AcceptButtonTooltipText       = "ACCEPT"
HeroSelector.AcceptButtonSizeX      = 0.085
HeroSelector.AcceptButtonSizeY      = 0.03
HeroSelector.AcceptButtonIsShown    = true --show this button in the Pick Phase
--Random Button
HeroSelector.RandomButtonTextPrefix = ""
HeroSelector.RandomButtonText       = "RANDOM" --tries Localizing
HeroSelector.RandomButtonTooltipText   = "String_RandomButtonTooltip" --tries Localizing
HeroSelector.RandomButtonTooltipText2   = { [false] ="String_RandomButtonTooltipSelect" ,[true] = "String_RandomButtonTooltipPick"} --tries Localizing
HeroSelector.RandomButtonSizeX      = 0.085
HeroSelector.RandomButtonSizeY      = 0.03
HeroSelector.RandomButtonIsShown    = true --show this button in the Pick Phase
HeroSelector.RandomButtonPick       = false --(true) pressing the random button will pick the option. (false) pressing the random button will select a button, random only heroes can not be selected, but that does not matter. This weak random and randomonly should not be combined.
--Repick Button
HeroSelector.RepickButtonTextPrefix = ""
HeroSelector.RepickButtonText       = "REPICK" --tries Localizing
HeroSelector.RepickButtonTooltipText       = "String_RepickButtonTooltip" --tries Localizing
HeroSelector.RepickButtonTextLimit  = "Repick (#)" --tries Localizing # is the variable for the remaining
HeroSelector.RepickButtonSizeX      = 0.085
HeroSelector.RepickButtonSizeY      = 0.03
HeroSelector.RepickButtonIsShown    = true --show this button in the Pick Phase
HeroSelector.RepickMax              = 0 -- when bigger than 0 only that amount of Repicks is allowed
--ShowButton
HeroSelector.ShowButtonTextPrefix = ""
HeroSelector.ShowButtonText       = "PICK-HERO" --tries Localizing
HeroSelector.ShowButtonSizeX      = 0.085
HeroSelector.ShowButtonSizeY      = 0.03
HeroSelector.ShowButtonIsShown    = false --show this button 
HeroSelector.ShowButtonX          = 0.42
HeroSelector.ShowButtonY          = 0.58
HeroSelector.ShowButtonPoint      = FRAMEPOINT_TOPLEFT
HeroSelector.ShowButtonParent     = function() return BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0) end
--Search
HeroSelector.SearchBoxToolTipText   = "Search in Name & Spells.|nNot used by Random."
HeroSelector.SearchBoxSizeX         = 0.10
HeroSelector.SearchBoxSizeY         = 0.035
HeroSelector.SearchBoxSizeIsShown   = false
--MUltiSelect Button
HeroSelector.MultiSelectButtonTextures    = {"ui\\widgets\\battlenet\\bnet-mainmenu-friends-disabled", "ui\\widgets\\battlenet\\bnet-mainmenu-friends-up"}
HeroSelector.MultiSelectButtonTooltipText = {"Multi Concentrate", "Multi Expand"}
HeroSelector.MultiSelectButtonIsShown    = true 
--MUltiSelect Button
HeroSelector.CategoryClearButtonTexture    = "replaceabletextures\\commandbuttons\\btncancel"
HeroSelector.CategoryClearButtonTooltipText = "Clear Category"
HeroSelector.CategoryClearButtonIsShown    = true
--Tooltip
HeroSelector.TooltipPrefix          = "|cffffcc00"
HeroSelector.TooltipOffsetX         = 0
HeroSelector.TooltipOffsetY         = 0
HeroSelector.TooltipPoint           = FRAMEPOINT_BOTTOM --pos the Tooltip with which Point
HeroSelector.TooltipRelativePoint   = FRAMEPOINT_TOP --pos the Tooltip to which Point of the Relative
HeroSelector.TooltipRelativIsBox    = false          --(true) use the box as anchor, (false) use the button as anchor
HeroSelector.TooltipRequires        = "QUESTCOMPONENTS"
HeroSelector.TooltipRequires2       = {
    [RACE_HUMAN] = "HUMAN"
    ,[RACE_ORC] = "ORC"
    ,[RACE_UNDEAD] = "UNDEAD"
    ,[RACE_NIGHTELF] = "NIGHT_ELF"
    ,[PLAYER_STATE_RESOURCE_HERO_TOKENS] = "HERO TOCKEN"
}


--Border
HeroSelector.BorderSize = {}
HeroSelector.BorderSize[RACE_HUMAN]     = 0.024 --border size seen by Race Human, this is needed cause the borders are different in size.
HeroSelector.BorderSize[RACE_ORC]       = 0.028
HeroSelector.BorderSize[RACE_UNDEAD]    = 0.032
HeroSelector.BorderSize[RACE_NIGHTELF]  = 0.032
HeroSelector.BorderSize[RACE_DEMON]     = 0.024


--This runs before the box is created with that the system has the needed data right when it is needed.
--you can add units somewhere else but it is done after the box was created you have to use the update function to update the textures of shown buttons
function HeroSelector.initHeroes()
    --create categories setuped in config
    local categories = HeroSelector.CategoryData
    HeroSelector.Category = {}
    for index, value in ipairs(categories) do
       HeroSelector.addCategory(value[1], value[2])
    end

    --read GUI, when the variable exist
    if HeroSelector.EnableGUICode and udg_HeroSelectorUnitCode then
        local index = 1
        --add from index 1 all random only heroes
        while udg_HeroSelectorRandomOnly[index] ~= 0 do
            HeroSelector.addUnit(udg_HeroSelectorRandomOnly[index], true)
            index = index + 1
        end

        -- additional0 counts the 0s since last valid number and only add them when a new valid number is found
        -- this is mostly done to allow a big default guiParser number and not add many 0 at the end
        local additional0 = 0
        --copy the setuped field
        for index = 1, HeroSelector.GUIParserLastIndex do
            if udg_HeroSelectorUnitCode[index] > 0 then
                if additional0 > 0 then
                    for i = 1, additional0 do
                        HeroSelector.addUnit(0)
                    end
                    additional0 = 0
                end
                HeroSelector.addUnit(udg_HeroSelectorUnitCode[index])
                if udg_HeroSelectorCategory and udg_HeroSelectorCategory[index] ~= 0 then
                    HeroSelector.addUnitCategory(udg_HeroSelectorUnitCode[index], udg_HeroSelectorCategory[index])
                end
            elseif udg_HeroSelectorUnitCode[index] == 0 then
                additional0 = additional0 + 1
            end            
        end

        --kill the tables
        udg_HeroSelectorUnitCode = nil
        udg_HeroSelectorRandomOnly = nil
        udg_HeroSelectorCategory = nil
    end

    --adding further units when using the GUI Array does not make much sense, except you would add rows.

    if HeroSelector.PickAbleHeroes and (type(HeroSelector.PickAbleHeroes) =="string" and string.len( HeroSelector.PickAbleHeroes ) >= 4 ) then HeroSelector.addUnit(HeroSelector.PickAbleHeroes) end

    local iCat = 1
    for index, value in ipairs(categories) do
        if value[3] then
            HeroSelector.addUnitCategory(value[3], iCat)
        end
        iCat=iCat*2
    end

    --skip further demo code
    if true then return end


    HeroSelector.addUnit("Hgam", true, 0) --antonidas is an only random Hero that can only be randomed by team 0 (for users 1).
    HeroSelector.addUnit("Eevi", true, 1) --evil Illidan is an only random Hero that can only be randomed by team 1 (for users 2).
    
    --Adds requirments
    --when you have a ban phase it might be better to add the requirments after the ban phase is over, otherwise one can only ban own options.
    --paladin requires having 5 footman, Archmage a Hero Tocken other human heroes can only picked by human-Race, as nightelf only for Nightelf
    HeroSelector.setUnitReq('Hpal', {FourCC('hfoo'), 5})
    HeroSelector.setUnitReq('Hamg', PLAYER_STATE_RESOURCE_HERO_TOKENS)
    HeroSelector.setUnitReq('Hblm', RACE_HUMAN)
    HeroSelector.setUnitReq('Hmkg', RACE_HUMAN)
    --HeroSelector.setUnitReq('Ofar', RACE_ORC)
    --HeroSelector.setUnitReq('Oshd', RACE_ORC)
    --HeroSelector.setUnitReq('Otch', RACE_ORC)
    --HeroSelector.setUnitReq('Obla', RACE_ORC)
    HeroSelector.setUnitReq('Emoo', RACE_NIGHTELF)
    HeroSelector.setUnitReq('Edem', RACE_NIGHTELF)
    HeroSelector.setUnitReq('Ekee', RACE_NIGHTELF)
    HeroSelector.setUnitReq('Ewar', RACE_NIGHTELF)
    --HeroSelector.setUnitReq('Udea', RACE_UNDEAD)
    --HeroSelector.setUnitReq('Ulic', RACE_UNDEAD)
    --HeroSelector.setUnitReq('Udre', RACE_UNDEAD)
    --HeroSelector.setUnitReq('Ucrl', RACE_UNDEAD)
    --[[
    local categoryMelee = 1 --autodetected
    local categoryRanged = 2 --autodetected
    local categoryStr = 4
    local categoryAgi = 8
    local categoryInt = 16
    HeroSelector.addUnitCategory('Hpal', categoryStr)
    HeroSelector.addUnitCategory('Hamg', categoryInt)
    HeroSelector.addUnitCategory('Hblm', categoryInt)
    HeroSelector.addUnitCategory('Hmkg', categoryStr)
    HeroSelector.addUnitCategory('Ofar', categoryInt)
    HeroSelector.addUnitCategory('Oshd', categoryAgi)
    HeroSelector.addUnitCategory('Otch', categoryAgi)
    HeroSelector.addUnitCategory('Obla', categoryAgi)
    HeroSelector.addUnitCategory('Emoo', categoryAgi)
    HeroSelector.addUnitCategory('Edem', categoryAgi)
    HeroSelector.addUnitCategory('Ekee', categoryInt)
    HeroSelector.addUnitCategory('Ewar', categoryAgi)
    HeroSelector.addUnitCategory('Udea', categoryStr)
    HeroSelector.addUnitCategory('Ulic', categoryInt)
    HeroSelector.addUnitCategory('Udre', categoryStr)
    HeroSelector.addUnitCategory('Ucrl', categoryStr)

    HeroSelector.setUnitCategory('Hgam', categoryInt + categoryRanged)
    HeroSelector.setUnitCategory("Eevi", categoryAgi + categoryMelee)
    --]]
    
    --[[
    HeroSelector.addUnit('Hpal') --add paladin as selectable Hero
    HeroSelector.addUnit('Hamg')
    HeroSelector.addUnit('Hblm')
    HeroSelector.addUnit('Hmkg')
    HeroSelector.addUnit("Obla", true) --this unit can only be randomed
    HeroSelector.addUnit("Ofar")
    HeroSelector.addUnit("Otch", 1) --this unit can only be randomed
    HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    HeroSelector.addUnit() 
    HeroSelector.addUnit("Oshd")
    HeroSelector.addUnit("Edem")
    HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    HeroSelector.addUnit() 
    HeroSelector.addUnit("Ekee")
    HeroSelector.addUnit("Emoo")
    HeroSelector.addUnit("Ewar",true)
    HeroSelector.addUnit("Udea")
    HeroSelector.addUnit("Ulic")
    HeroSelector.addUnit("Udre")
    HeroSelector.addUnit("Ucrl",1)
    --]]
end

function HeroSelector.autoDetectCategory(unitCode)
    if HeroSelector.CategoryAutoDetect then
        if IsUnitIdType(unitCode, UNIT_TYPE_MELEE_ATTACKER) then
            HeroSelector.UnitData[unitCode].Category = 1
        elseif IsUnitIdType(unitCode, UNIT_TYPE_RANGED_ATTACKER) then
            HeroSelector.UnitData[unitCode].Category = 2
        end
        if HeroSelector.CategoryAutoDetectHero and IsUnitIdType(unitCode, UNIT_TYPE_HERO) then
            local unit = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), unitCode, 0, 0, 270)
            local primaryAttribute = BlzGetUnitIntegerField(unit, UNIT_IF_PRIMARY_ATTRIBUTE)
            RemoveUnit(unit)
            if ConvertHeroAttribute(primaryAttribute) == HERO_ATTRIBUTE_STR then
                HeroSelector.UnitData[unitCode].Category = HeroSelector.UnitData[unitCode].Category + 4
            elseif ConvertHeroAttribute(primaryAttribute) == HERO_ATTRIBUTE_AGI then
                HeroSelector.UnitData[unitCode].Category = HeroSelector.UnitData[unitCode].Category + 8
            elseif ConvertHeroAttribute(primaryAttribute) == HERO_ATTRIBUTE_INT then 
                HeroSelector.UnitData[unitCode].Category = HeroSelector.UnitData[unitCode].Category + 16
            end
        end
    end
end

--what happens to the unit beeing picked, player is the one having pressed the button
function HeroSelector.unitCreated(player, unitCode, isRandom)
    bj_lastCreatedUnit = CreateUnit(player, unitCode, GetPlayerStartLocationX(player), GetPlayerStartLocationY(player), 0)
    local unit = bj_lastCreatedUnit
    HeroSelector.PickedUnit[player] = unit
    if isRandom then
        --randomed
    else
        --picked
    end

    if player == Player(1) then
                
    end

    PanCameraToTimedForPlayer(player, GetUnitX(unit), GetUnitY(unit),0)
    SelectUnitForPlayerSingle(unit, player)
    HeroSelector.enablePick(false, player) --only one pick for this player

    if HeroSelector.EnableGUICode then
        globals.udg_HeroSelectorEvent = 0
        globals.udg_HeroSelectorEvent = 1.0
    end
    --print(GetPlayerName(player),"picks",GetUnitName(unit))
end

--happens when the banButton is pressed, player is the one having pressed the button
function HeroSelector.unitBaned(player, unitCode)
    
    HeroSelector.enableBan(false, player) --only one ban
    --print(GetPlayerName(player),"bans",GetObjectName(unitCode))
end

function HeroSelector.buttonSelected(player, unitCode)
    --player who pressed the button
    --unitCode the unitCode selected
    --this is not picked.

    --print(GetPlayerName(player),"selects",GetObjectName(unitCode))
end

function HeroSelector.repick(unit, player)
    
    if not unit then return end

    UnitRemoveBuffsBJ(bj_REMOVEBUFFS_ALL, unit) --this is done to undo metamorph
    local unitCode = GetUnitTypeId(unit)
    if unitCode == 0 then return end

    HeroSelector.counterChangeUnitCode(unitCode, -1, player)

    if not player then
        player = GetOwningPlayer(unit)
    end
    HeroSelector.PickedUnit[player] = nil
    HeroSelector.show(true, player)
    HeroSelector.enablePick(true, player)
    RemoveUnit(unit)
end

function HeroSelector.actionBanDoneButton(frame, player)
    -- HeroSelector.enablePick(true) -> on click finish the Ban Phase and start pick Phase
    HeroSelector.enablePick(true)
end

function HeroSelector.actionShowButton(frame, player)
    if GetLocalPlayer() == player then BlzFrameSetVisible(HeroSelector.Window.WindowHead, not BlzFrameIsVisible(HeroSelector.Window.WindowHead)) end
end

--[[
This functions are found directly below the config and belong to the config.
They also can be hooked but you might lose the default. Could do it like it is done in TeamViewer create a Backup of the current then overwrite it and call the backup in the replacement.

function HeroSelector.unitCreated(player, unitCode, isRandom)
    this function is called when an unit is picked, add here you actions that have to be done for the picked unit

function HeroSelector.buttonSelected(player, unitCode)
    this function is called when an player selects an button, this is not the picking.

function HeroSelector.unitBaned(player, unitCode)
    this function is called when a player bans an unitCode.

function HeroSelector.actionBanDoneButton(frame, player)
    this function happens when the banDone Button is clicked

function HeroSelector.repick(unit[, player])
    if player is skiped unit owner sees the selection
    this will remove the unit from the game.
    Adds thie unitcode of the unit to the randompool

function HeroSelector.autoDetectCategory(unitCode)
    this called on every unit added. It is a good place for simple automatic categorizes, on default it categorizes melee as 1 and ranged as 2.

function HeroSelector.initHeroes()
    this function will be called before anything is created, when not using GUI to setup data you could add the selectable heroes here.
------
How use the Selection grid?
Each hero can only be once in the grid. When using HeroSelector.addUnit it will add a new slot. There are HeroSelector.ButtonColCount*HeroSelector.ButtonRowCount slots.
3 rows with 4 cols would result into:
01 02 03 04
05 06 07 08
09 10 11 12

When you want to leave fields in the grid empty use HeroSelector.addUnit(0) or HeroSelector.addUnit().
There is a GUI setup which works with indexes, not set indexes will be empty fields.
------
function HeroSelector.setUnitReq(unitCode, who)
    adds an requirement: can be a player, a force, a teamNumber, a race, a table {techcode, level}, playerState (need to have 1 of it, mostly useful for PLAYER_STATE_RESOURCE_HERO_TOKENS), skip who or nil will remove an requirment.
    unitCode can be a number or a string 'Hpal' or 'Hpal,Ofar,Ulic'
    Only when the local player fullfills than he can click the button.
    calling this will not update the selected buttonIndex of players nor does this update the clickability.
    To update the clickability when setting requirments after the Box was created use HeroSelector.update() and deselect indexes
    won't work when the unitCode wasn't added yet.
    
function HeroSelector.addUnit([unitCode, onlyRandom, requirement])
    unitCode can be a number or a string 'Hpal' or 'Hpal,Ofar,Ulic,0,Obla'
    can be called without arguments to hava a empty slot calling it with 0 has the same effect
    requirement works like who in HeroSelector.setUnitReq.

function HeroSelector.setUnitCategory(unitCode, category)
    unitCode can be a number or a string 'Hpal' or 'Hpal,Ofar,Ulic'
    sets the category of an added Option.
    Category should be a power 2 number. 1 2 4 8 16 32 ....

function HeroSelector.addUnitCategory(unitCode, category)
    unitCode can be a number or a string 'Hpal' or 'Hpal,Ofar,Ulic'
    Keeps previous setings untouched

function HeroSelector.addCategory(icon, text)
    icon is the enabled image, text is the tooltip text.

function HeroSelector.clearUnitData()
    removes all current UnitData this includes limit-counters, requirements, categories.

function HeroSelector.show(flag, [who])
    Shows/Hides HeroSelector to who
    flag = true show it, false = hide it
    who can be a player, a force, a teamNumber, a race or nothing = anyone
    teamNumbers are the warcraft 3 given teamNumbers starting with 0 for team 1.
    the force is expected to be kept alive

function HeroSelector.setFrameText(frame, text[, who])
    uses BlzFrameSetText onto frame when the local player is included in who by the rules of function HeroSelector.includesPlayer
function HeroSelector.setTitleText(text[, who])
    wrapper HeroSelector.setFrameText
function HeroSelector.setBanButtonText(text[, who])
    wrapper HeroSelector.setFrameText
function HeroSelector.setAcceptButtonText(text[, who])
    wrapper HeroSelector.setFrameText

function HeroSelector.viewMode([who])
    can click all herobuttons but has no ban/pick buttons

function HeroSelector.enablePick(flag[, who])
    enable/disable the accept/random button also makes them visible for that players and hides the ban Button.
    
function HeroSelector.enableBan(flag[, who])
    enable/disable the ban button also makes accept/random invisible for that players and shows the ban Button.

function HeroSelector.forceRandom([who])
    wrapper for doRandom for player

function HeroSelector.forcePick([who])
    forces to pick what currently is selected, if that fails doRandom

function HeroSelector.buttonRequirementDone(unitCode, player)

function HeroSelector.deselectButtons([buttonIndex])
    deselect selected buttons for all players with 0 or nil
    when an index is given only this specific buttonIndex

function HeroSelector.update()
    reDo possible selection, textures and enability for all heroButtons.

function HeroSelector.getDisabledIcon(icon)
    ReplaceableTextures\CommandButtons\BTNHeroPaladin.tga -> ReplaceableTextures\CommandButtonsDisabled\DISBTNHeroPaladin.tga

function HeroSelector.showFrame(frame, flag[, who])
    Set the visibility of frame to flag when who includes the local player by the rules of function HeroSelector.includesPlayer

function HeroSelector.includesPlayer(who, player)
    does player include who?
    return true, if yes.
    return false otherwise
    who can be a number(GetPlayerTeam), a race(GetPlayerRace), a player, a force(BlzForceHasPlayer) or
    nil => true    

function HeroSelector.counterChangeUnitCode(unitCode, add, player)
    increases/decreases the counter for picks of unitCode for the player's team.
    This can allow/disallow picking this unit for that team.

function HeroSelector.rollOption(player, includeRandomOnly, excludedIndex, category)
    get an random Unitcode from the added options
    returns an unitcode or nil when none could be found

function HeroSelector.categoryUsed([unitCode])
    returns a string listing the categories and the amount of heroes for each.
    Or a table containg the category numbers used by the Hero

function HeroSelector.listHeroes(returnOneLineUnitCodes)
    returns a string of pickable UnitCodes and names
    (true) return "UnitCode,UnitCode,UnitCode"
--]]


JASS:
library HeroSelector initializer init_function requires optional FrameLoader, optional Ascii
//HeroSelector V1.9h
//API
//=====
//HeroSelectorForcePick()
//HeroSelectorForcePickPlayer(player p)
//HeroSelectorForcePickRace(race r)
//HeroSelectorForcePickTeam(integer teamNr)

//HeroSelectorForceRandom()
//HeroSelectorForceRandomRace(race r)
//HeroSelectorForceRandomTeam(integer teamNr)

//HeroSelectorDoPick(player p)
//HeroSelectorDoRandom(player p)

//HeroSelectorShow(boolean flag)
//HeroSelectorShowForce(boolean flag, force f)
//HeroSelectorShowRace(boolean flag, race r)
//HeroSelectorShowPlayer(boolean flag, player p)
//HeroSelectorShowTeam(boolean flag, integer teamNr)

//HeroSelectorViewMode()

//HeroSelectorEnableBan(boolean flag)
//HeroSelectorEnableBanRace
//HeroSelectorEnableBanTeam
//HeroSelectorEnableBanPlayer
//HeroSelectorEnableBanForce

//HeroSelectorEnablePick(boolean flag)
//HeroSelectorEnablePickForce
//HeroSelectorEnablePickPlayer
//HeroSelectorEnablePickTeam
//HeroSelectorEnablePickRace

//HeroSelectorRollOption(player p, boolean includeRandomOnly, integer exculdedIndex, integer category) returns integer
//HeroSelectorCounterChangeUnitCode(integer unitCode, integer add, player p)
//HeroSelectorEnableButtonIndex(integer unitCode, integer buttonIndex)
//HeroSelectorDisableButtonIndex(integer buttonIndex, integer teamNr)
//HeroSelectorButtonRequirementDone(integer unitCode, player p) returns boolean
//HeroSelectorDeselectButton(integer buttonIndex)

//HeroSelectorAddUnit(integer unitCode, boolean onlyRandom)
//HeroSelectorAddUnitCategory(integer unitCode, integer category)
//HeroSelectorSetUnitCategory(integer unitCode, integer category)
//HeroSelectorSetUnitReqPlayer(integer unitCode, player p)
//HeroSelectorSetUnitReqPlayerState(integer unitCode, playerstate ps)
//HeroSelectorSetUnitReqRace(integer unitCode, race r)
//HeroSelectorSetUnitReqForce(integer unitCode, force f)
//HeroSelectorSetUnitReqTeam(integer unitCode, integer teamNr)
//HeroSelectorSetUnitReqTechLevel(integer unitCode, integer techCode, integer techLevel)

//HeroSelectorSetFrameText(framehandle frame, string text)
//HeroSelectorSetFrameTextPlayer
//HeroSelectorSetFrameTextForce
//HeroSelectorSetFrameTextTeam
//HeroSelectorSetFrameTextRace

//HeroSelectorSetTitleText(string text)
//HeroSelectorSetTitleTextRace
//HeroSelectorSetTitleTextPlayer
//HeroSelectorSetTitleTextForce
//HeroSelectorSetTitleTextTeam

//HeroSelectorSetBanButtonText
//HeroSelectorSetBanButtonTextPlayer
//HeroSelectorSetBanButtonTextRace
//HeroSelectorSetBanButtonTextForce
//HeroSelectorSetBanButtonTextTeam

//HeroSelectorSetRandomButtonText
//HeroSelectorSetRandomButtonTextPlayer
//HeroSelectorSetRandomButtonTextForce
//HeroSelectorSetRandomButtonTextTeam
//HeroSelectorSetRandomButtonTextRace

//HeroSelectorSetAcceptButtonText
//HeroSelectorSetAcceptButtonTextPlayer
//HeroSelectorSetAcceptButtonTextForce
//HeroSelectorSetAcceptButtonTextTeam
//HeroSelectorSetAcceptButtonTextRace

//HeroSelectorAddCategory(string icon, string text) //should only be used before the category Buttons are created

//HeroSelectorGetDisabledIcon(string iconPath)

//HeroSelectorUpdate()

//=====

    
    globals
        //Setup
        private string TocPath            = "war3mapImported\\HeroSelector.toc" //ex/import also "HeroSelector.fdf"
        //Box
        private string BoxFrameName            = "HeroSelectorRaceBox" //this is the background box being created
        private string BackgroundTexture      = ""  // if this is not "" it displays it over the box background
        private integer BackgroundTiles        = 1 // 0=streched  1=tileMode
        private real BoxPosX                   = 0.3
        private real BoxPosY                   = 0.4
        private framepointtype BoxPosPoint     = FRAMEPOINT_CENTER
        private boolean AutoShow               = true //(true) shows the box and the Selection at 0.0 for all players
        private boolean AutoCreate             = true //(true) create itself when gameStarted. (false) you need to HeroSelectorAction_InitHeroes(), HeroSelectorInit() FrameLoaderAdd(function HeroSelectorInit)
        //Unique Picks
        public integer UnitCount              = 2 //each hero is in total allowed to be picked this amount of times (includes random, repicking allows a hero again).
        public integer UnitCountPerTeam       = 1 //Each Team is allowed to pick this amount of each unitType
        private string  ToManyTooltip          = "OUTOFSTOCKTOOLTIP"
        public boolean ViewMode = false // set HeroSelector_ViewMode = true after picking was done to allow selecting all heroes in the ui but disables all

        // PickAbleHeroes is a Way to define pickable Units without touching initHeroes
        // pickable options expects one string like "Hamg,Hmkg,Hpal,Hblm". It can contain 0 to have empty slots "Hamg,0,Hpal,0,Hblm"
        // all melee heroes "Hamg,Hmkg,Hpal,Hblm,Obla,Ofar,Otch,Oshd,Udea,Ulic,Udre,Ucrl,Edem,Ekee,Emoo,Ewar,Nngs,Nbrn,Npbm,Nplh,Nbst,Nfir,Ntin,Nalc"
        // works only when you have Libary Ascii in your map
        // First PickAbleHeroes then InitHeroes
        public string PickAbleHeroes         = ""
        //Ban
        private boolean DelayBanUntilPick      = false //(true) baning will not be applied instantly, instead it is applied when HeroSelectorEnablePick is called the next time.
        //Category
        private boolean CategoryAffectRandom   = true  //(false) random will not care about selected category
        private boolean CategoryMultiSelect    = false //(false) deselect other category when selecting one, (true) can selected multiple categories and all heroes having any of them are not filtered.
        private real CategorySize              = 0.02  //the size of the Category Button
        private real CategorySpaceX            = 0.0008 //space between 2 category Buttons, it is meant to need only one line of Categoryy Buttons.
        private integer CategoryFilteredAlpha  = 45     // Alpha value of Heroes being filtered by unselected categories
        private boolean CategoryAutoDetect   = true  // try to set category of added options automatic
        private boolean CategoryAutoDetectHero = false // Will create and remove added Heroes to read and setup the Category for the primary Attribute Str(4) Agi(8) Int(16)
            //Icon path, tooltip Text (tries to localize)
        //Indicator
        private framehandle IndicatorSelected
        private framehandle IndicatorSelectedParent
        private string IndicatorPathPick       = "UI\\Feedback\\Autocast\\UI-ModalButtonOn.mdl" //this model is used by the indicator during picking
        private string IndicatorPathBan        = "war3mapImported\\HeroSelectorBan.mdl" //this model is used by the indicator during baning
        //Grid
        private real SpaceBetweenX             = 0.008 //space between 2 buttons in one row
        private real SpaceBetweenY             = 0.008 //space between 2 rows
        private integer ButtonColCount         = 4 //amount of buttons in one row
        private integer ButtonRowCount         = 4 //amount of rows
        
        private boolean ChainedButtons         = true //(true) connect to the previous button/ or row, (false) have a offset to the box topLeft in this moving a button has no effect on other buttons.
        //Button
        private real ButtonSize                = 0.036 //size of each button
        private boolean ButtonBlendAll         = false //(true) when a hero icon uses transparenzy
        private string EmptyButtonPath         = "UI\\Widgets\\EscMenu\\Human\\blank-background.blp"
        private boolean HideEmptyButtons       = true
        //Ban Button
        private string BanButtonTextPrefix     = "|cffcf2084" //Prefix Text for the Ban Button
        private string BanButtonText           = "CHAT_ACTION_BAN" //tries to get a Localized String
        public string BanButtonTooltipText   = "CHAT_ACTION_BAN" //over the ban button
        private real BanButtonSizeX            = 0.085
        private real BanButtonSizeY            = 0.03
        private string BanTooltip              = "DISALLOWED"
        private boolean BanIgnoreRequirment    = true // (true) Ban is not restricted by Requirments
        //BanDone Button
        private string BanDoneButtonTextPrefix    = "|cffcf2084" //Prefix Text for the Ban Button
        private string BanDoneButtonText          = "QUESTACCEPT" //tries to get a Localized String
        public string BanDoneButtonTooltipText   = "I am done with Banning" //tries to get a Localized String
        private real BanDoneButtonSizeX         = 0.085
        private real BanDoneButtonSizeY         = 0.03
        public boolean BanDoneButtonIsShown       = false //show this button in the Ban Phase
        //BanMany Button
        private string BanManyButtonTextPrefix    = "|cffcf2084" //Prefix Text for the Ban Button
        private string BanManyButtonTooltipText    = "String_BanManyButtonTooltip" //Prefix Text for the Ban Button
        private string BanManyButtonText          = "Massen Bann" //tries to get a Localized String
        private real BanManyButtonSizeX         = 0.085
        private real BanManyButtonSizeY         = 0.03
        public integer array BanManyBannRemain // how many bans this button does? write a number in HeroSelector_BanManyBannRemain[PlayerId] = x BanMany bans random heroes of current category
        public boolean BanManyButtonIsShown       = false //show this button in the Ban Phase
        //Accept Button
        private string AcceptButtonTextPrefix       = ""
        private string AcceptButtonText             = "ACCEPT"
        public string AcceptButtonTooltipText       = "Pick selected Hero"
        private real AcceptButtonSizeX              = 0.085
        private real AcceptButtonSizeY              = 0.03
        public boolean AcceptButtonIsShown         = true
        //Random Button
        private string RandomButtonTextPrefix       = ""
        private string RandomButtonText             = "RANDOM" //tries Localizing
        public string RandomButtonTooltipText      = "Includes Categories but no Text Search" //tries Localizing
        public string RandomButtonTooltipTextSel   = "Only Select"
        public string RandomButtonTooltipTextPick  =  "Select & Pick"
        private real RandomButtonSizeX              = 0.085
        private real RandomButtonSizeY              = 0.03
        private boolean RandomButtonIsShown         = true
        private boolean RandomButtonPick            = false //(true) pressing the random button will pick the option. (false) pressing the random button will select a button, random only heroes can not be selected, but that does not matter. This weak random and randomonly should not be combined.
        //Repick Button
        private string RepickButtonTextPrefix = ""
        private string RepickButtonText       = "REPICK" //tries Localizing
        public string RepickButtonTooltipText       = "Remove your Selected Hero" //tries Localizing
        private real RepickButtonSizeX      = 0.085
        private real RepickButtonSizeY      = 0.03
        public boolean RepickButtonIsShown    = true //show this button in the Pick Phase
        public integer RepickMax              = 0 // when bigger than 0 only that amount of Repicks is allowed
        public integer array RepicksDone
        //ShowButton
        private string ShowButtonTextPrefix = ""
        public string ShowButtonText       = "PICK-HERO" //tries Localizing
        public real ShowButtonSizeX      = 0.085
        public real ShowButtonSizeY      = 0.03
        public boolean ShowButtonIsShown    = false //show this button 
        public real ShowButtonX          = 0.42
        public real ShowButtonY          = 0.58
        public framepointtype ShowButtonPoint      = FRAMEPOINT_TOPLEFT
        //Tooltip
        private string TooltipPrefix                  = "|cffffcc00"
        private real TooltipOffsetX                   = 0
        private real TooltipOffsetY                   = 0
        private framepointtype TooltipPoint           = FRAMEPOINT_BOTTOM //pos the Tooltip with which Point
        private framepointtype TooltipRelativePoint   = FRAMEPOINT_TOP //pos the Tooltip to which Point of the Relative
        private boolean TooltipRelativIsBox           = false          //(true) use the box as anchor, (false) use the button as anchor
        private string TooltipRequires                = "QUESTCOMPONENTS"

        //System variables, Do not touch
        public integer HeroButtonCount        = ButtonRowCount*ButtonColCount
        public unit array PickedUnit
        public trigger CategoryClickTrigger   = CreateTrigger()
        public trigger AcceptButtonTrigger  = CreateTrigger()
        public trigger BanButtonTrigger      = CreateTrigger()
        public trigger BanManyButtonTrigger      = CreateTrigger()
        public trigger RandomButtonTrigger  = CreateTrigger()
        public trigger RepickButtonTrigger  = CreateTrigger()
        public trigger BanDoneButtonTrigger  = CreateTrigger()
        public trigger ShowButtonTrigger  = CreateTrigger()
        
        public framehandle BanButton
        public framehandle AcceptButton
        public framehandle RandomButton
        public framehandle BanDoneButton
        public framehandle BanManyButton
        public framehandle RepickButton
        public framehandle ShowButton
        private player array DelayBanPlayer
        private integer array DelayBanUnitCode
        private integer DelayBanCount = 0
        public framehandle array CategoryButton
        public framehandle array CategoryIconFrame
        public framehandle array CategoryIconPushedFrame        
        public framehandle array CategoryTooltipFrame
        public framehandle array CategoryTooltipFrameBox
        public string array CategoryText
        public string array CategoryTexture
        public string array CategoryTextureDisabled
        private integer array CategoryButtonValue
        public integer CategoryButtonCount = 0
        private integer array UsedTeamNr
        private integer UsedTeamNrCount = 0

        private integer ButtonHeroCount = 0        
        private integer array ButtonHeroUnitCode


        private integer array HeroTotalCount
        public integer array HeroCategory
        private integer array HeroRegType
        private player array HeroRegPlayer
        private playerstate array HeroRegPlayerState
        private force array HeroRegForce
        private integer array HeroRegNumber
        private integer array HeroRegNumber2
        private race array HeroRegRace

        private integer HeroCount = 0
        private integer array HeroUnitCode
        private integer array HeroButtonIndex


        public hashtable Hash = InitHashtable()
        framehandle HeroSelectorBox
        private framehandle HeroSelectorBoxSeperator
        private framehandle HeroSelectorBoxTitle

        private integer array HeroButtonUnitCode
        private integer array HeroButtonUnitCodeDisplayed
        private integer HeroButtonUnitCodeCount = 0
        private framehandle array HeroButtonIcon
        private framehandle array HeroButtonIconPushed        
        private framehandle array HeroButtonIconDisabled
        private framehandle array HeroButtonTooltip
        private framehandle array HeroButtonTooltipBox
        private framehandle array HeroButtonFrame
        private trigger HeroButtonClickTrigger = CreateTrigger()

        private integer array PlayerSelectedButtonIndex
        private integer array PlayerSelectedCategory
        private integer array PlayerLastSelectedCategoryIndex

        private integer LastAction = 0
    endglobals

    public function ShowButtonParent takes nothing returns framehandle
        return BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0)
    endfunction
    private function AutoDetectCategory takes integer unitCode returns integer
        local integer value = 0
        local unit u
        local integer primaryAttribute
        if CategoryAutoDetect then
            if IsUnitIdType(unitCode, UNIT_TYPE_MELEE_ATTACKER) then
                set value = 1            
            elseif IsUnitIdType(unitCode, UNIT_TYPE_RANGED_ATTACKER) then
                set value = 2
            endif
            if CategoryAutoDetectHero and IsUnitIdType(unitCode, UNIT_TYPE_HERO) then
                set u = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), unitCode, 0, 0, 270)
                set primaryAttribute = BlzGetUnitIntegerField(u, UNIT_IF_PRIMARY_ATTRIBUTE)
                call RemoveUnit(u)
                set u = null

                if ConvertHeroAttribute(primaryAttribute) == HERO_ATTRIBUTE_STR then
                    set value = value + 4
                elseif ConvertHeroAttribute(primaryAttribute) == HERO_ATTRIBUTE_AGI then
                    set value = value + 8
                elseif ConvertHeroAttribute(primaryAttribute) == HERO_ATTRIBUTE_INT then 
                    set value = value + 16
                endif
            endif
        endif
        return value
    endfunction

    private function GetBorderSize takes nothing returns real
        if GetPlayerRace(GetLocalPlayer()) == RACE_HUMAN then
            return 0.029
        elseif GetPlayerRace(GetLocalPlayer()) == RACE_ORC then
            return 0.029
        elseif GetPlayerRace(GetLocalPlayer()) == RACE_UNDEAD then
            return 0.035
        elseif GetPlayerRace(GetLocalPlayer()) == RACE_NIGHTELF then
            return 0.035
        elseif GetPlayerRace(GetLocalPlayer()) == RACE_DEMON then
            return 0.024
        else 
            return 0.0
        endif
    endfunction



  • HeroSelectorGUI Plus
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- Random Only Hero do not take slots --------
      • Set HeroSelectorRandomOnly[1] = Klingenmeister
      • Set HeroSelectorRandomOnly[2] = Tauren-Häuptling
      • Set HeroSelectorRandomOnly[3] = Gruftlord
      • Set HeroSelectorRandomOnly[4] = Wächterin
      • -------- Positions of the UnitCodes taking buttons. --------
      • -------- Can only use upto HeroSelector.ButtonColCount*HeroSelector.ButtonRowCount fields --------
      • -------- The first button start at the top left with index 1. --------
      • -------- Unset fields are autofilled with empty slots. --------
      • Set HeroSelectorUnitCode[2] = Paladin
      • Set HeroSelectorUnitCode[3] = Erzmagier
      • Set HeroSelectorUnitCode[5] = Bergkönig
      • Set HeroSelectorUnitCode[6] = Blutmagier
      • Set HeroSelectorUnitCode[7] = Scharfseher
      • Set HeroSelectorUnitCode[8] = Schattenjäger
      • Set HeroSelectorUnitCode[9] = Dämonenjäger
      • Set HeroSelectorUnitCode[10] = Hüter des Hains
      • Set HeroSelectorUnitCode[11] = Mond-Priesterin
      • Set HeroSelectorUnitCode[12] = Todesritter
      • Set HeroSelectorUnitCode[14] = Lich
      • Set HeroSelectorUnitCode[15] = Schreckenslord
  • Start
    • Events
      • Time - Elapsed game time is 0.10 seconds
    • Conditions
    • Actions
      • Player Group - Pick every player in (All players) and do (Actions)
        • Loop - Actions
          • Unit - Create 1 Circle der Macht for (Picked player) at ((Picked player) start location) facing Default building facing degrees
      • -------- When the autoShowing is off, the HeroSelector has to be shown first --------
      • Custom script: HeroSelector.show(true)
      • -------- Hide picking buttons and show the bann button --------
      • Custom script: HeroSelector.enableBan(true)
  • BaningPhase
    • Events
      • Time - Every 0.75 seconds of game time
    • Conditions
    • Actions
      • -------- Runs 3 times in 4 seconds changes the Title. --------
      • -------- Show Remaining Counts in the Titel --------
      • Set Count = (31 - (Execution count of (This trigger)))
      • Custom script: HeroSelector.setTitleText( "Baning: " .. udg_Count)
      • -------- Time Run up? --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Count Less than or equal to 0
        • Then - Actions
          • -------- Enable Picking --------
          • Custom script: HeroSelector.enablePick(true)
          • -------- Swap Active Trigger (Phase) --------
          • Trigger - Turn off (This trigger)
          • Trigger - Turn on Picking Phase <gen>
          • -------- Update Titel Right Now --------
          • Custom script: HeroSelector.setTitleText( "Picking: ")
        • Else - Actions
  • Picking Phase
    • Events
      • Time - Every 0.75 seconds of game time
    • Conditions
    • Actions
      • -------- Runs 3 times in 4 seconds changes the Title. --------
      • Set Count = (31 - (Execution count of (This trigger)))
      • Custom script: HeroSelector.setTitleText( "Picking: " .. udg_Count)
      • -------- The Time run up? --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • Count Less than or equal to 0
        • Then - Actions
          • -------- Pick everyone --------
          • Player Group - Pick every player in (All players) and do (Actions)
            • Loop - Actions
              • Custom script: bj_wantDestroyGroup = true
              • -------- This one has a hero? --------
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Random unit from (Units owned by (Picked player) matching (((Matching unit) is A Hero) Equal to True))) Equal to No unit
                • Then - Actions
                  • -------- No, force a Pick for him --------
                  • Custom script: HeroSelector.forcePick(GetEnumPlayer())
                • Else - Actions
          • Trigger - Turn off (This trigger)
          • Custom script: HeroSelector.destroy()
        • Else - Actions


Credits (jass):
Nestharus/Bribe

changelog:
2.2d/1.9i) skip some unneeded BlzFrameSetTexture, added HeroSelectorViewMode()/HeroSelector.viewMode([who])
2.2) new HeroInfos, bug Fix RandomPick, Split TeamViewer.UpdateNonAllies
2.1a) TeamViewer Compact
2.1) Improved HeroSelector.getDisabledIcon
2 Lua) scrollable, colapseable, HeroInfo can display generic spell fields
1.9 Lua) better multiselect, TextSearch, MassBann, TextButton Tooltips
1.9 vjass) MassBann, TextButton Tooltips,custom background image,Max Repicks, AutoCreation on/off

1.8d Lua) custom background image
1.8c Lua) some statistic functions
1.8b Lua)
Better support for DisabledIcons
Max Repicks Config
AutoCreation on/off
Improved Category Setup
Do/ForceRandom improved
1.8a) Teamviewer Bugfix, Added ShowButton
1.8)
added requirement playerstate
Improved Req Tooltip
Added config Options

1.7)
Add Repick Button
Add Teamviewer.Reset()
Add Teamviewer swap Feature
Add BanDoneButton
TocPath in the setup section.
1.6a) compatible with TasFrameLoader
1.6)
Added TeamViewer Scale
HeroInfo can display Skills.
(Lua)
Various Bugfixes
function HeroSelector.unitCreated takes now the unitCode as arg instead and has to create the unit.
Can now Prevent options from being Randomed (HeroSelector.UnitDataPool[unitCode] = false)
added function HeroSelector.clearUnitData() to clear current UnitData making it easy to fill it with new data.
Added a GUI Real-Variable-Event for the default unitPicked function in which last created unit is the picked unit
1.5c)
Lua: Fixed a nil Pointer when categories were added after the box was already created: using function HeroSelector.addCategory(icon, text)​
1.5b)
Changed Fdf
Added visual Feedback while pushing Hero/Category Buttons
Bann/Random/Accept Button play a clicked sound.[/INDENT]

1.5)
Improved Teamviewer and added a non Teambased one
Improved Tooltips
Disabled Options show the reason (out of stock / not allowed / requirment)
HeroSelector box border became smaller and a seperator was added
Title Text is limited to the HeroSelector box
Banning can now ignore Requirments (on default enable)
The vJass version allowed to select Heroes which were above the Team limit after HeroSelectorUpdate was called[/INDENT]
Requires a new fdf[/INDENT]
1.4)
Supports TechCode Level as req
GUI can setup manual category mods.
Skips example code in InitHeroes right after the GUI Loader[/INDENT]
1.3b) Various fixes for the demo map Triggers
1.3a) Teamviewer showed non-Allies, when one didn't wanted that.
1.3) Autodetects Primary Attribute of heroes, can be disabled
1.2.1 only vJass) Seperated the Action functions from the main Code, the functions are now evoked by a real Event System.
1.2)
RandomButton can now select, if wanted.
Added Category
Increased BorderSize HUMAN, ORC
Some Bugfixes
TeamViewer the TextFrame Button posing was replaced with a Left2Right boolean[/INDENT]
1.1)
Heroes can now have requirements
Replaced how Random works internally.
Added Settings for Random and Accept Button
The HeroButtons can now be created without being bound together.
Renamed: enablePicking, DelayBannUntilPicking
Teamviewer and HeroInfo overlap now HeroSelector.destroy
bug fixes.​
Contents

HeroSelector (Map)

HeroSelector Jass (Map)

HeroSelector V2+ (Map)

Updated Lua Version to V1.7)
Added a setup to choose the ToCPath so one does not need to change it inside the system code.
HeroSelector.TocPath = "war3mapImported\\HeroSelector.toc" --ex/import also "HeroSelector.fdf"

Added a BanDoneButton when clicked it calls a function which on default ends the ban phase and start the pick phase. The Button can be hidden with a setting. The button shares clickability with the banbutton.
  • This makes it easy to make a host ban phase. In which the host can ban what he wants.
  • Just need to remove HeroSelector.enableBan(false, player) from function HeroSelector.unitBaned(player, unitCode)
  • then right at the start HeroSelector.enableBan(false)
  • HeroSelector.enableBan(true, hostPlayer)

Added a Repick Button below the Pick/Random Button. (Can be hidden with a setup)

Teamviewer now has a swap feature. When one clicks on the HeroIcon of another player a swap is requested. When that player also clicks on the Icon of your TeamViewer Card the heroes get swaped. One also can click on the own face to request swap with all players, this can not finish the swap. Or when one clicks again removes the offer again.
An Icon is shown onto the herocard at the top right corner when that Player requested/offers a swap.
The swap feature only swaps the units picked not more.

Add Teamviewer.Reset()

Teamviewer got a commented additional line in AllowedPlayer when enabled only users are displayed.

Removed the Destroy feature.
Remove frameLoseFocus as TasFrameAction does that
Remove Anchor Button Setup

Edit: Updated the vjass version to V1.7 with alike features.
 
Last edited:
Updated Lua Version to V1.8)
can now require a playerstate, the playerstate is not consumed.

Displays now the reason why something is not allowed when requirements are not fullfilled. Could be changed in function HeroSelector.GetUnitReqString(unitCode)

HeroSelector.setUnitReq/.addUnit/.setUnitCategory/.addUnitCategory work with multiple unitCodes in a row, like in Object Editor Tavern Sell List "Hamg,Hmkg,Hpal,Hblm". This also supports 0 to have an empty slot "Hamg,Hmkg,Hpal,0,Hblm"

New config HeroSelector.PickAbleHeroes a way to setup Pickable options right in the config. "Hamg,Hmkg,Hpal,0,Hblm". Would add Archmage, Mountain King, Paladin, empty Slot, Bloodmage.

This works additional to the GUI parser and runs on default after it. Changing it after HeroSelector.initHeroes() run has no effect.

HeroSelector.EnableGUICode a config to dis/enable GUI pickable parser and real throw event. The Throw Event could lead to a nil pointer when globals.udg_HeroSelectorEvent was not in the map with the demo version of function HeroSelector.unitCreated(player, unitCode, isRandom).

added the skills of tavern melee heroes to HeroInfo
 
Last edited:
Updated Lua version to V1.8a)
Fixed a bug for Teamviewer and Teamviewer (one). When an player used Heroselector to random/pick a hero without having a slot in Teamviewer a nil pointer happened.

Added a ShowButton to HeroSelector. It is not a child of the Heroselector UI and can Show/Hide it for the clicking player. Default setup hides the button right after creation which can be changed. One could show it by calling HeroSelector.showFrame(HeroSelector.ShowButton, true)
 
Updated vjass Version to V1.8)
added requirement playerstate (mostly for PLAYER_STATE_RESOURCE_HERO_TOKENS)
added an independent Button which Shows/Hides heroselector for the clicking player (default setup hidden) ShowButton
added the tavern melee heroes to HeroInfo
added PickAbleHeroes a way to setup Pickable options right in the globals config of HeroSelector (requires ascii)
display what is required (Human, Team 1, Hero Token)

HeroSelectorAction works now without Teamviewer and without HeroInfo
 
Updated Lua Version to V1.8b)
Better support for DisabledIcons taken TasHeroLearnEx (cache disabledIcon Paths), will use the public lib when provided.
Handles now Limited amount of Repicks in the Config (only counts for the button)
Can now disable AutoCreation in Config.
Pushed the API info below the config

HeroSelector.CategoryData now supports a 3.Field the Heroes using that category.
example:
Lua:
HeroSelector.CategoryData = {
    --Icon path, tooltip Text (tries to localize)
    {"ReplaceableTextures\\CommandButtons\\BTNSteelMelee", "MELEE"},                 --1, automatic detected when adding an unit
    {"ReplaceableTextures\\CommandButtons\\BTNHumanMissileUpOne", "Ranged"},         --2, automatic detected when adding an unit
    {"ReplaceableTextures\\CommandButtons\\BTNGauntletsOfOgrePower", "STRENGTH", 'Hlgr,Nmag,Huth,Ecen,Hpal,Hmkg,Uanb,Ogrh,Nsjs,Utic,Nngs,Otch,Orex,Uear,Udea,Udth,Hhkl,Eill,Nplh,Nbst,Obla,Uvng,Hmgd,Opgh,H007'},  
    {"ReplaceableTextures\\CommandButtons\\BTNSlippersOfAgility", "AGILITY", 'Hdgo,Emfr,Hkal,Ucrl,Hmbr,Uwar,Ocb2,Othr,Nbbc,Eevi,Orkn,Usyl,Ewar,Etyr,Efur,Ntin,Npbm,Edem,Hvwd,Nbrn,Naka,Udre,Osam,Nklj,Umal,Hgam,H008,H00B'},        
    {"ReplaceableTextures\\CommandButtons\\BTNMantleOfIntelligence", "INTELLECT", 'Hamg,Hjai,Npld,Harf,Hant,Uvar,Uktl,Odrt,Ofar,Nman,Ocbh,Ogld,Oshd,Ulic,Hpb1,Ubal,Ewrd,Hblm,Emns,Ekgg,Emoo,Ekee,Nfir,Nalc,Hvsh,Hpb2'},  
    {"ReplaceableTextures\\CommandButtons\\BTNPortal", "String_Summoner", 'Hdgo,Harf,Hant,Uanb,Uvar,Ucrl,Odrt,Othr,Nbbc,Nsjs,Ofar,Nman,Eevi,Otch,Oshd,Uear,Ulic,Ubal,Usyl,Udea,Ewar,Efur,Emns,Ekgg,Udth,Ekee,Edem,Hvwd,Nfir,Nplh,Naka,Nalc,Udre,Osam,Nklj,Umal,Nbst,Npld,Hvsh,H008,H00B'},   

    {"ReplaceableTextures\\CommandButtons\\BTNStun", "String_Stun", 'Hpal,Hmkg,Uanb,Udth,Hmbr,Hmgd,Uvar,Hamg,Hant,Ekee,Emns,Ekgg,Ewar,Efur,Npbm,Naka,Obla,Otch,Udea,Utic,Orex,Uear,Nplh,Nbst,Nman,Ocbh,Ogld,Ulic,Ubal,Oshd,Orkn,Udre,Opgh'},    
    {"ReplaceableTextures\\CommandButtons\\BTNRingGreen", "String_Equip", 'Nmag,Huth,Hhkl,Hmgd,Uvar,Hdgo,Hkal,Edem,Ewar,Nsjs,Hpb1,Ocbh,Osam,Nalc,Ocb2'},   
}

Do/ForceRandom now work without having the UI created.
 
Last edited:
Hey Tasyen, so I been trying to modify the system itself in order to change from ban/accept buttons to just double clicking. I was able to do it for the "pick" part of the system, but the ban version seems to be rather different and even through looking at the vast majority of the internal ban functions, I cannot find where in the code the purple highlight is made visible in the system, which I assumed is when the unitcode is "picked" so you can use the ban button.
 
HeroSelector has only one SPRITE-Frame for the highlighter. It changes the used model and also the buttons when the enableBan or enablePick functions are used and the local player is included in the arg who.

actionPressHeroButton is the function that runs on hero button click. You could here make a check for double click

I talked about the Lua version.

Edit: All My Code in HeroSelector uses Ban not Bann.
 
Last edited:
Updated the Lua version to V1.9 which includes changes from the not published versions V1.8c and V1.8d.
V1.8c)
Added function HeroSelector.categoryUsed([untiCode]) Lists the categoires having heroes and how many.
Or when given an unitcode an table containing the category numbers
function HeroSelector.listHeroes(returnOneLineUnitCodes)
returns a string of pickable UnitCodes and names

V1.8d)
can display a custom background from map script in tile mode or stretched

V1.9 Lua)
Added a SearchBar, MassBann, Tooltips for Textbuttons, Multiselect expand/concentrate, Clear Category. the new buttons can be hidden in the options.

Searchbar allows to write texts and Alphas out heroes that dont have the text inside their name, tooltips or spell tooltips. Text searching is ignored by random.
MassBann is a button to bann many random heroes of the current category at once.
Teamviewer categories will not keep focus anymore, when clicked.
new fdf

HeroSelector.CategoryMultiSelect was replaced by HeroSelector.CategorySingleSelect. HeroSelector.CategorySingleSelect (array) (true) enforces Deselecting categories when choosing a new category can be set for each player unique but needs to be sync.

Multiselecting categories has now 2 modes units need to fit one or all categories. the user can choose the current mode when the Multiselect buttons are visible
Expand vs Conentrate.png
 
Last edited:
Updated Lua to V2)
Requires TasWindow and TasFrameList
Can now collapse HeroSelector and Scroll the hero buttons
Added HeroSelector.GUIParserLastIndex
Removed HeroSelector.ChainedButtons
Updated HeroInfo Skill Preview, it can now display generic spell fields additional to the text, this feature can be disabled.

one can still download V1.9 in case one wants HeroSelector without scrolling and without window. Although it wont get updates.
GenTooltipInfo a.jpg

Edit:
Small Fix V2.1)
HeroSelector.getDisabledIcon now returns "" in some edge cases

for default melee heroes it failed for goblin tinker hero 's ANeg skill
 
Last edited:
So i got this very strange bug with the Jass version that says that every hero always has the defend ability. Any clue what could cause this? I don't remember changing anything with the UI for abilities shown in the selection phase.
 
can happen with ability auto detection enabled and unit indexers, they often add a hidden defend ability to detetect/provide features and new events.

make the defend ability an item ability or its tooltip title to "" or " ".
Such abilities are ignored by HeroInfo.
if you need another filter edit this function in HeroInfo. one could add an new if case which return false for some other reason.
JASS:
public function ValidTooltip takes string text returns boolean
    if text == "Tool tip missing!" or text == "" or text == " " then
        return false
    endif
    return true
endfunction
public function abiFilter takes ability abi returns boolean
    // no skills markes as item skills
    if BlzGetAbilityBooleanField(abi, ABILITY_BF_ITEM_ABILITY) then
        return false
    endif
    if not ValidTooltip(BlzGetAbilityStringLevelField(abi, ABILITY_SLF_TOOLTIP_NORMAL, 0)) then
        return false
    endif
    return true
endfunction
 
Last edited:
I really like your work. Is there any possibility that in the future it could be compatible with a custom hero ability system? Like this. This basically works with engineering upgrade skills for the skills you want. It is the only thing that would be missing from this system (my opinion).
 
I made a heroSkill system. With a custimzeable skill pool, even so that this hero has a special skill pool. Check it out. It is not connected to HeroSelector.
 
I like this system better because of the introduction of the heroes and their powers, but it's okay if you don't want to do it. Something similar to this video, from minute 0:00 to 6:50, it would have been perfect in your system.
 
For something like legends of dota you need 2 things.
A skills set creator.
Then you pick a Hero.
Now that you have a hero unit, you need something to learn the custom skill set.

TasHeroLearnEx can do the learning after the skillset was choosen. You did this with many engineering upgrade skills & default warcraft 3 hero learning.

i am sure that HeroSelector could be used to pick skills instead of heroes with some tweaks. But I am currently not sure, if one can make HeroSelector be first a skill pick system and than a hero pick system (probably more work than making it with something different).
 
No problem! If you want in the future you could add it. As I was able to see that the heroes abilities could be seen and even generate descriptions of them in your system, I thought it would be easy to do something like that. I will wait patiently if there is a possible future update. Your systems are great!
 
the Lite version of TasItemShop uses the normal warcraft 3 buying, it adds the item to the shop and then orders it to buy it.
For that to work the shop skill (select Hero) needs to be able to select the buyer unit, range & aoe in the select hero skill.
Adding the item to the shop by trigger only works when the shop has the marketplace skill: 'Asid'

Both need to work, otherwise you cant buy items.
 
the Lite version of TasItemShop uses the normal warcraft 3 buying, it adds the item to the shop and then orders it to buy it.
For that to work the shop skill (select Hero) needs to be able to select the buyer unit, range & aoe in the select hero skill.
Adding the item to the shop by trigger only works when the shop has the marketplace skill: 'Asid'

Both need to work, otherwise you cant buy items.
Thank you very much!
 
Updated HeroSelector Lua - 2.1 to HeroSelector Lua - 2.1a
HeroSelector.ParentFunc had wrong default value
Added TeamViewer Compact
The HeroCards are only Icons with PlayerColor background. Details are in the tooltip.

TeamViewer Compact.jpg

Edit: Updated to V2.1b)
Fix ShowButton
Fix AutoShow (false)
both were working like in HeroSelector V1.9 version, but in V2+ they need to target
HeroSelector.Window.WindowHead.

Edit: V2.1c)
Fixed a bug in TeamViewer Compact, Category buttons were visible even when not hovered after the UI was minimized & maximized
 
Last edited:
Updated HeroSelector Lua - 2.1 to HeroSelector Lua - 2.1a
HeroSelector.ParentFunc had wrong default value
Added TeamViewer Compact
The HeroCards are only Icons with PlayerColor background. Details are in the tooltip.

View attachment 537553

Edit: Updated to V2.1b)
Fix ShowButton
Fix AutoShow (false)
both were working like in HeroSelector V1.9 version, but in V2+ they need to target
HeroSelector.Window.WindowHead.

Edit: V2.1c)
Fixed a bug in TeamViewer Compact, Category buttons were visible even when not hovered after the UI was minimized & maximized

Are there any plans to update the JASS version? It would also be good if you added the image examples.
 
I didnt want to update the vjass version. What features you want that the vjass version didnt got.

Edit 26 12 2025: The vjass version will 100% get
textbutton Tooltips
Max Repicks Config
AutoCreation on/off
custom background image

not sure about these yet
generic spell fields
text search
TeamViewer Compact
massbann
Scrolable
 
Last edited:
Hello,
in the hero preview area (the box with the hero icon, player name, and categories), it seems that when a hero is selected and its categories are displayed, clicking the Random button only changes the hero icon, while the categories from the previously selected hero remain unchanged.

In short, in the hero preview section, if a hero is selected and then the player decides to click Random, the categories are not updated accordingly.

I’ll give an example:
1. I have Paladin selected (Support).
1768992394476.png

2. I click the Random button.
1768992579236.png

3. It randomed Warden (Carry, Mage). The hero icon changes, but the categories do not.
1768992416710.png

4. The categories should update to match the randomed hero.
1768992541366.png
 
Hello,
in the hero preview area (the box with the hero icon, player name, and categories), it seems that when a hero is selected and its categories are displayed, clicking the Random button only changes the hero icon, while the categories from the previously selected hero remain unchanged.

In short, in the hero preview section, if a hero is selected and then the player decides to click Random, the categories are not updated accordingly.

I’ll give an example:
1. I have Paladin selected (Support).
View attachment 571434
2. I click the Random button.
View attachment 571437
3. It randomed Warden (Carry, Mage). The hero icon changes, but the categories do not.
View attachment 571435
4. The categories should update to match the randomed hero.
View attachment 571436
Ty, for your report should now be fixed. This error only happened with randombutton do pick a hero.

in the vjass version (V1.9a) only need to copy the new version of the teamviewer you wana use.

the Lua versions (V1.9 and V2.1c) had a error with random only hero hence need to update the heroselector too aswell as the teamviewer.
fixed a nilpointer with randomonly heroes in function HeroSelector.updateTooltip(unitCode)

Added TeamViewerCompact to Lua V1.9a
 
y, makes sense to not display the msg when swap is disabled. updated teamviewer any for all versions.

updated V2.1 to V2.2)
it got 2 new heroinfos
one heroinfoNew is the old with tooltipboxes and shows categories for the current selected. One can choose to have a simple texttooltip for the ability preview

heroInfoAttach changes heroselector ui, it places heroinfo buttons inside of heroselector and changes it a bit. shows no text boxes until you hover any of the new buttons.
 

Attachments

  • AttachA.jpg
    AttachA.jpg
    137.3 KB · Views: 18
  • HeroInfoNew.jpg
    HeroInfoNew.jpg
    167.7 KB · Views: 22
Last edited:
Thanks for this resource and your continued effort in updating it. I am the creator of the Mini Dota Reforged map, which is currently available on the W3Champions ladder (where another map, which also uses the Hero Selector (Frostcraft) was previously hosted). This resource is excellent, and I am currently implementing it into our map.

I believe I have found a critical issue in the system. Specifically, when attempting to ban a hero at the very last second, clicking the Ban button seems to overwrite the Picking Phase, effectively blocking access to all buttons available during that phase. (I am using the latest JASS version. I also tested this on your original map to make sure the issue is not caused by my own modifications.)

I consider this issue critical because it becomes impossible to pick a hero. What’s left is to wait until the end of the Picking Phase and get a random one.

After several attempts, I learned how to time the Ban button click in a way that allows me to trigger this bug . The time window in which this happens is extremely short. But at first, I was unable to reproduce the issue at all.

To better explain and visualize the problem, I recorded a short video where the issue is clearly visible. I also included frame by frame playback to potentially make debugging easier and to show exactly what happens during the transition.


It appears as if the game successfully enters the Picking Phase for a fraction of a second and then immediately reverts it. You can also notice that what seem to be random bans are reverted, hero icons become disabled briefly, and then return to their previous state.

Hero Selector Bug youtu.be/hxbV9OFygUU
 
this is bad, y.
I uploaded HeroSelector - jass - 1.9c, in the GUI ban phase trigger i added a disable ban phase when the counter is 0. this hopefully prevents clicking the ban-button in the pick phase.
this as first action in the if counter <= 0
call HeroSelectorEnableBan(false)
 
this is bad, y.
I uploaded HeroSelector - jass - 1.9c, in the GUI ban phase trigger i added a disable ban phase when the counter is 0. this hopefully prevents clicking the ban-button in the pick phase.
this as first action in the if counter <= 0
call HeroSelectorEnableBan(false)
Thanks for the quick update and for looking into this. Unfortunately, this change does not resolve the issue. The bug is still present and can still be reproduced by clicking the Ban button at the very end of the ban phase. The picking phase still gets overwritten.
 
Updated the GUI pick/ban phase, again.
added a 2s break between and it disables banning before the break starts.

the Lua versions additional got a Lua picking phase.
Looking at your solution, an idea came to mind. It might be enough to introduce a fraction of a second between the banning phase and the picking phase, where the Ban button is already disabled but Accept/Random buttons are not yet enabled, a minimal transitional window between the two phases, essentially a neutral idle state.

Based on this, I added a Wait 0.00 seconds between these actions. Despite being declared as zero seconds, it still introduces a minimal delay.
  • Then - Actions
    • -------- Disable Banning --------
    • Custom script: call HeroSelectorEnableBan(false)
    • Wait 0.00 seconds
    • -------- Enable Picking --------
    • Custom script: call HeroSelectorEnablePick(true)
From my tests, this completely resolves the issue. If this solution is acceptable and not invasive to your system, it might be worth considering.
 
Tasyen, may I ask you for help with a small customization of your system?
Is it possible to always display allies on the left side and enemies on the right side in the team viewer?
Currently, this layout works exactly like that for the team with index 0. However, for the team with index 1, it is reversed. Players on that team see their own preselections and picks on the right side, and their enemies on the left.

It seems to be hard-coded so that team 0 is always on the left side and team 1 on the right side. Instead of a layout based on the team index that looks the same for both teams, create a view where each team always sees themselves on the left and their enemies on the right.
Do you think this kind of customization would be possible to implement?

Current team index 1 view:
1769775057947.png


How I would like it to look like for team index 1:
1769775002381.png
 
Updated Teamviewer. It got the new setting OwnTeamAt0, if it is true then your own team uses the pos of Team 0 and team 0 uses your pos.

Added the 2 newer HeroInfos to Lua 1.9e also has a new fdf and a new lib that the new HeroInfos use. if you use the old HeroInfo only teamviewer changed with that new setting.
 
Thank you, this looks really great. Tasyen, I do not want to abuse your kindness, but I have one more small request. I promise this is the last thing I am asking for, and I hope it is not difficult to implement.

Currently there is this flag:
JASS:
private boolean UpdateNonAllies = true //update the image of non allies when the select or pick
I would like to split this behavior into two separate ones, so updating non allies can be handled independently on select and on pick.

Something like:
JASS:
private boolean UpdateNonAlliesOnSelect = false //update the image of non allies when the select
private boolean UpdateNonAlliesOnPick   = true  //update the image of non allies when the pick (including random)

Because showing non-allies picks does not necessarily mean I want to show their selections.

I have repick disabled, so once a player commits to a hero I would like that choice to be visible. Previously I handled this with text messages and multiboard hero icons, but your modern system visualizes it much better.

At the same time, I do not want to reveal enemy pre-selections, potential picks, draft intentions or strategies. A common tactic is to wait until the last second before picking, that would be completely lost if enemy selections were visible before the actual pick.
 
Hi, I recently came up with an idea that I think could be a great improvement to your system.

We already have all the information about every hero collected in one place (Hero Selector): their stats, abilities, and all values. It literally looks like a small in-game wiki for the custom game. I believe the ability to check this information during gameplay is brilliant, and it’s made possible through the commands:
JASS:
-show and -close
The key point is that after the Picking Phase, you are fully aware of all heroes in the matchup: your own hero, your allies heroes, and your enemies heroes. Being able to check hero stats and adapt your gameplay accordingly, for example by adjusting your item build, is extremely valuable. With just a few mouse clicks, you can access detailed information about anything related to the hero.

Well… almost any hero.

After the Picking Phase ends, hero icons/buttons in the hero selector become Disabled, including banned, picked, and randomized heroes.

Let’s say that during the game, someone wants to check how long enemies stun lasts, or what their own base attack cooldown of the hero. Having access to this information would significantly improve decision-making during the match. Unfortunately, this is currently impossible because you can’t click on the icons of heroes that were banned, picked, or randomized. In other words, you can’t access information about the heroes that are actually present in the match, which are the ones that matter the most.

My suggestion is to add a flag that enables the following behavior:

EnableAllHeroButtonsAfterPickingPhase

After the Picking Phase ends, all previously disabled hero icons/buttons should become active/enabled again. This would allow players to click the button and preview the hero’s information.

The purpose of this change is to allow the use of the -show and -close commands during gameplay to access information about heroes in the current matchup. Right now, this is not possible because the hero icon buttons remain locked, making it impossible to display their information.

Just look at how great the system already is, and yet we simply don’t have access to it when it matters most:
1771338297706.png

1771338320492.png
 
Last edited:
updated vjass version to V1.9h)
skips some unneeded BlzFrameSetTexture calls in HeroSelector & HeroInfo
Added call HeroSelectorViewMode(). in that mode you can click all herobuttons, but dont have any pick/bann buttons.
It should be possible to call HeroSelectorViewMode in GetLocalPlayer() context.

Edit: updated Lua version to V1.9i & 2.2d)
skips some unneeded BlzFrameSetTexture calls in HeroSelector, Teamviewer & HeroInfo
Added HeroSelector.viewMode([who]) same as HeroSelectorViewMode but uses the who rules.
 
Last edited:
Good morning Tasyen, I’ve tested your latest update and it’s great. You actually did even more than I asked for. Without the disabled "Accept" and "Random" buttons, it looks amazing.

The only thing I noticed is that after reenabling the icons in View Mode, the descriptions like “(Out of Stock)” or “(Disallowed)” are still displayed. Considering the purpose of View Mode, this text may look a bit unnatural when shown together with an active, clickable icon button.

1771535721438.png
1771535849513.png
 
Last edited:
Hello Tasyen, I discovered a critical bug in Hero Selector.

Specifically, it causes a desync for Observers or Referees when Hero Selector is used in TeamViewer mode (JASS version. I have not tested the Lua version).

Bug description:
Any player occupying an Observer or Referee slot in a game that uses the JASS Hero Selector in TeamViewer mode will experience a desync and game crash right at the beginning of the game.

Steps to reproduce the bug on your map:
  1. Switch Hero Selector to TeamViewer mode (by default it is set to TeamViewer OneTeam).
  2. Create a game on Battlenet with either Observer or Referee enabled (it does not matter which option you choose, the bug occurs in both cases).
    1772626710996.png
  3. Add at least one player as a computer, so that the Hero Selector has something to display.
    This step is important. If there are no players/computers in the game, the bug will not occur.
    Set yourself to the Observer/Referee slot and start the game.
    1772626749868.png
  4. About one second after the map loads, when the Hero Selector initializes, you will receive a desync and the game will crash.
    1772626773929.png
 
I assume that is because of teamnr of observers is -1 and jass dont likes -1 as array index. Updated Teamviewer to check for teamNr in allow Player and do not use teamat0 when localTeam is smaller than 0.
Uploaded updates to do that. vjass updated all teamviewer, Lua only default Teamviewer as the others did not mention team.
 
  • Like
Reactions: KPC
Hi! I have a suggestion that I think would improve the readability of the pick/ban phase in the Hero Selector.

The problem: when a player selects a hero and then bans it, that hero stays selected in the player's preview under their nickname. Allies read this as "I want to play this hero," when in fact the player just banned it (and hasn't selected the pick yet). It looks like the leftover icon in the preview under the nickname is a very strong indicator that signals an intention to pick, even though the hero was only selected in order to ban it.

Example (screenshot below): Knox banned Death Knight, but his allies didn't register it as a ban. Because Knox hadn't locked in his own pick yet, the DK icon stayed in his preview, and his allies read it as an intention to play DK and started drafting around it, going for the classic Undead combo (Lich, Crypt Lord). The leftover icon misled them into committing to picks built around a hero who wasn't even going to be in the game. By the time they realized it was a ban, the picking phase had practically run out. You can't see this on the screenshot itself, but in practice it cost the team a lot of time and led to misguided drafting decisions.

The suggestion: after clicking the "ban" button on a selected hero, the preview should be cleared, restoring the state from before any hero was selected. Specifically:
  • reset the hero icon back to a question mark,
  • remove the hero's class icons from under the player's nickname,
  • clean the panel with the hero's description and abilities, since no one is selected anymore so it shouldn't be displayed.
This would make a ban unambiguous and stop it from falsely suggesting a pick.
We would be grateful if you considered adding such a change.
Best regards, the Mini Dota community

1781515466877.webp
 
Back
Top