• 🏆 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] MVP Selection + Fullscreen Scoreboard

Status
Not open for further replies.
Level 14
Joined
Feb 7, 2020
Messages
387
This system is somewhat rudimentary but could be useful for others who do not wish to reinvent the wheel. It requires implementing your own metric tracking; if you want to add or track different things, you'll have to dive into the code to reconfigure it as desired. I tried to keep it clean and sensible.

In a nutshell, it is a score-keeping system, an MVP selector, and a final metrics page for the end of the game. It currently only generates and shows once when initialized, though it could be easily reconfigured to be a permanent leaderboard (I do not plan on pursuing this as it does not fit my selfish use case and I am over frame natives for a while).

Did a single test in multiplayer to make sure it doesn't desync.

edit 6/1/20 v1.1:
- removed local player code and made the leaderboard generation simpler.

WC3ScrnShot_053020_073954_001.png WC3ScrnShot_053020_074412_001.png

Lua:
-- system data (do not change)
local mvp                       = {}
local mvp_data                  = {}
mvp_data.playerName             = {} -- optional var; store original player names to prevent color code overload.
mvp_data.x                      = GetRectCenterX(gg_rct_mapCenter)
mvp_data.y                      = GetRectCenterY(gg_rct_mapCenter)
mvp_data.pDmg                   = {} -- score: track damage.
mvp_data.pHealing               = {} -- score: track healing.
mvp_data.pAbsorbed              = {} -- score: track damage absorbed.
mvp_data.pElim                  = {} -- score: track eliminations.
mvp_data.pDeath                 = {} -- score: track deaths.
mvp_data.pExp                   = {} -- score: track experience earned.
mvp_data.pObjCap                = {} -- score: track objective participation.
mvp_data.pClutch                = {} -- score: track clutch events (e.g. a life-saving heal).
mvp_data.weight                 = {} -- initiate score weights.
mvp_data.pScore                 = {} -- a player's score.
-- score config:
mvp_data.weight.damage          = 0.50   -- how much value a damage point is worth.
mvp_data.weight.absorb          = 1.15   -- how much value a damage point is worth.
mvp_data.weight.healing         = 0.90   -- how much value a healing point is worth.
mvp_data.weight.experience      = 5.00   -- how much value earned experience is worth.
mvp_data.weight.deathFactor     = 0.98   -- multiply the final score for a penalty per death.
mvp_data.weight.elimFactor      = 1.02   -- multiply the final score for a bonus per elimination.
mvp_data.weight.clutchFactor    = 1.01   -- multiply the final score for a bonus per clutch event (e.g. healing a very low health ally).
mvp_data.weight.objFactor       = 1.10   -- multiply the final score for a bonus per completed event (e.g. near an objective capture).
-- system config:
mvp_data.maxPlayers             = 10     -- how many players exist.
mvp_data.sepDist                = 120.0  -- how far aparts heroes are.
mvp_data.camDist                = 1233.0 -- how far the camera angle is.
mvp_data.camRot                 = 315.0  -- how the camera is angled.
mvp_data.teamDist               = 575.0  -- starting distance of each team row from x,y center.
mvp_data.mvpNudge               = 125.0  -- how far the MVP hero is pushed forward.
mvp_data.queuedDelay            = 1.5    -- the delay before the MVP system initiates.
mvp_data.enterDelay             = 0.25   -- how fast each hero enters the MVP formation.
mvp_data.selectDelay            = 2.55   -- the delay before an MVP is chosen.
mvp_data.lbDelay                = 2.55   -- the delay before the scoreboard shows.
mvp_data.teamColor              = true   -- enable to set heroes to matching colors for team clarity.
mvp_data.mvpColor               = true   -- reverts the selected MVP back to their original color for individual player identification.
mvp_data.teamIntSplit           = 6      -- the first player number (1-based) of team B (e.g. in a 5v5, typically is player 6).
mvp_data.backdropAlpha          = 190    -- transparency of leaderboard background.
mvp_data.teamColorA             = PLAYER_COLOR_MAROON -- team A color if teamColor is enabled.
mvp_data.teamColorB             = PLAYER_COLOR_NAVY   -- team B color if teamColor is enabled.
mvp_data.mvpAnim                = 'stand victory'     -- played animation for selected MVP.
mvp_data.effectMVP1             = 'Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl'
mvp_data.effectMVP2             = 'Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl'
mvp_data.enterEffect            = 'Abilities\\Spells\\Undead\\DarkRitual\\DarkRitualTarget.mdl'
mvp_data.textureMain            = 'war3mapImported\\ui_black_colorizer.tga'
mvp_data.teamTxtColorA          = '|cffff2c2c'
mvp_data.teamTxtColorB          = '|cff007dff'
mvp_data.titleColor             = '|cff00ffff'
mvp_data.mvpTxtC                = '|cffffff00'
mvp_data.showHideBtn            = {}
mvp_data.titles = { -- column headers
    [1] = 'Hero',
    [2] = 'Player',
    [3] = 'EXP Gained',
    [4] = 'Obj. Caps',
    [5] = 'Kills',
    [6] = 'Deaths',
    [7] = 'Damage',
    [8] = 'Healing',
    [9] = 'Absorbs',
    [10] = 'Clutch Spells',
    [11] = 'Score'
}
mvp_data.textColors = { -- color of the text in each column
    [1] = '|cffffffff',
    [2] = '|cffffffff',
    [3] = '|cffff00ff',
    [4] = '|cffffffff',
    [5] = '|cffffffff',
    [6] = '|cffffffff',
    [7] = '|cffff8505',
    [8] = '|cff5aff5a',
    [9] = '|cff00b1ff',
    [10] = '|cff8080ff',
    [11] = '|cffffaf00'
}
-- col:
-- :: hero icon | player name | exp earned | objcap | elims | deaths | dmg | healing | absorbs | clutch events | final score
-- rows:
-- :: Hero | Player | EXP | Obj Completed | Eliminations | Deaths | Damage Done | Healing Done | Damage Absorbed | Clutch Spells | Score
-- :: [player 1]
-- ::   . . .
-- :: [player 10]


