• 🏆 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!

UI: 3x3 Custom Unit Stats

That was requested on hive discord. Its an custom UI that "hides" the default unit infos and instead shows a 3 x 3 custom stats grid. It is written in Lua (not requested but no one said otherwise). The Custom stats do also support showing Tooltips and each of the frames can be changed during the game.
CustomStat Hero 3x3.jpg


Lua:
--[[
CustomStatFrame 1.2a
By Tasyen
That are the stuff one uses to access the customstat frames.
    CustomStatFrames.BoxS a parent for simpleframes hide it to easyly hide all of them
    CustomStatFrames.BoxF parent of the frames
    CustomStatFrames[index].Frame
    CustomStatFrames[index].FrameIcon = The icon of that CustomStat
    CustomStatFrames[index].FrameText = The Text of that CustomStat
    CustomStatFrames[index].FrameHover = Handle the mousehovering
    CustomStatFrames[index].ToolTipBox = The box of the tooltip
    CustomStatFrames[index].ToolTipTitle = The Title of the tooltip
    CustomStatFrames[index].ToolTipText = The text of the tooltip

--]]

CustomStatFrames = {Count = 0}
CustomStatSelectedUnit = {}
function CustomStatAdd(icon, text)
    CustomStatFrames.Count = CustomStatFrames.Count + 1
  
    local fh = BlzCreateSimpleFrame("CustomStat", CustomStatFrames.BoxS, CustomStatFrames.Count)
    local tooltipBox = BlzCreateFrame("BoxedText", CustomStatFrames.BoxF,0, CustomStatFrames.Count)
    local fhHover = BlzCreateFrameByType("FRAME", "CustomStatHover", CustomStatFrames.BoxF, "", CustomStatFrames.Count)
  
    BlzFrameSetPoint(fhHover, FRAMEPOINT_BOTTOMLEFT, fh, FRAMEPOINT_BOTTOMLEFT, 0, 0)
    BlzFrameSetPoint(fhHover, FRAMEPOINT_TOPRIGHT, BlzGetFrameByName("CustomStatText", CustomStatFrames.Count), FRAMEPOINT_TOPRIGHT, 0, 0)
    BlzFrameSetTooltip(fhHover, tooltipBox) --when faceHover is hovered with the mouse frame tooltip becomes visible.
  
    BlzFrameSetAbsPoint(tooltipBox, FRAMEPOINT_BOTTOM, 0.6, 0.2)
    BlzFrameSetSize(tooltipBox, 0.15, 0.08)

    BlzFrameSetText(BlzGetFrameByName("CustomStatText", CustomStatFrames.Count), text)
    BlzFrameSetText(BlzGetFrameByName("BoxedTextTitle", CustomStatFrames.Count), "TooltipTitle")
    BlzFrameSetText(BlzGetFrameByName("BoxedTextValue", CustomStatFrames.Count), text)
    BlzFrameSetTexture(BlzGetFrameByName("CustomStatIcon", CustomStatFrames.Count), icon, 0, true)
  
  
    --Dissallow having the TEXT-Tooltip-Frames gaining mouse control. This might produce problems, if the TEXT Frame has an disabled Color.
    BlzFrameSetEnable(BlzGetFrameByName("BoxedTextValue", CustomStatFrames.Count), false)
    BlzFrameSetEnable(BlzGetFrameByName("BoxedTextTitle", CustomStatFrames.Count), false)

    --[[ 4x3
    if CustomStatFrames.Count == 1 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.08)
    elseif CustomStatFrames.Count == 5 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.375, 0.08)
    elseif CustomStatFrames.Count == 9 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.435, 0.08)
    else
        BlzFrameSetPoint(fh, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("CustomStat", CustomStatFrames.Count - 1), FRAMEPOINT_BOTTOMLEFT, 0, 0)
    end
    --]]
    --[[ 4x4
    if CustomStatFrames.Count == 1 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.08)
    elseif CustomStatFrames.Count == 5 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.36, 0.08)
    elseif CustomStatFrames.Count == 9 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.41, 0.08)
    elseif CustomStatFrames.Count == 13 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.46, 0.08)
    else
        BlzFrameSetPoint(fh, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("CustomStat", CustomStatFrames.Count - 1), FRAMEPOINT_BOTTOMLEFT, 0, 0)
    end
    --]]

    --3x3
    if CustomStatFrames.Count == 1 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.08)
    elseif CustomStatFrames.Count == 4 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.375, 0.08)
    elseif CustomStatFrames.Count == 7 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.435, 0.08)
    else
        BlzFrameSetPoint(fh, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("CustomStat", CustomStatFrames.Count - 1), FRAMEPOINT_BOTTOMLEFT, 0, -0.005)
    end
  
    --save into table
    CustomStatFrames[CustomStatFrames.Count] = {}
    CustomStatFrames[CustomStatFrames.Count].Frame = fh
    CustomStatFrames[CustomStatFrames.Count].FrameIcon = BlzGetFrameByName("CustomStatIcon", CustomStatFrames.Count)
    CustomStatFrames[CustomStatFrames.Count].FrameText = BlzGetFrameByName("CustomStatText", CustomStatFrames.Count)
    CustomStatFrames[CustomStatFrames.Count].FrameHover = fhHover
    CustomStatFrames[CustomStatFrames.Count].ToolTipBox = tooltipBox
    CustomStatFrames[CustomStatFrames.Count].ToolTipTitle = BlzGetFrameByName("BoxedTextTitle", CustomStatFrames.Count)
    CustomStatFrames[CustomStatFrames.Count].ToolTipText = BlzGetFrameByName("BoxedTextValue", CustomStatFrames.Count)
