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", 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 or a Race. 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.

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 V1.6

------
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.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}, skip who or nil will remove an requirment.
    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])
    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)
    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)
    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.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.destroy()
    destroys and nil HeroSelector

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.frameLoseFocus(frame)
    this disables & enables frame for the local player to free current focus (enable hotkeys, chat ...).

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
--]]
HeroSelector = {}

--Box
HeroSelector.BoxFrameName           = "HeroSelectorRaceBox" --this is the background box being created
HeroSelector.BoxPosX                = 0.3
HeroSelector.BoxPosY                = 0.4
HeroSelector.BoxPosPoint            = FRAMEPOINT_CENTER
HeroSelector.AutoShow               = true --(true) shows the box and the Selection at 0.0 for all players
--Unique Picks
HeroSelector.UnitCount              = 2 --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"
--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.Category = {
    --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"},    --4
    {"ReplaceableTextures\\CommandButtons\\BTNSlippersOfAgility", "AGILITY"},        --8
    {"ReplaceableTextures\\CommandButtons\\BTNMantleOfIntelligence", "INTELLECT"},   --16
}
HeroSelector.CategoryAffectRandom   = true  --(false) random will not care about selected category
HeroSelector.CategoryMultiSelect    = false --(false) deselect other category when selecting one, (true) can selected multiple categories and all heroes having any of them are not filtered.
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.CategoryAutoDetectHero = true  -- Will create and remove added Heroes to setup the Category for the primary Attribute Str(4) Agi(8) Int(16)   

--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.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
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" --tries to get a Localized String
HeroSelector.BanButtonSizeX         = 0.13
HeroSelector.BanButtonSizeY         = 0.03
HeroSelector.BanTooltip             = "DISALLOWED"
HeroSelector.BanIgnoreRequirment    = true -- (true) Ban is not restricted by Requirments
--Accept Button
HeroSelector.AcceptButtonTextPrefix = ""
HeroSelector.AcceptButtonText       = "ACCEPT"
HeroSelector.AcceptButtonSizeX      = 0.085
HeroSelector.AcceptButtonSizeY      = 0.03
HeroSelector.AcceptButtonIsShown    = true
HeroSelector.AcceptButtonAnchor     = FRAMEPOINT_BOTTOMRIGHT --places the Accept button with which Point to the bottom, with right he is at the left
--Random Button
HeroSelector.RandomButtonTextPrefix = ""
HeroSelector.RandomButtonText       = "RANDOM" --tries Localizing
HeroSelector.RandomButtonSizeX      = 0.085
HeroSelector.RandomButtonSizeY      = 0.03
HeroSelector.RandomButtonIsShown    = true
HeroSelector.RandomButtonAnchor     = FRAMEPOINT_BOTTOMLEFT
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.
--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"

--Border
HeroSelector.BorderSize = {}
HeroSelector.BorderSize[RACE_HUMAN]     = 0.029 --border size seen by Race Human, this is needed cause the borders are different in size.
HeroSelector.BorderSize[RACE_ORC]       = 0.029
HeroSelector.BorderSize[RACE_UNDEAD]    = 0.035
HeroSelector.BorderSize[RACE_NIGHTELF]  = 0.035
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.Category
    HeroSelector.Category = {}
    for index, value in ipairs(categories)
    do
       HeroSelector.addCategory(value[1], value[2])
    end

    --read GUI, when the variable exist
    if 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

        --copy the setuped field
        for index = 1, HeroSelector.ButtonColCount*HeroSelector.ButtonRowCount,1 do
            HeroSelector.addUnit(udg_HeroSelectorUnitCode[index])
            if udg_HeroSelectorCategory[index] ~= 0 then
                HeroSelector.addUnitCategory(udg_HeroSelectorUnitCode[index], udg_HeroSelectorCategory[index])
            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.

    --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.
    --human only work for human, as nightelf only for Nightelf
    HeroSelector.setUnitReq('Hpal', RACE_HUMAN)
    HeroSelector.setUnitReq('Hamg', RACE_HUMAN)
    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() --this is an empty box. It still takes a slot.
    HeroSelector.addUnit("Oshd")
    HeroSelector.addUnit("Edem")
    HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    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 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

--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
    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 globals and globals.udg_HeroSelectorEvent 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)
    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.show(true, player)
    HeroSelector.enablePick(true, player)
    RemoveUnit(unit)
end
--=====
--code start
--=====
BlzLoadTOCFile("war3mapImported\\HeroSelector.toc") --ex/import also "HeroSelector.fdf"
HeroSelector.HeroButtons  = {} --the clickable Buttons
HeroSelector.UnitData = {} --all avaiable selections
HeroSelector.UnitDataPool = {} -- all possible random values
HeroSelector.CategoryButton = {}

HeroSelector.Frames = {}
HeroSelector.BanDelayed = {}

function HeroSelector.CategoryClickAction()
    local button = BlzGetTriggerFrame()
    local category = HeroSelector.CategoryButton[button]
    HeroSelector.frameLoseFocus(BlzGetTriggerFrame())
    local player = GetTriggerPlayer()
    local playerData = HeroSelector.Category[player] 
    if not playerData then
        playerData = 0
    end
    --has this category already?
    if BlzBitAnd(playerData, category.Value) ~= 0 then
        --yes, unable
        playerData = playerData - category.Value
        if GetLocalPlayer() == player then
            BlzFrameSetTexture(category.Icon, category.TextureDisabled, 0, true)
            BlzFrameSetTexture(category.IconPushed, category.TextureDisabled, 0, true)
        end

    else
        if not HeroSelector.CategoryMultiSelect and HeroSelector.CategoryButton[player]  then
            local lastCategory = HeroSelector.CategoryButton[player]
            BlzFrameSetTexture(lastCategory.Icon, lastCategory.TextureDisabled, 0, true)
            BlzFrameSetTexture(lastCategory.IconPushed, lastCategory.Texture, 0, true)
            if playerData ~= 0 then
                playerData = 0
            end
        end
        
        --no, enable
        playerData = playerData + category.Value
        if GetLocalPlayer() == player then
            BlzFrameSetTexture(category.Icon, category.Texture, 0, true)
            BlzFrameSetTexture(category.IconPushed, category.Texture, 0, true)
        end
        HeroSelector.CategoryButton[player] = category
    end
    
    HeroSelector.Category[player] = playerData

    if GetLocalPlayer() == player then
        --update all buttons
        --buttons not having at least 1 selected category becomes partly transparent
        for buttonIndex, value in ipairs(HeroSelector.HeroButtons)
        do
            local button = HeroSelector.HeroButtons[buttonIndex].Frame
            local unitCode = HeroSelector.UnitData[buttonIndex]
            if unitCode and unitCode > 0 then
                if playerData == 0 or BlzBitAnd(HeroSelector.UnitData[unitCode].Category, playerData) > 0 then
                    BlzFrameSetAlpha(button, 255)
                else
                    BlzFrameSetAlpha(button, HeroSelector.CategoryFilteredAlpha)
                end
            end
        end
    end
end

function HeroSelector.getDisabledIcon(icon)
    --ReplaceableTextures\CommandButtons\BTNHeroPaladin.tga -> ReplaceableTextures\CommandButtonsDisabled\DISBTNHeroPaladin.tga
    if string.sub(icon,35,35) ~= "\\" then return icon end --this string has not enough chars return it
    --string.len(icon) < 35 then return icon end --this string has not enough chars return it
    local prefix = string.sub(icon, 1, 34)
    local sufix = string.sub(icon, 36)
    return prefix .."Disabled\\DIS"..sufix
end

function HeroSelector.updateTooltip(unitCode)
    local tooltipFrame = HeroSelector.HeroButtons[HeroSelector.UnitData[unitCode].Index].Tooltip 
    local unitData = HeroSelector.UnitData[unitCode]
    if unitData.Count > HeroSelector.UnitCount then
        BlzFrameSetText(tooltipFrame, HeroSelector.TooltipPrefix..GetObjectName(unitCode).."\n|r("..GetLocalizedString(HeroSelector.BanTooltip)..")")
    else
        if unitData.Count == HeroSelector.UnitCount or unitData.InTeam[GetPlayerTeam(GetLocalPlayer())] >= HeroSelector.UnitCountPerTeam then
            BlzFrameSetText(tooltipFrame, HeroSelector.TooltipPrefix..GetObjectName(unitCode).."\n|r("..GetLocalizedString(HeroSelector.ToManyTooltip)..")")
        elseif not HeroSelector.buttonRequirementDone(unitCode, GetLocalPlayer()) then
            BlzFrameSetText(tooltipFrame, HeroSelector.TooltipPrefix..GetObjectName(unitCode).."\n|r("..GetLocalizedString(HeroSelector.TooltipRequires)..")")
        else
            BlzFrameSetText(tooltipFrame, HeroSelector.TooltipPrefix..GetObjectName(unitCode))
        end
    end
end

