• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[Lua] Minimalist UI

Status
Not open for further replies.
Level 14
Joined
Feb 7, 2020
Messages
386
[Attached is the final v1.1c (for now)]

latest v1.1c changes:
- removed some variables I forgot about that are not currently in use.

latest v1.1b preview:

wc3scrnshot_051720_141055_001-png.355207


Greetings,

Wanted to share the progress of a minimalist UI I crafted for my project after braving the inferno that is frame natives.

It uses the lazy "hide everything then unhide some of it" method. Could be useful for other amateurs to reference, or if you want to plop a different UI into a Lua map.

Before and after screens:

WC3ScrnShot_051020_103316_001.png WC3ScrnShot_051420_160205_001.png WC3ScrnShot_051420_154820_001.png

The code came out rather hideous as it was torn apart and put back together several times but it's a working demo with lots of comments.

(Note: this was my first time diving into local player code, so it's possible the end product is totally busted in multiplayer.)

Here are some known issues/problems:
1. Deselect won't update unit state bars; this was disabled because I fear desyncs due to its rapid call rate. The code for it can be uncommented if you want it back.
2. Local player code hasn't been live tested, though I was able to run the code in LAN.
3. The unit portrait only works in a few aspect ratios and might not show at all (or be off center).

4. The "config" values are somewhat erratic/arbitrary and are mostly for making tweaks. If you download it and want to change things, user be warned.
5. Won't yet update the unit info bars for focus unit when groups are selected.
6. The game crashes for some reason upon leaving the map.


Some issues I encountered with the unit portrait:
1. Changing the parent of any animated frame (portrait, minimap) messes with its animation speed.
2. The unit portrait obeys the pre-widescreen calculation equation when UI auto positioning is disabled, or something like that (i.e. it attempts to stretch the 4:3 UI frame to your current aspect ratio). I can't figure out the math on re-arranging it. I tried dozens of framepoint combos and couldn't get it to maintain its relative positioning.
3. Enabling UI auto positioning and trying to set points of an originframe to a different frame (i.e. unit portrait) does bizarre sizing things (and doesn't follow the same framepoint logic).

Above issues could have solutions but wanted to share.
Brave the ugly, if you dare. You might not come out the same.
Lua:
-- - - - - - - - - - - -
local mui                    = {}
local mui_data               = {}
-- - - - - - - - - - - -
mui_data.frameStack          = {}                            -- where custom frames are kept for reference.
-- - - - - - - - - - - -
mui_data.portraitSize        = 0.0225                        -- height and width.
mui_data.maxPlayers          = 9                             -- (0-based) how many players can play the map (starts at Player(0))
-- - - - - - - - - - - -
mui_data.consoleUIOffsetY    = -0.24                         -- pushes the console UI originframe down.
mui_data.minimapBtnPadding   = 0.02365                       -- spaces out the minimap buttons.
mui_data.menuBtnOffsetY      = 0.0218                        -- distance from bottom row of command bar.
mui_data.menuBtnPadding      = 0.0015                        -- menu button padding bottom and right.
mui_data.menuBtnWidth        = 0.065                         -- resized width.
mui_data.infoPanelOffsetX    = 0.4                           -- offset from left of screen.
mui_data.infoPanelOffsetY    = 0.00325                       -- offset from bottom of screen.
mui_data.infoPanelGutterY    = 0.004                         -- nudges the backdrop up for edge alignment.
mui_data.resourceBarOffsetX  = -0.004                        -- moves backdrop left and right
mui_data.resourceBarOffsetY  = -0.0005                       -- moves backdrop up and down.
mui_data.inventoryPadding    = 0.0256                        -- space between inventory buttons
mui_data.inventoryOffsetX    = -0.075                        -- pushes inventory buttons left of info panel.
mui_data.inventoryOffsetY    = 0.0041                        -- starting Y offset.
-- - - - - - - - - - - -
mui_data.commandBarFrameSize = 0.029                         -- somewhat arbitrary frame width that is hardcoded and scary to change. calcs length.
mui_data.commandBarGutterY   = 0.007                         -- gap between top and bottom rows.        
mui_data.commandBarOffsetX   = 0.315                         -- start X is calced based on icon size. this value nudges the command bar left or right.
mui_data.commandBarOffsetY   = 0.065                         -- offset from bottom of screen.
mui_data.commandBarPadding   = 0.01                          -- gap between command bar buttons.
mui_data.commandBarNudgeDiv  = 2.26                          -- psuedo-combined width, gutter, offset ratio. lower = move command bar left, vice versa.
mui_data.commandBarSize      = 0.035                         -- height and width.
mui_data.commandBarOrder     = {0,1,2,3,4,5,8,9,10,11,6,7}   -- requires 12 items. arranges the command bar top-to-bottom. see below for default rubric.
-- - - - - - - - - - - -
mui_data.getCenter           = 0.4                           -- approximate center of the original UI.
mui_data.getMaxY             = 0.6                           -- top edge of 4:3 screen.
mui_data.inventoryTxtAlpha   = 225                           -- 'Inventory' over the inv. pane.
mui_data.unitInfoHeight      = 0.605                         -- controls the hero portrait Y offset from center (percentage of parent info frame).
mui_data.unitInfoWidth       = 0.4265                        -- controls the hero portrait X offset from center (percentage of parent info frame).
mui_data.backdropAlpha       = 130                           -- (0 to 255-based int) set alpha for floating UI elements (default = ~51%).
mui_data.screenGutter        = mui_data.infoPanelGutterY + mui_data.infoPanelOffsetY
mui_data.txtColor            = '|cffffffff'
mui_data.textureMain         = 'war3mapImported\\ui_black_colorizer.tga'
mui_data.textureFlagBTN      = 'ReplaceableTextures\\CommandButtons\\BTNRallyPoint.blp'
-- - - - - - - - - - - -
--[[
    original command bar matrix (reference):
    row1:   | 0| | 1| | 2| | 3|
    row2:   | 4| | 5| | 6| | 7|
    row3:   | 8| | 9| |10| |11|

    default mui command bar matrix (reference):
    top:    | 0| | 1| | 2| | 3| | 4| | 5|
    bot:    | 8| | 9| |10| |11| | 6| | 7|
--]]
-- - - - - - - - - - - -

-- iterate through command buttons:
mui_data.commandBarMap = {
    [0] = "CommandButton_0",
    [1] = "CommandButton_1",
    [2] = "CommandButton_2",
    [3] = "CommandButton_3",
    [4] = "CommandButton_4",
    [5] = "CommandButton_5",
    [6] = "CommandButton_6",
    [7] = "CommandButton_7",
    [8] = "CommandButton_8",
    [9] = "CommandButton_9",
    [10] = "CommandButton_10",
    [11] = "CommandButton_11",
}

-- unhide desired components:
mui_data.unhideFramesByName = {
    [0] = "MiniMapFrame",
    [1] = "Multiboard",
    [2] = "UpperButtonBarFrame",
    [3] = "SimpleUnitStatsPanel",
    [4] = "SimpleHeroLevelBar",
    [5] = "ResourceBarFrame",
    [6] = "MinimapButtonBar"
}

mui_data.minimapButtons = {
    [0] = "FormationButton",
    [1] = "MiniMapCreepButton",
    [2] = "MiniMapAllyButton",
    [3] = "MiniMapTerrainButton",
    [4] = "MinimapSignalButton"
}

mui_data.inventoryButtons = {
    [0] = "InventoryButton_0",
    [1] = "InventoryButton_1",
    [2] = "InventoryButton_2",
    [3] = "InventoryButton_3",
    [4] = "InventoryButton_4",
    [5] = "InventoryButton_5"
}

mui_data.menuButtons = {
    [0] = "UpperButtonBarQuestsButton",
    [1] = "UpperButtonBarMenuButton",
    [2] = "UpperButtonBarAlliesButton",
    [3] = "UpperButtonBarChatButton"
}

-- rearrange originframes that were moved off screen:
mui_data.consoleFrames = {
    [0] = { frame = ORIGIN_FRAME_UNIT_MSG, x = 0.0, y = 0.25},
}


function mui.Init()

    for pInt = 0,mui_data.maxPlayers do

        if Player(pInt) == GetLocalPlayer() then

            -- temporary framehandle variable for multiple lookups + readability:
            local fh

            -- will stop the moving of frames when changing resolution or window mode when set to false:
            BlzEnableUIAutoPosition(false)

            -- move undesired items off screen:
            BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), FRAMEPOINT_BOTTOM, 0.0, mui_data.consoleUIOffsetY)

            -- fetch items we use repeatedly:
            mui_data.gameUI       = BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI,0)
            mui_data.portrait     = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT,0)
            mui_data.chatMsg      = BlzGetOriginFrame(ORIGIN_FRAME_CHAT_MSG,0)
            mui_data.infoPanel    = BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail",0))
            mui_data.minimap      = BlzGetFrameByName("MiniMapFrame",0)
            mui_data.minimapBar   = BlzGetFrameByName("MinimapButtonBar",0)
            mui_data.resourceBar  = BlzGetFrameByName("ResourceBarFrame",0)
            mui_data.inventoryTxt = BlzGetFrameByName("InventoryText", 0)

            -- reposition items move after nudging ConsoleUI off screen:
            for i = 0,#mui_data.consoleFrames do
                fh = BlzGetOriginFrame(mui_data.consoleFrames[i].frame,0)
                BlzFrameClearAllPoints(fh)
                BlzFrameSetAbsPoint(fh,FRAMEPOINT_BOTTOMLEFT,mui_data.consoleFrames[i].x,mui_data.consoleFrames[i].y + mui_data.screenGutter)
            end
            fh = BlzGetFrameByName("MiniMapFrame",0)
            BlzFrameClearAllPoints(fh)
            BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 0.0, 0.0 + mui_data.screenGutter)
            BlzFrameClearAllPoints(mui_data.chatMsg)
            -- for some reason the chat frame ignores x offset:
            BlzFrameSetPoint(mui_data.chatMsg, FRAMEPOINT_BOTTOMLEFT, mui_data.minimap, FRAMEPOINT_TOPLEFT, 0.0, 0.0)
            BlzFrameSetSize(mui_data.chatMsg, 0.5, 0.2)

            -- unhide:
            for i = 0,#mui_data.unhideFramesByName do
                BlzFrameSetVisible(BlzGetFrameByName(mui_data.unhideFramesByName[i],0), true)
            end
            for i = 0,#mui_data.inventoryButtons do
                BlzFrameSetVisible(BlzGetFrameByName(mui_data.inventoryButtons[i],0), true)
            end
            for i = 0,#mui_data.menuButtons do
                BlzFrameSetVisible(BlzGetFrameByName(mui_data.menuButtons[i],0), true)
            end

            -- hide:
            BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
            BlzFrameSetVisible(BlzFrameGetParent(BlzGetFrameByName("ResourceBarFrame",0)), false)

            -- unhide specific frames:
            for i = 0,4 do
                BlzFrameSetVisible(BlzGetOriginFrame(ORIGIN_FRAME_MINIMAP_BUTTON,i), true)
            end
            BlzFrameSetVisible(BlzGetOriginFrame(ORIGIN_FRAME_HERO_BAR,0), true)
            BlzFrameSetVisible(mui_data.infoPanel,true)

            -- cover cannot be hidden, has to be made 100% transparent:
            fh = BlzGetFrameByName("SimpleInventoryCover", 0)
            BlzFrameSetAlpha(fh, 0)
            BlzFrameSetAbsPoint(fh, FRAMEPOINT_BOTTOMLEFT, 1.0,1.0)
        
            -- set custom transparencies:
            BlzFrameSetAlpha(mui_data.inventoryTxt, mui_data.inventoryTxtAlpha)

            -- set custom text overrides:
            BlzFrameSetText(mui_data.inventoryTxt, mui_data.txtColor .. 'Inventory|r')

            -- move specific frames (these need to be done before backdrops are generated):
            mui.UnitPortraitAttachContainer()       -- move unit portrait.
            mui.UnitStatsPanelMove()                -- reposition unit stats panel.
            mui.CommandBarMove()                    -- load command bar.
            mui.MinimapMove()                       -- reposition minimap buttons.
            mui.InventoryMove()                     -- reposition inventory icons.
            mui.MenuButtonMove()                    -- moves top left menu buttons.

            -- create custom frames, assign stored index for future manipulation:
            mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(mui_data.minimap,'minimap_bd',mui_data.textureMain)
            mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(mui_data.infoPanel,
                'unitinfo_bd',mui_data.textureMain,mui_data.backdropAlpha,nil,mui_data.infoPanelGutterY)
            for i = 0,5 do
                mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(BlzGetFrameByName("InventoryButton_"..i,0),
                    'inv_bd_'..i,mui_data.textureMain,mui_data.backdropAlpha)
            end
            for i = 0,4 do
                mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(BlzGetFrameByName(mui_data.minimapButtons[i],0),
                    'minimapbar_bd_'..i,mui_data.textureMain,mui_data.backdropAlpha)
            end

            mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(mui_data.resourceBar,'resourcebar_bd',
                mui_data.textureMain,mui_data.backdropAlpha, mui_data.resourceBarOffsetX, mui_data.resourceBarOffsetY)

        end
    end

    -- keep outside of local player to stop desyncs:
    -- mui.InitUnitSelectedUpdate()