end

function CustomStatUpdate()
    local isVisible = BlzFrameIsVisible(CustomStatFrames.BoxS)
  
    BlzFrameSetText(CustomStatFrames[1].FrameText, BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 0))) --copy normal damage text
    BlzFrameSetText(CustomStatFrames[2].FrameText, BlzFrameGetText(BlzGetFrameByName("InfoPanelIconValue", 2))) --copy normal armor field
  
    BlzFrameSetText(CustomStatFrames[6].FrameText, GetUnitMoveSpeed(CustomStatSelectedUnit[GetLocalPlayer()]))
    BlzFrameSetVisible(CustomStatFrames.BoxF, BlzFrameIsVisible(CustomStatFrames.BoxS))
end

function CustomStatInit()

    --move the default unit infos out of the screen and scale it down
    local function CustomStatMoveOutOfScreen(frame)
        BlzFrameClearAllPoints(frame)
        BlzFrameSetAbsPoint(frame, FRAMEPOINT_CENTER, 3, 0)
    end
    for index = 0, 5, 1 do
        CustomStatMoveOutOfScreen(BlzGetFrameByName("InfoPanelIconBackdrop", index))      
    end
    CustomStatMoveOutOfScreen(BlzGetFrameByName("InfoPanelIconHeroIcon", 6))
    CustomStatMoveOutOfScreen(BlzGetFrameByName("InfoPanelIconAllyTitle", 7))
    CustomStatMoveOutOfScreen(BlzGetFrameByName("InfoPanelIconAllyGoldIcon", 7))

    --Create a Selection Trigger thats a pretty naive approach.
    local trig = CreateTrigger()
    TriggerAddAction(trig, function () CustomStatSelectedUnit[GetTriggerPlayer()] = GetTriggerUnit() end)
  
    local index = 0
    repeat
        TriggerRegisterPlayerSelectionEventBJ(trig, Player(index), true)
        --TriggerRegisterPlayerSelectionEventBJ(trig, Player(index), false)
        index = index + 1
    until index == bj_MAX_PLAYER_SLOTS


    --create a Parent for the Stats so you can easyly hide/show them-
    --Stats use SimpleUnitStatsPanel as parent that way they are only shown when an unit is selected which does not build.
    CustomStatFrames.BoxS = BlzCreateFrameByType("SIMPLEFRAME", "CustomStatFrames.BoxSBoss", BlzGetFrameByName("SimpleUnitStatsPanel",0), "", 0)
    CustomStatFrames.BoxF = BlzCreateFrameByType("FRAME", "CustomStatFrames.BoxFBoss", BlzGetOriginFrame(ORIGIN_FRAME_GAME_UI, 0), "", 0)
  
  
  
    BlzLoadTOCFile("war3mapimported\\CustomStat.toc")
    BlzLoadTOCFile("war3mapimported\\BoxedText.toc")
  
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test1")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test2")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test3")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test4")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test5")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test6")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test7")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test8")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test9")
    --[[
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test10")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test11")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test12")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test13")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test14")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test15")
    CustomStatAdd(udg_UnitStatIcon[CustomStatFrames.Count + 1],"Test16")
    --]]
    TimerStart(CreateTimer(), 0.1, true, CustomStatUpdate)
    print("Done")
end


Changes:
V1.3 Fixed Hover for newest Warcraft 3.
V1.2b Added turn off able GUI support. changed the hidding approach.
V1.2a The Tooltip TEXT Frames are now disabled with thay they can not fight for mouse control anymore.
V1.2 The default UnitInfos are now moved using their Icon instead of the container Frame. Does not scale them down anymore.
 