function HeroSelector.addCategory(icon, text)
    if HeroSelector.CategoryHighest then
        HeroSelector.CategoryHighest = HeroSelector.CategoryHighest*2
    else
        HeroSelector.CategoryHighest = 1
    end
    
    local newObject = {}
    table.insert(HeroSelector.Category, newObject)
    newObject.Value = HeroSelector.CategoryHighest
    newObject.Texture = icon
    newObject.TextureDisabled = HeroSelector.getDisabledIcon(icon)
    newObject.Text = text
    
    if HeroSelector.Box then
        local box = HeroSelector.Box
        local lastButton = HeroSelector.CategoryButton[#HeroSelector.CategoryButton]
        local button = BlzCreateFrame("HeroSelectorCategoryButton", box, 0, 0)
        local icon = BlzGetFrameByName("HeroSelectorCategoryButtonIcon", 0)
        local iconPushed = BlzGetFrameByName("HeroSelectorCategoryButtonIconPushed", 0)
        local tooltip = BlzCreateFrame("HeroSelectorText", box, 0, 0)
        BlzFrameSetText(tooltip, GetLocalizedString(text))
        newObject.Text = tooltip --when this is reached overwritte textframe with the tooltip
        BlzFrameSetTooltip(button, tooltip)
        BlzFrameSetPoint(tooltip, FRAMEPOINT_BOTTOM, button, FRAMEPOINT_TOP, 0, 0)
        BlzFrameSetSize(button, 0.02, 0.02)
        BlzFrameSetTexture(icon, newObject.TextureDisabled, 0, true)
        BlzFrameSetTexture(iconPushed, newObject.TextureDisabled, 0, true)
        HeroSelector.CategoryButton[button] = newObject
        TasButtonAction.Set(button, HeroSelector.CategoryClickAction)
        newObject.Icon = icon
        newObject.IconPushed = iconPushed
        newObject.Button = button

        if not lastButton then
            local titleSize = 0.015
            local borderSize = HeroSelector.BorderSize[GetPlayerRace(GetLocalPlayer())]
            local y = -borderSize - titleSize - 0.01
            local x = borderSize
            BlzFrameSetPoint(button, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, x, y)
        else
            BlzFrameSetPoint(button, FRAMEPOINT_LEFT, lastButton, FRAMEPOINT_RIGHT, 0, 0)
        end
        table.insert(HeroSelector.CategoryButton, button)
        table.insert(HeroSelector.Frames, tooltip)
        table.insert(HeroSelector.Frames, icon)
        table.insert(HeroSelector.Frames, button)
    end
end

function HeroSelector.addUnit(unitCode, onlyRandom, requirement)
    --no unitCode => empty field
    if not unitCode or unitCode == 0 then
        table.insert(HeroSelector.UnitData, 0)
    else
        --'Hpal' -> number?
        if type(unitCode) == "string" then
            unitCode = FourCC(unitCode)
        end

        --Such an object Exist?
        if GetObjectName(unitCode) == "" then print(('>I4'):pack(unitCode), "is not an valid Object") return end
        
        --only unique
        if HeroSelector.UnitData[unitCode] then return end
        HeroSelector.UnitDataPool[unitCode] = true -- add to random

        HeroSelector.UnitData[unitCode] = {Index = 0, Count = 0, InTeam = __jarray(0), Category = 0, Requirment = HeroSelector.convertReq(requirement), RequirmentData = requirement}
        HeroSelector.autoDetectCategory(unitCode)
        if not onlyRandom then
            table.insert(HeroSelector.UnitData, unitCode)
            HeroSelector.UnitData[unitCode].Index = #HeroSelector.UnitData   --remember the index for the unitCode
        end
      
        
    end

end

function HeroSelector.clearUnitData()
    HeroSelector.UnitDataPool = {}
    HeroSelector.UnitData = {}
    HeroSelector.BanDelayed = {}
    HeroSelector.update()
end

function HeroSelector.setFrameText(frame, text, who)
    if HeroSelector.includesPlayer(who, GetLocalPlayer()) then
        BlzFrameSetText(frame, text)
    end
end

function HeroSelector.setTitleText(text, who)
    HeroSelector.setFrameText(HeroSelector.Title, text, who)
end
function HeroSelector.setAcceptButtonText(text, who)
    HeroSelector.setFrameText(HeroSelector.AcceptButton, text, who)
end
function HeroSelector.setBanButtonText(text, who)
    HeroSelector.setFrameText(HeroSelector.BanButton, text, who)
end

function HeroSelector.isPlayerRace(unitCode, player)
    return HeroSelector.UnitData[unitCode].RequirmentData == GetPlayerRace(player) 
end
function HeroSelector.IsPlayerForce(unitCode, player)
    return BlzForceHasPlayer(HeroSelector.UnitData[unitCode].RequirmentData, player)
end
function HeroSelector.IsPlayerTeamNr(unitCode, player)
    return HeroSelector.UnitData[unitCode].RequirmentData == GetPlayerTeam(player)
end
function HeroSelector.isPlayer(unitCode, player)
    return HeroSelector.UnitData[unitCode].RequirmentData == player
end
function HeroSelector.HasPlayerTechLevel(unitCode, player)
    return GetPlayerTechCount(player, HeroSelector.UnitData[unitCode].RequirmentData[1], true) >= HeroSelector.UnitData[unitCode].RequirmentData[2]
end

function HeroSelector.convertReq(who)
    if not who then
        return nil
    elseif type(who) == "number" then
        return HeroSelector.IsPlayerTeamNr
    elseif type(who) == "table" then
        return HeroSelector.HasPlayerTechLevel
    elseif tostring(who):sub(1, 5) == "race:" then
        return HeroSelector.isPlayerRace
    elseif tostring(who):sub(1, 7) == "player:" then
        return HeroSelector.isPlayer
    elseif tostring(who):sub(1, 6) == "force:" then
        return HeroSelector.IsPlayerForce
    end
    return nil
end

function HeroSelector.setUnitReq(unitCode, who)
    if type(unitCode) == "string" then
        unitCode = FourCC(unitCode)
    end
    --Such an object Exist?
    if not HeroSelector.UnitData[unitCode] then return end

    HeroSelector.UnitData[unitCode].RequirmentData = who
    HeroSelector.UnitData[unitCode].Requirment = HeroSelector.convertReq(who)
end
function HeroSelector.setUnitCategory(unitCode, category)
    if type(unitCode) == "string" then
        unitCode = FourCC(unitCode)
    end
    --Such an object Exist?
    if not HeroSelector.UnitData[unitCode] then return end

    HeroSelector.UnitData[unitCode].Category = category
end
function HeroSelector.addUnitCategory(unitCode, category)
    if type(unitCode) == "string" then
        unitCode = FourCC(unitCode)
    end
    --Such an object Exist?
    if not HeroSelector.UnitData[unitCode] then return end

    HeroSelector.UnitData[unitCode].Category = BlzBitOr(category, HeroSelector.UnitData[unitCode].Category)
end

function HeroSelector.deselectButtons(buttonIndex)
    if buttonIndex and buttonIndex > 0 then
        if HeroSelector.HeroButtons[GetLocalPlayer()] == buttonIndex then
            BlzFrameSetVisible(HeroSelector.SelectedIndikator, false)
        end
        for index= 0, GetBJMaxPlayers() - 1,1 do
            if HeroSelector.HeroButtons[Player(index)] == buttonIndex then
                HeroSelector.HeroButtons[Player(index)] = 0 
            end
        end
    else
        for index= 0, GetBJMaxPlayers() - 1,1 do
            HeroSelector.HeroButtons[Player(index)] = 0 
        end
        BlzFrameSetVisible(HeroSelector.SelectedIndikator, false)
    end
end

function HeroSelector.buttonRequirementDone(unitCode, player)
    --true when no requirement is set or the requirment call is successful
    return not HeroSelector.UnitData[unitCode].Requirment or HeroSelector.UnitData[unitCode].Requirment(unitCode, player)
end

function HeroSelector.disableButtonIndex(buttonIndex, teamNr)
    if buttonIndex > 0 then
        if HeroSelector.includesPlayer(teamNr, GetLocalPlayer()) then
            BlzFrameSetEnable(HeroSelector.HeroButtons[buttonIndex].Frame, false)
        end
        if HeroSelector.HeroButtons[GetLocalPlayer()] == buttonIndex then
            BlzFrameSetVisible(HeroSelector.SelectedIndikator, false)
        end

        --deselect this Button from all players or the team
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if (not teamNr or teamNr == GetPlayerTeam(player)) and HeroSelector.HeroButtons[player] == buttonIndex then
                HeroSelector.HeroButtons[player] = 0                
            end
        end
    end
end

function HeroSelector.enableButtonIndex(unitCode, buttonIndex, teamNr)
    if buttonIndex > 0 and (not teamNr or teamNr == GetPlayerTeam(GetLocalPlayer())) then
        BlzFrameSetEnable(HeroSelector.HeroButtons[buttonIndex].Frame, true and (HeroSelector.BanIgnoreRequirment and BlzFrameIsVisible(HeroSelector.BanButton)) or HeroSelector.buttonRequirementDone(unitCode, GetLocalPlayer()))
    end
end

function HeroSelector.counterChangeUnitCode(unitCode, add, player)
    if not HeroSelector.UnitData[unitCode] then HeroSelector.UnitData[unitCode] = {Count = 0, InTeam = __jarray(0)} end
    local buttonIndex = HeroSelector.UnitData[unitCode].Index
    if not buttonIndex  then buttonIndex = 0 end

    local teamNr = GetPlayerTeam(player)
    
    HeroSelector.UnitData[unitCode].InTeam[teamNr] = HeroSelector.UnitData[unitCode].InTeam[teamNr] + add
    HeroSelector.UnitData[unitCode].Count = HeroSelector.UnitData[unitCode].Count + add

    if HeroSelector.UnitData[unitCode].Count >= HeroSelector.UnitCount then
        --disable for all
        HeroSelector.disableButtonIndex(buttonIndex)
    else
        --enable for all
        HeroSelector.enableButtonIndex(unitCode, buttonIndex)
        if HeroSelector.UnitData[unitCode].InTeam[teamNr] >= HeroSelector.UnitCountPerTeam  then
            --disable for this team
            HeroSelector.disableButtonIndex(buttonIndex, teamNr)
        end
    end
    HeroSelector.updateTooltip(unitCode)
end

HeroSelector.rollOptionData = {Count = 0}

function HeroSelector.rollOption(player, includeRandomOnly, excludedIndex, category)
    if not excludedIndex then excludedIndex = 0 end
    if not category then category = 0 end
    local teamNr = GetPlayerTeam(player)
    HeroSelector.rollOptionData.Count = 0
    for unitCode, value in pairs(HeroSelector.UnitDataPool)
    do
        
        local allowed = value
        --total limited reached?
        if HeroSelector.UnitData[unitCode].Count >= HeroSelector.UnitCount then
            allowed = false
            --print(GetObjectName(unitCode))
            --print("rejected total limit")
        end
        --team limited reached?
        if allowed and HeroSelector.UnitData[unitCode].InTeam[teamNr] >= HeroSelector.UnitCountPerTeam  then
            --print(GetObjectName(unitCode))
            --print("rejected team limit")
            allowed = false
        end
        --allow randomOnly?
        if allowed and not includeRandomOnly and HeroSelector.UnitData[unitCode].Index == 0  then
            --print(GetObjectName(unitCode))
            --print("rejected random only")
            allowed = false
        end
        --this index is excluded? This can make sure you get another button.
        if allowed and HeroSelector.UnitData[unitCode].Index > 0 and HeroSelector.UnitData[unitCode].Index == excludedIndex then
            --print(GetObjectName(unitCode))
            --print("rejected exclude")
            allowed = false
        end
        --fullfills the requirement?
        if allowed and not HeroSelector.buttonRequirementDone(unitCode, player) then
            --print(GetObjectName(unitCode))
            --print("rejected requirement")
            allowed = false
        end
        --when having an given an category only allow options having that category atleast partly
        if allowed and category and category > 0 and BlzBitAnd(category, HeroSelector.UnitData[unitCode].Category) == 0 then
            --print(GetObjectName(unitCode))
            --print("  rejected category", category, HeroSelector.UnitData[unitCode].Category)
            allowed = false
        end

        if allowed then
            HeroSelector.rollOptionData.Count = HeroSelector.rollOptionData.Count + 1
            HeroSelector.rollOptionData[HeroSelector.rollOptionData.Count] = unitCode
        end
    end
    --nothing is allwoed?
    if HeroSelector.rollOptionData.Count == 0 then return nil end

    return HeroSelector.rollOptionData[GetRandomInt(1, HeroSelector.rollOptionData.Count)]
end

function HeroSelector.doRandom(player)
    local category = 0
    if HeroSelector.CategoryAffectRandom then category = HeroSelector.Category[player] end
    local unitCode = HeroSelector.rollOption(player, true, 0, category)
    if not unitCode then return end

    HeroSelector.counterChangeUnitCode(unitCode, 1, player)
    HeroSelector.unitCreated(player, unitCode, true)
end

function HeroSelector.doPick(player)
    --pick what currently is selected, returns true on success returns false when something went wrong,
    if not HeroSelector.HeroButtons[player] then return false end
    local buttonIndex = HeroSelector.HeroButtons[player]
    if buttonIndex <= 0 then return false end --reject nothing selected
    local unitCode = HeroSelector.UnitData[buttonIndex]
    if not HeroSelector.buttonRequirementDone(unitCode, player) then return false end --requirment fullfilled

    HeroSelector.counterChangeUnitCode(unitCode, 1, player)
    HeroSelector.unitCreated(player, unitCode)
    return true
end

function HeroSelector.forceRandom(who)
    --this is a wrapper for doRandom allowing different dataTypes
    if not who then
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
                HeroSelector.doRandom(Player(index)) 
            end
        end
    elseif type(who) == "number" then
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerTeam(player) == who then
                    HeroSelector.doRandom(player)
                end
            end
        end
    elseif tostring(who):sub(1, 5) == "race:" then
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerRace(player) == who then
                    HeroSelector.doRandom(player) 
                end
            end
        end
    elseif tostring(who):sub(1, 7) == "player:" then
        HeroSelector.doRandom(who)
    elseif tostring(who):sub(1, 6) == "force:"then
        ForForce(who, function() HeroSelector.doRandom(GetEnumPlayer()) end)
    end
end

function HeroSelector.forcePick(who)
    if not who then
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
                if not HeroSelector.doPick(player) then --do picking, when that fails doRandom
                    HeroSelector.doRandom(player)
                end
            end
        end
    elseif type(who) == "number" then
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerTeam(player) == who then
                    if not HeroSelector.doPick(player) then
                        HeroSelector.doRandom(player) 
                    end
                end
            end
        end
    elseif tostring(who):sub(1, 5) == "race:" then
        for index= 0, GetBJMaxPlayers() - 1,1 do
            local player = Player(index)
            if GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerRace(player) == who then
                    if not HeroSelector.doPick(player) then
                        HeroSelector.doRandom(player) 
                    end
                end
            end
        end
    elseif tostring(who):sub(1, 7) == "player:" then
        if not HeroSelector.doPick(who) then
            HeroSelector.doRandom(who) 
        end
    elseif tostring(who):sub(1, 6) == "force:" then
        ForForce(who, function()
            local player = GetEnumPlayer()
            if not HeroSelector.doPick(player) then
                HeroSelector.doRandom(player) 
            end 
        end)
    end
end

function HeroSelector.includesPlayer(who, player)
    if not who then --there is no who -> everyone
        return true
    elseif type(who) == "number" and GetPlayerTeam(player) == who then
        return true
    elseif tostring(who):sub(1, 5) == "race:" and GetPlayerRace(player) == who  then
        return true
    elseif tostring(who):sub(1, 7) == "player:" and player == who then
        return true
    elseif tostring(who):sub(1, 6) == "force:" and BlzForceHasPlayer(who, player) then
        return true
    end
    return false
end

function HeroSelector.enablePick(flag, who)
    for index = #HeroSelector.BanDelayed, 1, -1
    do
        local ban = table.remove(HeroSelector.BanDelayed)
        HeroSelector.counterChangeUnitCode(ban[1], HeroSelector.UnitCount + 1, ban[2])
    end

    if HeroSelector.includesPlayer(who, GetLocalPlayer()) then
        BlzFrameSetVisible(HeroSelector.AcceptButton, true and HeroSelector.AcceptButtonIsShown)
        BlzFrameSetVisible(HeroSelector.RandomButton, true and HeroSelector.RandomButtonIsShown)
        BlzFrameSetVisible(HeroSelector.BanButton, false)
        BlzFrameSetEnable(HeroSelector.AcceptButton, flag)
        BlzFrameSetEnable(HeroSelector.RandomButton, flag)
        BlzFrameSetModel(HeroSelector.SelectedIndikator, HeroSelector.IndicatorPathPick, 0)
        if HeroSelector.BanIgnoreRequirment then HeroSelector.update() end
    end
    
end

function HeroSelector.enableBan(flag, who)  
    if HeroSelector.includesPlayer(who, GetLocalPlayer()) then
        BlzFrameSetVisible(HeroSelector.AcceptButton, false)
        BlzFrameSetVisible(HeroSelector.RandomButton, false)
        BlzFrameSetVisible(HeroSelector.BanButton, true)
        BlzFrameSetEnable(HeroSelector.BanButton, flag)
        BlzFrameSetModel(HeroSelector.SelectedIndikator, HeroSelector.IndicatorPathBan, 0)
        if HeroSelector.BanIgnoreRequirment then HeroSelector.update() end
    end    
end

function HeroSelector.destroy()
    for key, value in ipairs(HeroSelector.Frames)
    do
        BlzDestroyFrame(value)
    end
    HeroSelector.Frames = nil
    for key, value in ipairs(HeroSelector.HeroButtons)
    do
        BlzDestroyFrame(value.Tooltip)
        BlzDestroyFrame(value.Icon)
        BlzDestroyFrame(value.IconDisabled)
        BlzDestroyFrame(value.Frame)
    end
    HeroSelector.HeroButtons = nil

    BlzDestroyFrame(HeroSelector.SelectedIndikator)
    
    HeroSelector.UnitData = nil
    HeroSelector.BorderSize = nil
    HeroSelector = nil
    
end

function HeroSelector.frameLoseFocus(frame)
    if BlzFrameGetEnable(frame) then
        BlzFrameSetEnable(frame, false)
        BlzFrameSetEnable(frame, true)
    end
end

function HeroSelector.actionPressHeroButton()
    local button = BlzGetTriggerFrame()
    local player = GetTriggerPlayer()
    local buttonIndex = HeroSelector.HeroButtons[button]
    local unitCode = HeroSelector.UnitData[buttonIndex]
    HeroSelector.HeroButtons[player] = buttonIndex
    if GetLocalPlayer() == player then
        HeroSelector.frameLoseFocus(BlzGetTriggerFrame())
    end
    if GetLocalPlayer() == player then
        BlzFrameSetVisible(HeroSelector.SelectedIndikator, true)
        
        BlzFrameSetPoint(HeroSelector.SelectedIndikator, FRAMEPOINT_TOPLEFT, button, FRAMEPOINT_TOPLEFT, -0.001, 0.001)
        BlzFrameSetPoint(HeroSelector.SelectedIndikator, FRAMEPOINT_BOTTOMRIGHT, button, FRAMEPOINT_BOTTOMRIGHT, -0.0012, -0.0016)
    end
    HeroSelector.buttonSelected(player, unitCode)
end

function HeroSelector.actionRandomButton()
    local player = GetTriggerPlayer()
    HeroSelector.frameLoseFocus(BlzGetTriggerFrame())
    if HeroSelector.RandomButtonPick then
        HeroSelector.doRandom(player)
    else
        local unitCode = HeroSelector.rollOption(player, false, HeroSelector.HeroButtons[player], HeroSelector.Category[player])
        if unitCode and GetLocalPlayer() == player then
            local buttonIndex = HeroSelector.UnitData[unitCode].Index
            BlzFrameClick(HeroSelector.HeroButtons[buttonIndex].Frame)
        end
    end
end

function HeroSelector.actionAcceptButton()
    HeroSelector.frameLoseFocus(BlzGetTriggerFrame())
    HeroSelector.doPick(GetTriggerPlayer())
end

function HeroSelector.actionBanButton()
    local player = GetTriggerPlayer()
    HeroSelector.frameLoseFocus(BlzGetTriggerFrame())
    if not HeroSelector.HeroButtons[player] then return end
    local buttonIndex = HeroSelector.HeroButtons[player]
    if buttonIndex <= 0 then return end --reject nothing selected
    local unitCode = HeroSelector.UnitData[buttonIndex]
    if not HeroSelector.DelayBanUntilPick then
        HeroSelector.counterChangeUnitCode(unitCode, HeroSelector.UnitCount + 1, player)
    else
        table.insert(HeroSelector.BanDelayed, {unitCode, player})
    end
    HeroSelector.unitBaned(player, unitCode)
end

function HeroSelector.update()
    for buttonIndex, value in ipairs(HeroSelector.HeroButtons)
    do
        --have data for this button?
        
        if HeroSelector.UnitData[buttonIndex] and HeroSelector.UnitData[buttonIndex] > 0 then
            if HeroSelector.HideEmptyButtons then BlzFrameSetVisible(value.Frame, true) end
            local unitCode = HeroSelector.UnitData[buttonIndex]
            if HeroSelector.UnitData[unitCode].Count >= HeroSelector.UnitCount then
                --disable for all
                HeroSelector.disableButtonIndex(buttonIndex)
            else
                --enable for all
                HeroSelector.enableButtonIndex(unitCode, buttonIndex)
                for teamNr, inTeamCount in pairs(HeroSelector.UnitData[unitCode].InTeam) do
                    if HeroSelector.UnitData[unitCode].InTeam[teamNr] >= HeroSelector.UnitCountPerTeam  then
                        --disable for this team
                        HeroSelector.disableButtonIndex(buttonIndex, teamNr)
                    end
                end
            end
            BlzFrameSetTexture(value.Icon, BlzGetAbilityIcon(unitCode), 0, HeroSelector.ButtonBlendAll)
            BlzFrameSetTexture(value.IconPushed, BlzGetAbilityIcon(unitCode), 0, HeroSelector.ButtonBlendAll)
            BlzFrameSetTexture(value.IconDisabled, HeroSelector.getDisabledIcon(BlzGetAbilityIcon(unitCode)), 0, HeroSelector.ButtonBlendAll)
            --BlzFrameSetText(value.Tooltip, HeroSelector.TooltipPrefix..GetObjectName(unitCode))
            HeroSelector.updateTooltip(unitCode)
            
        else
            --no, make it unclickable and empty
            if HeroSelector.HideEmptyButtons then BlzFrameSetVisible(value.Frame, false) end
            BlzFrameSetEnable(value.Frame, false)
            BlzFrameSetTexture(value.Icon, HeroSelector.EmptyButtonPath, 0, true)
            BlzFrameSetTexture(value.IconPushed, HeroSelector.EmptyButtonPath, 0, true)
            BlzFrameSetTexture(value.IconDisabled, HeroSelector.EmptyButtonPath, 0, true)
        end
    end
end

function HeroSelector.showFrame(frame, flag, who)
    if HeroSelector.includesPlayer(who, GetLocalPlayer()) then
        BlzFrameSetVisible(frame, flag)
    end
end

function HeroSelector.show(flag, who)
    HeroSelector.showFrame(HeroSelector.Box, flag, who)
end

do
    local backUp = MarkGameStarted
    function MarkGameStarted()
        backUp()
        backUp = nil
        if HeroSelector.initHeroes then HeroSelector.initHeroes() end

    --for key, value in ipairs(HeroSelector.UnitData)
    --do
--        print(key, ('>I4'):pack(value), GetObjectName(value))
    --end

    local titleSize = 0.015
    local buttonSize = HeroSelector.ButtonSize
    local borderSize = HeroSelector.BorderSize[GetPlayerRace(GetLocalPlayer())]
    local colCount = HeroSelector.ButtonColCount
    local rowCount = HeroSelector.ButtonRowCount
    local box = BlzCreateFrame(HeroSelector.BoxFrameName, BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
    --local boxTitle = BlzCreateFrame(HeroSelector.BoxFrameName, box, 0, 0)
    local boxBottom = BlzCreateFrame("HeroSelectorRaceTopBox", box, 0, 0)
    local titel = BlzCreateFrame("HeroSelectorTitle", box, 0, 0)
    
    HeroSelector.Title = titel
    local indicatorParent = BlzCreateFrameByType("BUTTON", "MyHeroIndikatorParent", box, "", 0)
    HeroSelector.SelectedIndikator = BlzCreateFrameByType("SPRITE", "MyHeroIndikator", indicatorParent, "", 0)
    BlzFrameSetLevel(indicatorParent, 9)

    BlzFrameSetModel(HeroSelector.SelectedIndikator, HeroSelector.IndicatorPathPick, 0)
    BlzFrameSetScale(HeroSelector.SelectedIndikator, buttonSize/0.036) --scale the model to the button size.
    

    --BlzFrameSetPoint(boxTitle, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, 0, -(titleSize - 0.003 + 0.9*borderSize))
    --BlzFrameSetPoint(boxTitle, FRAMEPOINT_BOTTOMRIGHT, box, FRAMEPOINT_TOPRIGHT, 0, - 0.9*borderSize - titleSize - 0.003 -HeroSelector.CategorySize)
    --BlzFrameSetSize(boxTitle, 0.01, 0.1)
    -- human UI size differs much, needs other numbers
    if GetPlayerRace(GetLocalPlayer()) == RACE_HUMAN then
        BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, borderSize*0.055, - 0.9*borderSize - titleSize - 0.003 -HeroSelector.CategorySize)
        BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPRIGHT, box, FRAMEPOINT_TOPRIGHT, -borderSize*0.055, - 0.9*borderSize - titleSize - 0.003 -HeroSelector.CategorySize)
    else
        BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, borderSize*0.25, - 0.9*borderSize - titleSize - 0.003 -HeroSelector.CategorySize)
        BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPRIGHT, box, FRAMEPOINT_TOPRIGHT, -borderSize*0.25, - 0.9*borderSize - titleSize - 0.003 -HeroSelector.CategorySize)
    end
    BlzFrameSetSize(boxBottom, 0.01, 0.1)
    
    
    BlzFrameSetVisible(HeroSelector.SelectedIndikator, false)
    BlzFrameSetAbsPoint(box, HeroSelector.BoxPosPoint, HeroSelector.BoxPosX, HeroSelector.BoxPosY)   
    BlzFrameSetSize(box, borderSize*2 + buttonSize*colCount + HeroSelector.SpaceBetweenX*(colCount-1), borderSize*2 + buttonSize*rowCount + HeroSelector.SpaceBetweenY*(rowCount - 1) + titleSize + HeroSelector.CategorySize + 0.0145)
    BlzFrameSetTextAlignment(titel, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
    BlzFrameSetPoint(titel, FRAMEPOINT_TOP, box, FRAMEPOINT_TOP, 0, -borderSize*0.6)
    BlzFrameSetSize(titel, BlzFrameGetWidth(box) - borderSize*2, 0.03)
    
    BlzFrameSetText(titel, "Hero Selection")

    local rowRemaining = colCount
    if colCount*rowCount < #HeroSelector.UnitData then
        print("FieldCount:",colCount*rowCount, "HeroCount",#HeroSelector.UnitData)
    end
    local y = -borderSize - titleSize - 0.0125 - HeroSelector.CategorySize
    local x = borderSize
    for buttonIndex = 1, colCount*rowCount, 1 do
        local button = BlzCreateFrame("HeroSelectorButton", box, 0, buttonIndex)
        local icon = BlzGetFrameByName("HeroSelectorButtonIcon", buttonIndex)
        local iconPushed = BlzGetFrameByName("HeroSelectorButtonIconPushed", buttonIndex)
        local iconDisabled = BlzGetFrameByName("HeroSelectorButtonIconDisabled", buttonIndex)
        local tooltipBox = BlzCreateFrame("HeroSelectorTextBox", box, 0, buttonIndex)
        local tooltip = BlzCreateFrame("HeroSelectorText", tooltipBox, 0, buttonIndex)
        BlzFrameSetTooltip(button, tooltipBox)
        if not HeroSelector.TooltipRelativIsBox then
            BlzFrameSetPoint(tooltip, HeroSelector.TooltipPoint, button, HeroSelector.TooltipRelativePoint, HeroSelector.TooltipOffsetX ,HeroSelector.TooltipOffsetY)
        else
            BlzFrameSetPoint(tooltip, HeroSelector.TooltipPoint, box, HeroSelector.TooltipRelativePoint, HeroSelector.TooltipOffsetX ,HeroSelector.TooltipOffsetY)
        end
        BlzFrameSetPoint(tooltipBox, FRAMEPOINT_BOTTOMLEFT, tooltip, FRAMEPOINT_BOTTOMLEFT, -0.007, -0.007)
        BlzFrameSetPoint(tooltipBox, FRAMEPOINT_TOPRIGHT, tooltip, FRAMEPOINT_TOPRIGHT, 0.007, 0.007)
        TasButtonAction.Set(button, HeroSelector.actionPressHeroButton)
        BlzFrameSetSize(button, buttonSize, buttonSize)
 
        local heroButton = {}
        HeroSelector.HeroButtons[buttonIndex] = heroButton
        heroButton.Frame = button
        heroButton.Icon = icon
        heroButton.IconPushed = iconPushed
        heroButton.IconDisabled = iconDisabled
        heroButton.Tooltip = tooltip

        HeroSelector.HeroButtons[button] = buttonIndex
        if HeroSelector.ChainedButtons then --buttons are connected to the previous one or the previous row
            if buttonIndex == 1 then
                BlzFrameSetPoint(button, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, borderSize, y)
            elseif rowRemaining <= 0 then
                BlzFrameSetPoint(button, FRAMEPOINT_TOPLEFT, HeroSelector.HeroButtons[buttonIndex - colCount].Frame, FRAMEPOINT_BOTTOMLEFT, 0, -HeroSelector.SpaceBetweenY)
                rowRemaining = colCount
            else
                BlzFrameSetPoint(button, FRAMEPOINT_LEFT, HeroSelector.HeroButtons[buttonIndex - 1].Frame, FRAMEPOINT_RIGHT, HeroSelector.SpaceBetweenX, 0)
            end
        else --buttons have an offset to the TopLeft of the box
            if rowRemaining <= 0 then
                x = borderSize
                rowRemaining = colCount
                y = y - HeroSelector.SpaceBetweenY - buttonSize
            elseif buttonIndex ~= 1 then
                x = x + buttonSize + HeroSelector.SpaceBetweenX
            end
            BlzFrameSetPoint(button, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, x, y)
        end
        rowRemaining = rowRemaining - 1
    end
    local y = -0.9*borderSize - titleSize - 0.0025
    local x = borderSize*0.65
    --create category buttons added before the box was created
    for buttonIndex, value in ipairs(HeroSelector.Category)
    do
        local button = BlzCreateFrame("HeroSelectorCategoryButton", box, 0, 0)
        local icon = BlzGetFrameByName("HeroSelectorCategoryButtonIcon", 0)
        local iconPushed = BlzGetFrameByName("HeroSelectorCategoryButtonIconPushed", 0)
        local tooltipBox = BlzCreateFrame("HeroSelectorTextBox", box, 0, buttonIndex)
        local tooltip = BlzCreateFrame("HeroSelectorText", tooltipBox, 0, buttonIndex)
        BlzFrameSetPoint(tooltipBox, FRAMEPOINT_BOTTOMLEFT, tooltip, FRAMEPOINT_BOTTOMLEFT, -0.007, -0.007)
        BlzFrameSetPoint(tooltipBox, FRAMEPOINT_TOPRIGHT, tooltip, FRAMEPOINT_TOPRIGHT, 0.007, 0.007)
        BlzFrameSetText(tooltip, GetLocalizedString(value.Text))
        value.Text = tooltip
        BlzFrameSetPoint(tooltip, FRAMEPOINT_BOTTOM, button, FRAMEPOINT_TOP, 0, 0.007)
        BlzFrameSetTooltip(button, tooltipBox)
        BlzFrameSetSize(button, HeroSelector.CategorySize, HeroSelector.CategorySize)
        BlzFrameSetTexture(icon, value.TextureDisabled, 0, true)
        BlzFrameSetTexture(iconPushed, value.TextureDisabled, 0, true)
        HeroSelector.CategoryButton[button] = value
        TasButtonAction.Set(button, HeroSelector.CategoryClickAction)
        value.Icon = icon
        value.IconPushed = iconPushed
        value.Button = button

        local lastButton = HeroSelector.CategoryButton[buttonIndex - 1]
        if not lastButton then
            BlzFrameSetPoint(button, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, x, y)
        else
            BlzFrameSetPoint(button, FRAMEPOINT_LEFT, lastButton, FRAMEPOINT_RIGHT, HeroSelector.CategorySpaceX, 0)
        end
        table.insert(HeroSelector.CategoryButton, button)
        table.insert(HeroSelector.Frames, tooltip)
        table.insert(HeroSelector.Frames, icon)
        table.insert(HeroSelector.Frames, button)
    end

    local acceptButton = BlzCreateFrame("HeroSelectorTextButton", box, 0, 0)
    local randomButton = BlzCreateFrame("HeroSelectorTextButton", box, 0, 1)
    local banButton = BlzCreateFrame("HeroSelectorTextButton", box, 0, 2)

    TasButtonAction.Set(acceptButton, HeroSelector.actionAcceptButton)
    TasButtonAction.Set(randomButton, HeroSelector.actionRandomButton)
    TasButtonAction.Set(banButton, HeroSelector.actionBanButton)
    BlzFrameSetSize(acceptButton, HeroSelector.AcceptButtonSizeX, HeroSelector.AcceptButtonSizeY)
    BlzFrameSetSize(randomButton, HeroSelector.RandomButtonSizeX, HeroSelector.RandomButtonSizeY)
    BlzFrameSetSize(banButton, HeroSelector.BanButtonSizeX, HeroSelector.BanButtonSizeY)

    --OK, READY, ACCEPT
    BlzFrameSetText(acceptButton, HeroSelector.AcceptButtonTextPrefix .. GetLocalizedString(HeroSelector.AcceptButtonText))
    BlzFrameSetText(randomButton, HeroSelector.RandomButtonTextPrefix .. GetLocalizedString(HeroSelector.RandomButtonText))
    BlzFrameSetText(banButton, HeroSelector.BanButtonTextPrefix .. GetLocalizedString(HeroSelector.BanButtonText))
    BlzFrameSetPoint(acceptButton, HeroSelector.AcceptButtonAnchor, box, FRAMEPOINT_BOTTOM, 0, 0)
    BlzFrameSetPoint(randomButton, HeroSelector.RandomButtonAnchor, box, FRAMEPOINT_BOTTOM, 0, 0)
    BlzFrameSetPoint(banButton, FRAMEPOINT_BOTTOM, box, FRAMEPOINT_BOTTOM, 0, 0)
    HeroSelector.AcceptButton = acceptButton
    HeroSelector.RandomButton = randomButton
    HeroSelector.BanButton = banButton
    BlzFrameSetVisible(banButton, false)
    BlzFrameSetVisible(acceptButton, HeroSelector.AcceptButtonIsShown)
    BlzFrameSetVisible(randomButton, HeroSelector.RandomButtonIsShown)

    table.insert(HeroSelector.Frames, randomButton)
    table.insert(HeroSelector.Frames, acceptButton)
    table.insert(HeroSelector.Frames, titel)
    table.insert(HeroSelector.Frames, box)
    table.insert(HeroSelector.Frames, banButton)
    table.insert(HeroSelector.Frames, indicatorParent)
    
    HeroSelector.Box = box
    if not HeroSelector.AutoShow then
        BlzFrameSetVisible(box, false)
    else
        HeroSelector.AutoShow = nil
    end
    HeroSelector.update()
    --clean of globals
    HeroSelector.BoxPosPoint = nil
    HeroSelector.BoxPosX = nil
    HeroSelector.BoxPosY = nil
    HeroSelector.AcceptButtonTextPrefix = nil
    HeroSelector.RandomButtonTextPrefix = nil
    HeroSelector.BanButtonTextPrefix = nil
    if TeamViewer then TeamViewer.Init() end
    if HeroInfo then HeroInfo.Init() end
    end 
end


JASS:
library HeroSelector
//HeroSelector V1.5b
//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)

//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)
//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
        //Box
        private string BoxFrameName            = "HeroSelectorRaceBox" //this is the background box being created
        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
        //Unique Picks
        private integer UnitCount              = 2 //each hero is in total allowed to be picked this amount of times (includes random, repicking allows a hero again).
        private integer UnitCountPerTeam       = 1 //Each Team is allowed to pick this amount of each unitType
        private string  ToManyTooltip          = "OUTOFSTOCKTOOLTIP"
        //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 CategoryAutoDetectHero = true // 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"
        //Ban Button
        private string BanButtonTextPrefix     = "|cffcf2084" //Prefix Text for the Ban Button
        private string BanButtonText           = "CHAT_ACTION_BAN" //tries to get a Localized String
        private real BanButtonSizeX            = 0.13
        private real BanButtonSizeY            = 0.03
        private string BanTooltip              = "DISALLOWED"
        private boolean BanIgnoreRequirment    = true // (true) Ban is not restricted by Requirments
        //Accept Button
        private string AcceptButtonTextPrefix       = ""
        private string AcceptButtonText             = "ACCEPT"
        private real AcceptButtonSizeX              = 0.085
        private real AcceptButtonSizeY              = 0.03
        private boolean AcceptButtonIsShown         = true
        private framepointtype AcceptButtonAnchor   = FRAMEPOINT_BOTTOMRIGHT //places the Accept button with which Point to the bottom, with right he is at the left
        //Random Button
        private string RandomButtonTextPrefix       = ""
        private string RandomButtonText             = "RANDOM" //tries Localizing
        private real RandomButtonSizeX              = 0.085
        private real RandomButtonSizeY              = 0.03
        private boolean RandomButtonIsShown         = true
        private framepointtype RandomButtonAnchor   = FRAMEPOINT_BOTTOMLEFT
        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.
        //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
        private trigger CategoryClickTrigger   = CreateTrigger()
        private trigger AcceptButtonTrigger  = CreateTrigger()
        private trigger BanButtonTrigger      = CreateTrigger()
        private trigger RandomButtonTrigger  = CreateTrigger()
        private framehandle BanButton
        private framehandle AcceptButton
        private framehandle RandomButton
        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 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 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
    endglobals

    private function AutoDetectCategory takes integer unitCode returns integer
        local integer value = 0
        local unit u
        local integer primaryAttribute
        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
        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

    function HeroSelectorDestroy takes nothing returns nothing
        local integer buttonIndex = 1
        loop
            exitwhen buttonIndex > HeroButtonCount
            call BlzDestroyFrame(HeroButtonIcon[buttonIndex])
            call BlzDestroyFrame(HeroButtonIconDisabled[buttonIndex])
            call BlzDestroyFrame(HeroButtonFrame[buttonIndex])
            call BlzDestroyFrame(HeroButtonTooltip[buttonIndex])
            call BlzDestroyFrame(HeroButtonTooltipBox[buttonIndex])
            set HeroButtonTooltip[buttonIndex] = null
            set HeroButtonTooltipBox[buttonIndex] = null
            set HeroButtonFrame[buttonIndex] = null
            set HeroButtonIcon[buttonIndex] = null
            set buttonIndex = buttonIndex + 1
        endloop
        set buttonIndex = 1
        loop
            exitwhen buttonIndex > CategoryButtonCount
            call BlzDestroyFrame(CategoryButton[buttonIndex])
            call BlzDestroyFrame(CategoryIconFrame[buttonIndex])
            call BlzDestroyFrame(CategoryTooltipFrame[buttonIndex])
            call BlzDestroyFrame(CategoryTooltipFrameBox[buttonIndex])
            set CategoryButton[buttonIndex] = null
            set CategoryIconFrame[buttonIndex] = null
            set CategoryTooltipFrame[buttonIndex] = null
            set CategoryTooltipFrameBox[buttonIndex] = null
            set buttonIndex = buttonIndex + 1
        endloop

        call BlzDestroyFrame(HeroSelectorBox)
        call BlzDestroyFrame(HeroSelectorBoxSeperator)     
        call BlzDestroyFrame(HeroSelectorBoxTitle)
        call BlzDestroyFrame(BanButton)
        call BlzDestroyFrame(RandomButton)
        call BlzDestroyFrame(AcceptButton)
        call BlzDestroyFrame(IndicatorSelected)
        call BlzDestroyFrame(IndicatorSelectedParent)
     

        set HeroSelectorBox = null
        set HeroSelectorBoxTitle = null
        set HeroSelectorBoxSeperator = null
        set BanButton = null
        set RandomButton = null
        set AcceptButton = null
        set IndicatorSelected = null
     
        call DestroyTrigger(CategoryClickTrigger)
        call DestroyTrigger(BanButtonTrigger)
        call DestroyTrigger(RandomButtonTrigger)
        call DestroyTrigger(HeroButtonClickTrigger)

        set CategoryClickTrigger = null
        set BanButtonTrigger = null
        set RandomButtonTrigger = null
        set HeroButtonClickTrigger = null
     
    endfunction

    //=====
    //code start
    //=====

    function HeroSelectorGetDisabledIcon takes string icon returns string
        //ReplaceableTextures\CommandButtons\BTNHeroPaladin.tga -> ReplaceableTextures\CommandButtonsDisabled\DISBTNHeroPaladin.tga
        if SubString(icon, 34, 35) != "\\" then
            return icon
        endif //this string has not enough chars return it
        //string.len(icon) < 34 then return icon end //this string has not enough chars return it
        return SubString(icon, 0, 34) + "Disabled\\DIS" + SubString(icon, 35, StringLength(icon))
    endfunction

    function HeroSelectorAddCategory takes string icon, string text returns nothing
        //adds an data category construct
        set CategoryButtonCount = CategoryButtonCount + 1
        set CategoryText[CategoryButtonCount] = text
        set CategoryTexture[CategoryButtonCount] = icon
        set CategoryTextureDisabled[CategoryButtonCount] = HeroSelectorGetDisabledIcon(icon)
        if CategoryButtonCount > 1 then
            set CategoryButtonValue[CategoryButtonCount] = CategoryButtonValue[CategoryButtonCount - 1]*2
        else
            set CategoryButtonValue[CategoryButtonCount] = 1
        endif
    endfunction

    function HeroSelectorSetFrameText takes framehandle frame, string text returns nothing
        call BlzFrameSetText(frame, text)
    endfunction
    function HeroSelectorSetFrameTextPlayer takes framehandle frame, string text, player p returns nothing
        if GetLocalPlayer() == p then
            call BlzFrameSetText(frame, text)
        endif
    endfunction
    function HeroSelectorSetFrameTextTeam takes framehandle frame, string text, integer teamNr returns nothing
        if GetPlayerTeam(GetLocalPlayer()) == teamNr then
            call BlzFrameSetText(frame, text)
        endif
    endfunction
    function HeroSelectorSetFrameTextRace takes framehandle frame, string text, race r returns nothing
        if GetPlayerRace(GetLocalPlayer()) == r then
            call BlzFrameSetText(frame, text)
        endif
    endfunction
    function HeroSelectorSetFrameTextForce takes framehandle frame, string text, force f returns nothing
        if BlzForceHasPlayer(f, GetLocalPlayer()) then
            call BlzFrameSetText(frame, text)
        endif
    endfunction
 

    function HeroSelectorSetTitleText takes string text returns nothing
        call HeroSelectorSetFrameText(HeroSelectorBoxTitle, text)
    endfunction
    function HeroSelectorSetTitleTextPlayer takes string text, player who returns nothing
        call HeroSelectorSetFrameTextPlayer(HeroSelectorBoxTitle, text, who)
    endfunction
    function HeroSelectorSetTitleTextForce takes string text, force who returns nothing
        call HeroSelectorSetFrameTextForce(HeroSelectorBoxTitle, text, who)
    endfunction
    function HeroSelectorSetTitleTextTeam takes string text, integer who returns nothing
        call HeroSelectorSetFrameTextTeam(HeroSelectorBoxTitle, text, who)
    endfunction
    function HeroSelectorSetTitleTextRace takes string text, race who returns nothing
        call HeroSelectorSetFrameTextRace(HeroSelectorBoxTitle, text, who)
    endfunction

    function HeroSelectorSetBanButtonText takes string text returns nothing
        call HeroSelectorSetFrameText(BanButton, text)
    endfunction
    function HeroSelectorSetBanButtonTextPlayer takes string text, player who returns nothing
        call HeroSelectorSetFrameTextPlayer(BanButton, text, who)
    endfunction
    function HeroSelectorSetBanButtonTextForce takes string text, force who returns nothing
        call HeroSelectorSetFrameTextForce(BanButton, text, who)
    endfunction
    function HeroSelectorSetBanButtonTextTeam takes string text, integer who returns nothing
        call HeroSelectorSetFrameTextTeam(BanButton, text, who)
    endfunction
    function HeroSelectorSetBanButtonTextRace takes string text, race who returns nothing
        call HeroSelectorSetFrameTextRace(BanButton, text, who)
    endfunction
 
    function HeroSelectorSetRandomButtonText takes string text returns nothing
        call HeroSelectorSetFrameText(RandomButton, text)
    endfunction
    function HeroSelectorSetRandomButtonTextPlayer takes string text, player who returns nothing
        call HeroSelectorSetFrameTextPlayer(RandomButton, text, who)
    endfunction
    function HeroSelectorSetRandomButtonTextForce takes string text, force who returns nothing
        call HeroSelectorSetFrameTextForce(RandomButton, text, who)
    endfunction
    function HeroSelectorSetRandomButtonTextTeam takes string text, integer who returns nothing
        call HeroSelectorSetFrameTextTeam(RandomButton, text, who)
    endfunction
    function HeroSelectorSetRandomButtonTextRace takes string text, race who returns nothing
        call HeroSelectorSetFrameTextRace(RandomButton, text, who)
    endfunction
 
    function HeroSelectorSetAcceptButtonText takes string text returns nothing
        call HeroSelectorSetFrameText(AcceptButton, text)
    endfunction
    function HeroSelectorSetAcceptButtonTextPlayer takes string text, player who returns nothing
        call HeroSelectorSetFrameTextPlayer(AcceptButton, text, who)
    endfunction
    function HeroSelectorSetAcceptButtonTextForce takes string text, force who returns nothing
        call HeroSelectorSetFrameTextForce(AcceptButton, text, who)
    endfunction
    function HeroSelectorSetAcceptButtonTextTeam takes string text, integer who returns nothing
        call HeroSelectorSetFrameTextTeam(AcceptButton, text, who)
    endfunction
    function HeroSelectorSetAcceptButtonTextRace takes string text, race who returns nothing
        call HeroSelectorSetFrameTextRace(AcceptButton, text, who)
    endfunction

    function HeroSelectorSetUnitCategory takes integer unitCode, integer category returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroCategory[index] =  category
    endfunction

    function HeroSelectorAddUnitCategory takes integer unitCode, integer category returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroCategory[index] =  BlzBitOr(category, HeroCategory[index])
    endfunction

    function HeroSelectorDeselectButton takes integer buttonIndex returns nothing
        local integer playerIndex = 0
        if buttonIndex > 0 then
            if PlayerSelectedButtonIndex[GetPlayerId(GetLocalPlayer())] == buttonIndex then
               call BlzFrameSetVisible(IndicatorSelected, false)
            endif
            loop
                exitwhen playerIndex == GetBJMaxPlayers()
                if PlayerSelectedButtonIndex[playerIndex] == buttonIndex then
                set PlayerSelectedButtonIndex[playerIndex] = 0
                endif
                set playerIndex = playerIndex + 1
            endloop
        else
            loop
                exitwhen playerIndex == GetBJMaxPlayers()
                set PlayerSelectedButtonIndex[playerIndex] = 0
                set playerIndex = playerIndex + 1
            endloop
            call BlzFrameSetVisible(IndicatorSelected, false)
        endif
    endfunction

    function HeroSelectorSetUnitReqRace takes integer unitCode, race r returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroRegType[index] = 1
        set HeroRegRace[index] = r
    endfunction

    function HeroSelectorSetUnitReqForce takes integer unitCode, force f returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroRegType[index] = 4
        set HeroRegForce[index] = f
    endfunction

    function HeroSelectorSetUnitReqPlayer takes integer unitCode, player p returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroRegType[index] = 3
        set HeroRegPlayer[index] = p
    endfunction

    function HeroSelectorSetUnitReqTeam takes integer unitCode, integer teamNr returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroRegType[index] = 2
        set HeroRegNumber[index] = teamNr
    endfunction

    function HeroSelectorSetUnitReqTechLevel takes integer unitCode, integer techCode, integer techLevel returns nothing
        local integer index =  LoadInteger(Hash, unitCode, 0)
        set HeroRegType[index] = 5
        set HeroRegNumber[index] = techCode
        set HeroRegNumber2[index] = techLevel
    endfunction

    function HeroSelectorButtonRequirementDone takes integer unitCode, player p returns boolean
        //true when no requirement is set or the requirment call is successful
        local integer index =  LoadInteger(Hash, unitCode, 0)
        if HeroRegType[index] == 0 then //no requirement
            return true
        elseif HeroRegType[index] == 1 and HeroRegRace[index] == GetPlayerRace(p) then
            return true
        elseif HeroRegType[index] == 2 and HeroRegNumber[index] == GetPlayerTeam(p) then
            return true
        elseif HeroRegType[index] == 3 and HeroRegPlayer[index] == p then
            return true
        elseif HeroRegType[index] == 4 and BlzForceHasPlayer(HeroRegForce[index], p) then
            return true
        elseif HeroRegType[index] == 5 and GetPlayerTechCount(p, HeroRegNumber[index], true) >= HeroRegNumber2[index] then     
            return true
        endif
        return false
    endfunction

    function HeroSelectorDisableButtonIndex takes integer buttonIndex, integer teamNr returns nothing
        local integer playerIndex = 0
        if buttonIndex > 0 then
 
            if teamNr == -1 or teamNr == GetPlayerTeam(GetLocalPlayer()) then
               call BlzFrameSetEnable(HeroButtonFrame[buttonIndex], false)
            endif
            if PlayerSelectedButtonIndex[GetPlayerId(GetLocalPlayer())] == buttonIndex then
                call BlzFrameSetVisible(IndicatorSelected, false)
            endif

            //deselect this Button from all players or the team
            loop
                exitwhen playerIndex == GetBJMaxPlayers()
                if (teamNr == -1 or teamNr == GetPlayerTeam(Player(playerIndex))) and PlayerSelectedButtonIndex[playerIndex] == buttonIndex then
                    set PlayerSelectedButtonIndex[playerIndex] = 0             
                endif
                set playerIndex = playerIndex + 1
            endloop
        endif
    endfunction

    function HeroSelectorEnableButtonIndex takes integer unitCode, integer buttonIndex returns nothing
        if buttonIndex > 0 then
            call BlzFrameSetEnable(HeroButtonFrame[buttonIndex], true and (BanIgnoreRequirment and BlzFrameIsVisible(BanButton) ) or HeroSelectorButtonRequirementDone(unitCode, GetLocalPlayer()))
        endif
    endfunction

    function HeroSelectorUpdateTooltip takes integer unitCode returns nothing
        local integer unitCodeIndex = LoadInteger(Hash, unitCode, 0)
        local integer buttonIndex = HeroButtonIndex[unitCodeIndex]
        local framehandle frame = HeroButtonTooltip[buttonIndex]
        local integer teamKey = StringHash("TeamCount"+I2S(GetPlayerTeam(GetLocalPlayer())))
        if HeroTotalCount[unitCodeIndex] > UnitCount then
            call BlzFrameSetText(frame, TooltipPrefix + GetObjectName(unitCode) + "\n|r(" + GetLocalizedString(BanTooltip) + ")")
        else
            if HeroTotalCount[unitCodeIndex] == UnitCount or LoadInteger(Hash, unitCode, teamKey) >= UnitCountPerTeam then
                call BlzFrameSetText(frame, TooltipPrefix + GetObjectName(unitCode) + "\n|r(" + GetLocalizedString(ToManyTooltip) + ")")
            elseif not HeroSelectorButtonRequirementDone(unitCode, GetLocalPlayer()) then
                call BlzFrameSetText(frame, TooltipPrefix + GetObjectName(unitCode) + "\n|r(" + GetLocalizedString(TooltipRequires) + ")")
            else
                call BlzFrameSetText(frame, TooltipPrefix + GetObjectName(unitCode))
            endif
        endif
    endfunction

    function HeroSelectorCounterChangeUnitCode takes integer unitCode, integer add, player p returns nothing
        local integer unitCodeIndex = LoadInteger(Hash, unitCode, 0)
        local integer buttonIndex = HeroButtonIndex[unitCodeIndex]
        local integer teamNr = GetPlayerTeam(p)
        local integer teamKey = StringHash("TeamCount"+I2S(teamNr))
        set HeroTotalCount[unitCodeIndex] = HeroTotalCount[unitCodeIndex] + add
        call SaveInteger(Hash, unitCode, teamKey, LoadInteger(Hash, unitCode, teamKey) + add)

        if HeroTotalCount[unitCodeIndex] >= UnitCount then
            //disable for all
            call HeroSelectorDisableButtonIndex(buttonIndex, -1)
        else
            //enable for all
            call HeroSelectorEnableButtonIndex(unitCode, buttonIndex)
            if LoadInteger(Hash, unitCode, teamKey) >= UnitCountPerTeam  then
                //disable for this team
                call HeroSelectorDisableButtonIndex(buttonIndex, teamNr)
            endif
        endif
        call HeroSelectorUpdateTooltip(unitCode)
    endfunction

    function HeroSelectorRollOption takes player p, boolean includeRandomOnly, integer exculdedIndex, integer category returns integer
        local integer teamNr = GetPlayerTeam(p)
        local integer array options
        local integer optionCount = 0
        local integer teamKey = StringHash("TeamCount"+I2S(teamNr))
        local boolean allowed
        local integer index = 1
        local integer unitCode
        local integer unitCodeIndex
        loop
            exitwhen index > HeroCount
            set unitCode = HeroUnitCode[index]
            set unitCodeIndex = LoadInteger(Hash, unitCode, 0)
            set allowed = true
            //total limited reached?
            if HeroTotalCount[unitCodeIndex] >= UnitCount then
                set allowed = false
                //print(GetObjectName(unitCode))
                //print("rejected total limit")
            endif
            //team limited reached?
            if allowed and LoadInteger(Hash, unitCode, teamKey) >= UnitCountPerTeam  then
                //print(GetObjectName(unitCode))
                //print("rejected team limit")
                set allowed = false
            endif
            //allow randomOnly?
            if allowed and not includeRandomOnly and HeroButtonIndex[unitCodeIndex] == 0  then
                //print(GetObjectName(unitCode))
                //print("rejected random only")
                set allowed = false
            endif
            //this index is excluded? This can make sure you get another button.
            if allowed and HeroButtonIndex[unitCodeIndex] > 0 and HeroButtonIndex[unitCodeIndex] == exculdedIndex then
                //print(GetObjectName(unitCode))
                //print("rejected exclude")
                set allowed = false
            endif
            //fullfills the requirement?
            if allowed and not HeroSelectorButtonRequirementDone(unitCode, p) then
                //print(GetObjectName(unitCode))
                //print("rejected requirement")
                set allowed = false
            endif
            //when having an given an category only allow options having that category atleast partly
            if allowed and category > 0 and BlzBitAnd(category, HeroCategory[unitCodeIndex]) == 0 then
                //print(GetObjectName(unitCode))
                //print("  rejected category", category, HeroSelector.UnitData[unitCode].Category)
                set allowed = false
            endif

            if allowed then
                set optionCount = optionCount + 1
                set options[optionCount] = unitCode
            endif
            set index = index + 1
        endloop
        //nothing is allwoed?
        if optionCount == 0 then
            return 0
        else
            return options[GetRandomInt(1, optionCount)]
        endif
    endfunction

    function HeroSelectorEnablePickDelayBanAction takes nothing returns nothing
        loop
            exitwhen DelayBanCount <= 0
            call HeroSelectorCounterChangeUnitCode(DelayBanUnitCode[DelayBanCount], UnitCount + 1, DelayBanPlayer[DelayBanCount])
            set DelayBanCount = DelayBanCount - 1
        endloop
    endfunction

    function HeroSelectorUpdate takes nothing returns nothing
        local integer buttonIndex = 1
        local integer unitCodeIndex
        local integer teamNr
        local integer teamIndex = 0
        local integer teamKey
        loop
            exitwhen buttonIndex > HeroButtonCount
            //have data for this button?
            if HeroButtonUnitCode[buttonIndex] > 0 then
                set unitCodeIndex = LoadInteger(Hash, HeroButtonUnitCode[buttonIndex],0)
                if HeroTotalCount[unitCodeIndex] >= UnitCount then
                    //disable for all
                    call HeroSelectorDisableButtonIndex(buttonIndex, -1)
                else
                    //enable for all
                    call HeroSelectorEnableButtonIndex(HeroButtonUnitCode[buttonIndex], buttonIndex)
                    set teamIndex = 0
                    loop
                        exitwhen teamIndex > UsedTeamNrCount
                        set teamNr = UsedTeamNr[teamIndex]
                        set teamKey = StringHash("TeamCount"+I2S(teamNr))
                        if LoadInteger(Hash, HeroButtonUnitCode[buttonIndex], teamKey) >= UnitCountPerTeam  then
                            //disable for this team
                            call HeroSelectorDisableButtonIndex(buttonIndex, teamNr)
                        endif
                        set teamIndex = teamIndex + 1                 
                    endloop
                endif
                call BlzFrameSetTexture(HeroButtonIcon[buttonIndex], BlzGetAbilityIcon(HeroButtonUnitCode[buttonIndex]), 0, ButtonBlendAll)
                call BlzFrameSetTexture(HeroButtonIconPushed[buttonIndex], BlzGetAbilityIcon(HeroButtonUnitCode[buttonIndex]), 0, ButtonBlendAll)
                call BlzFrameSetTexture(HeroButtonIconDisabled[buttonIndex], HeroSelectorGetDisabledIcon(BlzGetAbilityIcon(HeroButtonUnitCode[buttonIndex])), 0, ButtonBlendAll)
                //call BlzFrameSetText(HeroButtonTooltip[buttonIndex], TooltipPrefix + GetObjectName(HeroButtonUnitCode[buttonIndex]))
                call HeroSelectorUpdateTooltip(HeroButtonUnitCode[buttonIndex])
            else
                //no, make it unclickable and empty
                call BlzFrameSetEnable(HeroButtonFrame[buttonIndex], false)
                call BlzFrameSetTexture(HeroButtonIcon[buttonIndex], EmptyButtonPath, 0, true)
                call BlzFrameSetTexture(HeroButtonIconPushed[buttonIndex], EmptyButtonPath, 0, true)
                call BlzFrameSetTexture(HeroButtonIconDisabled[buttonIndex], EmptyButtonPath, 0, true)
            endif
            set buttonIndex = buttonIndex + 1
        endloop
    endfunction
    function HeroSelectorEnablePickAction takes boolean flag returns nothing
        call BlzFrameSetVisible(AcceptButton, true and AcceptButtonIsShown)
        call BlzFrameSetVisible(RandomButton, true and RandomButtonIsShown)
        call BlzFrameSetVisible(BanButton, false)
        call BlzFrameSetEnable(AcceptButton, flag)
        call BlzFrameSetEnable(RandomButton, flag)
        call BlzFrameSetModel(IndicatorSelected, IndicatorPathPick, 0)
        if BanIgnoreRequirment then
            call HeroSelectorUpdate()
        endif
    endfunction

    function HeroSelectorEnablePick takes boolean flag returns nothing
        call HeroSelectorEnablePickDelayBanAction()
        call HeroSelectorEnablePickAction(flag) 
    endfunction

    function HeroSelectorEnablePickPlayer takes boolean flag, player p returns nothing
        call HeroSelectorEnablePickDelayBanAction()

        if GetLocalPlayer() == p then
            call HeroSelectorEnablePickAction(flag)
        endif
    endfunction

    function HeroSelectorEnablePickTeam takes boolean flag, integer teamNr returns nothing
        call HeroSelectorEnablePickDelayBanAction()

        if GetPlayerTeam(GetLocalPlayer()) == teamNr then
            call HeroSelectorEnablePickAction(flag)
        endif
    endfunction

    function HeroSelectorEnablePickRace takes boolean flag, race r returns nothing
        call HeroSelectorEnablePickDelayBanAction()

        if GetPlayerRace(GetLocalPlayer()) == r then
            call HeroSelectorEnablePickAction(flag)
        endif
    endfunction

    function HeroSelectorEnablePickForce takes boolean flag, force f returns nothing
        call HeroSelectorEnablePickDelayBanAction()

        if BlzForceHasPlayer(f, GetLocalPlayer()) then
            call HeroSelectorEnablePickAction(flag)
        endif
    endfunction

    function HeroSelectorEnableBanPlayerAction takes boolean flag returns nothing
        call BlzFrameSetVisible(AcceptButton, false)
        call BlzFrameSetVisible(RandomButton, false)
        call BlzFrameSetVisible(BanButton, true)
        call BlzFrameSetEnable(BanButton, flag)
        call BlzFrameSetModel(IndicatorSelected, IndicatorPathBan, 0)
        if BanIgnoreRequirment then
            call HeroSelectorUpdate()
        endif
    endfunction

    function HeroSelectorEnableBanPlayer takes boolean flag, player p returns nothing
        if GetLocalPlayer() == p then
            call HeroSelectorEnableBanPlayerAction(flag)
        endif
    endfunction

    function HeroSelectorEnableBanForce takes boolean flag, force f returns nothing
        if BlzForceHasPlayer(f, GetLocalPlayer()) then
            call HeroSelectorEnableBanPlayerAction(flag)
        endif
    endfunction

    function HeroSelectorEnableBanTeam takes boolean flag, integer teamNr returns nothing
        if GetPlayerTeam(GetLocalPlayer()) == teamNr then
            call HeroSelectorEnableBanPlayerAction(flag)
        endif
    endfunction

    function HeroSelectorEnableBanRace takes boolean flag, race r returns nothing
        if GetPlayerRace(GetLocalPlayer()) == r then
            call HeroSelectorEnableBanPlayerAction(flag)
        endif
    endfunction
    function HeroSelectorEnableBan takes boolean flag returns nothing
        call HeroSelectorEnableBanPlayerAction(flag)
    endfunction

    function HeroSelectorframeLoseFocus takes framehandle frame returns nothing
        if BlzFrameGetEnable(frame) then
            call BlzFrameSetEnable(frame, false)
            call BlzFrameSetEnable(frame, true)
        endif
    endfunction


    function HeroSelectorShowFramePlayer takes framehandle frame, boolean flag, player p returns nothing
        if GetLocalPlayer() == p then
            call BlzFrameSetVisible(frame, flag)
        endif
    endfunction

    function HeroSelectorShowFrameForce takes framehandle frame, boolean flag, force f returns nothing
        if BlzForceHasPlayer(f, GetLocalPlayer()) then
            call BlzFrameSetVisible(frame, flag)
        endif
    endfunction

    function HeroSelectorShowFrameTeam takes framehandle frame, boolean flag, integer teamNr returns nothing
        if GetPlayerTeam(GetLocalPlayer()) == teamNr then
            call BlzFrameSetVisible(frame, flag)
        endif
    endfunction

    function HeroSelectorShowFrameRace takes framehandle frame, boolean flag, race r returns nothing
        if GetPlayerRace(GetLocalPlayer()) == r then
            call BlzFrameSetVisible(frame, flag)
        endif
    endfunction

    function HeroSelectorShowPlayer takes boolean flag, player who returns nothing
        call HeroSelectorShowFramePlayer(HeroSelectorBox, flag, who)
    endfunction

    function HeroSelectorShowTeam takes boolean flag, integer who returns nothing
        call HeroSelectorShowFrameTeam(HeroSelectorBox, flag, who)
    endfunction

    function HeroSelectorShowRace takes boolean flag, race who returns nothing
        call HeroSelectorShowFrameRace(HeroSelectorBox, flag, who)
    endfunction

    function HeroSelectorShowForce takes boolean flag, force who returns nothing
        call HeroSelectorShowFrameForce(HeroSelectorBox, flag, who)
    endfunction
    function HeroSelectorShow takes boolean flag returns nothing
        call BlzFrameSetVisible(HeroSelectorBox, flag)
    endfunction

    function HeroSelectorAddUnit takes integer unitCode, boolean onlyRandom returns nothing
        //no unitCode => empty field
        if unitCode == 0 then
            set ButtonHeroCount = ButtonHeroCount + 1
        else
            //Such an object Exist? not unique?
            if GetObjectName(unitCode) == "" or HaveSavedBoolean(Hash, unitCode, 0) then
                return
            endif
         
            set HeroCount = HeroCount + 1
            set HeroUnitCode[HeroCount] = unitCode
         
            call SaveBoolean(Hash, unitCode, 0, true)
            call SaveInteger(Hash, unitCode, 0, HeroCount)
            set HeroCategory[HeroCount] = AutoDetectCategory(unitCode)
            if not onlyRandom then
                set ButtonHeroCount = ButtonHeroCount + 1
                set HeroButtonIndex[HeroCount] = ButtonHeroCount
                set ButtonHeroUnitCode[ButtonHeroCount] = unitCode     
            endif
        endif
    endfunction

    function HeroSelectorDoRandom takes player p returns nothing
        local integer category = 0
        local integer unitCode
        local unit u
        if CategoryAffectRandom then
            set category = PlayerSelectedCategory[GetPlayerId(p)]
        endif
        set unitCode = HeroSelectorRollOption(p, true, 0, category)
        if unitCode == 0 then
            return
        endif
        set u = CreateUnit(p, unitCode, 0, 0, 0)
     
        call HeroSelectorCounterChangeUnitCode(unitCode, 1, p)

        set udg_HeroSelectorEventUnit = u
        set udg_HeroSelectorEventIsRandom = true
        set udg_HeroSelectorEventUnitCode = unitCode
        set udg_HeroSelectorEventPlayer = p
        set udg_HeroSelectorEvent = 0.0
        set udg_HeroSelectorEvent = 1.0
        set udg_HeroSelectorEvent = 0.0
        set u = null
    endfunction

    function HeroSelectorDoPick takes player p returns boolean
        local integer unitCode
        local unit u
        //pick what currently is selected, returns true on success returns false when something went wrong,
        local integer buttonIndex = PlayerSelectedButtonIndex[GetPlayerId(p)]
        if buttonIndex <= 0 then
            return false
        endif //reject nothing selected
        set unitCode = HeroButtonUnitCode[buttonIndex]
        if not HeroSelectorButtonRequirementDone(unitCode, p) then
            return false
        endif //reject requirment not fullfilled
        set u = CreateUnit(p, unitCode, 0, 0, 0)
     
        call HeroSelectorCounterChangeUnitCode(unitCode, 1, p)

        set udg_HeroSelectorEventUnit = u
        set udg_HeroSelectorEventIsRandom = false
        set udg_HeroSelectorEventUnitCode = unitCode
        set udg_HeroSelectorEventPlayer = p
        set udg_HeroSelectorEvent = 0.0
        set udg_HeroSelectorEvent = 1.0
        set udg_HeroSelectorEvent = 0.0

        set u = null
        return true
    endfunction

    function HeroSelectorForceRandom takes nothing returns nothing
        //this is a wrapper for doRandom allowing different dataTypes
        local player p
        local integer playerIndex = 0
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set p = Player(playerIndex)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                call HeroSelectorDoRandom(p)
            endif
            set playerIndex = playerIndex + 1
        endloop
        set p = null
    endfunction

    function HeroSelectorForceRandomTeam takes integer who returns nothing
        //this is a wrapper for doRandom allowing different dataTypes
        local player p
        local integer playerIndex = 0
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set p = Player(playerIndex)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerTeam(p) == who then
                    call HeroSelectorDoRandom(p)
                endif
            endif
            set playerIndex = playerIndex + 1
        endloop
        set p = null
    endfunction

    function HeroSelectorForceRandomRace takes race who returns nothing
        //this is a wrapper for doRandom allowing different dataTypes
        local player p
        local integer playerIndex = 0
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set p = Player(playerIndex)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerRace(p) == who then
                    call HeroSelectorDoRandom(p)
                endif
            endif
            set playerIndex = playerIndex + 1
        endloop
        set p = null
    endfunction
    function HeroSelectorForcePick takes nothing returns nothing
        //this is a wrapper for doRandom allowing different dataTypes
        local player p
        local integer playerIndex = 0
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set p = Player(playerIndex)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                if not HeroSelectorDoPick(p) then
                    call HeroSelectorDoRandom(p)
                endif
            endif
            set playerIndex = playerIndex + 1
        endloop
    endfunction

    function HeroSelectorForcePickTeam takes integer who returns nothing
        //this is a wrapper for doRandom allowing different dataTypes
        local player p
        local integer playerIndex = 0
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set p = Player(playerIndex)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerTeam(p) == who then
                    if not HeroSelectorDoPick(p) then
                        call HeroSelectorDoRandom(p)
                    endif
                endif
            endif
            set playerIndex = playerIndex + 1
        endloop
    endfunction

    function HeroSelectorForcePickRace takes race r returns nothing
        //this is a wrapper for doRandom allowing different dataTypes
        local player p
        local integer playerIndex = 0
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set p = Player(playerIndex)
            if GetPlayerSlotState(p) == PLAYER_SLOT_STATE_PLAYING then
                if GetPlayerRace(p) == r then
                    if not HeroSelectorDoPick(p) then
                        call HeroSelectorDoRandom(p)
                    endif
                endif
            endif
            set playerIndex = playerIndex + 1
        endloop
    endfunction
 
    function HeroSelectorForcePickPlayer takes player p returns nothing
        if not HeroSelectorDoPick(p) then
            call HeroSelectorDoRandom(p)
        endif
    endfunction

    function HeroSelectorActionPressHeroButton takes nothing returns nothing
        local framehandle fh = BlzGetTriggerFrame()
        local player p = GetTriggerPlayer()
        local integer buttonIndex = LoadInteger(Hash, GetHandleId(fh), 0)
        local integer unitCode = HeroButtonUnitCode[buttonIndex]
        set PlayerSelectedButtonIndex[GetPlayerId(p)] = buttonIndex

        call HeroSelectorframeLoseFocus(BlzGetTriggerFrame())

        if GetLocalPlayer() == p then
            call BlzFrameSetVisible(IndicatorSelected, true)
         
            call BlzFrameSetPoint(IndicatorSelected, FRAMEPOINT_TOPLEFT, fh, FRAMEPOINT_TOPLEFT, -0.001, 0.001)
            call BlzFrameSetPoint(IndicatorSelected, FRAMEPOINT_BOTTOMRIGHT, fh, FRAMEPOINT_BOTTOMRIGHT, -0.0012, -0.0016)
        endif

        set udg_HeroSelectorEventUnit = null
        set udg_HeroSelectorEventUnitCode = unitCode
        set udg_HeroSelectorEventPlayer = p
        set udg_HeroSelectorEvent = 0.0
        set udg_HeroSelectorEvent = 2.0
        set udg_HeroSelectorEvent = 0.0

        set fh = null
        set p = null
    endfunction

    function HeroSelectorActionRandomButton takes nothing returns nothing
        local player p = GetTriggerPlayer()
        local integer playerIndex = GetPlayerId(p)
        local integer unitCode
        local integer unitCodeIndex
        local integer buttonIndex
        call HeroSelectorframeLoseFocus(BlzGetTriggerFrame())
        if RandomButtonPick then
            call HeroSelectorDoRandom(p)
        else
            set unitCode = HeroSelectorRollOption(p, false, PlayerSelectedButtonIndex[playerIndex], PlayerSelectedCategory[playerIndex])
            if unitCode > 0 and GetLocalPlayer() == p then
                set unitCodeIndex = LoadInteger(Hash, unitCode, 0)
                set buttonIndex = HeroButtonIndex[unitCodeIndex]
                call BlzFrameClick(HeroButtonFrame[buttonIndex])
            endif
        endif
        set p = null
    endfunction

    function HeroSelectorActionAcceptButton takes nothing returns nothing
        call HeroSelectorframeLoseFocus(BlzGetTriggerFrame())
        call HeroSelectorDoPick(GetTriggerPlayer())
    endfunction

    function HeroSelectorActionBanButton takes nothing returns nothing
        local player p = GetTriggerPlayer()
        local integer playerIndex = GetPlayerId(p)
        local integer buttonIndex = PlayerSelectedButtonIndex[playerIndex]
        local integer unitCode = HeroButtonUnitCode[buttonIndex]
        call HeroSelectorframeLoseFocus(BlzGetTriggerFrame())
        if buttonIndex > 0 then
            if not DelayBanUntilPick then
                call HeroSelectorCounterChangeUnitCode(unitCode, UnitCount + 1, p)
            else
                set DelayBanCount = DelayBanCount + 1
                set DelayBanPlayer[DelayBanCount] = p
                set DelayBanUnitCode[DelayBanCount] = unitCode
            endif

            set udg_HeroSelectorEventUnit = null
            set udg_HeroSelectorEventUnitCode = unitCode
            set udg_HeroSelectorEventPlayer = p
            set udg_HeroSelectorEvent = 0.0
            set udg_HeroSelectorEvent = 3.0
            set udg_HeroSelectorEvent = 0.0
        endif
        set p = null
    endfunction

    function HeroSelectorActionCategoryButton takes nothing returns nothing
        local integer buttonIndex
        local framehandle fh = BlzGetTriggerFrame()
        local integer categoryIndex = LoadInteger(Hash, GetHandleId(fh), 0)
        local integer lastCategoryIndex
        local player p = GetTriggerPlayer()
        local integer playerIndex = GetPlayerId(p)
        local integer unitCodeIndex
        call HeroSelectorframeLoseFocus(fh)
        //has this category already?
        if BlzBitAnd(PlayerSelectedCategory[playerIndex], CategoryButtonValue[categoryIndex]) != 0 then
            //yes, unable
            set PlayerSelectedCategory[playerIndex] = PlayerSelectedCategory[playerIndex] - CategoryButtonValue[categoryIndex]
            if GetLocalPlayer() == p then
                call BlzFrameSetTexture(CategoryIconFrame[categoryIndex], CategoryTextureDisabled[categoryIndex], 0, true)
                call BlzFrameSetTexture(CategoryIconPushedFrame[categoryIndex], CategoryTextureDisabled[categoryIndex], 0, true)
            endif

        else
            if not CategoryMultiSelect and PlayerSelectedCategory[playerIndex] != 0 then
                set lastCategoryIndex = PlayerLastSelectedCategoryIndex[playerIndex]
                call BlzFrameSetTexture(CategoryIconFrame[lastCategoryIndex], CategoryTextureDisabled[lastCategoryIndex], 0, true)
                call BlzFrameSetTexture(CategoryIconPushedFrame[lastCategoryIndex], CategoryTextureDisabled[lastCategoryIndex], 0, true)
                set PlayerSelectedCategory[playerIndex] = 0
            endif
         
            //no, enable
            set PlayerSelectedCategory[playerIndex] = PlayerSelectedCategory[playerIndex] + CategoryButtonValue[categoryIndex]
            if GetLocalPlayer() == p then
                call BlzFrameSetTexture(CategoryIconFrame[categoryIndex], CategoryTexture[categoryIndex], 0, true)
                call BlzFrameSetTexture(CategoryIconPushedFrame[categoryIndex], CategoryTexture[categoryIndex], 0, true)
             
            endif
            set PlayerLastSelectedCategoryIndex[playerIndex] = categoryIndex
        endif
     
        if GetLocalPlayer() == p then
            //update all buttons
            //buttons not having at least 1 selected category becomes partly transparent
            set buttonIndex = 1
            loop
                exitwhen buttonIndex > HeroButtonCount
                if HeroButtonUnitCode[buttonIndex] > 0 then
                    set unitCodeIndex = LoadInteger(Hash, HeroButtonUnitCode[buttonIndex],0)
                    if PlayerSelectedCategory[playerIndex] == 0 or BlzBitAnd(HeroCategory[unitCodeIndex], PlayerSelectedCategory[playerIndex]) > 0 then
                        call BlzFrameSetAlpha(HeroButtonFrame[buttonIndex], 255)
                    else
                        call BlzFrameSetAlpha(HeroButtonFrame[buttonIndex], CategoryFilteredAlpha)
                    endif
                endif
                set buttonIndex = buttonIndex + 1
            endloop
        endif
        set fh = null
        set p = null
    endfunction

    function HeroSelectorInit takes nothing returns nothing
        local boolean loaded = BlzLoadTOCFile("war3mapImported\\HeroSelector.toc") //ex/import also "HeroSelector.fdf"
        local integer buttonIndex
        local real titleSize = 0.015
        local real borderSize = GetBorderSize()
        local integer colCount = ButtonColCount
        local integer rowCount = ButtonRowCount
        local framehandle box = BlzCreateFrame(BoxFrameName, BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
        local framehandle boxBottom = BlzCreateFrame("HeroSelectorRaceTopBox", box, 0, 0)
        local integer rowRemaining = colCount
        local real y = -borderSize - titleSize - 0.0125 - CategorySize
        local real x = borderSize
        local integer playerIndex = 0
        local integer teamIndexLoop
        local integer teamNr
        call ExecuteFunc("HeroSelectorAction_InitHeroes")

        //find all Teams in usage
        loop
            exitwhen playerIndex == GetBJMaxPlayers()
            set teamNr = GetPlayerTeam(Player(playerIndex))
            if teamNr != -1 then
                set teamIndexLoop = UsedTeamNrCount
                loop
                    exitwhen teamIndexLoop == 0
                    exitwhen UsedTeamNr[teamIndexLoop] == teamNr
                    set teamIndexLoop = teamIndexLoop - 1
                endloop
                if teamIndexLoop == 0 then
                    set UsedTeamNrCount = UsedTeamNrCount + 1
                    set UsedTeamNr[UsedTeamNrCount] = teamNr
                endif
            endif
            set playerIndex = playerIndex + 1
        endloop

        call TriggerAddAction(HeroButtonClickTrigger, function HeroSelectorActionPressHeroButton)
        set HeroSelectorBoxTitle = BlzCreateFrame("HeroSelectorTitle", box, 0, 0)
        set IndicatorSelectedParent = BlzCreateFrameByType("BUTTON", "MyHeroIndikatorParent", box, "", 0)
        call BlzFrameSetLevel(IndicatorSelectedParent, 9)
        set IndicatorSelected = BlzCreateFrameByType("SPRITE", "MyHeroIndikator", IndicatorSelectedParent, "", 0)
        set HeroSelectorBox = box
        set HeroSelectorBoxSeperator = boxBottom
        call BlzFrameSetModel(IndicatorSelected, IndicatorPathPick, 0)
        call BlzFrameSetScale(IndicatorSelected, ButtonSize/0.036) //scale the model to the button size.

        call BlzFrameSetVisible(IndicatorSelected, false)
        call BlzFrameSetAbsPoint(box, BoxPosPoint, BoxPosX, BoxPosY)
        call BlzFrameSetSize(box, borderSize*2 + ButtonSize*colCount + SpaceBetweenX*(colCount-1), borderSize*2 + ButtonSize*rowCount + SpaceBetweenY*(rowCount - 1) + titleSize + CategorySize + 0.0145)
        call BlzFrameSetPoint(HeroSelectorBoxTitle, FRAMEPOINT_TOP, box, FRAMEPOINT_TOP, 0, -borderSize*0.6)
        call BlzFrameSetText(HeroSelectorBoxTitle, "Hero Selection")
        call BlzFrameSetTextAlignment(HeroSelectorBoxTitle, TEXT_JUSTIFY_MIDDLE, TEXT_JUSTIFY_CENTER)
        call BlzFrameSetSize(HeroSelectorBoxTitle, BlzFrameGetWidth(box) - borderSize*2, 0.03)


        // human UI size differs much, needs other numbers
        if GetPlayerRace(GetLocalPlayer()) == RACE_HUMAN then
            call BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, borderSize*0.055, - 0.9*borderSize - titleSize - 0.003 -CategorySize)
            call BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPRIGHT, box, FRAMEPOINT_TOPRIGHT, -borderSize*0.055, - 0.9*borderSize - titleSize - 0.003 -CategorySize)
        else
            call BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, borderSize*0.25, - 0.9*borderSize - titleSize - 0.003 -CategorySize)
            call BlzFrameSetPoint(boxBottom, FRAMEPOINT_TOPRIGHT, box, FRAMEPOINT_TOPRIGHT, -borderSize*0.25, - 0.9*borderSize - titleSize - 0.003 -CategorySize)
        endif
        call BlzFrameSetSize(boxBottom, 0.01, 0.1)
     
        if colCount*rowCount < ButtonHeroCount  then
            call BJDebugMsg("FieldCount:"+ I2S(colCount*rowCount) + "HeroCount" + I2S(ButtonHeroCount))
        endif
     
        set buttonIndex = 1
        loop
            exitwhen buttonIndex >  HeroButtonCount
            set HeroButtonFrame[buttonIndex] = BlzCreateFrame("HeroSelectorButton", box, 0, buttonIndex)
            set HeroButtonIcon[buttonIndex] = BlzGetFrameByName("HeroSelectorButtonIcon", buttonIndex)
            set HeroButtonIconPushed[buttonIndex] = BlzGetFrameByName("HeroSelectorButtonIconPushed", buttonIndex)
            set HeroButtonIconDisabled[buttonIndex] = BlzGetFrameByName("HeroSelectorButtonIconDisabled", buttonIndex)
            set HeroButtonUnitCode[buttonIndex] = ButtonHeroUnitCode[buttonIndex]
            call SaveInteger(Hash, GetHandleId(HeroButtonFrame[buttonIndex]), 0, buttonIndex)
            set HeroButtonTooltipBox[buttonIndex] = BlzCreateFrame("HeroSelectorTextBox", box, 0, buttonIndex)
            set HeroButtonTooltip[buttonIndex] = BlzCreateFrame("HeroSelectorText", HeroButtonTooltipBox[buttonIndex], 0, buttonIndex)
            call BlzFrameSetTooltip(HeroButtonFrame[buttonIndex], HeroButtonTooltipBox[buttonIndex])
            if not TooltipRelativIsBox then
               call BlzFrameSetPoint(HeroButtonTooltip[buttonIndex], TooltipPoint, HeroButtonFrame[buttonIndex], TooltipRelativePoint, TooltipOffsetX ,TooltipOffsetY)
            else
                call BlzFrameSetPoint(HeroButtonTooltip[buttonIndex], TooltipPoint, box, TooltipRelativePoint, TooltipOffsetX ,TooltipOffsetY)
            endif
            call BlzFrameSetPoint(HeroButtonTooltipBox[buttonIndex], FRAMEPOINT_BOTTOMLEFT, HeroButtonTooltip[buttonIndex], FRAMEPOINT_BOTTOMLEFT, -0.007, -0.007)
            call BlzFrameSetPoint(HeroButtonTooltipBox[buttonIndex], FRAMEPOINT_TOPRIGHT, HeroButtonTooltip[buttonIndex], FRAMEPOINT_TOPRIGHT, 0.007, 0.007)
            call BlzTriggerRegisterFrameEvent(HeroButtonClickTrigger, HeroButtonFrame[buttonIndex], FRAMEEVENT_CONTROL_CLICK)
            call BlzFrameSetSize(HeroButtonFrame[buttonIndex], ButtonSize, ButtonSize)
            if HeroButtonUnitCode[buttonIndex] > 0 then
                call BlzFrameSetTexture(HeroButtonIcon[buttonIndex], BlzGetAbilityIcon(HeroButtonUnitCode[buttonIndex]), 0, ButtonBlendAll)
                call BlzFrameSetTexture(HeroButtonIconPushed[buttonIndex], BlzGetAbilityIcon(HeroButtonUnitCode[buttonIndex]), 0, ButtonBlendAll)             
                call BlzFrameSetTexture(HeroButtonIconDisabled[buttonIndex], HeroSelectorGetDisabledIcon(BlzGetAbilityIcon(HeroButtonUnitCode[buttonIndex])), 0, ButtonBlendAll)
                call BlzFrameSetText(HeroButtonTooltip[buttonIndex], TooltipPrefix + GetObjectName(HeroButtonUnitCode[buttonIndex]))
                call BlzFrameSetEnable(HeroButtonFrame[buttonIndex], HeroSelectorButtonRequirementDone(HeroButtonUnitCode[buttonIndex], GetLocalPlayer()))
            else
                call BlzFrameSetEnable(HeroButtonFrame[buttonIndex], false)
                call BlzFrameSetTexture(HeroButtonIcon[buttonIndex], EmptyButtonPath, 0, true)
                call BlzFrameSetTexture(HeroButtonIconPushed[buttonIndex], EmptyButtonPath, 0, true)
                call BlzFrameSetTexture(HeroButtonIconDisabled[buttonIndex], EmptyButtonPath, 0, true)
            endif

            if ChainedButtons then //buttons are connected to the previous one or the previous row
                if buttonIndex == 1 then
                    call BlzFrameSetPoint(HeroButtonFrame[buttonIndex], FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, borderSize, y)
                elseif rowRemaining <= 0 then
                    call BlzFrameSetPoint(HeroButtonFrame[buttonIndex], FRAMEPOINT_TOPLEFT, HeroButtonFrame[buttonIndex - colCount], FRAMEPOINT_BOTTOMLEFT, 0, -SpaceBetweenY)
                    set rowRemaining = colCount
                else
                    call BlzFrameSetPoint(HeroButtonFrame[buttonIndex], FRAMEPOINT_LEFT, HeroButtonFrame[buttonIndex - 1], FRAMEPOINT_RIGHT, SpaceBetweenX, 0)
                endif
            else //buttons have an offset to the TopLeft of the box
                if rowRemaining <= 0 then
                    set x = borderSize
                    set rowRemaining = colCount
                    set y = y - SpaceBetweenY - ButtonSize
                elseif buttonIndex != 1 then
                    set x = x + ButtonSize + SpaceBetweenX
                endif
                call BlzFrameSetPoint(HeroButtonFrame[buttonIndex], FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, x, y)
            endif
            set rowRemaining = rowRemaining - 1
            set buttonIndex = buttonIndex + 1
        endloop
        set y = -0.9*borderSize - titleSize - 0.0025
        set x = borderSize*0.65
        //create category buttons added before the box was created
        set buttonIndex = 1
        loop
            exitwhen buttonIndex > CategoryButtonCount
            set CategoryButton[buttonIndex] = BlzCreateFrame("HeroSelectorCategoryButton", box, 0, 0)
            set CategoryIconFrame[buttonIndex] = BlzGetFrameByName("HeroSelectorCategoryButtonIcon", 0)
            set CategoryIconPushedFrame[buttonIndex] = BlzGetFrameByName("HeroSelectorCategoryButtonIconPushed", 0)
            set CategoryTooltipFrameBox[buttonIndex] = BlzCreateFrame("HeroSelectorTextBox", box, 0, buttonIndex)
            set CategoryTooltipFrame[buttonIndex] = BlzCreateFrame("HeroSelectorText", CategoryTooltipFrameBox[buttonIndex], 0, buttonIndex)
            call BlzFrameSetText(CategoryTooltipFrame[buttonIndex], GetLocalizedString(CategoryText[buttonIndex]))
         
            call BlzFrameSetPoint(CategoryTooltipFrameBox[buttonIndex], FRAMEPOINT_BOTTOMLEFT, CategoryTooltipFrame[buttonIndex], FRAMEPOINT_BOTTOMLEFT, -0.007, -0.007)
            call BlzFrameSetPoint(CategoryTooltipFrameBox[buttonIndex], FRAMEPOINT_TOPRIGHT, CategoryTooltipFrame[buttonIndex], FRAMEPOINT_TOPRIGHT, 0.007, 0.007)
            call BlzFrameSetPoint(CategoryTooltipFrame[buttonIndex], FRAMEPOINT_BOTTOM, CategoryButton[buttonIndex], FRAMEPOINT_TOP, 0, 0)
            call BlzFrameSetTooltip(CategoryButton[buttonIndex], CategoryTooltipFrameBox[buttonIndex])
            call BlzFrameSetSize(CategoryButton[buttonIndex], CategorySize, CategorySize)
            call BlzFrameSetTexture(CategoryIconFrame[buttonIndex], CategoryTextureDisabled[buttonIndex], 0, true)
            call BlzFrameSetTexture(CategoryIconPushedFrame[buttonIndex], CategoryTextureDisabled[buttonIndex], 0, true)

            call BlzTriggerRegisterFrameEvent(CategoryClickTrigger, CategoryButton[buttonIndex], FRAMEEVENT_CONTROL_CLICK)
            if buttonIndex == 1 then
                call BlzFrameSetPoint(CategoryButton[buttonIndex], FRAMEPOINT_TOPLEFT, box, FRAMEPOINT_TOPLEFT, x, y)
            else
                call BlzFrameSetPoint(CategoryButton[buttonIndex], FRAMEPOINT_LEFT, CategoryButton[buttonIndex - 1], FRAMEPOINT_RIGHT, CategorySpaceX, 0)
            endif
            call SaveInteger(Hash, GetHandleId(CategoryButton[buttonIndex]), 0, buttonIndex)
            set buttonIndex = buttonIndex + 1
        endloop

        call TriggerAddAction(CategoryClickTrigger, function HeroSelectorActionCategoryButton)

        set AcceptButton = BlzCreateFrame("HeroSelectorTextButton", box, 0, 0)
        set RandomButton = BlzCreateFrame("HeroSelectorTextButton", box, 0, 1)
        set BanButton = BlzCreateFrame("HeroSelectorTextButton", box, 0, 2)

        call TriggerAddAction(AcceptButtonTrigger, function HeroSelectorActionAcceptButton)
        call TriggerAddAction(RandomButtonTrigger, function HeroSelectorActionRandomButton)
        call TriggerAddAction(BanButtonTrigger, function HeroSelectorActionBanButton)
        call BlzTriggerRegisterFrameEvent(AcceptButtonTrigger, AcceptButton, FRAMEEVENT_CONTROL_CLICK)
        call BlzTriggerRegisterFrameEvent(RandomButtonTrigger, RandomButton, FRAMEEVENT_CONTROL_CLICK)
        call BlzTriggerRegisterFrameEvent(BanButtonTrigger, BanButton, FRAMEEVENT_CONTROL_CLICK)
        call BlzFrameSetSize(AcceptButton, AcceptButtonSizeX, AcceptButtonSizeY)
        call BlzFrameSetSize(RandomButton, RandomButtonSizeX, RandomButtonSizeY)
        call BlzFrameSetSize(BanButton, BanButtonSizeX, BanButtonSizeY)

        //OK, READY, ACCEPT
        call BlzFrameSetText(AcceptButton, AcceptButtonTextPrefix + GetLocalizedString(AcceptButtonText))
        call BlzFrameSetText(RandomButton, RandomButtonTextPrefix + GetLocalizedString(RandomButtonText))
        call BlzFrameSetText(BanButton, BanButtonTextPrefix + GetLocalizedString(BanButtonText))
        call BlzFrameSetPoint(AcceptButton, AcceptButtonAnchor, box, FRAMEPOINT_BOTTOM, 0, 0)
        call BlzFrameSetPoint(RandomButton, RandomButtonAnchor, box, FRAMEPOINT_BOTTOM, 0, 0)
        call BlzFrameSetPoint(BanButton, FRAMEPOINT_BOTTOM, box, FRAMEPOINT_BOTTOM, 0, 0)

        call BlzFrameSetVisible(BanButton, false)
        call BlzFrameSetVisible(AcceptButton, AcceptButtonIsShown)
        call BlzFrameSetVisible(RandomButton, RandomButtonIsShown)

     
        if not AutoShow then
            call BlzFrameSetVisible(box, false)
        endif

        call ExecuteFunc("HeroInfoInit")
        call ExecuteFunc("TeamViewerInit")
    endfunction

