diff --git a/leaderboard.lua b/leaderboard.lua index 0ca108d..2908dec 100644 --- a/leaderboard.lua +++ b/leaderboard.lua @@ -9,6 +9,7 @@ local prevLap = 0 local splits = {} local PATCH = nil local help = true +local UNCLAIMED = "Unclaimed Record" -- Retry / changelevel map local nextMap = nil @@ -59,6 +60,13 @@ local cv_enable = CV_RegisterVar({ end }) +local cv_saves = CV_RegisterVar({ + name = "lb_save_count", + defaultvalue = 20, + flags = CV_NETVAR, + PossibleValue = CV_Natural +}) + local cv_interrupt = CV_RegisterVar({ name = "lb_interrupt", defaultvalue = 0, @@ -266,8 +274,11 @@ local function changelevel(player, ...) else --Extended map numbers p = ALPH:find(p) - 1 - q = (tonumber(q) or ALPH:find(q) + 9) - mapnum = 36 * p + q + 100 + local qn = tonumber(q) + if qn == nil then + qn = ALPH:find(q) + 9 + end + mapnum = 36 * p + qn + 100 end if mapheaderinfo[mapnum] == nil then @@ -319,6 +330,10 @@ addHook("MapLoad", function() ) local function ticsToTime(tics) + if tics == 0 then + return "-:--:--" + end + return string.format( "%d:%02d:%02d", G_TicsToMinutes(tics), @@ -340,15 +355,30 @@ local function drawitem(v, x, y, scale, itempatch, vflags) ) end +local cursors = { + [1] = ". ", + [2] = " ." +} local function marquee(text, maxwidth) if #text <= maxwidth then return text end local shift = 16 + + -- Creates an index range ranging from -shift to #text + shift local pos = ((leveltime / 16) % (#text - maxwidth + shift * 2)) + 1 - shift + + local cursor = "" + if pos < #text - maxwidth + 1 then + cursor = cursors[((leveltime / 11) % #cursors) + 1] + end + + -- The pos is the index going from -shift to #text + shift + -- It's clamped within the text boundaries ie. + -- 0 < pos < #text - maxwidth pos = min(max(pos, 1), #text - maxwidth + 1) - return text:sub(pos, pos + maxwidth - 1) + return text:sub(pos, pos + maxwidth - 1) + cursor end -- Bats on ... @@ -359,6 +389,109 @@ local splitSymbol = {[true]="-", [false]="+"} local showSplit = 0 local VFLAGS = V_SNAPTOLEFT +local FACERANK_DIM = 16 +local function drawScore(v, player, i, gui, faceRank, color, name, time, sSplits, flags) + --draw Patch/chili + -- | OFFSET | + | PADDING | * |INDEX| + local h = ((200 / 4) + 4) + (FACERANK_DIM + 4) * (i - 1) + + v.draw(4, h, faceRank, V_HUDTRANS | VFLAGS, v.getColormap("sonic", color)) + if player.name == name then + v.draw(4, h, PATCH["CHILI"][(leveltime / 4) % 8], V_HUDTRANS | VFLAGS) + end + + --draw icons + + --draw name + if gui == GUI_ON or (gui == GUI_SPLITS and showSplit) then + -- Shorten long names + local stralign = "left" + local MAXWIDTH = 70 + local px = 6 + local py = 0 + if v.stringWidth(name) > MAXWIDTH then + stralign = "thin" + py = -1 + if v.stringWidth(name, 0, "thin") > MAXWIDTH then + stralign = "small" + py = 2 + if v.stringWidth(name, 0, "small") > MAXWIDTH then + name = marquee(name, 15) + end + end + end + + -- SPB + if flags & F_SPBATK then + local scale = FRACUNIT / 4 + drawitem( + v, + 4 - 2, + h - 2, + scale, + PATCH["SPB"], + V_HUDTRANS | VFLAGS + ) + if flags & F_SPBEXP then + drawitem( + v, + FACERANK_DIM, + h - 2, + scale, + PATCH["INV"][(leveltime / 4) % 6], + V_HUDTRANS | VFLAGS + ) + end + if flags & F_SPBBIG then + drawitem( + v, + 4 - 2, + h + FACERANK_DIM - 4, + scale, + PATCH["BIG"], + V_HUDTRANS | VFLAGS + ) + end + if flags & F_SPBJUS then + drawitem( + v, + FACERANK_DIM, + h + FACERANK_DIM - 4, + scale, + PATCH["HYUD"], + V_HUDTRANS | VFLAGS + ) + end + end + + v.drawString( + px + FACERANK_DIM, + h + py, + name, + V_HUDTRANSHALF | V_ALLOWLOWERCASE | VFLAGS, + stralign + ) + + -- Draw splits + if showSplit and sSplits and sSplits[prevLap] != nil then + local split = splits[prevLap] - sSplits[prevLap] + v.drawString( + px + FACERANK_DIM, + h + 8, + splitSymbol[split < 0] + ticsToTime(abs(split)), + V_HUDTRANSHALF | splitColor[split < 0] | VFLAGS + ) + else + v.drawString( + px + FACERANK_DIM, + h + 8, + ticsToTime(time), + V_HUDTRANSHALF | bodium[min(i, 4)] | VFLAGS + ) + end + end +end + local function drawScoreboard(v, player) if disable then return end if player != displayplayers[0] then return end @@ -366,9 +499,9 @@ local function drawScoreboard(v, player) cachePatches(v) local m = lb[lbID(gamemap, Flags)] - if m == nil then - return - end + --if m == nil then + -- return + --end local gui = cv_gui.value if leveltime < START_TIME or player.exiting or player.lives == 0 then @@ -376,108 +509,22 @@ local function drawScoreboard(v, player) end if gui then - for i, score in ipairs(m) do - local name = score["name"] - local skin = skins[score["skin"]] - if skin == nil then - skin = skins["sonic"] - end - local skinPatch = PATCH["FACERANK"][skin.name] - - -- | OFFSET | + | PADDING | * |INDEX| - local h = ((200 / 4) + 4) + (skinPatch.height + 4) * (i - 1) - - v.draw(4, h, skinPatch, V_HUDTRANS | VFLAGS, v.getColormap("sonic", score["color"])) - if player.name == name then - v.draw(4, h, PATCH["CHILI"][(leveltime / 4) % 8], V_HUDTRANS | VFLAGS) - end - - -- SPB - if score["flags"] & F_SPBATK then - local scale = FRACUNIT / 4 - drawitem( - v, - 4 - 2, - h - 2, - scale, - PATCH["SPB"], - V_HUDTRANS | VFLAGS - ) - if score["flags"] & F_SPBEXP then - drawitem( - v, - skinPatch.width, - h - 2, - scale, - PATCH["INV"][(leveltime / 4) % 6], - V_HUDTRANS | VFLAGS - ) - end - if score["flags"] & F_SPBBIG then - drawitem( - v, - 4 - 2, - h + skinPatch.height - 4, - scale, - PATCH["BIG"], - V_HUDTRANS | VFLAGS - ) - end - if score["flags"] & F_SPBJUS then - drawitem( - v, - skinPatch.width, - h + skinPatch.height - 4, - scale, - PATCH["HYUD"], - V_HUDTRANS | VFLAGS - ) - end - end - - if gui == GUI_ON or (gui == GUI_SPLITS and showSplit) - -- Shorten long names - local stralign = "left" - local MAXWIDTH = 70 - local px = 6 - local py = 0 - if v.stringWidth(name) > MAXWIDTH then - stralign = "thin" - py = -1 - if v.stringWidth(name, 0, "thin") > MAXWIDTH then - stralign = "small" - py = 2 - if v.stringWidth(name, 0, "small") > MAXWIDTH then - name = marquee(name, 15) - end - end + -- Draw placeholder score + if m == nil then + drawScore(v, player, 1, gui, PATCH["NORANK"], nil, UNCLAIMED, 0, nil, 0) + else + for i, score in ipairs(m) do + if i > 5 then + break end - v.drawString( - px + skinPatch.width, - h + py, - name, - V_HUDTRANSHALF | V_ALLOWLOWERCASE | VFLAGS, - stralign - ) - - -- Draw splits - if showSplit and score["splits"][prevLap] != nil then - local split = splits[prevLap] - score["splits"][prevLap] - v.drawString( - px + skinPatch.width, - h + 8, - splitSymbol[split < 0] + ticsToTime(abs(split)), - V_HUDTRANSHALF | splitColor[split < 0] | VFLAGS - ) - else - v.drawString( - px + skinPatch.width, - h + 8, - ticsToTime(score["time"]), - V_HUDTRANSHALF | bodium[min(i, 4)] | VFLAGS - ) + local name = score["name"] + local skin = skins[score["skin"]] + if skin == nil then + skin = skins["sonic"] end + local faceRank = PATCH["FACERANK"][skin.name] + drawScore(v, player, i, gui, faceRank, score["color"], score["name"], score["time"], score["splits"], score["flags"]) end end end @@ -493,6 +540,8 @@ function cachePatches(v) PATCH["CHILI"][i-1] = v.cachePatch("K_CHILI" + i) end + PATCH["NORANK"] = v.cachePatch("M_NORANK") + PATCH["FACERANK"] = {} for skin in skins.iterate do PATCH["FACERANK"][skin.name] = v.cachePatch(skin.facerank) @@ -555,7 +604,7 @@ local function saveTime(player) ) table.sort(m, lbComp) - while #m > 5 do + while #m > cv_saves.value do table.remove(m) end @@ -618,7 +667,7 @@ local function think() if disable then if cv_afk.value and ingame() > 1 then for p in players.iterate do - if p.valid and not p.spectator and not p.exiting then + if p.valid and not p.spectator and not p.exiting and p.lives > 0 then if p.cmd.buttons then p.afkTime = leveltime end