Attachments

  • Custom Stats Lua V1.2a.w3x
    23.5 KB · Views: 148
  • Custom Stats vjass V1.2b.w3x
    25.9 KB · Views: 52
  • Custom Stats Lua V1.3.w3x
    23 KB · Views: 3
  • Custom Stats vjass V1.3.w3x
    25.6 KB · Views: 2
Last edited:
Level 1
Joined
Jul 8, 2019
Messages
2
Hey Tasyen, I just made an account to thanks you for your amazing job.
That's exactly what I was looking for. Keep going !

If you have time I would love a JASS version I struggle trying to convert it in JASS.
 
Last edited:
wow dude keep up the awesome UI stuff! If I may ask...why isn't this uploaded in the spell section?

I'll wait for an answer before I bring down the storm of questions I have for this :p
Somehow I think its not ready for the spellsection. Although it should work as it is but misses in my opinion a nice hook in feature, currently one has to change the function CustomStatUpdate to show custom data.
 
That is bad, then it might crash probably for other people, too.
You could try changing that line maybe this line inside "function MouveOutOfScreen" is more dangerous then I thought. It moves the default unit info ui out of the screen.
Lua:
BlzFrameSetAbsPoint(fh, FRAMEPOINT_CENTER, 3, 3)
->
BlzFrameSetAbsPoint(fh, FRAMEPOINT_CENTER, 0, 0)
 
Level 1
Joined
Jan 23, 2015
Messages
160
I'm gonna do some tinkering with this, looks amazing. Was hoping to make a quasi-stat for an "endurance" system, this might be it. Bit late to this party but I wanted to not replace the entire interface but just add 1x1 stat grid, that would be on every unit instead of just hero units.
 
First you would have to thinker were you place your new stat on the screen.
After that you have to choose how to display the new stat, should it looke like Agi/Str/Int fields (only text), like Attack/Armor infos, like the custom fields of this system or be a bar that is filled to x% or something other fancy.

You have to save your values unit based, so you can display them when only that unit selected.
Create the frames needed to display the custom stat.

In this post the shown way to detect the current selected unit is quite naive, but works in single selection gameplay.
Might be better with something like this:
Code:
SelectedUnitsGroup = CreateGroup() --should not be destroyed
TimerStart(CreateTimer(), 0.1, true, function()
   
    if BlzFrameIsVisible(BlzGetFrameByName("SimpleUnitStatsPanel",0)) then --Single unit  selection unit stats frame shown
        GroupEnumUnitsSelected(SelectedUnitsGroup, GetLocalPlayer(), nil) --get the units selected of the local player
        local unit = FirstOfGroup(SelectedUnitsGroup) --only the first one we need
        if unit then --unit is not null
            -- do something that can be async, like changing text of frames icons....
        end
    end
end)
 
Level 1
Joined
Jan 23, 2015
Messages
160
Taysen!

This is a bit off but I couldn't use tags in profile msg, sry.

You helped me with a mana = stamina system and I am now trying to further develop it. Whereas I was using Mana, now I am using custom UI "Stamina" from your now deprecated system "MyResources" which I found more convenient for my needs.

I need help! I'm trying to configure the same mana loss equation to the new UI Stamina but it isn't working. Attached a map, the only thing that doesn't matter is "Equipment" triggers at top.

I got the Temperature and Fatigue to work, I am certain I can get the other fields, but "Stamina" field is very stubborn. Hope you have time, and sorry this wasn't in a better spot

-Edit 1: Tried to clean up

-Edit 2: So, so sorry, I connected too many systems through variables.

*
Only have to worry about (to the best of my ability)

-------- My Resource bar


>Demo >ResourceBar Changes >Stamina Change

The stuff under >Stamina Change is all I'm worried about. Sorry for bad navigation!
 

Attachments

  • Taysen Systems Help.w3x
    1.4 MB · Views: 96
Last edited:
UDex is used inside the if - condition, but not set in the same trigger.
Set UDex = (Custom value of (Picked unit))​

Same with PlayerIndex, has to be set to the correct index before writing onto that array otherwise -> some random players get displayed the values.
Set PlayerIndex = (100 x (Player number of (Owner of (Picked unit))))

But, you should ask such map specific questions not directly connected to this Thread, in a pm/conversation or in a trigger Help Thread.
 
Level 1
Joined
Jan 23, 2015
Messages
160
UDex is used inside the if - condition, but not set in the same trigger.
Set UDex = (Custom value of (Picked unit))​

Same with PlayerIndex, has to be set to the correct index before writing onto that array otherwise -> some random players get displayed the values.
Set PlayerIndex = (100 x (Player number of (Owner of (Picked unit))))

But, you should ask such map specific questions not directly connected to this Thread, in a pm/conversation or in a trigger Help Thread.

I promise I meant to do a DM, I couldn't get my upload or link tags to work when I tried DM preview. Thank you so much
 