endlibrary



  • 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:
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.​
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​
Requires a new fdf​
1.4)
Supports TechCode Level as req​
GUI can setup manual category mods.​
Skips example code in InitHeroes right after the GUI Loader​
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​
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.​
Previews
Contents

HeroSelector (Map)

HeroSelector Jass (Map)

Level 26
Joined
Jul 18, 2010
Messages
1,825

Can one have a unique selection pool per player with this system?
Yes and no.
No, in that is not built in directly.
Yes, with some actions one could mimic that, I think.
Hence, I worte "directly".
What I say now might work or might not, never tested it.

Option 1: You add all heroes into the pool. After the box creation run, hide all Buttons based on wanted Settings (the buttons and the hero index are the same), reason this works is a Player can not press hidden Buttons but still get click Events of other Players. Through you would also have to update the randompools, each Team has an own randompool when an hero is repicked it is readded to all randompools. This option would have the sideeffect that the box becomes bigger and shows alot of empty space, but should have the wanted effect. This Option requires you to know how to use GetLocalPlayer() to hide the Buttons based on the Player values, but you should not change the randompools in a GetLocalPlayer way, that would desync probably as soon that Player presses random. You might also Change the size of the box and the positions of the Buttons. But that requires some skill.

Code:
HeroSelector.HeroButtons[buttonIndex].Frame
HeroSelector.UnitData.Pool[teamNr]