-- set default values
function mvp.Init()
    Preload(mvp_data.enterEffect)
    Preload(mvp_data.effectMVP1)
    Preload(mvp_data.effectMVP2)
    for pInt = 1,mvp_data.maxPlayers do -- 1-based to match GUI's player index
        mvp_data.pDmg[pInt]           = 0
        mvp_data.pHealing[pInt]       = 0
        mvp_data.pAbsorbed[pInt]      = 0
        mvp_data.pClutch[pInt]        = 0
        mvp_data.pObjCap[pInt]        = 0
        -- if undesired, delete this and handle populated leaderboard names yourself:
        mvp_data.playerName[pInt]     = GetPlayerName(Player(pInt-1))
    end
end


-- queue mvp screen
function mvp.Initiate()
    -- remove units in center of map:
    mvp.ClearArea()
    -- setup camera and heroes:
    for pInt = 1,mvp_data.maxPlayers do -- 1-based
        mvp.ShowHero(pInt, false)
        mvp.SetCameraTimer(pInt)
        -- revive heroes if dead then move them
        if udg_playerHero[pInt] then
            -- if dead, revive:
            if not IsUnitAliveBJ(udg_playerHero[pInt]) then
                ReviveHero(udg_playerHero[pInt],-1000,800,false)
            end
            -- re-initiate freeze just in case
            PauseUnit(udg_playerHero[pInt],true)
            SetUnitInvulnerable(udg_playerHero[pInt],true)
            if mvp_data.teamColor and pInt < mvp_data.teamIntSplit then
                SetUnitColor( udg_playerHero[pInt], mvp_data.teamColorA )
             elseif mvp_data.teamColor then
                SetUnitColor( udg_playerHero[pInt], mvp_data.teamColorB )
            end
            -- get scores:
            mvp_data.pScore[pInt] = mvp.CalculateScore(pInt)
        end
    end
    -- begin timer to place heroes:
    local xOffset, yOffset, distance, offset, angle, pInt = 0.0, 0.0, mvp_data.teamDist, -mvp_data.sepDist, 180.0, 1
    TimerStart(NewTimer(),mvp_data.queuedDelay,false,function() -- do a short wait
        TimerStart(NewTimer(),mvp_data.enterDelay,true,function()
            -- flip projection angle for team B, moving them right of x,y instead of left.
            if pInt == mvp_data.teamIntSplit then
                angle = 360.0
                distance = mvp_data.teamDist
            end
            xOffset, yOffset = PolarProjectionXY(mvp_data.x, mvp_data.y, distance, angle)
            if udg_playerHero[pInt] then
                mvp.ShowHero(pInt, true)
                UnitRemoveBuffs(udg_playerHero[pInt], true, true)
                SetUnitX(udg_playerHero[pInt],xOffset)
                SetUnitY(udg_playerHero[pInt],yOffset)
                SetUnitFacing(udg_playerHero[pInt],270.0)
                SetUnitPathing(udg_playerHero[pInt],false)
                SetUnitLifePercentBJ(udg_playerHero[pInt], 100)
                DestroyEffect(AddSpecialEffect(mvp_data.enterEffect,xOffset,yOffset))
            end
            distance = distance + offset
            pInt = pInt + 1
            if pInt > mvp_data.maxPlayers then
                -- delay before an MVP is chosen:
                TimerStart(NewTimer(),mvp_data.selectDelay,false,function()
                    mvp_data.mvpWinner = mvp.SelectMVP()
                    TimerStart(NewTimer(),mvp_data.lbDelay,false,function()
                        mvp.ScoreboardGenerate()
                        ReleaseTimer()
                    end)
                    ReleaseTimer()
                end)
                ReleaseTimer()
            end
        end)
    end)
