diff --git a/components/Rect.lua b/components/Rect.lua new file mode 100644 index 0000000..9e036bf --- /dev/null +++ b/components/Rect.lua @@ -0,0 +1,22 @@ +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useCanvas = hooks.useCanvas + +return Solyd.wrapComponent("Rect", function(props) + local canvas = useCanvas(props.display, props.width, props.height)--Solyd.useContext("canvas") + + Solyd.useEffect(function() + if props.color then + for x = 1, props.width do + for y = 1, props.height do + canvas:setPixel(x, y, props.color) + end + end + end + return function() + canvas:markRect(1, 1, props.width, props.height) + end + end, { canvas, props.display, props.x, props.y, props.width, props.height, props.color }) + + return nil, { canvas = { canvas, props.x, props.y } } +end) diff --git a/config.lua b/config.lua index 365fb48..a3540d4 100644 --- a/config.lua +++ b/config.lua @@ -2,6 +2,10 @@ branding = { title = "Radon Shop" }, + settings = { + hideUnavailableProducts = false, + pollFrequency = 30 + }, theme = { formatting = { headerAlign = "center", diff --git a/core/ConfigValidator.lua b/core/ConfigValidator.lua index f458772..bd925bd 100644 --- a/core/ConfigValidator.lua +++ b/core/ConfigValidator.lua @@ -4,6 +4,10 @@ branding = { title = "string" }, + settings = { + hideUnavailableProducts = "boolean", + pollFrequency = "number", + }, theme = { formatting = { headerAlign = "enum<'left' | 'center' | 'right'>: alignment", diff --git a/core/ShopState.lua b/core/ShopState.lua index 26e9019..ade921e 100644 --- a/core/ShopState.lua +++ b/core/ShopState.lua @@ -1,4 +1,5 @@ local Krypton = require("Krypton") +local ScanInventory = require("core.inventory.ScanInventory") ---@class ShopState ---@field running boolean @@ -48,6 +49,11 @@ local event, transaction = os.pullEvent("transaction") --print("Received transaction on " .. transaction.source) end + end, function() + while state.running do + ScanInventory.updateProductInventory(state.products) + sleep(state.config.settings.pollFrequency) + end end) end diff --git a/core/inventory/ScanInventory.lua b/core/inventory/ScanInventory.lua new file mode 100644 index 0000000..2d2abbc --- /dev/null +++ b/core/inventory/ScanInventory.lua @@ -0,0 +1,145 @@ +local itemCache = {} + +local function getInventories() + peripherals = peripheral.getNames() + inventories = {} + for i = 1, #peripherals do + name = peripherals[i] + local methods = peripheral.getMethods(name) + local hasListItems = false + local hasPushItems = false + if methods then + for i = 1, #methods do + if methods[i] == "list" then + hasListItems = true + end + if methods[i] == "pushItems" then + hasPushItems = true + end + if hasListItems and hasPushItems then + break + end + end + end + if hasListItems and hasPushItems then + table.insert(inventories, peripheral.wrap(name)) + end + end + return inventories +end + +local function getInventoryItems(inventory) + local inventoryName = peripheral.getName(inventory) + local items = {} + local slots = inventory.list() + for slot, item in pairs(slots) do + if item then + item.inventory = inventoryName + item.slot = slot + table.insert(items, item) + end + end + return items +end + +local function getAllInventoryItems(inventories) + local items = {} + for i = 1, #inventories do + local inventory = inventories[i] + local inventoryItems = getInventoryItems(inventory) + for i = 1, #inventoryItems do + table.insert(items, inventoryItems[i]) + end + end + return items +end + +local partialObjectMatches +local function partialArrayMatches(partialArray, array) + for i = 1, #partialArray do + local found = false + for i = 1, #array do + if type(array[i]) == "table" then + if partialObjectMatches(partialArray[i], array[i]) then + found = true + break + end + elseif partialArray[i] == array[i] then + found = true + break + end + end + if not found then + return false + end + end + return true +end + +function partialObjectMatches(partialObject, object) + if type(object) ~= "table" then + return false + end + if object[1] then + return partialArrayMatches(partialObject, object) + end + for k,v in pairs(partialObject) do + if type(v) == "table" then + if not partialObjectMatches(v, object[k]) then + return false + end + elseif object[k] == nil or object[k] ~= v then + return false + end + end + return true +end + +local function predicateMatches(predicates, item) + local meta = peripheral.call(item.inventory, "getItemMeta", item.slot) + return partialObjectMatches(predicates, meta) +end + +local function findMatchingProducts(products, item) + local matchingProducts = {} + for i = 1, #products do + local product = products[i] + if item.name == product.modid then + if not product.predicates or predicateMatches(product.predicates, item) then + table.insert(matchingProducts, product) + end + end + end + return matchingProducts +end + +local function updateProductInventory(products) + for i = 1, #products do + products[i].newQty = 0 + end + local inventories = getInventories() + local items = getAllInventoryItems(inventories) + itemCache = items + for i = 1, #items do + local item = items[i] + local matchingProducts = findMatchingProducts(products, item) + for i = 1, #matchingProducts do + local product = matchingProducts[i] + product.newQty = product.newQty + item.count + end + end + for i = 1, #products do + local product = products[i] + product.quantity = product.newQty + product.newQty = nil + end +end + +local function getItemCache() + return itemCache +end + +return { + updateProductInventory = updateProductInventory, + getItemCache = getItemCache +} \ No newline at end of file diff --git a/modules/canvas.lua b/modules/canvas.lua index 1bf2cc9..82c43b4 100644 --- a/modules/canvas.lua +++ b/modules/canvas.lua @@ -545,7 +545,11 @@ for x, _ in pairs(row) do local targetX = ceil(x / 2) local currPixel = self.pixelCanvas.canvas[y][x] - local partition = partitions[floor(y/partitionSize)+1][floor(x/partitionSize)+1] + partitionY = math.min(floor(y/partitionSize)+1, #partitions) + partitionX = math.min(floor(x/partitionSize)+1, #partitions[1]) + --print("getting partition (partizion size " .. partitionSize .. ") at x: " .. x .. ", y: " .. y .. " -> ".. floor(x/partitionSize)+1 .. ", " .. floor(y/partitionSize)+1) + --print("Partitions size: " .. #partitions[1] .. ", " .. #partitions) + local partition = partitions[partitionY][partitionX] local found = false -- local foundText = false diff --git a/products.lua b/products.lua index 05d05cc..9f39985 100644 --- a/products.lua +++ b/products.lua @@ -1,14 +1,13 @@ return { { - modid = "minecraft:lapis_lazuli", - name = "Lapis Lazuli", + modid = "minecraft:lapis_block", + name = "Lapis Block", address = "lapis", - price = 1.0, - quantity = 999, -- DEBUG ONLY + price = 9.0, priceOverrides = { { currency = "tenebra", - price = 1.0 + price = 18.0 } }, }, @@ -17,7 +16,6 @@ name = "Diamond", address = "dia", price = 5.0, - quantity = 0, -- DEBUG ONLY priceOverrides = { { currency = "tenebra", @@ -30,7 +28,6 @@ name = "Gold Ingot", address = "gold", price = 5.0, - quantity = 16, -- DEBUG ONLY priceOverrides = { { currency = "tenebra", @@ -43,14 +40,18 @@ name = "Diamond Pickaxe eff5", address = "dpick", price = 50.0, - quantity = 4, predicates = { enchantments = { { - fullName = "Efficiency", - level = 5 + fullName = "Efficiency V" } } } + }, + { + modid = "scgoodies:lesbian_elytra", + name = "Lesbian Elytra", + address = "lely", + price = 5000.0 } } \ No newline at end of file diff --git a/radon.lua b/radon.lua index 969e42b..e40c56c 100644 --- a/radon.lua +++ b/radon.lua @@ -12,6 +12,7 @@ local SmolText = require("components.SmolText") local smolFont = require("fonts.smolfont") local BasicText = require("components.BasicText") +local Rect = require("components.Rect") local RenderCanvas = require("components.RenderCanvas") local Core = require("core.ShopState") local ShopRunner = require("core.ShopRunner") @@ -28,11 +29,22 @@ local display = Display.new({theme=config.theme}) +local function getDisplayedProducts(allProducts, settings) + local displayedProducts = {} + for _, product in ipairs(allProducts) do + if not settings.hideUnavailableProducts or product.quantity > 0 then + table.insert(displayedProducts, product) + end + end + return displayedProducts +end + local Main = Solyd.wrapComponent("Main", function(props) local canvas = useCanvas(display) local theme = props.config.theme - 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 } + 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 } + local flatCanvas = { header @@ -41,7 +53,7 @@ local maxAddrWidth = 0 local maxQtyWidth = 0 local maxPriceWidth = 0 - local shopProducts = props.shopState.products + local shopProducts = getDisplayedProducts(props.shopState.products, config.settings) local productsHeight = display.bgCanvas.height - 17 local heightPerProduct = math.floor(productsHeight / #shopProducts) local productTextSize @@ -57,8 +69,13 @@ productTextSize = theme.formatting.productTextSize end + if #shopProducts > 0 then + table.insert(flatCanvas, Rect { display=display, x=1, y=16, width=display.bgCanvas.width, height=1, color=theme.colors.productBgColor }) + end + for i = 1, #shopProducts do local product = shopProducts[i] + product.quantity = product.quantity or 0 if productTextSize == "large" then maxAddrWidth = math.max(maxAddrWidth, bigFont:getWidth(product.address .. "@")+2) maxQtyWidth = math.max(maxQtyWidth, bigFont:getWidth(tostring(product.quantity))+4) @@ -93,24 +110,25 @@ 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="center", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+6 }) + 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 }) 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="center", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+6 }) + 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 }) 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="center", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+6 }) + 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 }) end end return _.flat({ _.flat(flatCanvas) }), { canvas = {canvas, 1, 1}, config = props.config or {}, - shopState = props.shopState or {} + shopState = props.shopState or {}, + products = props.shopState.products } end)