Option 2:
When Players are Picking in turns one could empty and refill the heropool before the Picking including the randompools after that one would call HeroSelector.update() which will update the icons.
Through I did not test that, only thinkered about it, it might also break limited picking.

Option 3: You add fake heroes that Morph when being picked, would still be pickable for all Players, but the Morphing could differ based on the picking Player. To Morph the picked unit you have to overlap or Change the Content of "function HeroSelector.unitCreated(player, unit, isRandom)". This Option could be extened by altering the shown Infos in HeroInfo/Teamviewer and overwritting the Textures on and off Icons of the HeroButtons. The Textures won't Change itself after creation when not calling the update function which reloads the textures based on the connected Hero.
Lua:
--one can get the used buttonIndex of the unitCode as number with
local buttonIndex = HeroSelector.UnitData[unitCode].Index

BlzFrameSetTexture(HeroSelector.HeroButtons[buttonIndex].Icon, BlzGetAbilityIcon(unitCode), 0, HeroSelector.ButtonBlendAll)
BlzFrameSetTexture(HeroSelector.HeroButtons[buttonIndex].IconDisabled, HeroSelector.getDisabledIcon(BlzGetAbilityIcon(unitCode)), 0, HeroSelector.ButtonBlendAll)
Any plans to add this feature to the system? Is it too much work or just you never intended it to be part of this system?
I though about that feature. Would have to swap from a Team wise data layout to a Player wise.
 
