local HighScores = {} local MAX_SCORES = 10 local SAVE_FILE = "asteroids_scores.dat" local scores = {} local entry = { active = false, score = 0, letters = {"A", "A", "A"}, position = 1, blink = 0, } function HighScores.init() HighScores.load() end function HighScores.load() scores = {} if love.filesystem.getInfo(SAVE_FILE) then local data = love.filesystem.read(SAVE_FILE) if data then for line in data:gmatch("[^\n]+") do local initials, score = line:match("^(%a%a%a)%s+(%d+)$") if initials and score then table.insert(scores, {initials = initials:upper(), score = tonumber(score)}) end end end end table.sort(scores, function(a, b) return a.score > b.score end) while #scores > MAX_SCORES do table.remove(scores) end end function HighScores.save() local lines = {} for _, e in ipairs(scores) do table.insert(lines, string.format("%s %d", e.initials, e.score)) end love.filesystem.write(SAVE_FILE, table.concat(lines, "\n") .. "\n") end function HighScores.isHighScore(score) if score <= 0 then return false end if #scores < MAX_SCORES then return true end return score > scores[#scores].score end function HighScores.addScore(initials, score) table.insert(scores, {initials = initials:upper(), score = score}) table.sort(scores, function(a, b) return a.score > b.score end) while #scores > MAX_SCORES do table.remove(scores) end HighScores.save() end function HighScores.getScores() local result = {} for _, s in ipairs(scores) do table.insert(result, {initials = s.initials, score = s.score}) end return result end function HighScores.getHighest() if #scores > 0 then return scores[1].score end return 0 end function HighScores.startEntry(score) entry.active = true entry.score = score entry.letters = {"A", "A", "A"} entry.position = 1 entry.blink = 0 end function HighScores.isEntryActive() return entry.active end function HighScores.updateEntry(dt) entry.blink = entry.blink + dt end function HighScores.keypressedEntry(key) if not entry.active then return nil end if key == "left" then entry.position = math.max(1, entry.position - 1) elseif key == "right" then entry.position = math.min(3, entry.position + 1) elseif key == "up" then local b = entry.letters[entry.position]:byte() b = b + 1; if b > 90 then b = 65 end entry.letters[entry.position] = string.char(b) elseif key == "down" then local b = entry.letters[entry.position]:byte() b = b - 1; if b < 65 then b = 90 end entry.letters[entry.position] = string.char(b) elseif key == "return" or key == "kpenter" then local initials = table.concat(entry.letters) local score = entry.score entry.active = false HighScores.addScore(initials, score) return "done", {initials = initials, score = score} elseif key:match("^%a$") and #key == 1 then entry.letters[entry.position] = key:upper() if entry.position < 3 then entry.position = entry.position + 1 end end return nil end function HighScores.drawEntry(screenW, screenH, palette, fonts) local midX = screenW / 2 local midY = screenH * 0.25 local t = love.timer.getTime() love.graphics.setFont(fonts.large) local pulse = 0.7 + math.sin(t * 4) * 0.3 love.graphics.setColor(palette.bright[1], palette.bright[2], palette.bright[3], pulse) love.graphics.printf("NEW HIGH SCORE", 0, midY, screenW, "center") midY = midY + fonts.large:getHeight() + 16 love.graphics.setFont(fonts.medium) love.graphics.setColor(palette.fg) love.graphics.printf(string.format("%d", entry.score), 0, midY, screenW, "center") midY = midY + fonts.medium:getHeight() + 32 local boxW = math.floor(screenW * 0.06) local boxH = math.floor(boxW * 1.3) local gap = math.floor(boxW * 0.4) local totalW = boxW * 3 + gap * 2 local startX = midX - totalW / 2 love.graphics.setFont(fonts.large) local letterH = fonts.large:getHeight() for i = 1, 3 do local bx = startX + (i - 1) * (boxW + gap) local by = midY local isSelected = (i == entry.position) if isSelected then local blinkOn = math.floor(entry.blink * 3) % 2 == 0 love.graphics.setColor(blinkOn and palette.bright or palette.dim) love.graphics.setLineWidth(3) else love.graphics.setColor(palette.dim) love.graphics.setLineWidth(2) end love.graphics.rectangle("line", bx, by, boxW, boxH) if isSelected then love.graphics.setColor(palette.bright[1], palette.bright[2], palette.bright[3], 0.5) local arrowX = bx + boxW / 2 love.graphics.polygon("fill", arrowX-5, by-4, arrowX+5, by-4, arrowX, by-12) love.graphics.polygon("fill", arrowX-5, by+boxH+4, arrowX+5, by+boxH+4, arrowX, by+boxH+12) end love.graphics.setColor(palette.fg) local lw = fonts.large:getWidth(entry.letters[i]) love.graphics.print(entry.letters[i], bx + (boxW - lw)/2, by + (boxH - letterH)/2) end love.graphics.setFont(fonts.small) love.graphics.setColor(palette.dim) love.graphics.printf("TYPE LETTERS / ARROWS / ENTER TO CONFIRM", 0, midY + boxH + 32, screenW, "center") end function HighScores.drawTable(screenW, screenH, palette, fonts) local topY = screenH * 0.68 local lineH = fonts.medium:getHeight() + 4 love.graphics.setFont(fonts.medium) love.graphics.setColor(palette.bright) love.graphics.printf("HIGH SCORES", 0, topY, screenW, "center") topY = topY + lineH + 8 love.graphics.setFont(fonts.small) local entryH = fonts.small:getHeight() + 3 if #scores == 0 then return end local colW = math.floor(screenW * 0.4) local startX = (screenW - colW) / 2 for i, s in ipairs(scores) do local y = topY + (i - 1) * entryH if i == 1 then love.graphics.setColor(palette.bright) else love.graphics.setColor(palette.fg[1], palette.fg[2], palette.fg[3], 0.8) end love.graphics.print(string.format("%2d. %s", i, s.initials), startX, y) local sw = fonts.small:getWidth(string.format("%d", s.score)) love.graphics.print(string.format("%d", s.score), startX + colW - sw, y) end end return HighScores