feat: enhance billboard rendering with pie chart and storage ring display
This commit is contained in:
@@ -1450,8 +1450,8 @@ end
|
|||||||
|
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
-- Billboard rendering (raw monitor API, no Opus UI)
|
-- Billboard rendering (raw monitor API, no Opus UI)
|
||||||
-- Read-only goals display: storage bar, top items
|
-- Visual goals display with pie chart, storage
|
||||||
-- chart, stock alerts, activity indicators.
|
-- gauge, stock alerts, and activity indicators.
|
||||||
-------------------------------------------------
|
-------------------------------------------------
|
||||||
|
|
||||||
local BB = {} -- billboard color theme
|
local BB = {} -- billboard color theme
|
||||||
@@ -1470,10 +1470,24 @@ BB.alertLow = colors.red
|
|||||||
BB.alertWarn = colors.orange
|
BB.alertWarn = colors.orange
|
||||||
BB.activityOn = colors.lime
|
BB.activityOn = colors.lime
|
||||||
BB.activityOff = colors.gray
|
BB.activityOff = colors.gray
|
||||||
BB.graphBar = colors.cyan
|
|
||||||
BB.graphBarAlt = colors.lightBlue
|
|
||||||
BB.sectionHead = colors.yellow
|
BB.sectionHead = colors.yellow
|
||||||
|
|
||||||
|
-- Pie chart slice colors (12 distinct CC colors)
|
||||||
|
local PIE_COLORS = {
|
||||||
|
colors.red,
|
||||||
|
colors.orange,
|
||||||
|
colors.yellow,
|
||||||
|
colors.lime,
|
||||||
|
colors.green,
|
||||||
|
colors.cyan,
|
||||||
|
colors.lightBlue,
|
||||||
|
colors.blue,
|
||||||
|
colors.purple,
|
||||||
|
colors.magenta,
|
||||||
|
colors.pink,
|
||||||
|
colors.brown,
|
||||||
|
}
|
||||||
|
|
||||||
local function bbFormatNumber(n)
|
local function bbFormatNumber(n)
|
||||||
if n >= 1000000 then
|
if n >= 1000000 then
|
||||||
return string.format("%.1fM", n / 1000000)
|
return string.format("%.1fM", n / 1000000)
|
||||||
@@ -1509,24 +1523,17 @@ local function bbClearLine(y, bg)
|
|||||||
D.billboardMon.write(string.rep(" ", bbW))
|
D.billboardMon.write(string.rep(" ", bbW))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bbWriteCentered(y, text, fg, bg)
|
|
||||||
bbClearLine(y, bg or BB.bg)
|
|
||||||
bbSetColors(fg or BB.value, bg or BB.bg)
|
|
||||||
D.billboardMon.setCursorPos(math.floor((bbW - #text) / 2) + 1, y)
|
|
||||||
D.billboardMon.write(text)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function bbWriteAt(x, y, text, fg, bg)
|
local function bbWriteAt(x, y, text, fg, bg)
|
||||||
bbSetColors(fg or BB.value, bg or BB.bg)
|
bbSetColors(fg or BB.value, bg or BB.bg)
|
||||||
D.billboardMon.setCursorPos(x, y)
|
D.billboardMon.setCursorPos(x, y)
|
||||||
D.billboardMon.write(text)
|
D.billboardMon.write(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bbHLine(y, char, fg, bg)
|
local function bbHLine(y, fg)
|
||||||
bbClearLine(y, bg or BB.bg)
|
bbClearLine(y)
|
||||||
bbSetColors(fg or BB.border, bg or BB.bg)
|
bbSetColors(fg or BB.border, BB.bg)
|
||||||
D.billboardMon.setCursorPos(1, y)
|
D.billboardMon.setCursorPos(1, y)
|
||||||
D.billboardMon.write(string.rep(char or "-", bbW))
|
D.billboardMon.write(string.rep("\x8c", bbW))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function bbDrawBar(x, y, width, filled, fgColor, bgColor)
|
local function bbDrawBar(x, y, width, filled, fgColor, bgColor)
|
||||||
@@ -1541,52 +1548,171 @@ local function bbDrawBar(x, y, width, filled, fgColor, bgColor)
|
|||||||
D.billboardMon.setBackgroundColor(BB.bg)
|
D.billboardMon.setBackgroundColor(BB.bg)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Draw a pie chart using colored character cells.
|
||||||
|
--- slices = { { fraction=0.0-1.0, color=colors.X }, ... }
|
||||||
|
--- Draws into the rectangle (x1,y1) to (x1+size-1, y1+rows-1)
|
||||||
|
--- size = width in chars, rows = height in chars
|
||||||
|
local function bbDrawPie(x1, y1, size, rows, slices)
|
||||||
|
local cx = size / 2 -- center in char coords
|
||||||
|
local cy = rows / 2
|
||||||
|
local radius = math.min(cx, cy) - 0.5
|
||||||
|
-- Character cells are ~1.5x taller than wide; squish Y
|
||||||
|
local aspect = 1.5
|
||||||
|
|
||||||
|
-- Build cumulative angle boundaries
|
||||||
|
local angles = {}
|
||||||
|
local cumulative = 0
|
||||||
|
for i, slice in ipairs(slices) do
|
||||||
|
angles[i] = { start = cumulative, stop = cumulative + slice.fraction, color = slice.color }
|
||||||
|
cumulative = cumulative + slice.fraction
|
||||||
|
end
|
||||||
|
|
||||||
|
for row = 0, rows - 1 do
|
||||||
|
D.billboardMon.setCursorPos(x1, y1 + row)
|
||||||
|
for col = 0, size - 1 do
|
||||||
|
local dx = (col + 0.5 - cx)
|
||||||
|
local dy = (row + 0.5 - cy) * aspect
|
||||||
|
local dist = math.sqrt(dx * dx + dy * dy)
|
||||||
|
|
||||||
|
if dist <= radius then
|
||||||
|
-- Compute angle 0-1 (0 = top, clockwise)
|
||||||
|
local angle = math.atan2(dx, -dy) -- top = 0, clockwise
|
||||||
|
if angle < 0 then angle = angle + 2 * math.pi end
|
||||||
|
local frac = angle / (2 * math.pi)
|
||||||
|
|
||||||
|
-- Find which slice
|
||||||
|
local cellColor = BB.bg
|
||||||
|
for _, s in ipairs(angles) do
|
||||||
|
if frac >= s.start and frac < s.stop then
|
||||||
|
cellColor = s.color
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- Catch rounding at the very end
|
||||||
|
if cellColor == BB.bg and #angles > 0 then
|
||||||
|
cellColor = angles[#angles].color
|
||||||
|
end
|
||||||
|
|
||||||
|
D.billboardMon.setBackgroundColor(cellColor)
|
||||||
|
D.billboardMon.write(" ")
|
||||||
|
else
|
||||||
|
D.billboardMon.setBackgroundColor(BB.bg)
|
||||||
|
D.billboardMon.write(" ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
D.billboardMon.setBackgroundColor(BB.bg)
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Draw a storage ring/donut gauge.
|
||||||
|
--- Draws a ring showing used vs free with percentage in center.
|
||||||
|
local function bbDrawStorageRing(x1, y1, size, rows)
|
||||||
|
local cx = size / 2
|
||||||
|
local cy = rows / 2
|
||||||
|
local outerR = math.min(cx, cy) - 0.5
|
||||||
|
local innerR = outerR * 0.55
|
||||||
|
local aspect = 1.5
|
||||||
|
local ratio = cache.usedRatio or 0
|
||||||
|
|
||||||
|
local usedColor = BB.barFull
|
||||||
|
if ratio > 0.9 then usedColor = BB.barCrit
|
||||||
|
elseif ratio > 0.75 then usedColor = BB.barWarn end
|
||||||
|
|
||||||
|
for row = 0, rows - 1 do
|
||||||
|
D.billboardMon.setCursorPos(x1, y1 + row)
|
||||||
|
for col = 0, size - 1 do
|
||||||
|
local dx = (col + 0.5 - cx)
|
||||||
|
local dy = (row + 0.5 - cy) * aspect
|
||||||
|
local dist = math.sqrt(dx * dx + dy * dy)
|
||||||
|
|
||||||
|
if dist <= outerR and dist >= innerR then
|
||||||
|
-- In the ring — determine angle (top = 0, clockwise)
|
||||||
|
local angle = math.atan2(dx, -dy)
|
||||||
|
if angle < 0 then angle = angle + 2 * math.pi end
|
||||||
|
local frac = angle / (2 * math.pi)
|
||||||
|
|
||||||
|
if frac < ratio then
|
||||||
|
D.billboardMon.setBackgroundColor(usedColor)
|
||||||
|
else
|
||||||
|
D.billboardMon.setBackgroundColor(BB.barEmpty)
|
||||||
|
end
|
||||||
|
D.billboardMon.write(" ")
|
||||||
|
else
|
||||||
|
D.billboardMon.setBackgroundColor(BB.bg)
|
||||||
|
D.billboardMon.write(" ")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Write percentage in center of ring
|
||||||
|
local pct = tostring(math.floor(ratio * 100 + 0.5)) .. "%"
|
||||||
|
local textY = y1 + math.floor(cy)
|
||||||
|
local textX = x1 + math.floor(cx - #pct / 2)
|
||||||
|
bbWriteAt(textX, textY, pct, usedColor)
|
||||||
|
end
|
||||||
|
|
||||||
-- Billboard section: header
|
-- Billboard section: header
|
||||||
local function bbDrawHeader(y)
|
local function bbDrawHeader(y)
|
||||||
bbClearLine(y, BB.headerBg)
|
bbClearLine(y, BB.headerBg)
|
||||||
bbSetColors(BB.headerFg, BB.headerBg)
|
bbSetColors(BB.headerFg, BB.headerBg)
|
||||||
local title = " INVENTORY GOALS BILLBOARD "
|
local title = " INVENTORY BILLBOARD "
|
||||||
D.billboardMon.setCursorPos(math.floor((bbW - #title) / 2) + 1, y)
|
D.billboardMon.setCursorPos(math.floor((bbW - #title) / 2) + 1, y)
|
||||||
D.billboardMon.write(title)
|
D.billboardMon.write(title)
|
||||||
return y + 1
|
return y + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Billboard section: storage capacity
|
-- Billboard section: storage ring + stats (side by side)
|
||||||
local function bbDrawStorage(y)
|
local function bbDrawStorageSection(y)
|
||||||
bbHLine(y)
|
bbHLine(y)
|
||||||
y = y + 1
|
y = y + 1
|
||||||
bbWriteAt(2, y, "> STORAGE", BB.sectionHead)
|
|
||||||
y = y + 1
|
-- Ring takes up a square area
|
||||||
|
local ringSize = math.min(math.floor(bbW * 0.35), bbH - y - 6)
|
||||||
|
if ringSize < 5 then ringSize = 5 end
|
||||||
|
local ringRows = math.floor(ringSize / 1.5) -- aspect correction
|
||||||
|
if ringRows < 3 then ringRows = 3 end
|
||||||
|
|
||||||
|
-- Draw ring on the left
|
||||||
|
bbDrawStorageRing(2, y, ringSize, ringRows)
|
||||||
|
|
||||||
|
-- Stats text to the right of the ring
|
||||||
|
local statsX = ringSize + 4
|
||||||
|
local statsY = y + 1
|
||||||
|
|
||||||
|
bbWriteAt(statsX, statsY, "STORAGE", BB.sectionHead)
|
||||||
|
statsY = statsY + 1
|
||||||
|
|
||||||
local ratio = cache.usedRatio or 0
|
local ratio = cache.usedRatio or 0
|
||||||
local pct = math.floor(ratio * 100 + 0.5)
|
local usedColor = BB.barFull
|
||||||
local barColor = BB.barFull
|
if ratio > 0.9 then usedColor = BB.barCrit
|
||||||
if ratio > 0.9 then barColor = BB.barCrit
|
elseif ratio > 0.75 then usedColor = BB.barWarn end
|
||||||
elseif ratio > 0.75 then barColor = BB.barWarn end
|
|
||||||
|
|
||||||
local barW = bbW - 10
|
bbWriteAt(statsX, statsY, string.format("%s / %s slots",
|
||||||
if barW < 10 then barW = 10 end
|
bbFormatNumber(cache.usedSlots), bbFormatNumber(cache.totalSlots)), BB.value)
|
||||||
bbWriteAt(2, y, bbPadLeft(pct .. "%", 4), barColor)
|
statsY = statsY + 1
|
||||||
bbDrawBar(7, y, barW, ratio, barColor, BB.barEmpty)
|
|
||||||
y = y + 1
|
|
||||||
|
|
||||||
local stats = string.format(
|
bbWriteAt(statsX, statsY, string.format("%s total items",
|
||||||
"Slots: %s/%s | Items: %s | Chests: %d",
|
bbFormatNumber(cache.grandTotal)), BB.label)
|
||||||
bbFormatNumber(cache.usedSlots),
|
statsY = statsY + 1
|
||||||
bbFormatNumber(cache.totalSlots),
|
|
||||||
bbFormatNumber(cache.grandTotal),
|
bbWriteAt(statsX, statsY, string.format("%d chests", cache.chestCount), BB.label)
|
||||||
cache.chestCount
|
statsY = statsY + 1
|
||||||
)
|
|
||||||
bbWriteAt(2, y, stats, BB.label)
|
-- Mini capacity bar
|
||||||
y = y + 1
|
local barW = bbW - statsX - 1
|
||||||
return y
|
if barW > 3 then
|
||||||
|
bbWriteAt(statsX, statsY, "", BB.label)
|
||||||
|
bbDrawBar(statsX, statsY, barW, ratio, usedColor, BB.barEmpty)
|
||||||
|
end
|
||||||
|
|
||||||
|
return y + ringRows + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Billboard section: top items bar chart
|
-- Billboard section: pie chart + legend
|
||||||
local function bbDrawItems(y, maxRows)
|
local function bbDrawPieSection(y, maxH)
|
||||||
bbHLine(y)
|
bbHLine(y)
|
||||||
y = y + 1
|
y = y + 1
|
||||||
bbWriteAt(2, y, "> TOP ITEMS", BB.sectionHead)
|
bbWriteAt(2, y, "ITEM DISTRIBUTION", BB.sectionHead)
|
||||||
y = y + 1
|
y = y + 1
|
||||||
|
|
||||||
state.ensureItemList()
|
state.ensureItemList()
|
||||||
@@ -1596,49 +1722,96 @@ local function bbDrawItems(y, maxRows)
|
|||||||
return y + 1
|
return y + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Sort and pick top N for pie slices
|
||||||
local sorted = {}
|
local sorted = {}
|
||||||
for i, item in ipairs(items) do sorted[i] = item end
|
for i, item in ipairs(items) do sorted[i] = item end
|
||||||
table.sort(sorted, function(a, b) return a.total > b.total end)
|
table.sort(sorted, function(a, b) return a.total > b.total end)
|
||||||
|
|
||||||
local count = math.min(#sorted, maxRows, cfg.BILLBOARD_TOP_ITEMS or 20)
|
local maxSlices = math.min(#PIE_COLORS, cfg.BILLBOARD_TOP_ITEMS or 12, #sorted)
|
||||||
if count < 1 then count = 1 end
|
local topTotal = 0
|
||||||
|
for i = 1, maxSlices do
|
||||||
local maxVal = sorted[1].total
|
topTotal = topTotal + sorted[i].total
|
||||||
if maxVal < 1 then maxVal = 1 end
|
|
||||||
|
|
||||||
local numW = 8
|
|
||||||
local nameW = math.floor(bbW * 0.35)
|
|
||||||
if nameW < 12 then nameW = 12 end
|
|
||||||
if nameW > 25 then nameW = 25 end
|
|
||||||
local barW = bbW - nameW - numW - 5
|
|
||||||
if barW < 5 then barW = 5 end
|
|
||||||
|
|
||||||
for i = 1, count do
|
|
||||||
local item = sorted[i]
|
|
||||||
local name = shortName(item.name)
|
|
||||||
local num = bbFormatNumber(item.total)
|
|
||||||
local frac = item.total / maxVal
|
|
||||||
|
|
||||||
bbClearLine(y)
|
|
||||||
bbWriteAt(1, y, bbPadLeft(tostring(i), 2), BB.border)
|
|
||||||
bbWriteAt(4, y, bbPadRight(name, nameW), BB.value)
|
|
||||||
|
|
||||||
local barColor = (i % 2 == 0) and BB.graphBarAlt or BB.graphBar
|
|
||||||
local barStart = 4 + nameW + 1
|
|
||||||
bbDrawBar(barStart, y, barW, frac, barColor, BB.barEmpty)
|
|
||||||
bbWriteAt(barStart + barW + 1, y, bbPadLeft(num, numW), BB.value)
|
|
||||||
|
|
||||||
y = y + 1
|
|
||||||
end
|
end
|
||||||
return y
|
-- "Other" bucket for remaining items
|
||||||
|
local otherTotal = (cache.grandTotal or 0) - topTotal
|
||||||
|
local total = cache.grandTotal or 1
|
||||||
|
if total < 1 then total = 1 end
|
||||||
|
|
||||||
|
-- Build slices
|
||||||
|
local slices = {}
|
||||||
|
local legendItems = {}
|
||||||
|
for i = 1, maxSlices do
|
||||||
|
local frac = sorted[i].total / total
|
||||||
|
if frac < 0.005 then break end -- skip tiny slices
|
||||||
|
table.insert(slices, { fraction = frac, color = PIE_COLORS[i] })
|
||||||
|
table.insert(legendItems, {
|
||||||
|
name = shortName(sorted[i].name),
|
||||||
|
count = sorted[i].total,
|
||||||
|
pct = math.floor(frac * 100 + 0.5),
|
||||||
|
color = PIE_COLORS[i],
|
||||||
|
})
|
||||||
|
end
|
||||||
|
if otherTotal > 0 then
|
||||||
|
table.insert(slices, { fraction = otherTotal / total, color = BB.border })
|
||||||
|
table.insert(legendItems, {
|
||||||
|
name = "Other",
|
||||||
|
count = otherTotal,
|
||||||
|
pct = math.floor(otherTotal / total * 100 + 0.5),
|
||||||
|
color = BB.border,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Layout: pie on left, legend on right
|
||||||
|
local pieSize = math.min(math.floor(bbW * 0.4), maxH - 1)
|
||||||
|
if pieSize < 5 then pieSize = 5 end
|
||||||
|
local pieRows = math.floor(pieSize / 1.5)
|
||||||
|
if pieRows < 3 then pieRows = 3 end
|
||||||
|
if pieRows > maxH - 1 then pieRows = maxH - 1 end
|
||||||
|
|
||||||
|
-- Draw pie
|
||||||
|
if #slices > 0 then
|
||||||
|
bbDrawPie(2, y, pieSize, pieRows, slices)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Draw legend to the right
|
||||||
|
local legX = pieSize + 4
|
||||||
|
local legY = y
|
||||||
|
local legW = bbW - legX - 1
|
||||||
|
|
||||||
|
for i, item in ipairs(legendItems) do
|
||||||
|
if legY >= y + pieRows then break end
|
||||||
|
if legW < 10 then break end
|
||||||
|
|
||||||
|
-- Color swatch
|
||||||
|
D.billboardMon.setCursorPos(legX, legY)
|
||||||
|
D.billboardMon.setBackgroundColor(item.color)
|
||||||
|
D.billboardMon.write(" ")
|
||||||
|
D.billboardMon.setBackgroundColor(BB.bg)
|
||||||
|
D.billboardMon.write(" ")
|
||||||
|
|
||||||
|
-- Name + count
|
||||||
|
local label = item.name
|
||||||
|
local countStr = bbFormatNumber(item.count)
|
||||||
|
local pctStr = item.pct .. "%"
|
||||||
|
local infoW = legW - 4 -- 2 swatch + 1 space + padding
|
||||||
|
local detail = string.format("%s %s", pctStr, countStr)
|
||||||
|
local nameW = infoW - #detail - 1
|
||||||
|
if nameW < 4 then nameW = 4 end
|
||||||
|
if #label > nameW then label = label:sub(1, nameW - 1) .. "." end
|
||||||
|
|
||||||
|
bbWriteAt(legX + 3, legY, bbPadRight(label, nameW), BB.value)
|
||||||
|
bbWriteAt(legX + 3 + nameW + 1, legY, detail, BB.label)
|
||||||
|
|
||||||
|
legY = legY + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return y + pieRows
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Billboard section: stock alerts
|
-- Billboard section: stock alerts (compact)
|
||||||
local function bbDrawAlerts(y, maxRows)
|
local function bbDrawAlerts(y, maxRows)
|
||||||
bbHLine(y)
|
bbHLine(y)
|
||||||
y = y + 1
|
y = y + 1
|
||||||
bbWriteAt(2, y, "> STOCK ALERTS", BB.sectionHead)
|
|
||||||
y = y + 1
|
|
||||||
|
|
||||||
local alerts = state.activeAlerts
|
local alerts = state.activeAlerts
|
||||||
if not alerts or #alerts == 0 then
|
if not alerts or #alerts == 0 then
|
||||||
@@ -1646,13 +1819,18 @@ local function bbDrawAlerts(y, maxRows)
|
|||||||
return y + 1
|
return y + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
|
bbWriteAt(2, y, "ALERTS", BB.sectionHead)
|
||||||
|
local countStr = string.format("(%d)", #alerts)
|
||||||
|
bbWriteAt(9, y, countStr, BB.alertWarn)
|
||||||
|
y = y + 1
|
||||||
|
|
||||||
local colW = math.floor(bbW / 2)
|
local colW = math.floor(bbW / 2)
|
||||||
local twoCol = (bbW >= 40)
|
local twoCol = (bbW >= 30)
|
||||||
local row = 0
|
local row = 0
|
||||||
|
|
||||||
for i, alert in ipairs(alerts) do
|
for i, alert in ipairs(alerts) do
|
||||||
if row >= maxRows then
|
if row >= maxRows then
|
||||||
bbWriteAt(2, y, string.format(" ... +%d more", #alerts - i + 1), BB.alertWarn)
|
bbWriteAt(2, y, string.format(" +%d more", #alerts - i + 1), BB.alertWarn)
|
||||||
y = y + 1
|
y = y + 1
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@@ -1662,14 +1840,8 @@ local function bbDrawAlerts(y, maxRows)
|
|||||||
local minVal = alert.min or 0
|
local minVal = alert.min or 0
|
||||||
local ratio = minVal > 0 and (current / minVal) or 1
|
local ratio = minVal > 0 and (current / minVal) or 1
|
||||||
|
|
||||||
local icon, color
|
local color = ratio < 0.5 and BB.alertLow or BB.alertWarn
|
||||||
if ratio < 0.5 then
|
local text = string.format("! %s %s/%s", label, bbFormatNumber(current), bbFormatNumber(minVal))
|
||||||
icon, color = "!", BB.alertLow
|
|
||||||
else
|
|
||||||
icon, color = "!", BB.alertWarn
|
|
||||||
end
|
|
||||||
|
|
||||||
local text = string.format("%s %s: %s/%s", icon, label, bbFormatNumber(current), bbFormatNumber(minVal))
|
|
||||||
|
|
||||||
if twoCol then
|
if twoCol then
|
||||||
local col = ((i - 1) % 2 == 0) and 2 or (colW + 1)
|
local col = ((i - 1) % 2 == 0) and 2 or (colW + 1)
|
||||||
@@ -1689,43 +1861,45 @@ local function bbDrawAlerts(y, maxRows)
|
|||||||
return y
|
return y
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Billboard section: activity footer
|
-- Billboard section: activity bar (single line)
|
||||||
local function bbDrawActivity(y)
|
local function bbDrawActivityBar(y)
|
||||||
bbHLine(y)
|
bbClearLine(y, BB.headerBg)
|
||||||
y = y + 1
|
bbSetColors(BB.headerFg, BB.headerBg)
|
||||||
bbClearLine(y)
|
|
||||||
bbWriteAt(2, y, "ACTIVITY:", BB.sectionHead)
|
|
||||||
|
|
||||||
local labels = {
|
local labels = {
|
||||||
{ key = "sorting", label = "SORT" },
|
{ key = "sorting", label = "SORT" },
|
||||||
{ key = "scanning", label = "SCAN" },
|
{ key = "scanning", label = "SCAN" },
|
||||||
{ key = "smelting", label = "SMELT" },
|
{ key = "smelting", label = "SMLT" },
|
||||||
{ key = "dispensing", label = "DISPENSE" },
|
{ key = "dispensing", label = "DISP" },
|
||||||
{ key = "defragging", label = "DEFRAG" },
|
{ key = "defragging", label = "DEFR" },
|
||||||
{ key = "composting", label = "COMPOST" },
|
{ key = "composting", label = "COMP" },
|
||||||
{ key = "crafting", label = "CRAFT" },
|
{ key = "crafting", label = "CRFT" },
|
||||||
{ key = "autocrafting", label = "AUTOCRAFT" },
|
{ key = "autocrafting", label = "AUTO" },
|
||||||
{ key = "discarding", label = "DISCARD" },
|
{ key = "discarding", label = "DISC" },
|
||||||
}
|
}
|
||||||
|
|
||||||
local x = 12
|
local parts = {}
|
||||||
local anyActive = false
|
|
||||||
for _, entry in ipairs(labels) do
|
for _, entry in ipairs(labels) do
|
||||||
if activity[entry.key] then
|
if activity[entry.key] then
|
||||||
anyActive = true
|
table.insert(parts, entry.label)
|
||||||
if x + #entry.label + 2 > bbW then
|
|
||||||
y = y + 1
|
|
||||||
bbClearLine(y)
|
|
||||||
x = 3
|
|
||||||
end
|
|
||||||
bbWriteAt(x, y, entry.label, BB.activityOn)
|
|
||||||
x = x + #entry.label + 2
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if not anyActive then
|
local text
|
||||||
bbWriteAt(12, y, "IDLE", BB.activityOff)
|
if #parts > 0 then
|
||||||
|
text = " " .. table.concat(parts, " | ") .. " "
|
||||||
|
else
|
||||||
|
text = " IDLE "
|
||||||
end
|
end
|
||||||
|
|
||||||
|
D.billboardMon.setCursorPos(math.floor((bbW - #text) / 2) + 1, y)
|
||||||
|
if #parts > 0 then
|
||||||
|
bbSetColors(colors.white, colors.green)
|
||||||
|
else
|
||||||
|
bbSetColors(BB.label, BB.headerBg)
|
||||||
|
end
|
||||||
|
D.billboardMon.write(text)
|
||||||
|
|
||||||
return y + 1
|
return y + 1
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -1738,29 +1912,34 @@ function D.drawBillboard()
|
|||||||
D.billboardMon.clear()
|
D.billboardMon.clear()
|
||||||
|
|
||||||
local y = 1
|
local y = 1
|
||||||
|
|
||||||
|
-- Header bar
|
||||||
y = bbDrawHeader(y)
|
y = bbDrawHeader(y)
|
||||||
y = bbDrawStorage(y)
|
|
||||||
|
|
||||||
-- Allocate vertical space: alerts ~3-8 lines, footer ~2 lines
|
-- Storage ring + stats
|
||||||
local alertLines = state.activeAlerts and #state.activeAlerts or 0
|
y = bbDrawStorageSection(y)
|
||||||
local alertSectionH = math.max(3, math.min(6, math.ceil(alertLines / 2) + 2))
|
|
||||||
local footerH = 2
|
|
||||||
local itemRows = bbH - y - alertSectionH - footerH
|
|
||||||
if itemRows < 3 then itemRows = 3 end
|
|
||||||
|
|
||||||
y = bbDrawItems(y, itemRows)
|
-- Pie chart + legend (gets remaining space minus alerts + footer)
|
||||||
|
local alertCount = state.activeAlerts and #state.activeAlerts or 0
|
||||||
|
local alertH = math.max(2, math.min(5, math.ceil(alertCount / 2) + 2))
|
||||||
|
local footerH = 1 -- activity bar
|
||||||
|
local pieH = bbH - y - alertH - footerH
|
||||||
|
if pieH < 5 then pieH = 5 end
|
||||||
|
y = bbDrawPieSection(y, pieH)
|
||||||
|
|
||||||
local alertMaxRows = bbH - y - footerH
|
-- Alerts
|
||||||
if alertMaxRows < 2 then alertMaxRows = 2 end
|
local alertMaxRows = bbH - y - footerH - 1
|
||||||
|
if alertMaxRows < 1 then alertMaxRows = 1 end
|
||||||
y = bbDrawAlerts(y, alertMaxRows)
|
y = bbDrawAlerts(y, alertMaxRows)
|
||||||
|
|
||||||
-- Fill gap before footer
|
-- Fill gap before footer
|
||||||
local footerY = bbH - 1
|
while y < bbH do
|
||||||
while y < footerY do
|
|
||||||
bbClearLine(y)
|
bbClearLine(y)
|
||||||
y = y + 1
|
y = y + 1
|
||||||
end
|
end
|
||||||
bbDrawActivity(footerY)
|
|
||||||
|
-- Activity bar at very bottom
|
||||||
|
bbDrawActivityBar(bbH)
|
||||||
end
|
end
|
||||||
|
|
||||||
return D
|
return D
|
||||||
|
|||||||
Reference in New Issue
Block a user