Last edited:
Level 26
Joined
Jul 18, 2010
Messages
1,825
Uploaded HeroSelector V1.1.
It brings requirements. Requirements can be given when adding an Hero or afterwards. In a game having a ban Phase, I advise to add the requirement after the ban Phase is over. Otherwise one can ban only own heroes.

One can now choose that Buttons in the grid are not connected to the previous button/row allowing to move them around without moving other Buttons.

The Accept and Random Button gained more Settings.
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
With Box you mean the Background of HeroSelector?
Then: HeroSelector uses on Default the BACKDROP "EscMenuBackdrop" as Background/Box. To create it on uses
JASS:
local framehandle box = BlzCreateFrame(HeroSelector.BoxFrameName, BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), 0, 0)
call BlzFrameSetAbsPoint(box, FRAMEPOINT_CENTER, 0.4, 0.3)

If you mean HeroSelector:
Then it should Show itself on Default with the setting
HeroSelector.AutoShow = true
otherwise you have to call
HeroSelector.show(true)
 
Level 1
Joined
Nov 1, 2013
Messages
8
How to Install:

You map has to be in Lua-Mode.
Export and Import
war3mapImported\HeroSelector.fdf
war3mapImported\HeroSelector.toc
war3mapImported\HeroSelectorBan.mdx
Copy the trigger Folder "HeroSelector" into your map.