end


-- run an algorithm to calculate player's score; returns score
function mvp.CalculateScore(pInt) -- pass in 1-based value
    -- for a copy paste solution, enable to map existing score variables:
    -- mvp.MapVariableValues()

    local score           = 0
    local h               = mvp_data.pHealing[pInt]  * mvp_data.weight.healing
    local d               = mvp_data.pDmg[pInt]      * mvp_data.weight.damage
    local a               = mvp_data.pAbsorbed[pInt] * mvp_data.weight.absorb
    local e               = mvp_data.pExp[pInt]      * mvp_data.weight.experience

    score = h + d + a + e

    -- bonuses:
    if mvp_data.pElim[pInt] > 0 then
        score = score + (score * (mvp_data.weight.elimFactor-1)   * mvp_data.pElim[pInt])
    end
    if mvp_data.pClutch[pInt] > 0 then
        score = score + (score * (mvp_data.weight.clutchFactor-1) * mvp_data.pClutch[pInt])
    end
    if mvp_data.pObjCap[pInt] > 0 then
        score = score + (score * (mvp_data.weight.objFactor-1)    * mvp_data.pObjCap[pInt])
    end
    -- penalties:
    if mvp_data.pDeath[pInt] > 0 then
        score = score - (score * ((1-mvp_data.weight.deathFactor) * mvp_data.pDeath[pInt]))
    end

    score = math.floor(score)

    return score
end