end


function mui.UnitPortraitAttachContainer()
    mui_data.portraitContainer = mui.AttachBackdropByHandle(mui_data.infoPanel, 'portrait_container',
        mui_data.textureFlagBTN, 0, 0.0, BlzFrameGetHeight(mui_data.infoPanel)*mui_data.unitInfoHeight + 0.00672,
        mui_data.portraitSize, mui_data.portraitSize)
    BlzFrameSetAlpha(mui_data.portraitContainer, 0)
    mui.UnitPortraitMove()
end


function mui.UnitPortraitMove()
    BlzFrameClearAllPoints(mui_data.portrait)
    BlzFrameSetSize(mui_data.portrait,mui_data.portraitSize,mui_data.portraitSize)
    BlzFrameSetAbsPoint(mui_data.portrait,FRAMEPOINT_CENTER,mui_data.getCenter,BlzFrameGetHeight(mui_data.infoPanel) + 0.02265)
end


-- we have to do some janky stuff here to allow hp/text bars to update.
-- the short version of it is:
--   a) the unit selected needs to be present for all players or it can desync. we make one for each player so they are guaranteed to have visibility of it.
--   b) there is a few frames of delay between hp/mana text becoming enabled. trying to move it too early can cause a client crash.
function mui.InitUnitSelectedUpdate()
    for pSlot = 0,9 do
        local player = Player(pSlot)
        TimerStart(NewTimer(), 0.1, false, function()
            local u = CreateUnit(player, FourCC('hfoo'),0.0,0.0,270.0)
            PauseUnit(u,true)
            SetUnitVertexColorBJ(u, 0, 0, 0, 100)
            if GetLocalPlayer() == player then
                SelectUnit(u, true)
            end
            TimerStart(NewTimer(), 0.3, false, function()
                if GetLocalPlayer() == player then
                    local fh   = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_HP_TEXT, 0)
                    local fh2  = BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_MANA_TEXT, 0)

                    BlzFrameClearAllPoints(fh)
                    BlzFrameSetPoint(fh, FRAMEPOINT_CENTER, mui_data.portraitContainer,
                        FRAMEPOINT_CENTER,-(mui_data.portraitSize*2.5),-0.0056)

                    BlzFrameClearAllPoints(fh2)
                    BlzFrameSetPoint(fh2, FRAMEPOINT_CENTER, mui_data.portraitContainer,
                        FRAMEPOINT_CENTER,mui_data.portraitSize*2.5,-0.0056)

                    SelectUnit(u, false)
                end
                RemoveUnit(u)
                ReleaseTimer()
            end)
            ReleaseTimer()
        end)
    end
end


function mui.CommandBarMove()

    local fh
    local prevfh
    -- where top 1st btn starts on screen (expanding right):
    local top_row_x = mui_data.getCenter - (12*mui_data.commandBarFrameSize)/mui_data.commandBarNudgeDiv + mui_data.commandBarOffsetX
    -- where bot 1st btn starts on screen (expanding right):
    local bot_row_x = mui_data.getCenter - (12*mui_data.commandBarFrameSize)/mui_data.commandBarNudgeDiv + mui_data.commandBarOffsetX
    local reorder_index

    for i = 0,11 do
        reorder_index = mui_data.commandBarOrder[i+1]
        fh = BlzGetFrameByName(mui_data.commandBarMap[reorder_index],0)
        BlzFrameClearAllPoints(fh)
        if (i >= 6) then -- bot row
            if (i ~= 6) then
                prevfh = BlzGetFrameByName(mui_data.commandBarMap[mui_data.commandBarOrder[i]],0)
                BlzFrameSetPoint(fh, FRAMEPOINT_LEFT, prevfh, FRAMEPOINT_RIGHT, mui_data.commandBarPadding, 0.0)
            else
                BlzFrameSetAbsPoint(fh,FRAMEPOINT_RIGHT,bot_row_x,mui_data.commandBarOffsetY - mui_data.commandBarGutterY)
            end
        else -- top row
            if (i ~= 0) then
                prevfh = BlzGetFrameByName(mui_data.commandBarMap[mui_data.commandBarOrder[i]],0)
                BlzFrameSetPoint(fh, FRAMEPOINT_LEFT, prevfh, FRAMEPOINT_RIGHT, mui_data.commandBarPadding, 0.0)
            else
                BlzFrameSetAbsPoint(fh,FRAMEPOINT_RIGHT,top_row_x,
                    mui_data.commandBarOffsetY+mui_data.commandBarFrameSize+mui_data.commandBarPadding)
            end
        end
        BlzFrameSetSize(fh,mui_data.commandBarSize,mui_data.commandBarSize)
        BlzFrameSetLevel(fh, 1)
        mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(fh,'cmd_bd_'..i,mui_data.textureMain,mui_data.backdropAlpha)
    end

