Files
compressed-count-cc/compcount.lua
T
2026-06-01 04:08:50 +02:00

351 lines
8.2 KiB
Lua

local mon = peripheral.find("monitor")
assert(mon, "Kein Monitor gefunden")
local monName = peripheral.getName(mon)
local SCREEN_WIDTH = 40
mon.setTextScale(0.5)
local oldTerm = term.redirect(mon)
local Pine3D = require("Pine3D")
local mf = require("morefonts-pe")
local frame = Pine3D.newFrame()
frame:setBackgroundColor(colors.black)
local chainItemsByBaseId
local baseItemIds
local itemById
local defaultBaseId
local fallbackIcon = false
local backButton = false
local drawOverview
local function ensureChainsLoaded()
if not chainItemsByBaseId then
local file = assert(fs.open("atc_chains.json", "r"))
local data = textutils.unserializeJSON(file.readAll())
file.close()
assert(type(data) == "table" and type(data.chains) == "table", "Ungueltige JSON-Daten")
chainItemsByBaseId = {}
baseItemIds = {}
itemById = {}
for _, chain in ipairs(data.chains) do
if not defaultBaseId then
defaultBaseId = chain.base_id
end
baseItemIds[#baseItemIds + 1] = chain.base_id
local items = {}
local itemCount = 0
for _, item in ipairs(chain.items or {}) do
if item.stage ~= "item" then
itemCount = itemCount + 1
items[itemCount] = {
id = item.item_id,
icon_nfp = item.icon_nfp_16x16,
}
itemById[item.item_id] = items[itemCount]
end
end
chainItemsByBaseId[chain.base_id] = items
end
end
end
local function getPageItems(base_id)
ensureChainsLoaded()
return chainItemsByBaseId[base_id or defaultBaseId] or {}
end
local function getBaseItemIds()
ensureChainsLoaded()
return baseItemIds
end
local function getItemById(item_id)
ensureChainsLoaded()
return itemById[item_id]
end
local function parseNfpImage(nfp)
if type(nfp) ~= "string" or nfp == "" then
return nil
end
local image = {}
local y = 1
for line in (nfp .. "\n"):gmatch("(.-)\n") do
local row = {}
for x = 1, #line do
local c = line:sub(x, x)
if c ~= " " then
local value = tonumber(c, 16)
if value then
row[x] = 2 ^ value
end
end
end
image[y] = row
y = y + 1
end
return image
end
local function getItemIcon(item)
if not item then
return nil
end
if item.icon == nil then
item.icon = parseNfpImage(item.icon_nfp) or false
item.icon_nfp = nil
end
return item.icon or nil
end
local function getFallbackIcon()
if fallbackIcon == false then
local ok, image = pcall(paintutils.loadImage, "/icons16/diamond16.nfp")
fallbackIcon = ok and image or nil
end
return fallbackIcon
end
local function getBackButton()
if backButton == false then
backButton = parseNfpImage(table.concat({
"e e",
" e ",
"e e",
}, "\n"))
end
return backButton
end
local function imageSize(img)
local w, h = 0, 0
for y, row in pairs(img) do
h = math.max(h, y)
for x, value in pairs(row) do
if type(value) == "number" and value > 0 then
w = math.max(w, x)
end
end
end
return w, h
end
local function formatCount(n)
n = math.floor(tonumber(n) or 0)
if n < 1000 then
return tostring(n)
elseif n < 100000 then
local v = n / 1000
if v < 10 then
return string.format("%.1fK", v):gsub("%.0K", "K")
end
return tostring(math.floor(v)) .. "K"
elseif n < 100000000 then
local v = n / 1000000
if v < 10 then
return string.format("%.1fM", v):gsub("%.0M", "M")
end
return tostring(math.floor(v)) .. "M"
elseif n < 100000000000 then
local v = n / 1000000000
if v < 10 then
return string.format("%.1fB", v):gsub("%.0B", "B")
end
return tostring(math.floor(v)) .. "B"
else
local v = n / 1000000000000
if v < 10 then
return string.format("%.1fT", v):gsub("%.0T", "T")
end
return tostring(math.floor(v)) .. "T"
end
end
local function drawNfpScaled(buffer, img, x, y, scaleX, scaleY)
scaleX = scaleX or 2
scaleY = scaleY or 3
for imgY, row in pairs(img) do
for imgX, col in pairs(row) do
-- WICHTIG:
-- Nur echte ComputerCraft-Farben in den Pine3D-Buffer schreiben.
-- Keine Strings, keine nils, keine 0.
if type(col) == "number" and col > 0 then
local px = x + (imgX - 1) * scaleX
local py = y + (imgY - 1) * scaleY
for dy = 0, scaleY - 1 do
for dx = 0, scaleX - 1 do
buffer:setPixel(px + dx, py + dy, col)
end
end
end
end
end
end
local function assertBufferValid(frame)
local valid = {
[colors.white] = true,
[colors.orange] = true,
[colors.magenta] = true,
[colors.lightBlue] = true,
[colors.yellow] = true,
[colors.lime] = true,
[colors.pink] = true,
[colors.gray] = true,
[colors.lightGray] = true,
[colors.cyan] = true,
[colors.purple] = true,
[colors.blue] = true,
[colors.brown] = true,
[colors.green] = true,
[colors.red] = true,
[colors.black] = true,
}
for y = 1, frame.buffer.height do
for x = 1, frame.buffer.width do
local c = frame.buffer.colorValues[y][x]
if not valid[c] then
error("Ungueltiger Pixel im Buffer bei x=" .. x .. ", y=" .. y .. ": " .. tostring(c))
end
end
end
end
local function drawItem(img, cellX, cellY, count)
-- Normale Monitor-Zellen in Pine3D-Teletext-Pixel umrechnen.
local x = (cellX - 1) * 2 + 1
local y = (cellY - 1) * 3 + 1
local imgW, imgH = 0, 0
local scaleX = 1
local scaleY = 1
if img then
imgW, imgH = imageSize(img)
drawNfpScaled(frame.buffer, img, x, y, scaleX, scaleY)
end
local text = formatCount(count)
local rightX = x + 16 -- x + math.max(imgW * scaleX - 1, 7)
local bottomY = y + 16 -- y + math.max(imgH * scaleY - 1, 4)
local options = {
font = "fonts/QuinqueFive",
textAlign = "right",
anchorHor = "right",
anchorVer = "bottom",
condense = true,
scale = 1,
dx = 1,
dy = 1,
}
-- Schatten
mf.writeOn(frame, text, colors.black, rightX + 1, bottomY + 1, options)
-- Weißer Count
mf.writeOn(frame, text, colors.white, rightX, bottomY, options)
end
local function drawPage(base_id)
local pageItems = getPageItems(base_id)
local defaultIcon = getFallbackIcon()
local backIcon = getBackButton()
local backWidth, backHeight = imageSize(backIcon)
local backX = SCREEN_WIDTH - backWidth + 1
frame.buffer:clear()
drawNfpScaled(frame.buffer, backIcon, (backX - 1) * 2 + 1, 1)
-- TODO LATER get item counts from me-system using item ids. For now, just use random numbers.
for i = 1, math.min(math.max(#pageItems, 9), 9) do
local item = pageItems[i]
local icon = getItemIcon(item) or defaultIcon
drawItem(icon, 4+(8+4)*((i-1)%3), 2+(5+1)*math.floor((i-1)/3), math.random(0, 1000000000000))
end
assertBufferValid(frame)
frame:drawBuffer()
while true do
local _, side, x, y = os.pullEvent("monitor_touch")
if side == monName and x >= backX and x < backX + backWidth and y >= 1 and y <= backHeight then
drawOverview()
return
end
end
end
drawOverview = function()
local items = getBaseItemIds()
local visibleItemCount = math.min(math.max(#items, 9), 9)
-- TODO LATER: get item counts from me-system using item, then sort items according to their counts and display the top 9. For now, just use random numbers.
frame.buffer:clear()
for i = 1, visibleItemCount do
local item = getItemById(items[i])
local icon = getItemIcon(item) or getFallbackIcon()
drawItem(icon, 4+(8+4)*((i-1)%3), 2+(5+1)*math.floor((i-1)/3), math.random(0, 1000000000000))
end
assertBufferValid(frame)
frame:drawBuffer()
while true do
local _, side, x, y = os.pullEvent("monitor_touch")
if side == monName then
local col = math.floor((x - 4) / 12)
local row = math.floor((y - 2) / 6)
if col >= 0 and col < 3 and row >= 0 and row < 3 then
local index = row * 3 + col + 1
if index <= visibleItemCount then
drawPage(items[index])
return
end
end
end
end
end
drawOverview()
term.redirect(oldTerm)