I tested the map and the selector is really awesome, but i tried the method you teached to import and doesn't works, i tested the import in 3 diferent maps, without any alteration in the units or the triggers, i used the jass map version, i don't know whats happening maybe is some kind of unrecognized variable, (i tried test with a map without any another trigger, so the possibility of being a conflict is descosiderable), any suggest of what a should do?
 
Level 1
Joined
Nov 1, 2013
Messages
8
Nothing, normaly the helper send a syntax error and block the inicialization, but in this case he just start the game as normal, but the box is just with the category icons
 
Last edited:
Level 12
Joined
Feb 11, 2008
Messages
810
hi im using the LUA version of this system and ive just been playing around making adjustments and ran across a few very weird things first off,

JASS:
function HeroSelector.autoDetectCategory(unitCode)
    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
    elseif IsUnitIdType(unitCode, UNIT_IF_STRENGTH) then
        HeroSelector.UnitData[unitCode].Category = 4
        print("STR HERO")
    elseif IsUnitIdType(unitCode, UNIT_IF_AGILITY) then
        HeroSelector.UnitData[unitCode].Category = 8
        print("AGI HERO")
    elseif IsUnitIdType(unitCode, UNIT_IF_INTELLIGENCE) then
        HeroSelector.UnitData[unitCode].Category = 16
        print("INT HERO")
    end