end


function mui.InventoryMove()
    local fh
    local nudgeX = 0.0
    local nudgeY = 0.0
    -- inventory txt:
    BlzFrameClearAllPoints(mui_data.inventoryTxt)
    BlzFrameSetPoint(mui_data.inventoryTxt,FRAMEPOINT_TOPRIGHT,mui_data.infoPanel,FRAMEPOINT_TOPLEFT,
            mui_data.inventoryOffsetX + BlzFrameGetWidth(mui_data.inventoryTxt)/2 + mui_data.inventoryPadding/4.33,
            mui_data.inventoryOffsetY + mui_data.inventoryPadding*0.65)
    -- inventory buttons:
    for i = 0,5 do
        fh = BlzGetFrameByName(mui_data.inventoryButtons[i],0)
        if i == 2 or i == 4 then
            nudgeY = nudgeY + -(BlzFrameGetWidth(fh)/2 + mui_data.inventoryPadding)
        end
        if i == 1 or i == 3 or i == 5 then
            nudgeX = BlzFrameGetWidth(fh)/2 + mui_data.inventoryPadding
        else
            nudgeX = 0.0
        end
        BlzFrameClearAllPoints(fh)
        BlzFrameSetPoint(fh,FRAMEPOINT_TOPRIGHT,mui_data.infoPanel,FRAMEPOINT_TOPLEFT,
            mui_data.inventoryOffsetX + nudgeX, mui_data.inventoryOffsetY + nudgeY)
    end
end


function mui.MenuButtonMove()
    local fh
    for i = 0,#mui_data.menuButtons do
        fh = BlzGetFrameByName(mui_data.menuButtons[i],0)
        BlzFrameClearAllPoints(fh)
        BlzFrameSetSize(fh, mui_data.menuBtnWidth, BlzFrameGetHeight(fh))
        --BlzFrameSetTexture(fh, mui_data.textureMain, 0, true)
        --[[BlzFrameSetAlpha(fh,0.0)
        mui_data.frameStack[#mui_data.frameStack + 1] = mui.AttachBackdropByHandle(fh,'menu_bd_'..i,
                mui_data.textureMain,mui_data.backdropAlpha,0.0,0.0)--]]
    end
    BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[0],0),FRAMEPOINT_TOPLEFT,BlzGetFrameByName(mui_data.commandBarMap[8],0),
        FRAMEPOINT_BOTTOMLEFT,-mui_data.menuBtnPadding,-(mui_data.menuBtnOffsetY - mui_data.commandBarGutterY))
    BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[1],0),FRAMEPOINT_LEFT,BlzGetFrameByName(mui_data.menuButtons[0],0),
        FRAMEPOINT_RIGHT,mui_data.menuBtnPadding,0.0)
    BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[2],0),FRAMEPOINT_LEFT,BlzGetFrameByName(mui_data.menuButtons[1],0),
        FRAMEPOINT_RIGHT,mui_data.menuBtnPadding,0.0)
    BlzFrameSetPoint(BlzGetFrameByName(mui_data.menuButtons[3],0),FRAMEPOINT_LEFT,BlzGetFrameByName(mui_data.menuButtons[2],0),
        FRAMEPOINT_RIGHT,mui_data.menuBtnPadding,0.0)
end


function mui.UnitStatsPanelMove()
    BlzFrameClearAllPoints(mui_data.infoPanel)
    BlzFrameSetAbsPoint(mui_data.infoPanel,FRAMEPOINT_BOTTOM,mui_data.infoPanelOffsetX,mui_data.infoPanelOffsetY)
end


function mui.MinimapMove()
    local fh
    for i = 0,4 do
        fh = BlzGetFrameByName(mui_data.minimapButtons[i],0)
        BlzFrameClearAllPoints(fh)
        BlzFrameSetPoint(fh, FRAMEPOINT_BOTTOMLEFT, mui_data.minimap, FRAMEPOINT_BOTTOMRIGHT,0.005,mui_data.minimapBtnPadding*i)
    end
end


-- must be run after a cinematic or hero portrait resets position:
function mui.AfterCinematic()
    BlzFrameSetVisible(BlzGetFrameByName("ConsoleUIBackdrop",0), false)
    BlzFrameSetVisible(BlzGetFrameByName("CinematicPortrait",0), false)
    BlzFrameSetAlpha(BlzGetFrameByName("SimpleInventoryCover", 0), 0)
    BlzFrameSetAbsPoint(BlzGetFrameByName("SimpleInventoryCover", 0), FRAMEPOINT_BOTTOMLEFT, 1.0,1.0)
    BlzFrameSetAbsPoint(mui_data.chatMsg,FRAMEPOINT_BOTTOMLEFT,0.15,0.20)
    mui.UnitPortraitMove()
end


-- create a backdrop and set it to be positioned at the desired frame via FRAMEPOINT_CENTER.
-- @fh = target this frame
-- @newFrameNameString = enter a custom value to manipulate backdrop by name if needed e.g. "myInventoryBackdrop"
-- @texturePathString = [optional; usually needed] texture (.blp) as the background
-- @alphaValue = [optional] if the backdrop should be transparent, pass in a value 0-255
-- @offsetx = [optional] nudge the frame left or right (percentage of screen)
-- @offsety = [optional] nudge the frame up or down (percentage of screen)
-- @width, @height = [optional] override the frame size
-- :: returns framehandle
function mui.AttachBackdropByHandle(fh, newFrameNameString, texturePathString, alphaValue, offsetx, offsety, width, height)
    local nh = BlzCreateFrameByType("BACKDROP", newFrameNameString, BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0), "", 0)
    local x = offsetx or 0.0
    local y = offsety or 0.0
    local w = width or BlzFrameGetWidth(fh)
    local h = height or BlzFrameGetHeight(fh)
    BlzFrameSetSize(nh, w, h)
    BlzFrameSetPoint(nh, FRAMEPOINT_CENTER, fh, FRAMEPOINT_CENTER, 0.0 + x, 0.0 + y)
    if texturePathString then
        BlzFrameSetTexture(nh, texturePathString, 0, true)
    end
    BlzFrameSetLevel(nh, 0)
    if alphaValue then
        BlzFrameSetAlpha(nh, math.ceil(alphaValue))
    end
    return nh
end


-- show or hide custom made UI elements during a cinematic (be sure to use local player only)
-- @bool = should it be visible? true = visible; false = hide
function mui.ShowHideCustomUI(bool)
    for i = 1,#mui_data.frameStack do
        if mui_data.frameStack[i] ~= nil then
            BlzFrameSetVisible(mui_data.frameStack[i], bool)
        end
    end
end
 

Attachments

  • lua_minimalist_moba_ui_v1.1c.w3m
    322.7 KB · Views: 339
Last edited:
I've been trying to accomplish something similar (trying to somewhat recreate the Reforged UI) but have been having issues getting the minimap buttons to relocate (I think they're off-screen but I can't seem to get them to show up at all). Trying to move health/mana that's under the portrait is crashing and I can't even find the frame that holds unit information (attack, armour, hero atttributes, etc). I've tried reading your code but I'm not great at reading Lua. Any pointers?
 
Level 14
Joined
Feb 7, 2020
Messages
386
I've been trying to accomplish something similar (trying to somewhat recreate the Reforged UI) but have been having issues getting the minimap buttons to relocate (I think they're off-screen but I can't seem to get them to show up at all). Trying to move health/mana that's under the portrait is crashing and I can't even find the frame that holds unit information (attack, armour, hero atttributes, etc). I've tried reading your code but I'm not great at reading Lua. Any pointers?
Health/mana states:
Unfortunately, hiding the originframes seems to toss those items into oblivion; they're one of the frames with a mind of their own. That's the only reason I went through the process of making the custom bars, otherwise I would have preferred the lazy route. :cool:2 Their functionality of being hidden/unhidden seems to be turned off by the hide native.

