13 Commits

5 changed files with 526 additions and 306 deletions

View File

@ -1,15 +1,21 @@
local leaderboard = nil
local MapRecords
local maps
local mapIndex = 1
local scrollPos = 1
local modes = nil
local mode = 1
local prefMode = nil
local ModeSep
---- Imported functions ----
-- Imported functions -------
-- lb_common.lua
local ZoneAct = lb_ZoneAct
local TicsToTime = lb_TicsToTime
-- lb_store.lua
local GetMapRecords = lb_get_map_records
-----------------------------
local cv_kartencore
@ -25,11 +31,9 @@ end
local function updateModes()
-- set available modes for this map
modes = {}
for mode, scoreTable in pairs(leaderboard) do
if scoreTable[getMap()] then
for mode, _ in pairs(MapRecords) do
table.insert(modes, mode)
end
end
table.sort(modes)
mode = 1
@ -46,6 +50,8 @@ local function updateMapIndex(n)
mapIndex = mapIndexOffset(n)
scrollPos = 1
MapRecords = GetMapRecords(maps[mapIndex], ModeSep)
updateModes()
end
@ -266,15 +272,18 @@ local function drawFlags(v, x, y, flags)
end
end
local MSK_SPEED = 0xF0
local MSK_WEIGHT = 0xF
local function drawStats(v, x, y, skin, stats)
local s = skins[skin]
if not (s
and s.kartspeed == stats["speed"]
and s.kartweight == stats["weight"]
)
and stats then
v.drawString(x-2, y-2, stats["speed"], V_ALLOWLOWERCASE, "thin")
v.drawString(x + 13, y + 9, stats["weight"], V_ALLOWLOWERCASE, "thin")
if stats
and not (s
and s.kartspeed == (stats & MSK_SPEED) >> 4
and s.kartweight == stats & MSK_WEIGHT
) then
v.drawString(x-2, y-2, (stats & MSK_SPEED) >> 4, V_ALLOWLOWERCASE, "thin")
v.drawString(x + 13, y + 9, stats & MSK_WEIGHT, V_ALLOWLOWERCASE, "thin")
end
end
@ -334,7 +343,7 @@ local function drawScore(v, i, pos, score, highlight)
end
local function drawBrowser(v, player)
if not leaderboard then return end
if not MapRecords then return end
v.fadeScreen(0xFF00, 16)
@ -355,23 +364,20 @@ local function drawBrowser(v, player)
if not modes then return end
local gamemode = leaderboard[modes[mode]]
if not gamemode then return end
local records = MapRecords[modes[mode]]
if not records then return end
local scoreTable = gamemode[getMap()]
if not scoreTable then return end
local scores = #scoreTable
scrollPos = max(min(scrollPos, scores - 3), 1)
local endi = min(scrollPos + 7, scores)
local record_count = #records
scrollPos = max(min(scrollPos, record_count - 3), 1)
local endi = min(scrollPos + 7, record_count)
for i = scrollPos, endi do
drawScore(v, i - scrollPos + 1, i, scoreTable[i], scoreTable[i].name == player.name)
drawScore(v, i - scrollPos + 1, i, records[i], records[i].name == player.name)
end
end
rawset(_G, "DrawBrowser", drawBrowser)
local function initBrowser(lb)
leaderboard = lb
local function initBrowser(modeSep)
ModeSep = modeSep
-- set mapIndex to current map
for i, m in ipairs(maps) do
@ -381,6 +387,9 @@ local function initBrowser(lb)
end
end
-- initialize MapRecords
MapRecords = GetMapRecords(gamemap, ModeSep)
scrollPos = 1
updateModes()
end
@ -482,6 +491,7 @@ local function netvars(net)
mode = net($)
prefMode = net($)
scrollPos = net($)
leaderboard = net($)
MapRecords = net($)
ModeSep = net($)
end
addHook("NetVars", netvars)

View File

