diff --git a/components/BigText.lua b/components/BigText.lua index 009244e..85e2970 100644 --- a/components/BigText.lua +++ b/components/BigText.lua @@ -26,7 +26,7 @@ cx = props.width - bigFont:getWidth(props.text) - 2 end end - bigFont:write(canvas, props.text, 2 + cx, bgHeight-3, props.color or colors.white) + bigFont:write(canvas, props.text, math.max(2, cx), bgHeight-3, props.color or colors.white) return function() canvas:markRect(1, 1, fw, bigFont.height+bgHeight) diff --git a/components/Button.lua b/components/Button.lua index c015012..ecbc249 100644 --- a/components/Button.lua +++ b/components/Button.lua @@ -10,6 +10,8 @@ -- local canvas = useCanvas() return BigText { + display = props.display, + align = props.align, text = props.text, x = props.x, y = props.y, @@ -18,6 +20,6 @@ width = props.width, }, { -- canvas = canvas, - aabb = useBoundingBox(props.x, props.y, props.width or bigFont:getWidth(props.text), bigFont.height+3, props.onClick), + aabb = useBoundingBox(props.x, props.y, props.width or bigFont:getWidth(props.text), bigFont.height+6, props.onClick), } end) diff --git a/components/SmolButton.lua b/components/SmolButton.lua new file mode 100644 index 0000000..084ed93 --- /dev/null +++ b/components/SmolButton.lua @@ -0,0 +1,25 @@ +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useBoundingBox = hooks.useBoundingBox + +local SmolText = require("components.SmolText") +local smolFont = require("fonts.smolfont") + +return Solyd.wrapComponent("SmolButton", function(props) + -- local canvas = Solyd.useContext("canvas") + -- local canvas = useCanvas() + + return SmolText { + display = props.display, + align = props.align, + text = props.text, + x = props.x, + y = props.y, + bg = props.bg, + color = props.color, + width = props.width, + }, { + -- canvas = canvas, + aabb = useBoundingBox(props.x, props.y, props.width or smolFont:getWidth(props.text), smolFont.height+3, props.onClick), + } +end) diff --git a/config.lua b/config.lua index a3540d4..aaca41f 100644 --- a/config.lua +++ b/config.lua @@ -4,7 +4,9 @@ }, settings = { hideUnavailableProducts = false, - pollFrequency = 30 + pollFrequency = 30, + categoryCycleFrequency = 20, + activityTimeout = 60, }, theme = { formatting = { @@ -24,7 +26,18 @@ productNameColor = colors.white, outOfStockNameColor = colors.lightGray, priceColor = colors.lime, - addressColor = colors.white + addressColor = colors.white, + currencyTextColor = colors.white, + currency1Color = colors.green, + currency2Color = colors.pink, + currency3Color = colors.lightBlue, + currency4Color = colors.yellow, + catagoryTextColor = colors.white, + category1Color = colors.pink, + category2Color = colors.orange, + category3Color = colors.lime, + category4Color = colors.lightBlue, + activeCategoryColor = colors.black, }, palette = { [colors.black] = 0x181818, @@ -49,7 +62,7 @@ { id = "krist", -- if not krist or tenebra, must supply endpoint -- node = "https://krist.dev" - host = "kristallie", + host = "ksbangelco", name = "radon.kst", pkey = "", pkeyFormat = "raw", -- Either 'raw' or 'kristwallet', defaults to 'raw' diff --git a/core/ConfigValidator.lua b/core/ConfigValidator.lua index bd925bd..1538349 100644 --- a/core/ConfigValidator.lua +++ b/core/ConfigValidator.lua @@ -7,6 +7,8 @@ settings = { hideUnavailableProducts = "boolean", pollFrequency = "number", + categoryCycleFrequency = "number", + activityTimeout = "number", }, theme = { formatting = { @@ -26,7 +28,18 @@ productNameColor = "color", outOfStockNameColor = "color", priceColor = "color", - addressColor = "color" + addressColor = "color", + currencyTextColor = "color", + currency1Color = "color", + currency2Color = "color", + currency3Color = "color", + currency4Color = "color", + catagoryTextColor = "color", + category1Color = "color", + category2Color = "color", + category3Color = "color", + category4Color = "color", + activeCategoryColor = "color", }, palette = { [colors.black] = "number", @@ -78,8 +91,9 @@ name = "string?", address = "string", order = "number?", - price = "number", quantity = "number?", + category = "string?", + price = "number", priceOverrides = { __type = "array?", __entry = { diff --git a/core/ShopState.lua b/core/ShopState.lua index ade921e..d1e438e 100644 --- a/core/ShopState.lua +++ b/core/ShopState.lua @@ -12,6 +12,11 @@ self.running = false self.config = config self.products = products + self.selectedCurrency = config.currencies[1] + self.selectedCategory = 1 + self.numCategories = 1 + self.productsChanged = false + self.lastTouched = os.epoch("utc") return self end @@ -52,8 +57,22 @@ end, function() while state.running do ScanInventory.updateProductInventory(state.products) + if state.config.settings.hideUnavailableProducts then + state.productsChanged = true + end sleep(state.config.settings.pollFrequency) end + end, function() + while state.running do + if os.epoch("utc") > state.lastTouched + (state.config.settings.activityTimeout * 1000) then + state.selectedCategory = state.selectedCategory + 1 + if state.selectedCategory > state.numCategories then + state.selectedCategory = 1 + end + state.productsChanged = true + end + sleep(state.config.settings.categoryCycleFrequency) + end end) end diff --git a/fonts/smolfont.lua b/fonts/smolfont.lua index ad017d4..5b07892 100644 --- a/fonts/smolfont.lua +++ b/fonts/smolfont.lua @@ -2,6 +2,6 @@ local createFont = require("modules.font") local smolFontSheet = loadRIF("res/smolfont.rif") -local smolFont = createFont(smolFontSheet, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:;<=>?[\\]^_{|}~\128` !\"#$%&'()*+,-./@") +local smolFont = createFont(smolFontSheet, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:;<=>?[\\]^_{|}~\128` !\"#$%&'()*+,-./@\164") return smolFont diff --git a/products.lua b/products.lua index 9f39985..f8446fa 100644 --- a/products.lua +++ b/products.lua @@ -3,6 +3,7 @@ modid = "minecraft:lapis_block", name = "Lapis Block", address = "lapis", + category = "ore", price = 9.0, priceOverrides = { { @@ -15,6 +16,7 @@ modid = "minecraft:diamond", name = "Diamond", address = "dia", + category = "ore", price = 5.0, priceOverrides = { { @@ -27,6 +29,7 @@ modid = "minecraft:gold_ingot", name = "Gold Ingot", address = "gold", + category = "ore", price = 5.0, priceOverrides = { { @@ -39,6 +42,7 @@ modid = "minecraft:diamond_pickaxe", name = "Diamond Pickaxe eff5", address = "dpick", + category = "tool", price = 50.0, predicates = { enchantments = { diff --git a/radon.lua b/radon.lua index e40c56c..12578e2 100644 --- a/radon.lua +++ b/radon.lua @@ -7,6 +7,8 @@ local hooks = require("modules.hooks") local useCanvas = hooks.useCanvas +local Button = require("components.Button") +local SmolButton = require("components.SmolButton") local BigText = require("components.BigText") local bigFont = require("fonts.bigfont") local SmolText = require("components.SmolText") @@ -31,7 +33,9 @@ local function getDisplayedProducts(allProducts, settings) local displayedProducts = {} - for _, product in ipairs(allProducts) do + for i = 1, #allProducts do + local product = allProducts[i] + product.id = i if not settings.hideUnavailableProducts or product.quantity > 0 then table.insert(displayedProducts, product) end @@ -39,12 +43,79 @@ return displayedProducts end +local function getProductPrice(product, currency) + local price = product.price / currency.value + if product.priceOverrides then + for i = 1, #product.priceOverrides do + local override = product.priceOverrides[i] + if override.currency == currency.id then + price = override.price + break + end + end + end + return price +end + +local function getCurrencySymbol(currency, productTextSize) + local currencySymbol + if currency.krypton and currency.krypton.currency then + currencySymbol = currency.krypton.currency.currency_symbol + elseif not currencySymbol and currency.name:find("%.") then + currencySymbol = currency.name:sub(currency.name:find("%.")+1, #currency.name) + elseif currency.id == "tenebra" then + currencySymbol = "tst" + else + currencySymbol = "KST" + end + if currencySymbol == "TST" then + currencySymbol = "tst" + end + if currencySymbol:lower() == "kst" and productTextSize == "medium" then + currencySymbol = "kst" + elseif currencySymbol:lower() == "kst" then + currencySymbol = "\164" + end + return currencySymbol +end + +local function getCategories(products) + local categories = {} + for _, product in ipairs(products) do + local category = product.category + if not category then + category = "*" + end + local found = nil + for i = 1, #categories do + if categories[i].name == category then + found = i + break + end + end + if not found then + if category == "*" then + table.insert(categories, 1, {name=category, products={}}) + found = 1 + else + table.insert(categories, {name=category, products={}}) + found = #categories + end + end + table.insert(categories[found].products, product) + end + return categories +end + local Main = Solyd.wrapComponent("Main", function(props) local canvas = useCanvas(display) local theme = props.config.theme local header = BigText { display=display, text="Radon Shop", x=1, y=1, align=theme.formatting.headerAlign, bg=theme.colors.headerBgColor, color = theme.colors.headerColor, width=display.bgCanvas.width } + if props.shopState.productsChanged then + canvas:markRect(1, 1, canvas.width, canvas.height) + end local flatCanvas = { header @@ -53,7 +124,11 @@ local maxAddrWidth = 0 local maxQtyWidth = 0 local maxPriceWidth = 0 - local shopProducts = getDisplayedProducts(props.shopState.products, config.settings) + local categories = getCategories(props.shopState.products) + props.shopState.numCategories = #categories + local selectedCategory = props.shopState.selectedCategory + local catName = categories[selectedCategory].name + local shopProducts = getDisplayedProducts(categories[selectedCategory].products, config.settings) local productsHeight = display.bgCanvas.height - 17 local heightPerProduct = math.floor(productsHeight / #shopProducts) local productTextSize @@ -73,27 +148,32 @@ table.insert(flatCanvas, Rect { display=display, x=1, y=16, width=display.bgCanvas.width, height=1, color=theme.colors.productBgColor }) end + local currency = props.shopState.selectedCurrency + local currencySymbol = getCurrencySymbol(currency, productTextSize) for i = 1, #shopProducts do local product = shopProducts[i] product.quantity = product.quantity or 0 + local productPrice = getProductPrice(product, props.shopState.selectedCurrency) if productTextSize == "large" then maxAddrWidth = math.max(maxAddrWidth, bigFont:getWidth(product.address .. "@")+2) maxQtyWidth = math.max(maxQtyWidth, bigFont:getWidth(tostring(product.quantity))+4) - maxPriceWidth = math.max(maxPriceWidth, bigFont:getWidth(tostring(product.price) .. "kst")+2) + maxPriceWidth = math.max(maxPriceWidth, bigFont:getWidth(tostring(productPrice) .. currencySymbol)+2) elseif productTextSize == "medium" then maxAddrWidth = math.max(maxAddrWidth, smolFont:getWidth(product.address .. "@")+2) maxQtyWidth = math.max(maxQtyWidth, smolFont:getWidth(tostring(product.quantity))+4) - maxPriceWidth = math.max(maxPriceWidth, smolFont:getWidth(tostring(product.price) .. "kst")+2) + maxPriceWidth = math.max(maxPriceWidth, smolFont:getWidth(tostring(productPrice) .. currencySymbol)+2) else maxAddrWidth = math.max(maxAddrWidth, #(product.address .. "@")+1) maxQtyWidth = math.max(maxQtyWidth, #tostring(product.quantity)+2) - maxPriceWidth = math.max(maxPriceWidth, #(tostring(product.price) .. "kst")+1) + maxPriceWidth = math.max(maxPriceWidth, #(tostring(productPrice) .. currencySymbol)+1) end end for i = 1, #shopProducts do local product = shopProducts[i] -- Display products in format: --
+ product.quantity = product.quantity or 0 + local productPrice = getProductPrice(product, props.shopState.selectedCurrency) local qtyColor = theme.colors.normalQtyColor if product.quantity == 0 then qtyColor = theme.colors.outOfStockQtyColor @@ -107,28 +187,96 @@ productNameColor = theme.colors.outOfStockNameColor end if productTextSize == "large" then - table.insert(flatCanvas, BigText { display=display, text=tostring(product.quantity), x=1, y=17+((i-1)*15), align="center", bg=theme.colors.productBgColor, color=qtyColor, width=maxQtyWidth }) - table.insert(flatCanvas, BigText { display=display, text=product.name, x=maxQtyWidth+1, y=17+((i-1)*15), align=theme.formatting.productNameAlign, bg=theme.colors.productBgColor, color=productNameColor, width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth }) - table.insert(flatCanvas, BigText { display=display, text=tostring(product.price) .. "kst", x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, y=17+((i-1)*15), align="right", bg=theme.colors.productBgColor, color=theme.colors.priceColor, width=maxPriceWidth }) - table.insert(flatCanvas, BigText { display=display, text=product.address .. "@", x=display.bgCanvas.width-3-maxAddrWidth, y=17+((i-1)*15), align="right", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+4 }) + table.insert(flatCanvas, BigText { key="qty-"..catName..tostring(product.id), display=display, text=tostring(product.quantity), x=1, y=17+((i-1)*15), align="center", bg=theme.colors.productBgColor, color=qtyColor, width=maxQtyWidth }) + table.insert(flatCanvas, BigText { key="name-"..catName..tostring(product.id), display=display, text=product.name, x=maxQtyWidth+1, y=17+((i-1)*15), align=theme.formatting.productNameAlign, bg=theme.colors.productBgColor, color=productNameColor, width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth }) + table.insert(flatCanvas, BigText { key="price-"..catName..tostring(product.id), display=display, text=tostring(productPrice) .. currencySymbol, x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, y=17+((i-1)*15), align="right", bg=theme.colors.productBgColor, color=theme.colors.priceColor, width=maxPriceWidth }) + table.insert(flatCanvas, BigText { key="addr-"..catName..tostring(product.id), display=display, text=product.address .. "@", x=display.bgCanvas.width-3-maxAddrWidth, y=17+((i-1)*15), align="right", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+4 }) elseif productTextSize == "medium" then - table.insert(flatCanvas, SmolText { display=display, text=tostring(product.quantity), x=1, y=17+((i-1)*9), align="center", bg=theme.colors.productBgColor, color=qtyColor, width=maxQtyWidth }) - table.insert(flatCanvas, SmolText { display=display, text=product.name, x=maxQtyWidth+1, y=17+((i-1)*9), align=theme.formatting.productNameAlign, bg=theme.colors.productBgColor, color=productNameColor, width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth }) - table.insert(flatCanvas, SmolText { display=display, text=tostring(product.price) .. "kst", x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, y=17+((i-1)*9), align="right", bg=theme.colors.productBgColor, color=theme.colors.priceColor, width=maxPriceWidth }) - table.insert(flatCanvas, SmolText { display=display, text=product.address .. "@", x=display.bgCanvas.width-3-maxAddrWidth, y=17+((i-1)*9), align="right", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+4 }) + table.insert(flatCanvas, SmolText { key="qty-"..catName..tostring(product.id), display=display, text=tostring(product.quantity), x=1, y=17+((i-1)*9), align="center", bg=theme.colors.productBgColor, color=qtyColor, width=maxQtyWidth }) + table.insert(flatCanvas, SmolText { key="name-"..catName..tostring(product.id), display=display, text=product.name, x=maxQtyWidth+1, y=17+((i-1)*9), align=theme.formatting.productNameAlign, bg=theme.colors.productBgColor, color=productNameColor, width=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth-maxQtyWidth }) + table.insert(flatCanvas, SmolText { key="price-"..catName..tostring(product.id), display=display, text=tostring(productPrice) .. currencySymbol, x=display.bgCanvas.width-3-maxAddrWidth-maxPriceWidth, y=17+((i-1)*9), align="right", bg=theme.colors.productBgColor, color=theme.colors.priceColor, width=maxPriceWidth }) + table.insert(flatCanvas, SmolText { key="addr-"..catName..tostring(product.id), display=display, text=product.address .. "@", x=display.bgCanvas.width-3-maxAddrWidth, y=17+((i-1)*9), align="right", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+4 }) else - table.insert(flatCanvas, BasicText { display=display, text=tostring(product.quantity), x=1, y=6+((i-1)*1), align="center", bg=theme.colors.productBgColor, color=qtyColor, width=maxQtyWidth }) - table.insert(flatCanvas, BasicText { display=display, text=product.name, x=maxQtyWidth+1, y=6+((i-1)*1), align=theme.formatting.productNameAlign, bg=theme.colors.productBgColor, color=productNameColor, width=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth-maxQtyWidth }) - table.insert(flatCanvas, BasicText { display=display, text=tostring(product.price) .. "kst", x=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth, y=6+((i-1)*1), align="right", bg=theme.colors.productBgColor, color=theme.colors.priceColor, width=maxPriceWidth }) - table.insert(flatCanvas, BasicText { display=display, text=product.address .. "@ ", x=(display.bgCanvas.width/2)-1-maxAddrWidth, y=6+((i-1)*1), align="right", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+2 }) + table.insert(flatCanvas, BasicText { key="qty-"..catName..tostring(product.id), display=display, text=tostring(product.quantity), x=1, y=6+((i-1)*1), align="center", bg=theme.colors.productBgColor, color=qtyColor, width=maxQtyWidth }) + table.insert(flatCanvas, BasicText { key="name-"..catName..tostring(product.id), display=display, text=product.name, x=maxQtyWidth+1, y=6+((i-1)*1), align=theme.formatting.productNameAlign, bg=theme.colors.productBgColor, color=productNameColor, width=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth-maxQtyWidth }) + table.insert(flatCanvas, BasicText { key="price-"..catName..tostring(product.id), display=display, text=tostring(productPrice) .. currencySymbol, x=(display.bgCanvas.width/2)-1-maxAddrWidth-maxPriceWidth, y=6+((i-1)*1), align="right", bg=theme.colors.productBgColor, color=theme.colors.priceColor, width=maxPriceWidth }) + table.insert(flatCanvas, BasicText { key="addr-"..catName..tostring(product.id), display=display, text=product.address .. "@ ", x=(display.bgCanvas.width/2)-1-maxAddrWidth, y=6+((i-1)*1), align="right", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+2 }) end end + local currencyX = 3 + for i = 1, #props.config.currencies do + local symbol = getCurrencySymbol(props.config.currencies[i], productTextSize) + local symbolSize = bigFont:getWidth(symbol)+6 + local bgColor + if i % 4 == 1 then + bgColor = theme.colors.currency1Color + elseif i % 4 == 2 then + bgColor = theme.colors.currency2Color + elseif i % 4 == 3 then + bgColor = theme.colors.currency3Color + elseif i % 4 == 0 then + bgColor = theme.colors.currency4Color + end + table.insert(flatCanvas, Button { + display = display, + align = "center", + text = symbol, + x = currencyX, + y = 1, + bg = bgColor, + color = theme.colors.currencyTextColor, + width = symbolSize, + onClick = function() + props.shopState.selectedCurrency = props.config.currencies[i] + props.shopState.lastTouched = os.epoch("utc") + end + }) + currencyX = currencyX + symbolSize + 2 + end + + local categoryX = display.bgCanvas.width - 2 + for i = #categories, 1, -1 do + local category = categories[i] + local categoryName = category.name + local categoryColor + if i == selectedCategory then + categoryColor = theme.colors.activeCategoryColor + categoryName = "[" .. categoryName .. "]" + elseif i % 4 == 1 then + categoryColor = theme.colors.category1Color + elseif i % 4 == 2 then + categoryColor = theme.colors.category2Color + elseif i % 4 == 3 then + categoryColor = theme.colors.category3Color + elseif i % 4 == 0 then + categoryColor = theme.colors.category4Color + end + local categoryWidth = smolFont:getWidth(categoryName)+6 + categoryX = categoryX - categoryWidth - 2 + + table.insert(flatCanvas, SmolButton { + display = display, + align = "center", + text = categoryName, + x = categoryX, + y = 4, + bg = categoryColor, + color = theme.colors.categoryTextColor, + width = categoryWidth, + onClick = function() + props.shopState.selectedCategory = i + props.shopState.lastTouched = os.epoch("utc") + canvas:markRect(1, 16, canvas.width, canvas.height-16) + end + }) + end + return _.flat({ _.flat(flatCanvas) }), { canvas = {canvas, 1, 1}, config = props.config or {}, shopState = props.shopState or {}, - products = props.shopState.products + products = props.shopState.products, } end) @@ -208,17 +356,12 @@ deltaTimer = os.startTimer(0) hooks.tickAnimations(dt) - --[[elseif name == "monitor_touch" then + elseif name == "monitor_touch" then local x, y = e[3], e[4] - local player = auth.reconcileTouch(x, y) - if player then - local node = hooks.findNodeAt(context.aabb, x, y) - if node then - node.onClick(player) - end - else - -- TODO: Yell at the players - end]] + local node = hooks.findNodeAt(context.aabb, x, y) + if node then + node.onClick() + end end end end) diff --git a/res/smolfont.rif b/res/smolfont.rif index 7c63e5a..c421cb4 100644 --- a/res/smolfont.rif +++ b/res/smolfont.rif Binary files differ