Minimap buttons:
I was able to get them moved around with the following method:
Lua:
BlzFrameClearAllPoints(BlzGetFrameByName('MiniMapAllyButton',0))
BlzFrameSetPoint(BlzGetFrameByName('MiniMapAllyButton',0), FRAMEPOINT_CENTER, mui_data.portraitContainer, FRAMEPOINT_CENTER,0.0,0.10)

Where mui_data.portraitContainer could be any other default frame, e.g. BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail",0))

Could probably be done with all of these by frame name:
Code:
MiniMapAllyButton
MiniMapCreepButton
MinimapSignalButton
MiniMapTerrainButton
FormationButton

Worth noting that these are probably attached to the ConsoleUI frame. If you used the "move it off screen" trick some originframes probably get dragged with it and have to be pushed back onto the screen.

upload_2020-5-15_6-12-28.png



Info panel:
I think you mean this frame: BlzFrameGetParent(BlzGetFrameByName("SimpleInfoPanelUnitDetail",0))
I had some trouble with this one and ended up grabbing the whole thing, which also includes the inventory frame.

Were you just wanting to move it or interact with its innards?

To manipulate its content is something I haven't dived into but there's a long list of items that are stored at different contexts:

Code:
SimpleInfoPanelBuildingDetail [1]
SimpleInfoPanelCargoDetail [2]
SimpleInfoPanelDestructableDetail [4]
SimpleInfoPanelIconAlly [7]
SimpleInfoPanelIconArmor [2]
SimpleInfoPanelIconDamage [0 & 1]
SimpleInfoPanelIconFood [4]
SimpleInfoPanelIconGold [5]
SimpleInfoPanelIconHero [6]
SimpleInfoPanelIconHeroText [6]
SimpleInfoPanelIconRank [3]
SimpleInfoPanelItemDetail [3]
SimpleInfoPanelUnitDetail
InfoPanelIconAllyFoodIcon, 7
InfoPanelIconAllyFoodValue, 7
InfoPanelIconAllyGoldIcon, 7
InfoPanelIconAllyGoldValue, 7
InfoPanelIconAllyTitle, 7
InfoPanelIconAllyUpkeep, 7
InfoPanelIconAllyWoodIcon, 7
InfoPanelIconAllyWoodValue, 7
InfoPanelIconBackdrop, 0
InfoPanelIconBackdrop, 1
InfoPanelIconBackdrop, 2
InfoPanelIconBackdrop, 3
InfoPanelIconBackdrop, 4
InfoPanelIconBackdrop, 5
InfoPanelIconHeroAgilityLabel, 6
InfoPanelIconHeroAgilityValue, 6
InfoPanelIconHeroIcon, 6
InfoPanelIconHeroIntellectLabel, 6
InfoPanelIconHeroIntellectValue, 6
InfoPanelIconHeroStrengthLabel, 6
InfoPanelIconHeroStrengthValue, 6
InfoPanelIconLabel, 0
InfoPanelIconLabel, 1
InfoPanelIconLabel, 2
InfoPanelIconLabel, 3
InfoPanelIconLabel, 4
InfoPanelIconLabel, 5
InfoPanelIconLevel, 0
InfoPanelIconLevel, 1
InfoPanelIconLevel, 2
InfoPanelIconLevel, 3
InfoPanelIconLevel, 4
InfoPanelIconLevel, 5
InfoPanelIconValue, 0
InfoPanelIconValue, 1
InfoPanelIconValue, 2
InfoPanelIconValue, 3
InfoPanelIconValue, 4
InfoPanelIconValue, 5
The context meaning every frame is stored at a different index level, so you'd have to loop through each one e.g. for InfoPanelIconLabel:

BlzGetFrameByName("InfoPanelIconLabel",0)
BlzGetFrameByName("InfoPanelIconLabel",1)
BlzGetFrameByName("InfoPanelIconLabel",2)

And finding out what some of these do is an unfortunate and sometimes-frustrating game of trial and error.
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,183
I have mixed feelings about this.

I really like the background replacement, along with the inventory.

My main issue would be that the unit panel is not centered.

Also, mixed feelings when it comes to the unit frame/hp bar design.

But even with these complaints, I would probably consider this an upgrade over the default UI.
 
Last edited:
Level 14
Joined
Feb 7, 2020
Messages
386
I have mixed feelings about this.

I really like the background replacement, along with the inventory.

My main issue would be that the unit panel is note cenetered.

Also mixed feelings when it comes to the unit frame/hp bar design.

But even with these complaints I would probably consider this an upgrade over the default UI.

The arrangement has a lot of issues I'd like to address. Mismatched edges are triggering, for example.

What do you think of this mock up?

edit: I just realized this solves the unit portrait positioning with the centered info panel. If it remains at 0.40 x offset it stays centered. I think I'll for-sure try this arrangement.
 

Attachments

  • rearranged_ui_draft.jpg
    rearranged_ui_draft.jpg
    893.6 KB · Views: 305
Level 14
Joined
Feb 7, 2020
Messages
386
Hmm, the health/mana displayed doesn't show the value of the focus unit (seems to show the value of the last unit selected?). Tabbing through selected units doesn't update it either. Could it maybe retrieve those values info from the actual hp/mana frames?
Forgot to add that to the list of known issues. Looking into it eventually. Have yet to delve into detecting the unit in focus or when focus changes. The whole part of repetitive selection sync has me a bit scared it might not be viable.
 
Last edited:
Hey ! I test it on Reforged and it seems to work almost perfectly (and the values displayed on the health/mana bar are correct), even if there are somethings to fix (like the reload time effect on the buttons which is out the frame).

Your idea is really nice and it could be infinitely useful for map makers. Keep going guy !
 

Attachments

  • WC3ScrnShot_051520_215747_001.png
    WC3ScrnShot_051520_215747_001.png
    3.3 MB · Views: 227
  • WC3ScrnShot_051520_220519_001.png
    WC3ScrnShot_051520_220519_001.png
    3.3 MB · Views: 217
Last edited:
(Oh and sorry for double-post but I fixed some base icons to avoid that)

upload_2020-5-15_22-15-39.png


(Path: UI\Widgets\Console\Human\)
 

Attachments

  • infocard-armor-hero.dds
    85.5 KB · Views: 151
  • infocard-armor-large.dds
    85.5 KB · Views: 141
  • infocard-armor-unarmored.dds
    85.5 KB · Views: 106
  • infocard-attack-hero.dds
    85.5 KB · Views: 103
  • infocard-attack-magic.dds
    85.5 KB · Views: 150
  • infocard-attack-melee.dds
    85.5 KB · Views: 94
  • infocard-attack-piercing.dds
    85.5 KB · Views: 93
  • infocard-attack-siege.dds
    85.5 KB · Views: 101
  • infocard-gold.dds
    85.5 KB · Views: 92
  • infocard-neutral-armor-large.dds
    85.5 KB · Views: 141
  • infocard-neutral-armor-unarmored.dds
    85.5 KB · Views: 154
  • infocard-neutral-attack-hero.dds
    85.5 KB · Views: 142
  • infocard-neutral-attack-magic.dds
    85.5 KB · Views: 92
  • infocard-neutral-attack-melee.dds
    85.5 KB · Views: 92
  • infocard-neutral-attack-piercing.dds
    85.5 KB · Views: 141
  • infocard-neutral-attack-siege.dds
    85.5 KB · Views: 97
  • infocard-supply.dds
    85.5 KB · Views: 91
Level 14
Joined
Feb 7, 2020
Messages
386
Behold.

Version 1.1 featuring a total realignment courtesy of @Chaosy's nudge about centering.

Unit portrait should now work on all screens. Other known issues still persistent.

The unit state bars now collapse from right to left. I pondered for a moment that they could each collapse inward toward the hero portrait but other progress bars in the game don't follow this logic so it seemed like a misfit design.

Before and after:

WC3ScrnShot_051520_162657_001.png WC3ScrnShot_051520_162418_001.png

UI screens:

WC3ScrnShot_051520_162107_001.png WC3ScrnShot_051520_162538_001.png
 

Attachments

  • lua_minimalist_moba_ui_v1.1.w3m
    39.4 KB · Views: 120
Level 14
Joined
Feb 7, 2020
Messages
386
Quick update:
- Squared up some edges. Brought back the day/night indicator.
- Separating things in the resource bar proved to be a major pain, so not putting any effort into it for now. Lazily put it back in the top right since day/night can't be touched effectively.
- I attempted to use Tasyen's Group Selection snippet to acquire the focus unit but it didn't play well in multiplayer and it would require a beefy modification to determine mouse event selections of units in the info pane.
- With above issue, I instead opted to go down the rabbit hole of bringing the default text back and ditched the not-fancy unit state bars. What a nightmare that was. Hours gone! But, it is possible (more below).