Level 3
Joined
Oct 13, 2017
Messages
10
Sadly first 3 vertical icons (top ones) and sometimes 2-nd vertical icons have non-stop blinking tooltip when I hover a mouse on them, can't find what I did wrong. Can you help me figure it out?
vJass version btw
 
Last edited:
Can't say anything about your code without seeing, But I think the error comes from my inefficent removage of the default Unit Infos (attack, armor, etc). The default Unit Infos are reposed quite often, when such an repos happens the default Unit Info can take over the Control of that place.
Some time after I published this I found out that when one moves the UnitInfo Icons directly and clears their points the reposing stops.

But I kinda think it was a misstake to remove attack and armor Unit Info, it probably would be better to compress them.

Uploaded: V1.2 moving the Icons or the ones being relative to the container instead of the containers.
 
Last edited:
Oh you are right that also could be the problem. The tooltip text can take the mouse focus which would hide itself (cause it is a tooltip only visible when a specific frame is hovered with the mouse) with that the owner would regain the focus making the tooltip visible again, that repeats and would produce that blinking. Solution either move the tooltip away or one disables the texts can be done inside the code or in fdf.

I think that happen to me aswell when I was doing some tests with some other UI-Frame-System.
 
After all of them were created you use a GetLocalPlayer code which hides
CustomStat[3 to 9]
JASS:
if GetLocalPlayer() != Player(0) then
    call BlzFrameSetVisible(CustomStatFrame_Frame[3], false)
    call BlzFrameSetVisible(CustomStatFrame_Frame[4], false)
    call BlzFrameSetVisible(CustomStatFrame_Frame[5], false)
    call BlzFrameSetVisible(CustomStatFrame_Frame[6], false)
    call BlzFrameSetVisible(CustomStatFrame_Frame[7], false)
    call BlzFrameSetVisible(CustomStatFrame_Frame[8], false)
    call BlzFrameSetVisible(CustomStatFrame_Frame[9], false)
endif
 
Level 4
Joined
Nov 4, 2019
Messages
47
Tasyen, thank you!

And one more question. How do I make the standard formula damage reduction in the armor tooltip?

I tried to count the armor when selecting a unit in the GUI and changed the description in the "update" function, but in the end an empty string in the tooltip.
 
Last edited:
It might be smarter to use the default Armor frame. But that might not be what you want. To change the displayed Text inside the tooltip one has to write to this frames

Lua
CustomStatFrames[index].FrameText
CustomStatFrames[index].ToolTipTitle

vjass
but wait you use the jass version
CustomStatFrame_ToolTipText[index]
CustomStatFrame_ToolTipTitle[index]


If one wants to use Attack 1 and Armor.
One reposing the Attack 1 which is frame 0 and Armor which is info 2 after the custom stat frames where created.
Lua:
--repos Attack 1
    local fh = BlzGetFrameByName("InfoPanelIconBackdrop", 0)
    BlzFrameSetText(BlzGetFrameByName("InfoPanelIconLabel", 0), "")
    BlzFrameClearAllPoints(BlzGetFrameByName("InfoPanelIconValue", 0))
    BlzFrameSetPoint(BlzGetFrameByName("InfoPanelIconValue", 0), FRAMEPOINT_LEFT, fh, FRAMEPOINT_RIGHT, 0, 0)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.08)
    BlzFrameSetSize(fh, 0.015, 0.015)

    --repos Armor
    fh = BlzGetFrameByName("InfoPanelIconBackdrop", 2)
    BlzFrameSetText(BlzGetFrameByName("InfoPanelIconLabel", 2), "")
    BlzFrameClearAllPoints(BlzGetFrameByName("InfoPanelIconValue", 2))
    BlzFrameSetPoint(BlzGetFrameByName("InfoPanelIconValue", 2), FRAMEPOINT_LEFT, fh, FRAMEPOINT_RIGHT, 0, 0)
    BlzFrameClearAllPoints(fh)
    BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.06)
    BlzFrameSetSize(fh, 0.015, 0.015)

Second one has to change the posing of the custom stats a bit.
Lua:
--3x3
    if CustomStatFrames.Count == 1 then
        --BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.08)
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.31, 0.04)
    elseif CustomStatFrames.Count == 2 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.375, 0.08)
    elseif CustomStatFrames.Count == 5 then
        BlzFrameSetAbsPoint(fh, FRAMEPOINT_TOPLEFT, 0.435, 0.08)
    else
        BlzFrameSetPoint(fh, FRAMEPOINT_TOPLEFT, BlzGetFrameByName("CustomStat", CustomStatFrames.Count - 1), FRAMEPOINT_BOTTOMLEFT, 0, -0.005)
    end
 
Top