end

does not work as if WC3 cannot detect what the heroes main attributes are yet or maybe im doing something wrong.

Second thing is weird also why are all of these capitalized and then 'Ranged' is not and yet if you capitalize it in game the others are not.

JASS:
table.insert(HeroSelector.Category, {"ReplaceableTextures\\CommandButtons\\BTNSteelMelee", "MELEE"})                 --1, automatic detected when adding an unit
table.insert(HeroSelector.Category, {"ReplaceableTextures\\CommandButtons\\BTNHumanMissileUpOne", "Ranged"})         --2, automatic detected when adding an unit
table.insert(HeroSelector.Category, {"ReplaceableTextures\\CommandButtons\\BTNGauntletsOfOgrePower", "STRENGTH"})    --4
table.insert(HeroSelector.Category, {"ReplaceableTextures\\CommandButtons\\BTNSlippersOfAgility", "AGILITY"})        --8
table.insert(HeroSelector.Category, {"ReplaceableTextures\\CommandButtons\\BTNMantleOfIntelligence", "INTELLECT"})   --16

and also are you able to set the title text for each individual player instead of everyone at once? if that not would be a great feature.
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
does not work as if WC3 cannot detect what the heroes main attributes are yet or maybe im doing something wrong.
That UNIT_IF is for BlzGetUnitIntegerField. I also don't know of any direct way to get the primary attribute from an unitcode. There is the wayaround: create an unit, read the UnitIntegerField and kill it again. I could built that in.

UNIT_IF_PRIMARY_ATTRIBUTE should be used to get the primary attribute.


Second thing is weird also why are all of these capitalized and then 'Ranged' is not and yet if you capitalize it in game the others are not.
HeroSelector tries to read a localized String (so each player has texts in his language), Blizzards localized Strings are UpperCase only, but there is no RANGED. Hence i just wrote the english "Ranged".

and also are you able to set the title text for each individual player instead of everyone at once? if that not would be a great feature.
Yes, that is built in. function HeroSelector.setTitleText(text[, who]). who can be a player, a force, a teamNumber, a race or nothing = anyone
 
Level 12
Joined
Feb 11, 2008
Messages
810
Yea being able to auto-detect all the categories would be a great addition, also thanks for the information ive always used GUI and some simple JASS but never really any coding such as LUA and im really enjoying it so far.

1 more thing in the example map whenever you choose a hero each game its spawning me at a different players starting area instead of my specific starting area and i can see that this is the section that should be moving the unit to mine

JASS:
SetUnitPosition(unit, GetPlayerStartLocationX(player), GetPlayerStartLocationY(player))

but for some reason it is not returning the correct player each time
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
Updated to version 1.3.0. Added the option to autodetect the primary Hero Attribute: Str, Agi or Int as category, on default enabled. That is achived by creating the hero for neutral extra and using BlzGetUnitIntegerField, then the unit is removed again. That probably will also preload object editor added stuff and models of that heroes when enabled.

HeroSelector.CategoryAutoDetectHero = true

private boolean CategoryAutoDetectHero = true

1 more thing in the example map whenever you choose a hero each game its spawning me at a different players starting area instead of my specific starting area and i can see that this is the section that should be moving the unit to mine



but for some reason it is not returning the correct player each time
Is your Circle at the same spot as your hero is created or is it not the spot set in editor?
When it is not the spot set in Editor forcing fixed starting positions should fix that. Otherwise I did something wrong.
 
Last edited:
Level 12
Joined
Feb 11, 2008
Messages
810
The circle of power is spawning in the same area as the hero but it is at a different players start location each time, how would i check to see if the players have fixed starting positions?, and btw i just using your example map so should be the same as what your seeing.

also nice update man that was fast thanks again for this system.
 
Level 12
Joined
Feb 11, 2008
Messages
810
Found some glitches with Team Viewer plug-in

1. if you set it to false the computer players name is still visible
2. the position of the names/icons are then moved above your accept button instead of staying on the top left

EDIT*

also having issues importing it, everything seems to be functioning except the hero icons are not displaying and i have all the imported models plus i just copy and paste your entire folder with the hero selection, heres screenshots.

Screenshots | HIVE

EDIT2*

also i wanted to show i have units added via this code

JASS:
    HeroSelector.addUnit('Hpal') --add paladin as selectable Hero
    HeroSelector.addUnit('Hamg')
    HeroSelector.addUnit('Hblm')
    HeroSelector.addUnit('Hmkg')
    HeroSelector.addUnit("Obla")
    HeroSelector.addUnit("Ofar")
    HeroSelector.addUnit("Otch")
    --HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    --HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    HeroSelector.addUnit("Oshd")
    HeroSelector.addUnit("Edem")
    --HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    --HeroSelector.addUnit() --this is an empty box. It still takes a slot.
    HeroSelector.addUnit("Ekee")
    HeroSelector.addUnit("Emoo")
    HeroSelector.addUnit("Ewar")
    HeroSelector.addUnit("Udea")
    HeroSelector.addUnit("Ulic")
    HeroSelector.addUnit("Udre")
    HeroSelector.addUnit("Ucrl")

it works on the example map with that code but once i import it into my map it no longer shows hero icons even though they are the same default hero codes.
 
Last edited:
Level 26
Joined
Jul 18, 2010
Messages
1,825
Found some glitches with Team Viewer plug-in

1. if you set it to false the computer players name is still visible
2. the position of the names/icons are then moved above your accept button instead of staying on the top left
Thanks for reporting that bug.

EDIT2*

also i wanted to show i have units added via this code



it works on the example map with that code but once i import it into my map it no longer shows hero icons even though they are the same default hero codes.
Lua Only:
Do you have the GUI arrays in your map and did not use them at all? If they exist HeroSelector fills all button positions with their values, so all shown fields are empty seperators. If you not want the GUI part just remove the whole GUI folder with its variables or remove the lines reading that arrays. Then HeroSelector skips reading the GUI array.
 
Level 12
Joined
Feb 11, 2008
Messages
810
Thanks for reporting that bug.


Lua Only:
Do you have the GUI arrays in your map and did not use them at all? If they exist HeroSelector fills all button positions with their values, so all shown fields are empty seperators. If you not want the GUI part just remove the whole GUI folder with its variables or remove the lines reading that arrays. Then HeroSelector skips reading the GUI array.

yea i didnt even copy the gui folder over and like i said everything i have done i also done the same way on the example map before attempting to import and it worked perfectly fine also noticed that when i exported and then re-imported the files they seemed to change size not sure if that has anything to do with it

Code:
LUA Start
    Events
        Time - Elapsed game time is 0.10 seconds
    Conditions
    Actions
        Custom script:   MapSetup()

this is only GUI in the entire map lol

EDIT*

thanks for the help btw i know its annoying at times but ive tried to figure this one out and cant seem to track down whats causing it to fail when exporting/importing since everything works great before i switch maps.
 
Last edited:
Level 26
Joined
Jul 18, 2010
Messages
1,825
Updated to 1.3a Teamviewer should be fixed. The wrong position was not a bug. That was just bad default setup for the Lua version when enemies are hidden.
yea i didnt even copy the gui folder over and like i said everything i have done i also done the same way on the example map before attempting to import and it worked perfectly fine also noticed that when i exported and then re-imported the files they seemed to change size not sure if that has anything to do with it
There is an folder named GUI inside the HeroSelector system. Do you have that in your map? If yes remove it with the GUI variables.
 
Level 12
Joined
Feb 11, 2008
Messages
810
Updated to 1.3a Teamviewer should be fixed. The wrong position was not a bug. That was just bad default setup for the Lua version when enemies are hidden.

There is an folder named GUI inside the HeroSelector system. Do you have that in your map? If yes remove it with the GUI variables.

Heres 2 maps, the first one is my custom map that the icons are not displaying on and the second one is your example map with my exact same code except its working great, from looking at the hero defense map can you see what is causing it to not display?

Maps | HIVE
 
Level 12
Joined
Feb 11, 2008
Messages
810
For some wierd reasons in your map: the toc and fdf files swaped content. That breaks all custom Frames HeroSelector defines.
The fdf should contain the frame definitions and the toc should be a batch to load fdf files.

that is what i was talking about and all i did was export them and import them into the new map, i figured thats what was causing the issue but still cannot figure out why its changing them

EDIT*

i just tried exporting again and as soon as it exports into the folder the files have changed its such a weird bug

EDIT2*

FOUND THE ISSUE : when exporting all files at once the editor is glitching and removing file properties but when you export them 1 at a time then import 1 at a time this issue does not occur!

might want to put a warning about this in your post seems like exporting all at once is breaking the files for whatever reason.
 
Last edited:
Level 12
Joined
Feb 11, 2008
Messages
810
Still some really weird stuff going on this team viewer i just filled all the slots with some enemy computers and 2 teammate computers and overlapping is still happening plus it doesn't seem to be displaying correctly.

EDIT*

and also for some reason when using an actual player as player 2 set on the same team is not displaying at all when team viewer show non-allies is set to false.

EDIT2*

overlooked that you had stated it was only set up for 2 teams so that solves the overlapping of the computer names so sorry i missed that