@Spellbound found out how to keep the default mana and health text. It requires not using the hide originframes function and instead moving the console UI off screen BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), FRAMEPOINT_BOTTOM, 0.0, -0.24). Everything you want back then needs to be moved back on screen. Lastly, the top menu bar will no longer be hidden, so you have to go through the gauntlet of replacing the console UI .blp paths with empty alpha textures. Supposedly you should be able to move the top of the ConsoleUI to hide this top piece, but I couldn't get it to work without crashes. And lastly, lastly, lastly, you have to update the text frames with a unit selected (and there's a few frames delay with it appearing so you gotta run a very short timer).

There's more on page 2 here Hiding / Showing original UI frames nightmare...

I did find a strange issue when using SetScale on the hp/mana text frames. When mana is hidden/unhidden between units it runs the scale function again. Shame, the text looks much more crisp at scale 1.4 or 1.3.

This version needs a test in Reforged if you're available to do so @Zephyrius2412. I had to do some janky UI path imports for HD and SD.
 

Attachments

  • WC3ScrnShot_051720_141053_001.png
    WC3ScrnShot_051720_141053_001.png
    3.5 MB · Views: 195
  • WC3ScrnShot_051720_141055_001.png
    WC3ScrnShot_051720_141055_001.png
    3.5 MB · Views: 1,578
  • lua_minimalist_moba_ui_v1.1a.w3m
    323.7 KB · Views: 117
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Its easier to just remove everything and code the UI from scratch. I got great results with that that look beautiful and since you coded everything yourself, it doesnt lock you into premade usage patterns and allows more artistic freedom.

I posted an example below. The only UI element i kept from the original UI is the menu button I moved to the bottom right which will get a matching texture later.
 

Attachments

  • WC3ScrnShot_033020_214540_001.png
    WC3ScrnShot_033020_214540_001.png
    2.2 MB · Views: 230
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
I post has ed an example below. The only UI element i kept from the original UI is the menu button I moved to the bottom right which will get a matching texture later.

This is indeed beautiful.
I had the same thought about a complete custom UI, but my conclusion was that it is only viable in a very narrow range of contexts.
there are so many frames you cannot emulate easily if you need them, like units info panel, minimap and the buttons you needs for it.
Last but not least, in your beautiful bottom interface, the center ring between health and mana bar would greatly benefit from displaying the portrait.

This said, in a RPG and single player context, I clearly think your approach would be the way to go.

@Planetary :
You know how much I can relate to everything you went through there, and I really appreciate the effort you made to give people parameters to change things to adapt to their needs.

As for the HP/Mana bars, in all fairness the bars were much more appealing than the plain text.
Maybe give users the option to keep the text, that is easy and has no downside but is quite ugly, or use custom hp/mana bars.
My advice for custom hp/mana bars is to create a set of bars for every player then you don't need to change values locally, but only track player selections (I use a simple global to store the current selected unit. Of course this does not solve the focus selection in a multiple selection context, but thats not a major drawback.

The only real difficulty with the health and mana bars is actually detecting regenerations. I didn't have this issue in y specific case because my units do not regenerate, so all I had to do was track the heals.

Keep up the good work !!!

@Spellbound :
So far, I gave up on trying to change the visibility of the sub frames of the Unit Info Panel :
- I tried to hide the attribute buttons on heroes with 0 attributes (used heroes like that to revive my recycled creeps) and attempting to do this in the context number 6 that @Tasyen mentioned crashes the game. Maybe our UI master has found a better solution, I haven't had time to check his code for custom hero attrivutes.

EDIT :
@Planetary :
I looked at your code, and I am afraid it is not suitable for multiplayer as you keep creating handles in the local player context. This happens in "function mui.Init()" for example, and you also set a table value in the local player context though timer utils.
You should only keep visibility modifiers in the local player context, or there is something I missed.
By the way, it's definitely something minor, but I found it was better to store the local player or its number in a global variable rather than constantly call GetLocalPlayer().

EDIT 2 :
Your periodic timer does the proper job to handle regenerations for your hp/mana bars.
I guess all you need to make it perfect (besides the multiple selection focus issue) is use a couple of bars for each player, then use the selection event and the damage event.
But you should keep track of the currently selected unit for each player in order to only update a player's bars when he/she is actually having the related unit selected.
If you do this, you'll have much less trouble with local player context.

I believe you minimalist UI doesn't need much more to become perfect :)
 
Last edited:
So I made some progress on my own UI re-arrangements and I'm probably going to have to go the move-stuff-out-of-the-way approach because I can't get the HP and Mana to show. I'm also running into an issue where item icons and cooldown/autocast overlays are not resizing. Any tips on how to remedy to that? Minimap icon still not showing up...
 
Level 12
Joined
Jan 30, 2020
Messages
875
Could you provide us with a sample map showing your issue ?

I don't see any reason the minimap buttons would not show if you used this minimalist UI.
As for the overlays not resizing, I am not 100% sure as I have 0 experience in resizing the inventory or command buttons, but I believe it simply means that there are child frames in the FDF definitions, and the question is : are they accessible (GetFrameByName? GetFrameByType?) to be resized too.

This UI API is a blessing and a curse at the same time, Blizzard gave us tools, but they are incomplete.

There are unfixable issues currently, like placing the UNIT_MSG frame (the one showing messages to players) exactly where you want, you will get issues if it collides with the minimap original position, and you will need to figure out how it interacts with the DisplayTextToPlayer-like natives as they all use coordinates.
Also, you cannot put the Inactive Worker icon back to a proper location once you have moved the ConsoleUI backdrop down, and if you don't move this one down, you can't get rid of the unclickable zone originally behind the Command Buttons Bar.

The reason is there is no way to access these frames at the moment, until Blizzards decides to give us more natives (like a generic frame event that would allow us to get the name of all the native frames the game uses) or just more Origin Frames names.
 
So I made some progress on my own UI re-arrangements and I'm probably going to have to go the move-stuff-out-of-the-way approach because I can't get the HP and Mana to show. I'm also running into an issue where item icons and cooldown/autocast overlays are not resizing. Any tips on how to remedy to that? Minimap icon still not showing up...

You need to edit the cooldown model and resize it or custom code your own command button system. I recommend the first option.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Thats a very useful information, and will save many who want to fix this a lot of time. It also reminds us that there are quite a few models used by the game UI and that nothing stops us from modifying them.
Thank you, @TriggerHappy !

If only the console UI was driven by a model, things would be so much easier.
Now all the Console UI model does is place the 4 basic UI textures...
 
Could you provide us with a sample map showing your issue ?
Well, it's in Wurst. Here's the code, but it's kind of a mess. I was mostly making it up as I go: Wurstbin - The Wurst & Jass Pastebin

