3 Commits

Author SHA1 Message Date
Not
a840a5fa83 revert maploaded cvars 2022-08-24 16:18:24 +02:00
Not
bd5e3f24c6 add extra checks for spbattack 2022-08-23 17:10:10 +02:00
Not
27e596967c console record browser 2022-08-21 04:38:12 +02:00

View File

@ -12,7 +12,6 @@ local splits = {}
local PATCH = nil local PATCH = nil
local help = true local help = true
local EncoreInitial = nil local EncoreInitial = nil
local cv_teamchange
local scoreTable local scoreTable
@ -34,8 +33,8 @@ local RedFlash = {
local StatTrack = false local StatTrack = false
local UNCLAIMED = "Unclaimed Record" local UNCLAIMED = "Unclaimed Record"
local HELP_MESSAGE = "\x89Leaderboard Commands:\nretry exit findmap changelevel spba_clearcheats lb_gui rival scroll encore" local HELP_MESSAGE = "\x89Leaderboard Commands:\nretry exit findmap changelevel spba_clearcheats lb_gui rival scroll encore records"
local FILENAME = "leaderboard" local FILENAME = "leaderboard.txt"
-- Retry / changelevel map -- Retry / changelevel map
local nextMap = nil local nextMap = nil
@ -91,6 +90,10 @@ local ticsToTime
local allowJoin local allowJoin
--------------- ---------------
-- cvars
local cv_teamchange
local cv_spbatk
local cv_gui = CV_RegisterVar({ local cv_gui = CV_RegisterVar({
name = "lb_gui", name = "lb_gui",
defaultvalue = GUI_ON, defaultvalue = GUI_ON,
@ -150,36 +153,15 @@ local cv_interrupt = CV_RegisterVar({
end end
}) })
local function djb2(message)
local digest = 5381
for c in message:gmatch(".") do
digest = (($ << 5) + $) + string.byte(c)
end
return digest
end
local function mapChecksum(mapnum)
local mh = mapheaderinfo[mapnum]
if not mh then
return nil
end
local digest = string.format("%04x", djb2(mh.lvlttl+mh.subttl+mh.zonttl))
return string.sub(digest, #digest - 3)
end
local function setST(t, map, flags, scoreTable) local function setST(t, map, flags, scoreTable)
local mode = flags & ST_SEP local mode = flags & ST_SEP
local cks = mapChecksum(map)
t[mode] = t[mode] or {} t[mode] = t[mode] or {}
t[mode][tostring(map)..(cks or "")] = scoreTable t[mode][map] = scoreTable
end end
local function getST(t, map, flags) local function getST(t, map, flags)
local mode = flags & ST_SEP local mode = flags & ST_SEP
local cks = mapChecksum(map) return t[mode] and t[mode][map] or nil
return t[mode] and t[mode][tostring(map)..(cks or "")] or nil
end end
local function setScoreTable(map, flags, scoreTable) local function setScoreTable(map, flags, scoreTable)
@ -243,7 +225,7 @@ local cv_spb_separate = CV_RegisterVar({
end end
}) })
local function score_t(map, name, skin, color, time, splits, flags, stat, checksum) local function score_t(map, name, skin, color, time, splits, flags, stat)
return { return {
["map"] = map, ["map"] = map,
["name"] = name, ["name"] = name,
@ -252,8 +234,7 @@ local function score_t(map, name, skin, color, time, splits, flags, stat, checks
["time"] = time, ["time"] = time,
["splits"] = splits, ["splits"] = splits,
["flags"] = flags, ["flags"] = flags,
["stat"] = stat, ["stat"] = stat
["checksum"] = checksum
} }
end end
@ -275,47 +256,12 @@ local function stat_str(stat)
return "0" return "0"
end end
local function writeScore(fh, score)
fh: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"]), "\t",
score["checksum"], "\n"
)
end
local function saveScores()
local f = assert(io.open(FILENAME + ".txt", "w"))
if f == nil then
print("Failed to open file for writing: " + FILENAME + ".txt")
return
end
for _, tbl in pairs(lb) do
for _, scoreTable in pairs(tbl) do
for _, score in ipairs(scoreTable) do
writeScore(f, score)
end
end
end
f:close()
end
-- Read the leaderboard -- Read the leaderboard
local f = io.open(FILENAME + ".txt", "r") local f = io.open(FILENAME, "r")
if f then if f then
-- track if scores checksums have been written
local checksumWrite = false
for l in f:lines() do for l in f:lines() do
-- Leaderboard is stored in the following tab separated format -- Leaderboard is stored in the following tab separated format
-- mapnum, name, skin, color, time, splits, flags, stat, map_checksum -- mapnum, name, skin, color, time, splits, flags, stat
local t = {} local t = {}
for word in (l+"\t"):gmatch("(.-)\t") do for word in (l+"\t"):gmatch("(.-)\t") do
table.insert(t, word) table.insert(t, word)
@ -344,41 +290,17 @@ if f then
end end
end end
local mapnum = tonumber(t[1])
-- Checksums
local cks = t[9]
if cks == nil then
checksumWrite = true
cks = mapChecksum(mapnum)
-- We have no previous recognition of this map
-- remove it
if cks == nil then
local rsfh = io.open(FILENAME + "_removed_scores.txt", "a")
if not rsfh then
print("Failed to open file: ", FILENAME + "_removed_scores.txt")
continue
end
rsfh:write(l, "\n")
rsfh:close()
continue
end
end
table.insert( table.insert(
scoreTable, scoreTable,
score_t( score_t(
mapnum, tonumber(t[1]),
t[2], t[2],
t[3], t[3],
t[4], t[4],
tonumber(t[5]), tonumber(t[5]),
spl, spl,
flags, flags,
stats, stats
cks
) )
) )
@ -387,13 +309,8 @@ if f then
sortScores() sortScores()
f:close() f:close()
-- save scores
if checksumWrite then
saveScores()
end
else else
print("Failed to open file: ", FILENAME + ".txt") print("Failed to open file: ", FILENAME)
end end
function allowJoin(v) function allowJoin(v)
@ -469,6 +386,20 @@ local function exitlevel(player, ...)
end end
COM_AddCommand("exit", exitlevel) COM_AddCommand("exit", exitlevel)
local function zoneAct(map)
local z = ""
if map.zonttl != "" then
z = " " + map.zonttl
elseif not(map.levelflags & LF_NOZONE) then
z = " Zone"
end
if map.actnum != "" then
z = $ + " " + map.actnum
end
return z
end
local function findMap(player, ...) local function findMap(player, ...)
local search = ... local search = ...
@ -481,28 +412,13 @@ local function findMap(player, ...)
} }
local lvltype, map, lvlttl local lvltype, map, lvlttl
local zoneAct = function(zone, act, levelflags)
local z = ""
if zone != "" then
z = " " + zone
elseif not(levelflags & LF_NOZONE) then
z = " Zone"
end
if act != "" then
z = $ + " " + act
end
return z
end
for i = 1, #mapheaderinfo do for i = 1, #mapheaderinfo do
map = mapheaderinfo[i] map = mapheaderinfo[i]
if map == nil then if map == nil then
continue continue
end end
lvlttl = map.lvlttl + zoneAct(map.zonttl, map.actnum, map.levelflags) lvlttl = map.lvlttl + zoneAct(map)
if not search or lvlttl:lower():find(search:lower()) then if not search or lvlttl:lower():find(search:lower()) then
-- Only care for up to TOL_MATCH (0x10) -- Only care for up to TOL_MATCH (0x10)
@ -529,10 +445,129 @@ local function findMap(player, ...)
end end
COM_AddCommand("findmap", findMap) COM_AddCommand("findmap", findMap)
local function mapNotExists(player, map) local function mapnumFromExtended(map)
CONS_Printf(player, string.format("Map doesn't exist: %s", map:upper())) local p, q = map:upper():match("MAP(%w)(%w)$", 1)
if not (p and q) then
return nil
end
local mapnum = 0
local A = string.byte("A")
if tonumber(p) != nil then
-- Non extended map numbers
if tonumber(q) == nil then
return nil
end
mapnum = tonumber(p) * 10 + tonumber(q)
else
--Extended map numbers
p = string.byte(p) - A
local qn = tonumber(q)
if qn == nil then
qn = string.byte(q) - A + 10
end
mapnum = 36 * p + qn + 100
end
return mapnum
end end
local SPBModeSym = {
[F_SPBEXP] = "X",
[F_SPBBIG] = "B",
[F_SPBJUS] = "J",
}
local function modeToString(mode)
local modestr = "Time Attack"
if mode & F_SPBATK then
modestr = "SPB"
for k, v in pairs(SPBModeSym) do
if mode & k then
modestr = $ + v
end
end
end
return modestr
end
local function records(player, ...)
local mapid = ...
local mapnum = gamemap
if mapid then
mapnum = mapnumFromExtended(mapid)
if not mapnum then
CONS_Printf(player, string.format("Invalid map name: %s", mapid))
return
end
end
local map = mapheaderinfo[mapnum]
if map then
CONS_Printf(player,
string.format(
"\x83%s%8s",
map.lvlttl,
(map.menuflags & LF2_HIDEINMENU and "\x85HELL") or ""
)
)
local zoneact = zoneAct(map)
-- print the zone/act on the right hand size under the title
CONS_Printf(
player,
string.format(
string.format("\x83%%%ds%%s\x80 - \x88%%s", #map.lvlttl - #zoneact / 2 - 1),
" ",
zoneAct(map),
map.subttl
)
)
else
CONS_Printf(player, "\x85UNKNOWN MAP")
end
for mode, maps in pairs(lb) do
local maptbl = maps[mapnum]
if not maptbl then continue end
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
CONS_Printf(
player,
string.format(
"%2d %-21s \x89%8s \x80%s",
i,
tbl["name"],
ticsToTime(tbl["time"]),
modeToString(tbl["flags"])
)
)
end
else
for i, tbl in ipairs(maptbl) do
CONS_Printf(
player,
string.format(
"%2d %-21s \x89%8s",
i,
tbl["name"],
ticsToTime(tbl["time"])
)
)
end
end
end
end
COM_AddCommand("records", records)
local function changelevel(player, ...) local function changelevel(player, ...)
if not doyoudare(player) then if not doyoudare(player) then
return return
@ -547,35 +582,13 @@ local function changelevel(player, ...)
return return
end end
local p, q = map:upper():match("MAP(%w)(%w)$", 1) local mapnum = mapnumFromExtended(map)
if not (p and q) then if not mapnum then
CONS_Printf(player, string.format("Invalid map name: %s", map)) CONS_Printf(player, string.format("Invalid map name: %s", map))
return
end
local mapnum = 0
local A = string.byte("A")
if tonumber(p) != nil then
-- Non extended map numbers
if tonumber(q) == nil then
mapNotExists(player, map)
return
end
mapnum = tonumber(p) * 10 + tonumber(q)
else
--Extended map numbers
p = string.byte(p) - A
local qn = tonumber(q)
if qn == nil then
qn = string.byte(q) - A + 10
end
mapnum = 36 * p + qn + 100
end end
if mapheaderinfo[mapnum] == nil then if mapheaderinfo[mapnum] == nil then
mapNotExists(player, map) CONS_Printf(player, string.format("Map doesn't exist: %s", map:upper()))
return return
end end
@ -698,12 +711,6 @@ local function findRival(player, ...)
return a["rival"]["map"] < b["rival"]["map"] return a["rival"]["map"] < b["rival"]["map"]
end end
local modestrings = {
[F_SPBEXP] = "X",
[F_SPBBIG] = "B",
[F_SPBJUS] = "J",
}
for mode, tbl in pairs(scores) do for mode, tbl in pairs(scores) do
if i >= stop then break end if i >= stop then break end
@ -717,15 +724,7 @@ local function findRival(player, ...)
if i >= stop then break end if i >= stop then break end
i = i + 1 i = i + 1
local modestr = "TA" local modestr = modeToString(score["rival"]["flags"])
if score["rival"]["flags"] & F_SPBATK then
modestr = "SPB"
for k, v in pairs(modestrings) do
if score["rival"]["flags"] & k then
modestr = $ + v
end
end
end
if score["your"] then if score["your"] then
local diff = score["your"]["time"] - score["rival"]["time"] local diff = score["your"]["time"] - score["rival"]["time"]
@ -1215,8 +1214,7 @@ local function saveTime(player)
timeFinished, timeFinished,
splits, splits,
Flags, Flags,
stat_t(player.HMRs or pskin.kartspeed, player.HMRw or pskin.kartweight), stat_t(player.HMRs or pskin.kartspeed, player.HMRw or pskin.kartweight)
mapChecksum(gamemap)
) )
-- Check if you beat your previous best -- Check if you beat your previous best
@ -1261,7 +1259,30 @@ local function saveTime(player)
StatTrack = true StatTrack = true
end end
saveScores() 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 end
-- DEBUGGING -- DEBUGGING
@ -1361,10 +1382,15 @@ local function think()
end end
end end
end end
if not cv_spbatk then
cv_spbatk = CV_FindVar("spbatk")
end
-- Gamemode flags -- Gamemode flags
Flags = $ & !(F_SPBATK | F_SPBEXP | F_SPBBIG | F_SPBJUS) Flags = $ & !(F_SPBATK | F_SPBEXP | F_SPBBIG | F_SPBJUS)
if leveltime > START_TIME - (3 * TICRATE) / 2 and server.SPBArunning then if server.SPBArunning
and cv_spbatk.value
and leveltime > START_TIME - (3 * TICRATE) / 2 then
Flags = $ | F_SPBATK Flags = $ | F_SPBATK
if server.SPBAexpert then if server.SPBAexpert then
Flags = $ | F_SPBEXP Flags = $ | F_SPBEXP
@ -1387,10 +1413,18 @@ local function think()
end end
end end
-- make sure the spb actually spawned
if 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
if not (Flags & F_SPBATK) then end
else
hud.enable("freeplay") hud.enable("freeplay")
end end
end end
scoreTable = getScoreTable(gamemap, Flags) scoreTable = getScoreTable(gamemap, Flags)