but still is glitching when trying to display a real player on team 1 with you choosing heros

also the hero info plugin's extended tooltip display on the right of the icons is not displaying for player 2 when they are a real player they can select the icons and choose a hero just fine but none of the description is displaying.
 

Attachments

  • WC3ScrnShot_122019_210304_01.png
    WC3ScrnShot_122019_210304_01.png
    2.3 MB · Views: 133
Last edited:
Level 26
Joined
Jul 18, 2010
Messages
1,825
You would either modify
function HeroSelectorUnitCreated inside "HeroSelectorAction" to create your starting units based on unit u.
Or use real Event udg_HeroSelectorEvent == 1.0 (HeroSelector Lua does not provide this event)
udg_HeroSelectorEventPlayer
udg_HeroSelectorEventUnit
udg_HeroSelectorEventIsRandom

or create unit enter map events checking for the entering hero.

For the starting units you might reuse something from Blizzard.j
function MeleeStartingUnitsForPlayer takes race whichRace, player whichPlayer, location loc, boolean doHeroes

You can find Blizzard.j in "Documents\Warcraft III\JassHelper" when you have saved/validated a map with world editor at least once with jasshelper enabled.
 
Level 7
Joined
Aug 16, 2019
Messages
53
You would either modify
function HeroSelectorUnitCreated inside "HeroSelectorAction" to create your starting units based on unit u.
Or use real Event udg_HeroSelectorEvent == 1.0 (HeroSelector Lua does not provide this event)
udg_HeroSelectorEventPlayer
udg_HeroSelectorEventUnit
udg_HeroSelectorEventIsRandom

or create unit enter map events checking for the entering hero.

For the starting units you might reuse something from Blizzard.j
function MeleeStartingUnitsForPlayer takes race whichRace, player whichPlayer, location loc, boolean doHeroes

You can find Blizzard.j in "Documents\Warcraft III\JassHelper" when you have saved/validated a map with world editor at least once with jasshelper enabled.
Thank you so much for helping me!
 
Level 7
Joined
Aug 16, 2019
Messages
53
I apologize in advance for bad English
Feedback:
This experience allowed me to make a convenient choice of a non-standard race (in the screenshot (russian) there is one race - Grommash Hellscream [Chieftain of the Warsong Clan]) for my Altered Melee - WarCraft Legends (russian). The additional information (on the right) shows the quote of the hero, as well as the advantages and disadvantages of this race. It looks much more presentable than the choice through the "tavern".
The operating time is very flexible and easily customizable even with poor knowledge of jass functions.

Also in the left corner you can see the operating time "Talents" of the same author.
 

Attachments

  • SelectHero.jpg
    SelectHero.jpg
    178.4 KB · Views: 67
Level 10
Joined
May 16, 2020
Messages
584
Hi Tasyen, I experience some issues with the test map, and there are different issues in both:
  • In the JASS folder version, the window stays open for a few seconds after banning and picking
  • In the "normal" folder version, -ar does nothing
  • In the "normal" folder version, there is no banning phase
  • In both versions -close, -open does nothing
Not sure if I missed something, but really great system from what I saw.
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
Ty for your feedback.
I updated it to 1.3b, updates to the demo triggers.
Close & Show also now need a - infront to be typed in as first letter, further all 6 players in the demo map can use it.
After the picking Phase is over Trigger-Repick is disabled. (Can't pick anymore at that point, hence no repick should be allowed)
In the Lua version increased ban Phase from 0.75s to ~16s.

The demo triggers are quite simple, I haven't added any action in the demo that hides HeroSelector for the picking player. It is "hidden" by its destruction when the Picking Phase is over.
In the Jass version one would add such hiding in HeroSelectorAction function HeroSelectorUnitCreated. You could copy paste the custom line from the Close Trigger when you want that.

You were player red when you tried -ar, only he can use it?
 
Level 10
Joined
May 16, 2020
Messages
584
Thank you! And yes I was player red when chosing -ar, but seems it works in the newest version.

A few more questions though, since I'd love to use this system for my map:
  1. Are all units within "HeroSelectorUnitCode[x]" preloaded? At least that's my guess why there is a delay at the start of the game. I have like 60 heroes in my map...
  2. Why are Demon Hunter, Keeper of the Grove and Priestess of the Moon disabled in the testmap? I didn't find anything that would cause this (I know only GUI though)
  3. In ban/picking phase: Why run every 0.75 instead of 1 second? Is there something wrong with the internal clock?
  4. When clicking the "random" button: Can I make the system automatically pick a hero (and not just choose it)?
  5. When clicking the "accept" button: Is it possible to delay the hero creation until the "picking" phase is over? So essentially picking fast does not have any advantage, since all heroes get created at the same time as soon as the last player picks his hero
And fully support the Item Shop idea :)
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
  1. All added Heroes are created to autocalc the categories. You can stop that by changing a boolean in the Config of HeroSelector.
    • private boolean CategoryAutoDetectHero = true // Will create and remove added Heroes to read and setup the Category for the primary Attribute Str(4) Agi(8) Int(16)
    • -> private boolean CategoryAutoDetectHero = false
  2. They use Requirments set with HeroSelectorSetUnitReqRace in HeroSelectorAction public function InitHeroes. In the Demo map Nightelf and Human Heroes can only be picked by Plaers of their Races.
  3. I personly find 0.75s more fluid, one can just change that if one doesn't like that.
  4. You have to change a value in the config in HeroSelector: private boolean RandomButtonPick = true
  5. That feature was not added. You would have to hide/Pause/Stun the Hero when it is picked until Picking Phase is finished.
You find the Config in the globals block of HeroSelector which is found at the top after the API (the outcommented function Heads.
 
Level 10
Joined
May 16, 2020
Messages
584
Thank you - managed to get it to work :)

As feared though, the preload time with CategoryAutoDetectHero = true is quite long for me. So:
  • Could I somehow hook into the system and define myself (manually) if HeroSelectorUnitCode[1] = Strength, HeroSelectorUnitCode[2] = Intelligence etc.? If not, can I hide the 3 symbols at the top?
  • Out of interest: Why can the game quickly recognize what is "range" and "melee" (this creates zero preload lag for me), but not what is the attribute?

Some more questions which popped up when implementing the system:
  • Can I adjust the font within the right box (where the story, stats etc are)? I see that I can change the TextAreaSizeX/Y, but didn't find anything related to font size.
  • What is the criteria for the icons to go "disabled"? These 3 icons have a BTN and DISBTN icon, but when using it in the system it doesn't work:
  • 1.png
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
Could I somehow hook into the system and define myself (manually) if HeroSelectorUnitCode[1] = Strength, HeroSelectorUnitCode[2] = Intelligence etc.? If not, can I hide the 3 symbols at the top?
The system allows to add/remove categories, hence on can also add categories to heroes. This can only be done in code though. After the heroes were added, use call HeroSelectorAddUnitCategory(integer unitCode, integer category) for example call HeroSelectorAddUnitCategory('Hpal', 4) adds the Str category to paladin. The 4 is a power 2 number, the 2^categoryIndex, might change if you add/remove/reorder them.
If you want to add remove categories you have to remove/add call HeroSelectorAddCategory lines
It is intended to do such things in public function InitHeroes of
HeroSelectorAction.
  • Out of interest: Why can the game quickly recognize what is "range" and "melee" (this creates zero preload lag for me), but not what is the attribute?
Ranged/Melee can be read without creating an unit, using only the unitCode. It uses the functionality which can be found in GUI boolean comparison - Unit - UnitType Class check. Unlike detect Primary attribute that creates the hero and reads the primaray attribute unit field, I haven't found a better way for that.

Can I adjust the font within the right box (where the story, stats etc are)? I see that I can change the TextAreaSizeX/Y, but didn't find anything related to font size.
The code to change Font during game does not work for that type of Text Box. Therefore it can only be changed inside the imported fdf. It is
Frame "TEXTAREA" "HeroSelectorTextArea" in which one would change the number in FrameFont "InfoPanelTextFont", 0.011, "", to choose a different Text-Size.

What is the criteria for the icons to go "disabled"? These 3 icons have a BTN and DISBTN icon, but when using it in the system it doesn't work:
When a category is added one image path is given. This path is used as enabled version out of the enabled version it should autofind the disabled version. The autofinder requires that the 34/35 char is "\". If it fails you either have to change the image path or set the paths yourself. The manual setting has to be done after the category was added.
JASS:
set HeroSelector_CategoryTextureDisabled[index] = iconPath. //index starts with 1.
set HeroSelector_CategoryTextureDisabled[4] = "ReplaceableTextures\\CommandButtons\\BTNInvisibility" // would change the agi disabled Icon to the invis ability icon
set HeroSelector_CategoryTexture[3] = "ReplaceableTextures\\CommandButtons\\BTNFootman" // changes the str enabled icon to Footman icon
 
Level 10
Joined
May 16, 2013
Messages
203
This is really cool but I have a question.
1) Can we add tech requirement for heroes? I saw we can add 4 type of requirements right now. Tech requirements would be really useful.
2) Is function HeroSelectorEnablePickPlayer really works? I tried everything but still players can pick. I'm calling HeroSelectorEnablePickPlayer(false, Player(1)) and I can still pick.
3)Also how can I hide specific players from teamviewer?
 
Last edited:
Level 26
Joined
Jul 18, 2010
Messages
1,825
2) Is function HeroSelectorEnablePickPlayer really works? I tried everything but still players can pick. I'm calling HeroSelectorEnablePickPlayer(false, Player(1)) and I can still pick.
It disables the random and pick button. Player(1) is Blue. You were Blue?
1) Can we add tech requirement for heroes? I saw we can add 4 type of requirements right now. Tech requirements would be really useful.
Techs as req are not supported. Only TeamNr, Player, Race, Force. Kinda created with rpg/moba like in mind in which tech req didn't make much sense, at least I thought so when this was created.
On a short look, It wouldn't be so difficult to add it.
3)Also how can I hide specific players from teamviewer?
The default one can be setuped to show only allies and some pos stuff. One would have to modify Teamviewer or write a custom one to do more.
 
Level 10
Joined
May 16, 2020
Messages
584
The system allows to add/remove categories, hence on can also add categories to heroes. This can only be done in code though. After the heroes were added, use
call HeroSelectorAddUnitCategory(integer unitCode, integer category)
for example
call HeroSelectorAddUnitCategory('Hpal', 4)
adds the Str category to paladin. The 4 is a power 2 number, the 2^categoryIndex, might change if you add/remove/reorder them.
If you want to add remove categories you have to remove/add
call HeroSelectorAddCategory
lines
It is intended to do such things in public function InitHeroes of
HeroSelectorAction.

Mmm maybe I'm doing something wrong. First, I disabled the autodetect:
JASS:
private boolean CategoryAutoDetectHero = false

Then I added the unit ID for a strength unit into a custom script:
2.png


1.png


But in-game, filtering on "Strength" doesn't do anything.

Therefore it can only be changed inside the imported fdf.
What program do you use to open fdf files?


The rest worked - thanks a lot :)
 
Level 10
Joined
May 16, 2013
Messages
203
It disables the random and pick button. Player(1) is Blue. You were Blue?

Techs as req are not supported. Only TeamNr, Player, Race, Force. Kinda created with rpg/moba like in mind in which tech req didn't make much sense, at least I thought so when this was created.
On a short look, It wouldn't be so difficult to add it.

The default one can be setuped to show only allies and some pos stuff. One would have to modify Teamviewer or write a custom one to do more.

Sorry my bad for the first one I guess, well I'm working on something else and tech requirement would be awesome for me. I think it will be cool as another feature.
 
Level 26
Joined
Jul 18, 2010
Messages
1,825
But in-game, filtering on "Strength" doesn't do anything.
That GUI trigger happens before the heroes are added to HeroSelector. It only prepares data. But the manual category modification can only happen after the heroes were added to HeroSelector. I probably add an integer array to GUI to add categories to heroes the same way one can set the heroes.
What program do you use to open fdf files?
Any Texteditor can do that. I use vscode or notepad++.

Edit: Update 1.4)
Supports TechCode Level as req. (Beaware that Heroselector does not update automatically. When after a tech was researched or an unit was trained/killed one has to force an update with an
HeroSelector.update()/HeroSelectorUpdate()
)
GUI can add manual categories (for the Heroes) with an GUI integer array next to the normal Hero setup.
added a return to skip example code in InitHeroes right after the GUI Loader.
 
Last edited:
Level 26
Joined
Jul 18, 2010
Messages
1,825
Updated to 1.5) contains a new fdf.
Teamviewer
Player Face Tooltip Text showed the wrong text in some circumstances
Placed Players into boxes.
Player Names are not playerColored anymore. The playercolor is seen at the top of the box.
Added an Actionfunction when clicking the PlayerHeroButton
supports Playercolors not matching the playerIndex
Calls a function filtering unwanted players out.
Added an alternative Teamviewer without Team focus. (you should only enable one of them)​
Tooltip Texts are now in Boxes, new fdf.
The selected Indicator is now above the Buttons.
Selectable options when disabled shows the reason but only one (Not allowed / out of stock / Requirments).
Reduced the Border Size of the HeroSelectorBox by ~25%.
Added a seperator between the category Buttons and Hero Buttons.
Limited the Title Text to the space above the category Buttons, it supports upto 2 Lines and will not leave the box anymore.
The demo GUI Triggers now use 2 game Strings, instead of custom ones. You can still replace it with anything wanted.
Added a new Config HeroSelector.BanIgnoreRequirment, on default true. When enabled it allows to Ban Selections one does not fullfill the requirments for.
Fixed a Bug in the jass version, HeroSelectorUpdate did not respect the teams pick limit
Lua)
Replaced the self execution by Timer with hooking into MarkGameStarted
Heroselector calls Teamviewer and Heroinfo instead of them executing themself
 
Level 10
Joined
May 16, 2020
Messages
584
Ahh ok thanks - now it worked.

I'm also trying to re-align the windows with HeroInfo and TeamViewer... but not very successful:

WC3-Scrn-Shot-092620-142015-001.png


Ideally I want the following set-up:
  • Main window in the middle (where is this setting?)
  • HeroInfo window directly at the bottom of the main window (I tried BOTTOMMIDDLE, this should be possible as shown here)
  • TeamViewer windows to be a lot more left / right, as they limit the main window space (I tried TeamPosX[0] = 0.00, but then the window disappears for some reason)
Also, is it possible to change Player 1 (red) and Player 7 (green)'s icon to a custom image, not a questionmark? Or better yet: To remove their image completely and just leave the "name" of these players as title for the teams. These are computer players and so they will never pick.
 
Level 26
Joined
Jul 18, 2010
Messages
1,825

warcraft-3-coord-system-3-jpg.359576

coords-image-56-jpg.325821


Main window in the middle (where is this setting?)
The pos of HeroSelector is set in Heroselector globals
private real BoxPosX = 0.3
private real BoxPosY = 0.4
private framepointtype BoxPosPoint = FRAMEPOINT_CENTER

HeroInfo window directly at the bottom of the main window (I tried BOTTOMMIDDLE, this should be possible as shown here)
private framepointtype TextAreaPoint = FRAMEPOINT_TOPRIGHT //pos the Tooltip with which Point
private framepointtype TextAreaRelativePoint = FRAMEPOINT_BOTTOMRIGHT //pos the Tooltip to which Point of the Relative


TeamViewer windows to be a lot more left / right, as they limit the main window space (I tried TeamPosX[0] = 0.00, but then the window disappears for some reason)
You have to change a line in Teamviewer inside function TeamViewerInit:

set playerParentFrame[playerIndex] = BlzCreateFrame("HeroSelectorTextBox", HeroSelectorBox, 0, createContext)
->
set playerParentFrame[playerIndex] = BlzCreateFrame("HeroSelectorTextBox", BlzGetFrameByName("ConsoleUIBackdrop", 0), 0, createContext)

That change allows them to be placed further left and right, also allows them to exist without HeroSelector.
(BlzGetFrameByName("ConsoleUIBackdrop", 0) is reforged only)


You could prevent Red and Green from taking a Teamviewer slot. That is done in TeamViewer private function AllowPlayer.
JASS:
// (true) allows includes the player in TeamViewer
private function AllowPlayer takes player p returns boolean
    if p == Player(0) then
        return false
    endif
    if p == Player(6) then
        return false
    endif
    return GetPlayerSlotState(p) ==  PLAYER_SLOT_STATE_PLAYING
endfunction
If you want to keep them one could do some frame calls to make the Icon be hidden. One would add that at the end of function TeamViewerInit.
call BlzFrameSetVisible(playerFaceButton[0], false)
call BlzFrameSetVisible(playerFaceButton[6], false)
 
Top