You need to edit the cooldown model and resize it or custom code your own command button system.
Hmmm, how would you even go about coding a custom command button system? Afaik there's no way to determine the focus unit [type?] when making ground selections and tabbing through them, so figuring out what command card to display is going to be a challenge if not impossible. Wished we had more natives to work with. Can't even retrieve min/max value of a framehandle :(

PS: I assume you'd have to resize the autocast frame sparkle thing as well?
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Hello again, sorry I was out for the day.


I don't know wurst, but it is quite easy to read, so as for your minimap buttons, why do you position them relative to each other ?
You should try to position them all in relation to the minimap, like it is done in this minimalist UI :
Lua:
function mui.MinimapMove()
    local fh
    for i = 0,4 do
        fh = BlzGetFrameByName(mui_data.minimapButtons[i],0)
        BlzFrameClearAllPoints(fh)
        BlzFrameSetPoint(fh, FRAMEPOINT_BOTTOMLEFT, mui_data.minimap, FRAMEPOINT_BOTTOMRIGHT,0.005,mui_data.minimapBtnPadding*i)
    end
end

While you try to set them according to each other incremently :

Wurst:
       minimapButton[2]
       ..show()..clearAllPoints()
       ..setSize(MINIMAP_BUTTON_WIDTH, MINIMAP_BUTTON_HEIGHT)
       ..setPoint(FRAMEPOINT_BOTTOMLEFT, minimapButton[1], FRAMEPOINT_BOTTOMRIGHT, MINIMAP_SEPARATOR, MINIMAP_SEPARATOR + MINIMAP_BUTTON_HEIGHT)

If this still doesn't work, try to comment out the SetSize() methods just to see if it makes any difference.

Now for your command buttons cooldowns, I think this is precisely why @TriggerHappy advised you rescaled the model, it will be much easier if you don't need to change the size dynamically ingame. Fact is the problem with displaying a custom command bar system is the same as the HP / Mana Bars.

Furthermore, I did not understand your problem with the HP and Mana bars :

Code:
       /*
           The idea here is to create custom bars for both health and mana and reveal them whenever the actual
           health and mana frames become visible. Unfortunately, I can't seem to make those become visible at,
           and there's the added issue of being unable to retrieve the max value of a framehandle, so you can't
           even do this at all.
       */



---

/**
   This function checks every .05 seconds if the health and mana framehandles are visible. If they are, reveal the
   corresponsing status bar and set their value accordingly. There's is a pretty significant issue there, unfortunately.
   There is no BlzGetFrameMaxValue() so I can't actually retrieve the maximum value of the HP/Mana framehandles, so
   this entire system is moot unless I can somehow determine the focus unit.
*/

You don't need to know the max value, you just need to display a % of the bar corresponding to the % of health of the currently selected unit.
For example, here is my function (in Lua sorry) to update the bars :

Lua:
function UpdateHP(u, life, maxlife, on, pn, color)
    if color then
        BlzFrameSetVertexColor(Bar[pn], BlzConvertColor(255, RedB[on], GreenB[on], BlueB[on]))
    end
    BlzFrameSetValue(BlzGetFrameByName("HPBarEx",pn), life/maxlife*100)
    BlzFrameSetText(BlzGetFrameByName("HPBarExText",pn), "HP: "..life)
end

u is the unit, on is the player number of the owner of the unit and pn is the player number of the player selecting the unit.
What I do is save the currently selected unit for each player in a small array called called Select[*player number*]
I update this value with a UNIT_SELECTED/DESELECTED event :

Lua:
function ManageSelections()
    RangeID=FourCC("n00R")
    RangeIndicator={}
    Sel,Desel=CreateTrigger(),CreateTrigger()
    for i=1, 4 do
        if Playing[i] then
            local p=Player(i-1)
            RangeIndicator[i]=CreateUnit(p, RangeID, 0, 0, 270.0)
            SetUnitVertexColor(RangeIndicator[i], Red[i], Green[i], Blue[i], 255)
            ShowUnit(RangeIndicator[i], false)
            TriggerRegisterPlayerUnitEvent(Sel, p, EVENT_PLAYER_UNIT_SELECTED, nil)
            TriggerRegisterPlayerUnitEvent(Desel, p, EVENT_PLAYER_UNIT_DESELECTED, nil)
        end
    end -- for HP Bars, we also need to manage BallsMaster units selections
    TriggerRegisterPlayerUnitEvent(Sel, BallsMaster, EVENT_PLAYER_UNIT_SELECTED, nil)
    TriggerRegisterPlayerUnitEvent(Desel, BallsMaster, EVENT_PLAYER_UNIT_DESELECTED, nil)
    TriggerAddCondition(Sel, Condition(function ()
        local u=GetTriggerUnit()
        local on,pn=GetPlayerId(GetOwningPlayer(u))+1,GetPlayerId(GetTriggerPlayer())+1
        BlzFrameClearAllPoints(TimerDialogFH)
        BlzFrameSetAbsPoint(TimerDialogFH, BOTTOMRIGHT, 0.56, 0.0)
        BlzFrameClearAllPoints(GameBoardFH)
        BlzFrameSetAbsPoint(GameBoardFH, TOPRIGHT, 0.93, 0.60)
        BlzFrameSetVertexColor(Bar[pn], BlzConvertColor(255, RedB[on], GreenB[on], BlueB[on]))
        if (IsUnitType(u, HERO) and (on~=9)) or IsUnitType(u, STORE) or IsUnitType(u, HEAL) or IsUnitType(u, PROTECTOR)  then
            BlzFrameSetValue(BlzGetFrameByName("HPBarEx",pn), 100)
            BlzFrameSetText(BlzGetFrameByName("HPBarExText",pn), GetUnitName(u))
        else -- Towers and Balls HP Bars
            local life,maxlife=math.floor(GetWidgetLife(u)),BlzGetUnitMaxHP(u)
            if (on==9) then
                maxlife=CurrentMaxHP
            end
            UpdateHP(u, life, maxlife, on, pn, false)
            if IsUnitType(u, MECH) and (pn==on) then -- We only show the Range Indicator for the tower owner
                local range,scale=RangeIndicator[on],GetUnitAcquireRange(u)/400
                SetUnitFlyHeight(range, 258.00-BlzGetUnitZ(u), 0)
                SetUnitPosition(range, GetUnitX(u), GetUnitY(u))
                SetUnitScale(range, scale, scale, scale)
                ShowUnit(range, true)
            end
        end
        Select[pn]=u -- we save the selected unit as the current selection for the player triggering the selection
        return false
    end))
    TriggerAddCondition(Desel, Condition(function ()
        local u=GetTriggerUnit()
        local on,pn=GetPlayerId(GetOwningPlayer(u))+1,GetPlayerId(GetTriggerPlayer())+1
        BlzFrameClearAllPoints(TimerDialogFH)
        BlzFrameSetAbsPoint(TimerDialogFH, BOTTOMRIGHT, 0.56, 0.0)
        BlzFrameClearAllPoints(GameBoardFH)
        BlzFrameSetAbsPoint(GameBoardFH, TOPRIGHT, 0.93, 0.6)
        if (pn==on) then  -- We only hide the Range Indicator for the tower owner
            ShowUnit(RangeIndicator[on], false)
            SetUnitPosition(RangeIndicator[on], 0, 0)
        end
        BlzFrameSetValue(BlzGetFrameByName("HPBarEx", pn), 0)
        BlzFrameSetText(BlzGetFrameByName("HPBarExText", pn), " ")
        Select[pn]=nil
        return false
    end))
end
u, on and pn are the same here, I know I should use more descriptive variables names, but at least I try to be consistent.

ignore the range indicator parts, and the fact that I don't display the values but the name for invulnerable units.

I also keep track of heals, and for regenerations all you have to do is use a periodic timer (not as short as 0.05, it doesn't need to be updated 20 times per second !!!) to update the values.

As you can see I use 1 HP bar for each player, that makes things much easier and safer as you do not need to do things in the local player context.

Note that the only drawback of this system is that it is really difficult to keep track of tabbing in multiple selections context.

EDIT :
I forgot to specify that I also update the bars with a unit damaged event :
Lua:
function DamagedBallActions()
    local t=CreateTimer()
    local b=GetTriggerUnit()
    TimerData[t]=b
    TimerStart(t, 0.0, false, function()
        local t=GetExpiredTimer()
        local b=TimerData[t]
        DestroyTimer(t)
        local life=math.floor(GetWidgetLife(b))
        local size=life*ResizeFactor
        if (size<MinSize) then
            size=MinSize
        end
        SetUnitScale(b, size, size, size)
        for i=1,4 do
            if (b==Select[i]) then
                UpdateHP(b, life, CurrentMaxHP, 9, i, true)
            end
        end
    end)
    local a=GetEventDamageSource()
    if IsUnitType(a, MECH) then
        if (GetUnitTypeId(a)==TowerID[17]) then -- Wealth Tower
            local o=GetOwningPlayer(a)
            SetPlayerState(o, EUROS, GetPlayerState(o, EUROS)+WealthGain)
        else
            local sn=GetUnitUserData(a)
            if (sn>0) then
                if (BlzGetUnitAbilityCooldownRemaining(a, SpellID[sn])==0) then
                    if (sn<5) then
                        IssueDelayedImmediateOrder(a, sn, 0.1)
                    elseif not(BossLevel or Incapacitated(b)) then
                        IssueDelayedTargetOrder(a, sn, b, 0.1)
                    end
                end
            end
        end
    end
    return false
end

Ignore the "WealthGain" and delayed issed orders, they're just for a Wealth tower and to autocast custom autocast spells.

Note the 0.0 second timer, to make sure the bars are updated just after the event triggered the callback function, if you do that with no timer, the values will not be updated as the event fires before the damage actually occurs.

Note this trigger keep tracks of HP (my map does not use mana at all but tits the exact same principle) of my map's attacking creeps (Balls) but I have a simpler one for my other units (towers).
 
Last edited:
Well, I managed to get the minimap buttons to show up by revealing the parent of the origin frame ORIGIN_FRAME_MINIMAP_BUTTON.
JASS:
constant MINIMAP_BUTTON_FRAME = getOriginFrame(ORIGIN_FRAME_MINIMAP_BUTTON).getParent()

init
    MINIMAP_BUTTON_FRAME.show()
Which I tried multiple times today but it just started working now so I'm not really sure what I've been doing wrong this entire time.

I've also had issues resizing the ORIGIN_FRAME_UBERTOOLTIP frame, which I'm not entirely sure is possible given that since the height changes dynamically, there's probably some hardcoded value to the width that allows the game to calculate how high it should go. Still, worth checking if anyone was able to lengthen/shorten it. You can, however, move it, thankfully.
Moved the resource bar to stand over the command card but this also moved ping/fps so I need to move those back up. Anybody know what those frames are called?

As for the HP/MANA bars, how reliable is that selection event thing to obtain the focus unit when selecting multiple units (and tabbing through them)? If I could just pick the focus unit I would be basing the hp/mana bar values on that but it doesn't seem possible atm, which is why I was trying to have the custom status bars fetch values directly from the origin HP/Mana framehandles. However, it doesn't seem like it's even possible to unhide them after hiding the game UI? Potentially renders this entire exercise moot, I was mostly trying this as an exercise to see if I could get around the fickle nature of these two framehandles.

@Planetary I hope I'm not hijacking your thread, btw. The subject matter is the same so I figured I'd post here, but if you want I'll make a separate thread for my own UI woes :p
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
I have to admit I have not yet tinkered enough with resizing the origin frames yet, but I intend to do so.

I initially went the same route as Planetary for my Custom UI, but in the end I went back the other way, hiding what I didn't need (but for some borders I had to give some frames a fully small transparent texture through World Editor's Game Interface menu.

Then, moving the Console UI with BlzFrameSetAbsPoint(BlzGetFrameByName("ConsoleUI", 0), BOTTOM, 0.0, -0.24) not only hides the HP/Mana text by putting it off screen but more importantly hides the unclickable area from the original command buttons area that is inaccessible.
The issue with using the HP/Mana texts is that they don't exist ingame before a first unit was selected. So you can access them, but only after initializing them right after map init by creating any unit, [edit : and select it , of course] then wait something like 0.6s with a timer before saving the handles of HP/Mana text frames.

With this way though, it becomes possible as the frames are not hidden, so their values will actually change properly when using the "hide what you don't need" route, and thus your idea would work... except that then you have to remember the values are different for every player (as their selection is), thus you need to assign the values directly in the function that alters the custom hp/mana bars values and text, inside the local player context. You can not create local variables in this context without a high desync chance.

But I believe it would work !

Unfortunately I think the FPS frame cannot be accessed (unless I missed something). But you could hide the origin Resource Bar by setting its alpha to 0 rather than hiding it with the hiding native and create your own Ressource Bar using the existing template in resourcebar.fdf as a SIMPLEFRAME, for each player, then I believe it will be really easy to update the values. This way the FPS text will not be altered.

Lastly, I don't think we are hijacking @Planetary 's thread as we discuss using the minimalist UI with some alterations, and I believe he could use this discussion to add more features to it. Would be nice to have this as an alternative to @Quilnez 's UI Utils that has a much more general purpose.
 
Last edited:
Level 14
Joined
Feb 7, 2020
Messages
386
@Planetary I hope I'm not hijacking your thread, btw. The subject matter is the same so I figured I'd post here, but if you want I'll make a separate thread for my own UI woes :p

Fear not. Utility over vanity.

Attached is my last update for this project until its need arises again. Can't remember what I changed but I live tested this version. It probably needs a rebuild from the ground up to be a sensible submitted resource. 1.1b worked in multiplayer standalone + when integrated with my own project. Pretty sweet. People seem to really like it.
 

Attachments

  • lua_minimalist_moba_ui_v1.1b.w3m
    324 KB · Views: 115

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
This is indeed beautiful.
I had the same thought about a complete custom UI, but my conclusion was that it is only viable in a very narrow range of contexts.
there are so many frames you cannot emulate easily if you need them, like units info panel, minimap and the buttons you needs for it.
Last but not least, in your beautiful bottom interface, the center ring between health and mana bar would greatly benefit from displaying the portrait.

This said, in a RPG and single player context, I clearly think your approach would be the way to go.
It's a matter of imagination, really.

If you want to keep most of the WC3 controls anyway, then yes, getting rid of default panels would not be clever.

However, when it comes to displaying info and stuff, my thought would mostly be: "Maybe there's a better way to display all that without the screen clutter WC3 suffers from?". You know, the essentials of a minimalist UI: actually being minimalistic instead of just moving stuff around.

So, do I need the unit info panel in most gameplay applications? No. I don't even really need it in WC3 ladder games. I don't care what the armor value of my Paladin is. I know that more is better and that's all I need. I also don't need to know my damage range, etc.
Do I need a displayed portrait? No. That one is obvious.

Do I need a minimap? Maybe. Then again, keeping the regular minimap also locks me into the premade minimap style. What if I want to use a different approach, possibly with a transparent overlay? Or maybe I want one that follows the orientation of the camera?


I get what you're saying, though. There are game types in which the default UI kinda already works.
 
Level 12
Joined
Jan 30, 2020
Messages
875
I also see your point there, and trust me I am not going to discourage anyone going for an original path, on the contrary.
Hell that was even the complete point of my map to start with, although it still remains a TD in spite of all my efforts to make it different.

Thats the reason I kept many features from the UI, although drastically modified to clear a lot of display space.

But I only did this because my map has enough unique features, and I don't want to get TD fans lost more than they should.

If I wanted to make a project with even more originality, like not following such a common genre, I would happily use an approach like yours :)
 
Level 12
Joined
Jan 30, 2020
Messages
875
Yes @Tasyen talked about it extensively on Discord, and i already implemented these 2 new UI Natives in my map as they solve some (not all far from that) problems with previously inaccessible frames ^^
 
Level 12
Joined
Jan 30, 2020
Messages
875
Well yes I now can access worker icon directly, it is the only child of the 8th child of ConsoleUI (so number 7 as it starts with 0).
Fun enough I can even access the number of inactive workers displayed on the worker icon, because thats a direct child of this worker icon.

As for the top bar I can't remember the child number.
I also read @Tasyen had issues with the time indicator. For the moment I just hide it with an empty model, but that would indeed be nice to get more control overall.
 
Could you grab the idle worker icon / time indicator / top bar background with this?
idle worker icon, yes
time indicator, well I found the frame but it does not want to move as wanted. Although it is quite easy to move the mouse taking frame of that Day Time Frame. Gameui.5.0 should be the clock and Gameui.5.0.0 is the mouse listener.
top bar background, no because they are Textures and String&Texture do not exist for this frame child natives.

Added a List of current found to my Originframe tutorial in a hidden block UI: OriginFrames
 
Level 12
Joined
Jan 30, 2020
Messages
875
Remember HP and Mana frames only exist after a first unit has been selected.

The technique I use was taught to me by @Tasyen, it consists of creating a unit at game start, then select it, start a 0.3s timer then remove the unit on expiration. After that you can access the HP and Mana Frames.
 
I did try that, but somehow I still can't get it to show. I've selected a unit, waited 2 seconds, then stored the frames in a variable. After that I tried hiding the UI and unhiding the various bits, but it did nothing when I tried revealing the HP/MANA frames. It's been a hot minute, though, so I'll give it another shot soon.
 
Level 12
Joined
Jan 30, 2020
Messages
875
I don't use mana in my entire map, but I can show you the code I use to move the HP text off screen :

Here is the function started after the game host has chosen difficulties. As thats where a hero builder is created for each player, thats where I initialize the HP text :
Lua:
function GameStarted()
    Hero={}
    HeroLevel,HeroSkill={},{}
    PlayGlobalSound(SNDEnd, SNDEndDur, 0)
    for i=1,4 do
        if Playing[i] then
            local startX,startY=GetStartLocationX(i-1),GetStartLocationY(i-1)
            Hero[i]=CreateUnit(Player(i-1), FourCC("HBU1"), startX, startY, 360.0-i*90)
            SetupHero(i, Hero[i])
            Select[i]=Hero[i]
            if (i==LocalPn) then
                SetCameraQuickPosition(startX, startY)
                SelectUnit(Hero[i], true)
            end
        end
    end
    TimerStart(CreateTimer(), 0.3, false, function () -- Hinding original Portrait HP Text by moving it offscreen
        DestroyTimer(GetExpiredTimer())
        local fh=BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_HP_TEXT, 0)
        BlzFrameClearAllPoints(fh)
        BlzFrameSetAbsPoint(fh, BOTTOM, 0.0, -0.24)
    end)
    -- etc...