-- get the highest score of the winning team.
function mvp.SelectMVP()
    local pIntWinner = 1
    local pIntHighScore = 0 -- default a score comparison.
    for pInt = 1,10 do -- loop through every player and compare scores, shuffling winner value based on highest score.
        if mvp_data.pScore[pInt] > pIntHighScore then -- if highest value is beat, set the temporary winner of the sort loop.
            pIntWinner    = pInt
            pIntHighScore = mvp_data.pScore[pInt] -- set the new highest value to beat.
        end
    end
    -- run mvp effects:
    local x,y = PolarProjectionXY(GetUnitX(udg_playerHero[pIntWinner]),GetUnitY(udg_playerHero[pIntWinner]),mvp_data.mvpNudge,270.0)
    if mvp_data.mvpColor then
        SetUnitColor( udg_playerHero[pIntWinner], GetPlayerColor( Player(pIntWinner-1) ) )
    end
    SetUnitX(udg_playerHero[pIntWinner], x)
    SetUnitY(udg_playerHero[pIntWinner], y)
    DestroyEffect(AddSpecialEffect(mvp_data.effectMVP1, x, y))
    DestroyEffect(AddSpecialEffect(mvp_data.effectMVP2, x, y))
    SetUnitAnimation(udg_playerHero[pIntWinner], mvp_data.mvpAnim)
    ClearTextMessages()
    DisplayTextToForce( GetPlayersAll(), "|cff00b1ffMVP:|r " .. GetPlayerName( Player(pIntWinner-1) ) .. '|cff00b1ff!|r')
    return pIntWinner
end


-- set up the camera
function mvp.SetCamera(pInt) -- pass in 1-based value
    PanCameraToTimedForPlayer( Player(pInt-1), mvp_data.x, mvp_data.y, 0.0 )
    SetCameraFieldForPlayer( Player(pInt-1), CAMERA_FIELD_TARGET_DISTANCE, mvp_data.camDist, 0.33 )
    SetCameraFieldForPlayer( Player(pInt-1), CAMERA_FIELD_ANGLE_OF_ATTACK, mvp_data.camRot, 0.33 )
end


-- run a timer to keep camera fixed in place
function mvp.SetCameraTimer(pInt)
    TimerStart(NewTimer(),0.03,true,function()
        if GetLocalPlayer() == Player(pInt-1) then
            mvp.SetCamera(pInt)
        end
    end)
end


-- show the UI component
-- @bool = true for show, false for hide
function mvp.ScoreboardShow(bool)
    BlzFrameSetVisible(mvp_data.leaderboard_bd, bool)
end