@ -24,3 +24,23 @@ rawset(_G, "lb_ZoneAct", function(map)
return z
end)
rawset(_G, "lb_stat_t", function(speed, weight)
if speed and weight then
return (speed << 4) | weight
end
return 0
end)
local F_SPBBIG = 0x4
local F_SPBEXP = 0x8
-- True if a is better than b
rawset(_G, "lb_comp", function(a, b)
-- Calculates the difficulty, harder has higher priority
-- if s is positive then a is harder
-- if s is negative then b is harder
-- if s is 0 then compare time
local s = (a.flags & (F_SPBEXP | F_SPBBIG)) - (b.flags & (F_SPBEXP | F_SPBBIG))
return s > 0 or not(s < 0 or a.time >= b.time)
end)

238
lb_store.lua Normal file
View File

@ -0,0 +1,238 @@
-- This file handles the storage and related netvars of the leaderboard
---- Imported functions ----
-- lb_common.lua
local stat_t = lb_stat_t
local lbComp = lb_comp
----------------------------
local LEADERBOARD_FILE = "leaderboard.txt"
-- ColdStore are records loaded from lua addons
-- this table should never be modified outside of the AddColdStore function
local ColdStore = {}
-- Livestore are new records nad records loaded from leaderboard.txt file
local LiveStore = {}
-- GLOBAL
-- Returns a list of all maps with records
local function MapList()
local maps = {}
for map in pairs(ColdStore) do
maps[map] = true
end
for map in pairs(LiveStore) do
maps[map] = true
end
local maplist = {}
for map in pairs(maps) do
table.insert(maplist, map)
end
table.sort(maplist)
return maplist
end
rawset(_G, "lb_map_list", MapList)
-- GLOBAL
-- Function for adding records from lua
local function AddColdStore(record)
ColdStore[record.map] = $ or {}
table.insert(ColdStore[record.map], record)
end
rawset(_G, "lb_add_coldstore_record", AddColdStore)
-- Insert mode separated records from the flat sourceTable into dest
local function insertRecords(dest, sourceTable, modeSep)
if not sourceTable then return end
local mode = nil
for _, record in ipairs(sourceTable) do
mode = record.flags & modeSep
dest[mode] = $ or {}
table.insert(dest[mode], record)
end
end
-- GLOBAL
-- Construct the leaderboard table of the supplied mapid
-- combines the ColdStore and LiveStore records
local function GetMapRecords(map, modeSep)
local mapRecords = {}
-- Insert ColdStore records
insertRecords(mapRecords, ColdStore[map], modeSep)
-- Insert LiveStore records
insertRecords(mapRecords, LiveStore[map], modeSep)
-- Sort records
for _, records in pairs(mapRecords) do
table.sort(records, lbComp)
end
-- Remove duplicate entries
for _, records in pairs(mapRecords) do
local players = {}
local i = 1
while i <= #records do
if players[records[i].name] then
table.remove(records, i)
else
players[records[i].name] = true
i = i + 1
end
end
end
return mapRecords
end
rawset(_G, "lb_get_map_records", GetMapRecords)
local function insertOrReplaceRecord(map, score, modeSep)
LiveStore[map] = $ or {}
for i, record in ipairs(LiveStore[map]) do
-- Replace the record
if record.name == score.name
and (record.flags & modeSep) == (score.flags & modeSep) then
LiveStore[map][i] = score
return
end
end
table.insert(LiveStore[map], score)
-- TODO: remove excess records
end
local MSK_SPEED = 0xF0
local MSK_WEIGHT = 0xF
local function stat_str(stat)
if stat then
return string.format("%d%d", (stat & MSK_SPEED) >> 4, stat & MSK_WEIGHT)
end
return "0"
end
-- GLOBAL
-- Save a record to the LiveStore and write to disk
-- SaveRecord will replace the record holders previous record but it will not compare any record times
local function SaveRecord(score, map, modeSep)
insertOrReplaceRecord(map, score, modeSep)
print("Saving score")
if not isserver then return end
local f = assert(
io.open(LEADERBOARD_FILE, "w"),
"Failed to open file for writing: "..LEADERBOARD_FILE
)
f:setvbuf("line")
for mapid, records in pairs(LiveStore) do
for _, record in ipairs(records) do
f:write(
mapid, "\t",
record.name, "\t",
record.skin, "\t",
record.color, "\t",
record.time, "\t",
table.concat(record.splits, " "), "\t",
record.flags, "\t",
stat_str(record.stat), "\n"
)
end
end
f:close()
end
rawset(_G, "lb_save_record", SaveRecord)
local function netvars(net)
LiveStore = net($)
end
addHook("NetVars", netvars)
local function score_t(map, name, skin, color, time, splits, flags, stat)
return {
["map"] = map,
["name"] = name,
["skin"] = skin,
["color"] = color,
["time"] = time,
["splits"] = splits,
["flags"] = flags,
["stat"] = stat
}
end
local function parseScore(str)
-- Leaderboard is stored in the following tab separated format
-- mapnum, name, skin, color, time, splits, flags, stat
local t = {}
for word in (str.."\t"):gmatch("(.-)\t") do
table.insert(t, word)
end
local splits = {}
if t[6] != nil then
for str in t[6]:gmatch("([^ ]+)") do
table.insert(splits, tonumber(str))
end
end
local flags = 0
if t[7] != nil then
flags = tonumber(t[7])
end
local stats = nil
if t[8] != nil then
if #t[8] >= 2 then
local speed = tonumber(string.sub(t[8], 1, 1))
local weight = tonumber(string.sub(t[8], 2, 2))
stats = stat_t(speed, weight)
end
end
return score_t(
tonumber(t[1]), -- Map
t[2], -- Name
t[3], -- Skin
t[4], -- Color
tonumber(t[5]), -- Time
splits,
flags,
stats
)
end
rawset(_G, "lb_parse_score", parseScore)
-- Load the livestore
do
if isserver then
local f = assert(
io.open(LEADERBOARD_FILE, "r"),
"Failed to open file: "..LEADERBOARD_FILE
)
for l in f:lines() do
local score = parseScore(l)
LiveStore[score.map] = $ or {}
table.insert(LiveStore[score.map], score)
end
f:close()
end
end