This works perfectly.
Note that hiding the frame or changing its visibility tends to crash the game, thats why it is moved offscreen.
Before using custom HP bars, I was moving this text on top of the portrait.


Now I use the method of Hiding what I don't want, I don't hide origin frames as a whole as it can be messy at times.
Especially because it hides some parents of frames you want to control.
If the parents are not visible, neither will the children be. Might be the problem in your situation ?

EDIT :

BTW, remember you're in my map credits :D
 
Hmm, I did try grabbing the parent frames of the HP/Mana bars and unhiding those, but that didn't do anything either. I guess hiding the origin frames is just... not an options for HP/Mana? Crashes happen almost regardless of how I try to manipulate those.

EDIT: anyone knows which child frame(s) has the ping/fps display?
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
So far, IIRC, the only message frames we can access are : ORIGIN_FRAME_CHAT_MSG, ORIGIN_FRAME_UNIT_MSG and ORIGIN_FRAME_TOP_MSG.

About HP/Mana bars, you are referring to those just under hero icons, right ?
In all fairness as my player hero is invulnerable and don't use mana, I just hide them at map init.

I see no reason why they refuse to show again in your case if the parent is visible.
I am not sure you can change their parent, as using custom parents is another great way to show/hide some difficult frames.
 
Sorry, not the bar, I keep thinking of them as bars because that's what they were when Blizzard demo'd Reforged during blizz2018. The unit's health and mana under the portrait are what I'm talking about.