-- create the UI component
-- this should only be called after players have chosen their heroes
function mvp.ScoreboardGenerate()
    local lb_x          = 0.84
    local lb_y          = 0.36
    local lb_xpad       = 0.03
    local lb_ypad       = 0.01
    local lb_yoffset    = 0.058
    local count_rows    = 11
    local count_cols    = 11
    local cell_width    = lb_x/count_rows*0.96
    local cell_height   = lb_y/count_cols
    local cellPadding   = cell_width*0.04
    local lb_xnudge     = cell_width*0.24 -- move the lb contents to the left slightly
    local yOffset       = 0.0
    local xOffset       = 0.0
    local isIcon        = false
    local concatLength  = 17 -- limit player name length to prevent overlap/ugliness
    local fh                 -- temp handle for readability
    local teamC              -- team color text
    FloorPlayerScoreValues(pInt) -- flatten values

    mvp_data.lb_table = {}
    mvp_data.leaderboard_bd = mvp.AttachBackdropByHandle(mvp_data.gameUI, 'leaderboard_bd',
        mvp_data.textureMain, mvp_data.backdropAlpha, 0.0, 0.0, lb_x, lb_y)
    BlzFrameClearAllPoints(mvp_data.leaderboard_bd)

    for col = 1,count_cols do -- columns
        mvp_data.lb_table[col] = {}
        xOffset = mvp.ScoreboardOffsetCalcX(col, cell_width, lb_xnudge + cellPadding)

        for row = 1,count_rows do -- rows
            -- player name color text:
            if row < mvp_data.teamIntSplit+1 then -- team A
                teamC = mvp_data.teamTxtColorA
            else -- team B
                teamC = mvp_data.teamTxtColorB
            end

            mvp_data.lb_table[col][row] = {}
            yOffset = mvp.ScoreboardOffsetCalcY(row, cell_height)
            if row > 1 and math.fmod(row,2) == 0 then
                mvp_data.lb_table[col][row].bd = mvp.AttachBackdropByHandle(mvp_data.leaderboard_bd, 'leaderboard_bd_row-'..row,
                    mvp_data.textureMain, 33, 0.0, 0.0, xOffset, yOffset)
            end

            if row == 1 then -- create headers
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.titles[col])
            elseif col == 1 then -- hero icon
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "BACKDROP")
                BlzFrameSetTexture(fh, udg_mbHeroIcon[row-1], 0, true) -- sub 1 for header row
                isIcon = true -- flag to size differently
            elseif col == 2 then -- player name
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                if mvp_data.mvpWinner == row-1 then -- mvp text color
                    BlzFrameSetText(fh, mvp_data.mvpTxtC .. 'MVP:|r ' .. teamC .. string.sub(mvp_data.playerName[row-1],1,concatLength-5) .. '|r')
                else
                    BlzFrameSetText(fh, teamC .. string.sub(mvp_data.playerName[row-1],1,concatLength) .. '|r') -- sub 1 for header row
                end
            elseif col == 3 then -- exp
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pExp[row-1])
            elseif col == 4 then -- obj
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pObjCap[row-1])
            elseif col == 5 then -- elims
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pElim[row-1])
            elseif col == 6 then -- deaths
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pDeath[row-1])
            elseif col == 7 then -- dmg
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pDmg[row-1])
            elseif col == 8 then -- healing
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pHealing[row-1])
            elseif col == 9 then -- absorbs
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pAbsorbed[row-1])
            elseif col == 10 then -- clutch spells
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                BlzFrameSetText(fh, mvp_data.pClutch[row-1])
            elseif col == 11 then -- score
                fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")
                if mvp_data.mvpWinner == row-1 then -- mvp text color
                    BlzFrameSetText(fh, mvp_data.mvpTxtC .. mvp_data.pScore[row-1] .. '|r')
                else
                    BlzFrameSetText(fh, mvp_data.pScore[row-1])
                end
            end

            if col == 1 and row ~= 1 then -- hero icon move
                BlzFrameSetPoint(fh,FRAMEPOINT_TOPLEFT,mvp_data.leaderboard_bd,FRAMEPOINT_TOPLEFT,
                    xOffset+(cell_width/2)-(cell_height/2)+cellPadding,yOffset)
            else -- standard column
                BlzFrameSetPoint(fh,FRAMEPOINT_TOPLEFT,mvp_data.leaderboard_bd,FRAMEPOINT_TOPLEFT,xOffset,yOffset)
            end
            if isIcon then
                BlzFrameSetSize(fh,cell_height - cellPadding, cell_height - cellPadding)
                isIcon = false
            else
                BlzFrameSetSize(fh,cell_width, cell_height)
            end
            BlzFrameSetVisible(fh, true)
            BlzFrameSetAlpha(fh, 255)
            BlzFrameSetTextSizeLimit(fh, 1)
            if row == 1 then -- title color
                BlzFrameSetText(fh, mvp_data.titleColor .. BlzFrameGetText(fh) .. '|r')
            elseif col ~= 1 then -- row color
                BlzFrameSetText(fh, mvp_data.textColors[col] .. BlzFrameGetText(fh) .. '|r')
            end
            BlzFrameSetTextAlignment(fh, TEXT_JUSTIFY_CENTER, TEXT_JUSTIFY_CENTER)

        end
    end

    BlzFrameSetSize(mvp_data.leaderboard_bd, lb_x + lb_xpad, lb_y + lb_ypad)
    BlzFrameSetPoint(mvp_data.leaderboard_bd, FRAMEPOINT_CENTER, mvp_data.gameUI, FRAMEPOINT_CENTER, 0.0 - lb_xpad/2, lb_yoffset + lb_ypad/2)
    for pInt = 1,10 do
        CreateShowHideButton(pInt) -- show/hide btn
    end
end


-- generate a cell in the leaderboard table:
-- :: returns framehandle
function mvp.ScoreboardCreateFrame(pInt, col, row, frameType)
    mvp_data.lb_table[col][row] = BlzCreateFrameByType(frameType, "lb-"..col.."-"..row, mvp_data.leaderboard_bd, "", 0)
    return mvp_data.lb_table[col][row]