View File

@ -1,18 +1,17 @@
-- Leaderboards written by Not
-- Reusable
-- Leaderboard Table
-- [mode][mapnum][scoreTable]
local lb = {}
-- Holds the current maps records table including all modes
local MapRecords = {}
local timeFinished = 0
local TimeFinished = 0
local disable = false
local prevLap = 0
local splits = {}
local PATCH = nil
local help = true
local EncoreInitial = nil
local scoreTable
local ScoreTable
-- Text flash on finish
@ -29,9 +28,6 @@ local RedFlash = {
[1] = 0
}
-- Tracks if stats have been written or not
local StatTrack = false
local UNCLAIMED = "Unclaimed Record"
local HELP_MESSAGE = "\x89Leaderboard Commands:\nretry exit findmap changelevel spba_clearcheats lb_gui rival scroll encore records levelselect"
local FILENAME = "leaderboard.txt"
@ -94,11 +90,18 @@ local allowJoin
-- lb_common.lua
local ticsToTime = lb_TicsToTime
local zoneAct = lb_ZoneAct
local stat_t = lb_stat_t
local lbComp = lb_comp
-- browser.lua
local InitBrowser = InitBrowser
local DrawBrowser = DrawBrowser
local BrowserController = BrowserController
-- lb_store.lua
local GetMapRecords = lb_get_map_records
local SaveRecord = lb_save_record
local MapList = lb_map_list
---------------
-- cvars
@ -164,79 +167,17 @@ local cv_interrupt = CV_RegisterVar({
end
})
local function setST(t, map, flags, scoreTable)
local mode = flags & ST_SEP
t[mode] = t[mode] or {}
t[mode][map] = scoreTable
end
local function getST(t, map, flags)
local mode = flags & ST_SEP
return t[mode] and t[mode][map] or nil
end
local function setScoreTable(map, flags, scoreTable)
setST(lb, map, flags, scoreTable)
end
local function getScoreTable(map, flags)
return getST(lb, map, flags)
end
-- True if a is better than b
local function lbComp(a, b)
-- Calculates the difficulty, harder has higher priority
-- if s is positive then a is harder
-- if s is negative then b is harder
-- if s is 0 then compare time
local s = (a["flags"] & (F_SPBEXP | F_SPBBIG)) - (b["flags"] & (F_SPBEXP | F_SPBBIG))
return s > 0 or not(s < 0 or a["time"] >= b["time"])
end
local function sortScores()
for mode, t in pairs(lb) do
for map, scoreTable in pairs(t) do
table.sort(scoreTable, lbComp)
setScoreTable(map, mode, scoreTable)
end
end
end
-- Reinitialize lb based on new ST_SEP value
local function reinit_lb()
local nlb = {}
for mode, t in pairs(lb) do
for map, scoreTable in pairs(t) do
for i, score in ipairs(scoreTable) do
local st = getST(nlb, map, score["flags"]) or {}
table.insert(st, score)
setST(nlb, map, score["flags"], st)
end
end
end
lb = nlb
sortScores()
end
local cv_spb_separate = CV_RegisterVar({
name = "lb_spb_combined",
defaultvalue = 1,
flags = CV_NETVAR | CV_CALL | CV_NOINIT,
PossibleValue = CV_YesNo,
func = function(v)
local curSep = ST_SEP
if v.value then
ST_SEP = F_SPBATK
else
ST_SEP = F_SPBATK | F_SPBBIG | F_SPBEXP
end
if curSep != ST_SEP then
reinit_lb()
end
end
})
@ -253,80 +194,8 @@ local function score_t(map, name, skin, color, time, splits, flags, stat)
}
end
local function stat_t(speed, weight)
if speed and weight then
return {
["speed"] = speed,
["weight"] = weight
}
end
return nil
end
local function stat_str(stat)
if stat then
return string.format("%d%d", stat["speed"], stat["weight"])
end
return "0"
end
-- Read the leaderboard
local f = io.open(FILENAME, "r")
if f then
for l in f:lines() do
-- Leaderboard is stored in the following tab separated format
-- mapnum, name, skin, color, time, splits, flags, stat
local t = {}
for word in (l+"\t"):gmatch("(.-)\t") do
table.insert(t, word)
end
local flags = 0
if t[7] != nil then
flags = tonumber(t[7])
end
scoreTable = getScoreTable(tonumber(t[1]), flags) or {}
local spl = {}
if t[6] != nil then
for str in t[6]:gmatch("([^ ]+)") do
table.insert(spl, tonumber(str))
end
end
local stats = nil
if t[8] != nil then
if #t[8] >= 2 then
local speed = tonumber(string.sub(t[8], 1, 1))
local weight = tonumber(string.sub(t[8], 2, 2))
stats = stat_t(speed, weight)
end
end
table.insert(
scoreTable,
score_t(
tonumber(t[1]),
t[2],
t[3],
t[4],
tonumber(t[5]),
spl,
flags,
stats
)
)
setScoreTable(tonumber(t[1]), flags, scoreTable)
end
sortScores()
f:close()
else
print("Failed to open file: ", FILENAME)
end
local MSK_SPEED = 0xF0
local MSK_WEIGHT = 0xF
function allowJoin(v)
if not cv_interrupt.value then
@ -421,7 +290,7 @@ local function initBrowser(player)
return
end
InitBrowser(lb)
InitBrowser(ST_SEP)
drawState = DS_BROWSER
player.afkTime = leveltime
@ -525,6 +394,7 @@ end
local function records(player, ...)
local mapid = ...
local mapnum = gamemap
local mapRecords = MapRecords
if mapid then
mapnum = mapnumFromExtended(mapid)
@ -532,6 +402,8 @@ local function records(player, ...)
CONS_Printf(player, string.format("Invalid map name: %s", mapid))
return
end
mapRecords = GetMapRecords(mapnum, ST_SEP)
end
local map = mapheaderinfo[mapnum]
@ -559,35 +431,33 @@ local function records(player, ...)
CONS_Printf(player, "\x85UNKNOWN MAP")
end
for mode, maps in pairs(lb) do
local maptbl = maps[mapnum]
if not maptbl then continue end
for mode, records in pairs(mapRecords) do
CONS_Printf(player, "")
CONS_Printf(player, modeToString(mode))
-- don't print flags for time attack
if mode then
for i, tbl in ipairs(maptbl) do
for i, score in ipairs(records) do
CONS_Printf(
player,
string.format(
"%2d %-21s \x89%8s \x80%s",
i,
tbl["name"],
ticsToTime(tbl["time"]),
modeToString(tbl["flags"])
score["name"],
ticsToTime(score["time"]),
modeToString(score["flags"])
)
)
end
else
for i, tbl in ipairs(maptbl) do
for i, score in ipairs(records) do
CONS_Printf(
player,
string.format(
"%2d %-21s \x89%8s",
i,
tbl["name"],
ticsToTime(tbl["time"])
score["name"],
ticsToTime(score["time"])
)
)
end
@ -691,21 +561,26 @@ local function findRival(player, ...)
local totalScores = 0
local totalDiff = 0
CONS_Printf(player, string.format("\x89%s's times:", rival))
CONS_Printf(player, "MAP Time Diff Mode")
for mode, tbl in pairs(lb) do
scores[mode] = {}
local maplist = MapList()
local mapRecords
local rivalScore
local yourScore
for i = 1, #maplist do
mapRecords = GetMapRecords(maplist[i], ST_SEP)
for map, scoreTable in pairs(tbl) do
local rivalScore = nil
local yourScore = nil
for mode, records in pairs(mapRecords) do
scores[mode] = $ or {}
for _, score in pairs(scoreTable) do
if score["name"] == player.name then
rivalScore = nil
yourScore = nil
for _, score in ipairs(records) do
if score.name == player.name then
yourScore = score
elseif score["name"] == rival then
elseif score.name == rival then
rivalScore = score
end
@ -715,7 +590,7 @@ local function findRival(player, ...)
end
if rivalScore and yourScore then
totalDiff = totalDiff + yourScore["time"] - rivalScore["time"]
totalDiff = totalDiff + yourScore.time - rivalScore.time
end
if rivalScore then
@ -723,8 +598,8 @@ local function findRival(player, ...)
table.insert(
scores[mode],
{
["rival"] = rivalScore,
["your"] = yourScore
rival = rivalScore,
your = yourScore
}
)
end
@ -828,7 +703,7 @@ COM_AddCommand("rival", findRival)
--end
addHook("MapLoad", function()
timeFinished = 0
TimeFinished = 0
splits = {}
prevLap = 0
drawState = DS_DEFAULT
@ -838,6 +713,8 @@ addHook("MapLoad", function()
allowJoin(true)
--printTable(lb)
MapRecords = GetMapRecords(gamemap, ST_SEP)
end
)
@ -987,11 +864,11 @@ local function drawScore(v, player, pos, x, y, gui, faceRank, score, drawPos, te
local pskin = score["skin"] and skins[score["skin"]]
if stat and not (
pskin
and pskin.kartweight == stat["weight"]
and pskin.kartspeed == stat["speed"]
and pskin.kartweight == stat & MSK_WEIGHT
and pskin.kartspeed == (stat & MSK_SPEED) >> 4
) then
v.drawString(x + FACERANK_DIM - 2, y + 4, stat["speed"], V_HUDTRANS | VFLAGS, "small")
v.drawString(x + FACERANK_DIM - 2, y + 8, stat["weight"], V_HUDTRANS | VFLAGS, "small")
v.drawString(x + FACERANK_DIM - 2, y + 4, (stat & MSK_SPEED) >> 4, V_HUDTRANS | VFLAGS, "small")
v.drawString(x + FACERANK_DIM - 2, y + 8, stat & MSK_WEIGHT, V_HUDTRANS | VFLAGS, "small")
end
if gui == GUI_ON or (gui == GUI_SPLITS and showSplit) then
@ -1148,12 +1025,14 @@ local function drawScoreboard(v, player)
cachePatches(v)
local gui = cv_gui.value
-- Force enable gui at start and end of the race
if leveltime < START_TIME or player.exiting or player.lives == 0 then
gui = GUI_ON
end
if gui then
stateFunctions[drawState](v, player, scoreTable, gui)
stateFunctions[drawState](v, player, ScoreTable, gui)
end
local pos = 0
@ -1193,7 +1072,7 @@ end
-- Find location of player and scroll to it
function scroll_to(player)
local m = scoreTable or {}
local m = ScoreTable or {}
scrollToPos = 2
for pos, score in ipairs(m) do
@ -1207,23 +1086,59 @@ function scroll_to(player)
end
-- Write skin stats to each score where there are none
local function writeStats()
for _, t in pairs(lb) do
for _, scoreTable in pairs(t) do
for _, score in ipairs(scoreTable) do
local skin = skins[score["skin"]]
if skin and not score["stat"] then
local stats = stat_t(skin.kartspeed, skin.kartweight)
score["stat"] = stats
end
end
--local function writeStats()
-- for _, t in pairs(lb) do
-- for _, scoreTable in pairs(t) do
-- for _, score in ipairs(scoreTable) do
-- local skin = skins[score["skin"]]
-- if skin and not score["stat"] then
-- local stats = stat_t(skin.kartspeed, skin.kartweight)
-- score["stat"] = stats
-- end
-- end
-- end
-- end
--end
local function checkFlags(p)
local flags = 0
-- Encore
if encoremode then
flags = $ | F_ENCORE
end
if not cv_spbatk then
cv_spbatk = CV_FindVar("spbatk")
end
-- SPBAttack
if server.SPBArunning and cv_spbatk.value then
flags = $ | F_SPBATK
if server.SPBAexpert then
flags = $ | F_SPBEXP
end
if p.SPBAKARTBIG then
flags = $ | F_SPBBIG
end
if p.SPBAjustice then
flags = $ | F_SPBJUS
end
end
return flags
end
local function saveTime(player)
-- Disqualify if the flags changed mid trial.
if checkFlags(player) != Flags then
print("Game mode change detected! Time has been disqualified.")
S_StartSound(nil, 110)
return
end
scoreTable = $ or {}
ScoreTable = $ or {}
local pskin = skins[player.mo.skin]
local newscore = score_t(
@ -1231,23 +1146,16 @@ local function saveTime(player)
player.name,
player.mo.skin,
player.skincolor,
timeFinished,
TimeFinished,
splits,
Flags,
stat_t(player.HMRs or pskin.kartspeed, player.HMRw or pskin.kartweight)
)
-- Check if you beat your previous best
for i = 1, #scoreTable do
if scoreTable[i]["name"] == player.name then
if lbComp(newscore, scoreTable[i]) then
table.remove(scoreTable, i)
S_StartSound(nil, 130)
FlashTics = leveltime + TICRATE * 3
FlashRate = 1
FlashVFlags = YellowFlash
break
else
for i = 1, #ScoreTable do
if ScoreTable[i].name == player.name then
if not lbComp(newscore, ScoreTable[i]) then
-- You suck lol
S_StartSound(nil, 201)
FlashTics = leveltime + TICRATE * 3
@ -1259,62 +1167,36 @@ local function saveTime(player)
end
end
print("Saving score")
table.insert(
scoreTable,
newscore
)
table.sort(scoreTable, lbComp)
while #scoreTable > cv_saves.value do
table.remove(scoreTable)
end
-- Save the record
SaveRecord(newscore, gamemap, ST_SEP)
-- Set players text flash and play chime sfx
S_StartSound(nil, 130)
FlashTics = leveltime + TICRATE * 3
FlashRate = 1
FlashVFlags = YellowFlash
-- Reload the MapRecords
MapRecords = GetMapRecords(gamemap, ST_SEP)
-- Set the updated ScoreTable
ScoreTable = MapRecords[Flags]
-- Scroll the gui to the player entry
scroll_to(player)
setScoreTable(gamemap, Flags, scoreTable)
if not StatTrack then
writeStats()
StatTrack = true
end
local f = assert(io.open(FILENAME, "w"))
if f == nil then
print("Failed to open file for writing: " + FILENAME)
return
end
for _, tbl in pairs(lb) do
for _, scoreTable in pairs(tbl) do
for _, score in ipairs(scoreTable) do
f:write(
score["map"], "\t",
score["name"], "\t",
score["skin"], "\t",
score["color"], "\t",
score["time"], "\t",
table.concat(score["splits"], " "), "\t",
score["flags"], "\t",
stat_str(score["stat"]), "\n"
)
end
end
end
f:close()
end
-- DEBUGGING
--local function saveLeaderboard(player, ...)
-- timeFinished = tonumber(... or player.realtime)
-- TimeFinished = tonumber(... or 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
if player.laps > prevLap and TimeFinished == 0 then
prevLap = player.laps
table.insert(splits, player.realtime)
showSplit = 5 * TICRATE
@ -1387,13 +1269,7 @@ local function think()
end
-- Autospec
-- Encore
if leveltime == 1 then
Flags = $ & !F_ENCORE
if encoremode then
Flags = $ | F_ENCORE
end
if p then
for s in players.iterate do
if s.valid and s.spectator then
@ -1402,58 +1278,44 @@ local function think()
end
end
end
if not cv_spbatk then
cv_spbatk = CV_FindVar("spbatk")
end
-- Gamemode flags
Flags = $ & !(F_SPBATK | F_SPBEXP | F_SPBBIG | F_SPBJUS)
if server.SPBArunning
and cv_spbatk.value
and leveltime > START_TIME - (3 * TICRATE) / 2 then
Flags = $ | F_SPBATK
if server.SPBAexpert then
Flags = $ | F_SPBEXP
end
if leveltime > START_TIME - (3 * TICRATE) / 2 then
if clearcheats then
clearcheats = false
for q in players.iterate do
q.SPBAKARTBIG = false
q.SPBAjustice = false
q.SPBAshutup = false
if p then
p.SPBAKARTBIG = false
p.SPBAjustice = false
p.SPBAshutup = false
end
end
if p then
if p.SPBAKARTBIG then
Flags = $ | F_SPBBIG
end
if p.SPBAjustice then
Flags = $ | F_SPBJUS
end
end
Flags = checkFlags(p)
-- make sure the spb actually spawned
if leveltime == START_TIME - 1 then
if server.SPBArunning and leveltime == START_TIME - 1 then
if not (server.SPBAbomb and server.SPBAbomb.valid) then
-- it didn't spawn, clear spb flags
Flags = $ & !(F_SPBATK | F_SPBEXP | F_SPBBIG | F_SPBJUS)
end
end
else
hud.enable("freeplay")
end
end
scoreTable = getScoreTable(gamemap, Flags)
ScoreTable = MapRecords[ST_SEP & Flags]
if not cv_teamchange then
cv_teamchange = CV_FindVar("allowteamchange")
end
if p then
-- must be done before browser control
if p.laps >= mapheaderinfo[gamemap].numlaps and TimeFinished == 0 then
TimeFinished = p.realtime
saveTime(p)
end
-- Scroll controller
-- Spectators can't input buttons so let the gamer do it
if drawState == DS_SCROLL then
@ -1478,7 +1340,7 @@ local function think()
end
-- disable spba hud
if server.SPBAdone then
if server.SPBArunning and server.SPBAdone then
server.SPBArunning = false
p.pflags = $ & !(PF_TIMEOVER)
p.exiting = 100
@ -1509,10 +1371,6 @@ local function think()
end
end
if p.laps >= mapheaderinfo[gamemap].numlaps and timeFinished == 0 then
timeFinished = p.realtime
saveTime(p)
end
regLap(p)
elseif cv_teamchange.value == 0 then
allowJoin(true)
@ -1544,8 +1402,8 @@ local function netvars(net)
splits = net($)
prevLap = net($)
drawState = net($)
StatTrack = net($)
EncoreInitial = net($)
lb = net($)
MapRecords = net($)
TimeFinished = net($)
end
addHook("NetVars", netvars)

94
tools/coldstore.py Executable file
View File

@ -0,0 +1,94 @@
#!/bin/python
import sys
from os import linesep
if len(sys.argv) != 4 or not sys.argv[1] or not sys.argv[2] or not sys.argv[3]:
print("Usage: coldstore.py <leaderboard.txt> <coldstore.txt> <leaderboard_records.lua>")
quit()
leaderboard_txt = sys.argv[1]
coldstore_txt = sys.argv[2]
records_lua = sys.argv[3]
def ParseScore(score):
# Map Name Skin Color Time Splits Flags Stat
split = score.split("\t")
return {
"map": split[0],
"name": split[1],
"skin": split[2],
"color": split[3],
"time": int(split[4]),
"splits": split[5],
"flags": int(split[6]),
"stat": split[7]
}
# Compare scores
def CompareScore(a, b):
return a["time"] < b["time"]
F_SEP = 0xF
def SameScore(a, b):
return a["name"] == b["name"] and (a["flags"] & F_SEP) == (b["flags"] & F_SEP)
def LoadRecordsFromFile(path):
records = []
with open(path, "r") as f:
for line in f.readlines():
records.append(ParseScore(line.strip()))
return records
def AddScore(records, score):
mapid = score["map"]
mapTable = records.get(mapid) or []
for i in range(len(mapTable)):
scoreb = mapTable[i]
if SameScore(score, scoreb):
if CompareScore(score, scoreb):
mapTable[i] = score
records[mapid] = mapTable
return
mapTable.append(score)
records[mapid] = mapTable
# load leaderboard.txt and coldstore.txt
recordsList = LoadRecordsFromFile(leaderboard_txt)
recordsList.extend(LoadRecordsFromFile(coldstore_txt))
# construct the map tables
records = {}
for score in recordsList:
AddScore(records, score)
# convert records to flat list
recordsList = []
for mapTable in records.values():
for score in mapTable:
recordsList.append("\t".join([str(v) for v in list(score.values())]))
# truncate and write records to coldstore
with open(coldstore_txt, "w") as f:
for score in recordsList:
f.write(score + linesep)
luaA = """local ParseScore = lb_parse_score
local AddColdStore = lb_add_coldstore_record
local records = {
"""
luaB = """}
for _, str in ipairs(records) do
AddColdStore(ParseScore(str))
end
"""
# pack the records.lua file
with open(records_lua, "w") as f:
f.write(luaA)
for score in recordsList:
f.write("\"{}\",{}".format(score, linesep))
f.write(luaB)
# truncate leaderboard.txt
with open(leaderboard_txt, "w"): pass