I've gone the other way and used the move-offscreen method, and while I'm able to reposition the HP/Mana frames, they disappear after I select a unit/click hero ability icon/etc. Strangely enough, my portrait frame is also not visible anymore? I think they're not repositioning properly since I'm using relative positioning (Abs pos on mana text frame, relative pos on hp text frame above mana, and relative pos of portrait above that one)

EDIT: OKAY, I think I know why it's borking. I've been trying to move it through FRAMEPOINT_TOP/BOTTOM instead of CENTER and that's basically what's been the issue. /sigh

EDIT2: It's moving, but it's off-center /reeeeeeeeeeeeeeeeeee
 
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Can you post a screen of your current UI ?

As I needed a custom HP bar, I ended up taking @Tasyen's HP Bar as a model, and thus using the Text subframe to display the value. I could not properly make the original HP test keep the color I wanted (it kept defaulting back to green!) . Using the same Frame Definition File, I could also add a countdown progress bar based on the same model.

To give you an idea, here is a screen of my current UI :

aMazingBallsTD UI.png


I don't know what brings the issues with your HP and Mana texts, but when I tried to use them, this is how I used to set its position centered inside the HP Bar, after the first selection mentioned in the post above :
Lua:
    TimerStart(CreateTimer(), 0.3, false, function () -- Positioning HP Text in HP Bar
        DestroyTimer(GetExpiredTimer())
        local fh=BlzGetOriginFrame(ORIGIN_FRAME_PORTRAIT_HP_TEXT, 0)
        BlzFrameClearAllPoints(fh)
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_CENTER, -0.07, 0.13)
    end)

And if I did not try to mess with the text color,it was staying there the whole time when needed.
As you can see it only uses the BOTTOM absolute point.
 
To explain what's happening in this image:
- All top bar elements have been moved to the bottom (sadly, that includes the ping / fps counters as well, which I'll need to eventually pop back up)
- I'm trying to resize the menu buttons but as you can see it has a different text when disabled, and another when highligted. I need to find a way to disable that, somehow. Ideally I would like to replace that with a small little icon, but I don't know how to get the tooltip highlight to show up still when doing that. I'm also not sure if I can open these specfic menus via trigger, so I'm sticking with the default ones for now.
- HP and Mana text aren't center-aligned with the portrait and it's really, really bothering me.
- Need to get rid of top-bar texture. Would like to be able to do that without imports, but I guess if there's nothing else to do, then I'll get to that eventually.
- Time of day mouse listener was moved to over the frame that says [0.515], but it's underneath the actual text child frame, which makes it so the tooltip disappears when I'm mousing over the actual texts. I'll need to find a way to move the mouse listener to an 'above' layer. This same technique, I'm assuming, is what I'll have to do to put a texture underneath all of this stuff to give it a background.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Ok.
Not in front of WE right now, but I seem to remember some things.
As for the menu buttons, I believe I changed their text from the Advanced - Game Interface. I believe there are fields for both enabled and highlighted, although probably not explicitly. You may want to dig in that menu because there are still things that need to be done with its help.
As for hiding the top bar texture, you indeed need to import a texture, but note that it can be very tiny. I think you can even use something like a fully transparent 16x16 texture or even smaller.
Same for the time indicator model, if you wanted to hide it. @Tasyen has found that we could access it with the new natives but the behavior seems to be problematic.
As far as I am concerned, I use my empty model and it works wonders.
I have attached both my "empty" model and texture for you to try them.

If you look at my screenshot, I have added textures (they were initially borders only, then I used the tooltip semi transparent texture inside.
If you just create the frame as child of Console UI as a Backdrop, it will tend to sit on top.

What I did to put it behind is use a common parent (that also helps hiding them altogether in situations like cinematic).
Here is the parent in question, related to the world frame :

Lua:
Parent=BlzCreateFrameByType("FRAME", "", BlzGetOriginFrame(ORIGIN_FRAME_WORLD_FRAME, 0),"", 0)

And here are my 2 bottom borders (recently changed them to global variables to hide or show them whenever required) :

Lua:
---
    -- Command buttons border
    ComBorderFh=BlzCreateFrameByType("BACKDROP", "CommandBorder", Parent, "", 0) -- Saving it in a global for later individual manipulation
    BlzFrameSetSize(ComBorderFh, 0.1745, 0.131)
    BlzFrameSetAbsPoint(ComBorderFh, BOTTOMLEFT, 0.218, 0.0)
    BlzFrameSetTexture(ComBorderFh, "ui\\commandbar-border.blp",0, true)
    -- InfoPanel border
    InfBorderFh=BlzCreateFrameByType("BACKDROP", "InfoPanelBorder", Parent, "", 0) -- Saving it in a global for later individual manipulation
    BlzFrameSetSize(InfBorderFh, 0.192, 0.12)
    BlzFrameSetAbsPoint(InfBorderFh, BOTTOMLEFT, 0.022, 0.0)
    BlzFrameSetTexture(InfBorderFh, "ui\\infopanel-border.blp",0, true)
---

So far the hiding of the command bar border (I should call it backdrop lol) happens when the player selects a unit he does not own, and the InfoPanel border is hidden when a unit is deselected or removed when it was selected by the player.

Now as for the mouse listener for the time of the day, I have 0 experience with that frame. But I suspect you could tinker by changing parents to make the listener come on top of the text frame, but as I have not tested it, don't take my word for it, not all frames accept to to to foster parents :D

Now the fact your HP and Mana texts refuse to center themselves puzzles me, could you post the code you are using with them ?

Hope this will help to partially solve your UI nightmares, believe me I know how it feels.

EDIT :
To get a better understanding of the parent child hierarchy, I would advise you to follow thats thread were @Tasyen gave extensive explanation with a sketch to clarify the confusion.
It was very instructive indeed :
UI - The concept of Parent-Frames
 

Attachments

  • EmptyTexture.blp
    788 bytes · Views: 58
  • EmptyModel.mdx
    1.8 KB · Views: 54
Last edited:
Aha! Fostering the Time Of Day listener framehandle on the little timer text worked nicely, thanks.

As far as the menu buttons go, I think I'll investigate if there's a way to replace them entirely. I don't want text or anything, I just want to turn them into little borderless icons, although I would like it if they retained the mouse listeners to show the tooltips.

What's the path for the imports?

Snippet for HP/Mana bit Wurstbin - The Wurst & Jass Pastebin
 
Level 12
Joined
Jan 30, 2020
Messages
875
Yes, if you want to gain full control of the buttons, your best bet is to use your own. For this I would recommend GLUEBUTTONs.
You'll find extended explanations in this tutorial by @Tasyen : UI: GLUEBUTTON

As for the empty model and texture, the path is up to you, just browse for them in the Advanced - Game Interface menu in the dedicated fields.

I am not sure positioning the HP/Mana text relative to the portrait is a good idea. Try this and let me know if it works :
Code:
    HP_FRAME
       ..clearAllPoints()
       ..SetAbsPoint(FRAMEPOINT_CENTER, xHP, yHP)
    MANA_FRAME
       ..clearAllPoints()
       ..SetAbsPoint(FRAMEPOINT_CENTER, xMana, yMana)

You'll have to find out the x and y for HP and Mana text though. But I am confident it will place them properly and centered.

Good luck !
 
So it appears that the text frames were not centering properly because their center point is actually either to the right or left... so I had to move them half their width + half the portrait's width. I'm still a bit bamboozled by this but it looks like it's working. Absolute positioning was necessary, so thank you again!
 
Status
Not open for further replies.
Top