commit f0703eaafdee1f9abf9202ec74c116dd29f68ac1 Author: Not Date: Mon Mar 28 12:48:16 2022 +0200 v1.1.1 diff --git a/leaderboard.lua b/leaderboard.lua new file mode 100644 index 0000000..d1370d1 --- /dev/null +++ b/leaderboard.lua @@ -0,0 +1,218 @@ +-- Leaderboards written by Not + +local FILENAME = "leaderboard.txt" +local lb = {} +local timeFinished = 0 +local disable = true +local prevLap = 0 +local splits = {} + +-- Read the leaderboard +local f = io.open(FILENAME, "r") +if f then + for l in f:lines() do + local t = {} + for word in (l+"\t"):gmatch("(.-)\t") do + table.insert(t, word) + end + --local level, name, skin, color, time, splts = l:match("(.*)\t(.*)\t(.*)\t(.*)\t(.*)\t(.*)") + + if lb[t[1]] == nil then + lb[t[1]] = {} + end + local spl = {} + if t[6] != nil then + for str in t[6]:gmatch("([^ ]+)") do + table.insert(spl, tonumber(str)) + end + end + table.insert(lb[t[1]], {t[2], t[3], t[4], tonumber(t[5]), spl}) + end + f:close() +else + print("Failed to open file: ", FILENAME) +end + +local function initLeaderboard() + local ingame = 0 + for p in players.iterate do + if p.valid and not p.spectator then + ingame = ingame + 1 + end + end + + disable = ingame > 1 + + if disable then + --print("To many players in game, leaderboard has been disabled") + return + end +end +addHook("PlayerSpawn", initLeaderboard) + +local function retry(player, ...) + if disable or player.spectator then + print("How dare you") + return + end + + G_SetCustomExitVars(gamemap, 2) + G_ExitLevel() +end +COM_AddCommand("retry", retry) + + +--DEBUGGING +--local function printTable(tb) +-- for k, v in pairs(tb) do +-- for i = 1, #v do +-- print("TABLE: " + k, tb[k]) +-- if v[i] != nil then +-- print(v[i][1], v[i][2], v[i][3], v[i][4], table.concat(v[i][5], ",")) +-- end +-- end +-- end +--end + +addHook("MapLoad", function() + timeFinished = 0 + splits = {} + prevLap = 0 + end +) + +local function ticsToTime(tics) + return string.format( + "%d:%02d:%02d", + G_TicsToMinutes(tics), + G_TicsToSeconds(tics), + G_TicsToCentiseconds(tics) + ) +end + +local bodium = {V_YELLOWMAP, V_GRAYMAP, V_BROWNMAP, 0, 0} +local splitColor = {[true]=V_SKYMAP, [false]=V_REDMAP} +local splitSymbol = {[true]="-", [false]="+"} + +local showSplit = 0 +local function drawScoreboard(v) + if disable then return end + + local m = lb[tostring(gamemap)] + if m == nil then + return + end + + for i = 1, #m do + local score = m[i] + local skin = skins[score[2]] + if skin == nil then + skin = skins["sonic"] + end + + local patch = v.cachePatch(skin.facerank) + -- | OFFSET | + | PADDING | * |INDEX| + local h = ((200 / 4) + 4) + (patch.height + 4) * (i - 1) + v.draw(4, h, patch, V_HUDTRANS | bodium[i], v.getColormap("sonic", score[3])) + v.drawString(8 + patch.width, h, score[1], V_HUDTRANSHALF | V_ALLOWLOWERCASE) + + -- Draw splits + if showSplit > 0 and score[5][prevLap] != nil then + local split = splits[prevLap] - score[5][prevLap] + v.drawString(8 + patch.width, h + 8, splitSymbol[split < 0] + ticsToTime(abs(split)), V_HUDTRANSHALF | splitColor[split < 0]) + else + v.drawString(8 + patch.width, h + 8, ticsToTime(score[4]), V_HUDTRANSHALF | bodium[i]) + end + end +end +hud.add(drawScoreboard, "game") + +local function lbComp(a, b) + return a[4] < b[4] +end + +local function saveTime(player) + -- Check if you beat your previous best + local m = lb[tostring(gamemap)] + if m == nil then + m = {} + end + + for i = 1, #m do + if m[i][1] == player.name then + if m[i][4] > timeFinished then + table.remove(m, i) + S_StartSound(nil, 130) + break + else + -- You suck lol + S_StartSound(nil, 201) + return + end + end + end + + print("Saving score") + table.insert(m, {player.name, player.mo.skin, player.mo.color, timeFinished, splits}) + + table.sort(m, lbComp) + while #m > 5 do + table.remove(m) + end + + lb[tostring(gamemap)] = m + + local f = assert(io.open(FILENAME, "w")) + if f == nil then + print("Failed to open file for writing: " + FILENAME) + return + end + + for k, v in pairs(lb) do + for i = 1, #v do + local s = v[i] + f:write(k, "\t", s[1], "\t", s[2], "\t", s[3], "\t", s[4], "\t", table.concat(s[5], " "), "\n") + end + end + + f:close() +end + +-- DEBUGGING +--local function saveLeaderboard(player, ...) +-- timeFinished = player.realtime +-- splits = {1000, 2000, 3000} +-- saveTime(player) +--end +--COM_AddCommand("save", saveLeaderboard) + +local function regLap(player) + if player.laps > prevLap and timeFinished == 0 then + prevLap = player.laps + table.insert(splits, player.realtime) + showSplit = 5 * TICRATE + end +end + +local function captureFinish() + if disable then + return + end + if showSplit > 0 then + showSplit = showSplit - 1 + end + + for p in players.iterate do + if p.laps >= mapheaderinfo[gamemap].numlaps and timeFinished == 0 then + timeFinished = p.realtime + saveTime(p) + end + regLap(p) + end +end +addHook("ThinkFrame", captureFinish) + +local function netvars(net) + lb = net($) +end +addHook("NetVars", netvars)