end


-- shift to next col:
function mvp.ScoreboardOffsetCalcX(col, cell_width, nudge)
    if col > 1 then
        return (cell_width * (col-1)) - nudge
    else
        return cell_width/3 * (col-1)
    end
end


-- start a column and iterate down its rows:
function mvp.ScoreboardOffsetCalcY(row, cell_height)
    return -(cell_height * (row-1))
end


-- create a line item for the UI component
function mvp.ScoreboardGenerateRow()

end


-- remove clutter from staging area
function mvp.ClearArea()
    local g = CreateGroup()
    GroupEnumUnitsInRange(g, 0, 0, 1250.0, Condition(function()
        if not IsUnitType(GetFilterUnit(),UNIT_TYPE_HERO) or IsUnitType(GetFilterUnit(),UNIT_TYPE_ANCIENT) then
           RemoveUnit(GetFilterUnit())
           return true
        end
    end))
    DestroyGroup(g)
end


-- hide heroes to prep for mvp sequence
function mvp.ShowHero(pInt, bool)
    ShowUnit(udg_playerHero[pInt], bool)
    if not bool then -- move somewhere out of the way
        SetUnitX(udg_playerHero[pInt], mvp_data.y + 800)
        SetUnitY(udg_playerHero[pInt], mvp_data.x - 800)
    end
end


-- show clutch callout eyecandy
-- @unit    = location of arcing text
-- @dBool   = is it damage? (false for healing, true for damage)
function mvp.ArcingClutchText(unit, dBool)
    if dBool then
        ArcingTextTag('|cffff8505Clutch!|r', unit)
    else
        ArcingTextTag('|cff00b1ffClutch!|r', unit)
    end
end


-- increase the damage score of a player
function mvp.IncrementDamage(pInt, val) -- 1-based pInt
    if val < 5000 then -- don't increment if it's hacky damage e.g. instant kill
        mvp_data.pDmg[pInt] = mvp_data.pDmg[pInt] + val
    end
end


-- increase the healing score of a player
function mvp.IncrementHealing(pInt, val) -- 1-based pInt
    if val < 0 then local val = -val end -- control for negatives i.e. healing via negative damage.
    if val < 5000 then -- don't increment if it's hacky healing e.g. full heal
        mvp_data.pHealing[pInt] = mvp_data.pHealing[pInt] + val
    end
end


-- increase the absorbed damage score of a player
function mvp.IncrementAbsorbed(pInt, val) -- 1-based pInt
    if val < 5000 then -- don't increment if it's hacky effect e.g. invul
        mvp_data.pAbsorbed[pInt] = mvp_data.pAbsorbed[pInt] + val
    end
end


-- increase the completed objectives of a player
function mvp.IncrementObjectiveCap(pInt, val) -- 1-based pInt
    mvp_data.pObjCap[pInt] = mvp_data.pObjCap[pInt] + val
end


-- increase the clutch spell use events of a player
function mvp.IncrementClutch(pInt, val) -- 1-based pInt
    mvp_data.pClutch[pInt] = mvp_data.pClutch[pInt] + val
end


-- if needed, map existing variables to mvp data (these GUI vars exist in my personal project as an example):
function mvp.MapVariableValues()
    -- change the set variables to your map values e.g. udg_myHeroDeaths
    for pInt = 1,10 do
        mvp_data.pExp[pInt]   = udg_mbXPEarned[pInt]
        mvp_data.pElim[pInt]  = udg_mbEliminations[pInt]
        mvp_data.pDeath[pInt] = udg_mbDeaths[pInt]
    end
    -- ... add any others as needed (e.g. damage, healing, etc.)
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 mvp.AttachBackdropByHandle(fh, newFrameNameString, texturePathString, alphaValue, offsetx, offsety, width, height)
    local nh = BlzCreateFrameByType("BACKDROP", newFrameNameString, mvp_data.gameUI, "", 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


-- callback when show/hide is clicked:
function ShowHideBtnClick(pInt)
    if GetLocalPlayer() == Player(pInt-1) then
        if not BlzFrameIsVisible(mvp_data.leaderboard_bd) then
            mvp.ScoreboardShow(true)
            BlzFrameSetText(mvp_data.showHideBtn[pInt], "Hide Leaderboard")
        else
            mvp.ScoreboardShow(false)
            BlzFrameSetText(mvp_data.showHideBtn[pInt], "Show Leaderboard")
        end
    end
end


-- create button to show/hide the board:
function CreateShowHideButton(pInt)
    local trig = CreateTrigger()
    mvp_data.showHideBtn[pInt] = BlzCreateFrame("ScriptDialogButton", mvp_data.gameUI, 0.0, 0.0)
    BlzFrameSetSize(mvp_data.showHideBtn[pInt], 0.13, 0.03)
    BlzFrameSetPoint(mvp_data.showHideBtn[pInt], FRAMEPOINT_CENTER, mvp_data.leaderboard_bd, FRAMEPOINT_BOTTOM, 0.0, 0.0)
    BlzFrameSetText(mvp_data.showHideBtn[pInt], "Hide Leaderboard")
    BlzTriggerRegisterFrameEvent(trig, mvp_data.showHideBtn[pInt], FRAMEEVENT_CONTROL_CLICK)
    TriggerAddAction(trig, function() ShowHideBtnClick(GetConvertedPlayerId(GetTriggerPlayer())) end)
    -- hide btn for other players:
    for i = 1,mvp_data.maxPlayers do
        if GetLocalPlayer() == Player(i-1) and GetLocalPlayer() ~= Player(pInt-1) then
            BlzFrameSetVisible(mvp_data.showHideBtn[pInt],false)
        end
    end
end


-- flatten values:
function FloorPlayerScoreValues()
    for pInt = 1,10 do
        mvp_data.pDmg[pInt]           = math.floor(math.abs(mvp_data.pDmg[pInt]))
        mvp_data.pHealing[pInt]       = math.floor(math.abs(mvp_data.pHealing[pInt]))
        mvp_data.pAbsorbed[pInt]      = math.floor(math.abs(mvp_data.pAbsorbed[pInt]))
        mvp_data.pElim[pInt]          = math.floor(math.abs(mvp_data.pElim[pInt]))
        mvp_data.pDeath[pInt]         = math.floor(math.abs(mvp_data.pDeath[pInt]))
        mvp_data.pExp[pInt]           = math.floor(math.abs(mvp_data.pExp[pInt]))
        mvp_data.pObjCap[pInt]        = math.floor(math.abs(mvp_data.pObjCap[pInt]))
        mvp_data.pClutch[pInt]        = math.floor(math.abs(mvp_data.pClutch[pInt]))
        mvp_data.pScore[pInt]         = math.floor(math.abs(mvp_data.pScore[pInt]))
    end
end
[/end]
 

Attachments

  • lua_mvp_board_1.1.w3m
    69.5 KB · Views: 106
Last edited:
Level 12
Joined
Jan 30, 2020
Messages
875
Really nice work there, but I foresee a problem that should definitely desync :

"fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")"

You create frames in the local player context, thats a multiplayer no-go.
Just create them outside the context and everything should be fine from there.

Thanks for adding Lua resources for the community, much appreciated.

Take care !
 
Level 14
Joined
Feb 7, 2020
Messages
387
Really nice work there, but I foresee a problem that should definitely desync :

"fh = mvp.ScoreboardCreateFrame(pInt, col, row, "TEXT")"

You create frames in the local player context, thats a multiplayer no-go.
Just create them outside the context and everything should be fine from there.

Thanks for adding Lua resources for the community, much appreciated.

Take care !
Thanks, it should now be fixed.
 
Level 12
Joined
Jan 30, 2020
Messages
875
Great !!

Yes I can't see any problem any more so far :)

I don't need a score board in my TD, but I will definitely need one for my next project, and this will definitely help a lot.

Thank you !
 
Status
Not open for further replies.
Top