diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0309195 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Krypton"] + path = Krypton + url = https://github.com/Allymonies/Krypton diff --git a/Krypton b/Krypton new file mode 160000 index 0000000..f39844b --- /dev/null +++ b/Krypton @@ -0,0 +1 @@ +Subproject commit f39844bb8a0c80b88f6eaa2e882cda3d469e3b00 diff --git a/components/BasicText.lua b/components/BasicText.lua new file mode 100644 index 0000000..ded46b6 --- /dev/null +++ b/components/BasicText.lua @@ -0,0 +1,31 @@ +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useTextCanvas = hooks.useTextCanvas + +return Solyd.wrapComponent("BasicText", function(props) + local fw = props.width or #props.text + local canvas = useTextCanvas(props.display) + + Solyd.useEffect(function() + local text = props.text + if props.width then + if props.align == "center" then + local leftPad = math.floor((props.width - #text) / 2) + local rightPad = props.width - #text - leftPad + text = string.rep(" ", leftPad) .. text .. string.rep(" ", rightPad) + elseif props.align == "right" then + text = string.rep(" ", props.width - #text) .. text + else + text = text .. string.rep(" ", props.width - #text) + end + end + canvas:write(text, props.x, props.y, props.color or colors.white, props.bg or colors.black) + + return function() + canvas:markText(text, props.x, props.y) + end + end, { canvas, props.display, props.align, text, props.color, props.bg, fw }) + + local x = props.right and props.x-canvas.width+1 or props.x + return nil, { canvas = { canvas, x, props.y } } +end) diff --git a/components/BigText.lua b/components/BigText.lua index 4d28aba..009244e 100644 --- a/components/BigText.lua +++ b/components/BigText.lua @@ -7,7 +7,7 @@ return Solyd.wrapComponent("BigText", function(props) local fw = props.width or bigFont:getWidth(props.text)+2 local bgHeight = 6 - local canvas = useCanvas(fw, bigFont.height+bgHeight)--Solyd.useContext("canvas") + local canvas = useCanvas(props.display, fw, bigFont.height+bgHeight)--Solyd.useContext("canvas") Solyd.useEffect(function() if props.bg then @@ -18,13 +18,20 @@ end end - local cx = props.width and math.floor((props.width - bigFont:getWidth(props.text)) / 2) or 0 + local cx = 0 + if props.width then + if props.align == "center" then + cx = math.floor((props.width - bigFont:getWidth(props.text)) / 2) + elseif props.align == "right" then + cx = props.width - bigFont:getWidth(props.text) - 2 + end + end bigFont:write(canvas, props.text, 2 + cx, bgHeight-3, props.color or colors.white) return function() canvas:markRect(1, 1, fw, bigFont.height+bgHeight) end - end, { canvas, props.text, props.color, props.bg, fw }) + end, { canvas, props.display, props.align, props.text, props.color, props.bg, fw }) local x = props.right and props.x-canvas.width+1 or props.x return nil, { canvas = { canvas, x, props.y } } diff --git a/components/SmolText.lua b/components/SmolText.lua new file mode 100644 index 0000000..92a9fe5 --- /dev/null +++ b/components/SmolText.lua @@ -0,0 +1,38 @@ +local Solyd = require("modules.solyd") +local hooks = require("modules.hooks") +local useCanvas = hooks.useCanvas + +local smolFont = require("fonts.smolfont") + +return Solyd.wrapComponent("SmolText", function(props) + local fw = props.width or smolFont:getWidth(props.text)+2 + local bgHeight = 3 + local canvas = useCanvas(props.display, fw, smolFont.height+bgHeight)--Solyd.useContext("canvas") + + Solyd.useEffect(function() + if props.bg then + for x = 1, fw do + for y = 1, smolFont.height+bgHeight do + canvas:setPixel(x, y, props.bg) + end + end + end + + local cx = 0 + if props.width then + if props.align == "center" then + cx = math.floor((props.width - smolFont:getWidth(props.text)) / 2) + elseif props.align == "right" then + cx = props.width - smolFont:getWidth(props.text) - 2 + end + end + smolFont:write(canvas, props.text, 2 + cx, bgHeight, props.color or colors.white) + + return function() + canvas:markRect(1, 1, fw, smolFont.height+bgHeight) + end + end, { canvas, props.display, props.align, props.text, props.color, props.bg, fw }) + + local x = props.right and props.x-canvas.width+1 or props.x + return nil, { canvas = { canvas, x, props.y } } +end) diff --git a/config.lua b/config.lua index 131eee0..365fb48 100644 --- a/config.lua +++ b/config.lua @@ -3,14 +3,48 @@ title = "Radon Shop" }, theme = { - bgColor = colors.gray, - headerBgColor = colors.red, - headerColor = colors.white + formatting = { + headerAlign = "center", + productNameAlign = "center", + productTextSize = "auto" + }, + colors = { + bgColor = colors.lightGray, + headerBgColor = colors.red, + headerColor = colors.white, + productBgColor = colors.blue, + outOfStockQtyColor = colors.red, + lowQtyColor = colors.orange, + warningQtyColor = colors.yellow, + normalQtyColor = colors.white, + productNameColor = colors.white, + outOfStockNameColor = colors.lightGray, + priceColor = colors.lime, + addressColor = colors.white + }, + palette = { + [colors.black] = 0x181818, + [colors.blue] = 0x182B52, + [colors.purple] = 0x7E2553, + [colors.green] = 0x008751, + [colors.brown] = 0xAB5136, + [colors.gray] = 0x565656, + [colors.lightGray] = 0x9D9D9D, + [colors.red] = 0xFF004C, + [colors.orange] = 0xFFA300, + [colors.yellow] = 0xFFEC23, + [colors.lime] = 0x00A23C, + [colors.cyan] = 0x29ADFF, + [colors.magenta] = 0x82769C, + [colors.pink] = 0xFF77A9, + [colors.lightBlue] = 0x3D7EDB, + [colors.white] = 0xECECEC + } }, currencies = { { id = "krist", -- if not krist or tenebra, must supply endpoint - -- endpoint = "https://krist.dev" + -- node = "https://krist.dev" host = "kristallie", name = "radon.kst", pkey = "", @@ -21,7 +55,7 @@ }, { id = "tenebra", -- if not krist or tenebra, must supply endpoint - -- endpoint = "https://krist.dev" + -- node = "https://krist.dev" host = "tttttttttt", name = "radon.tst", pkey = "", @@ -35,5 +69,9 @@ monitor = nil, exchangeChest = nil, outputChest = nil, + }, + exchange = { + enabled = true, + node = "https://localhost:8000/" } } \ No newline at end of file diff --git a/core/ConfigValidator.lua b/core/ConfigValidator.lua index 9966b98..f458772 100644 --- a/core/ConfigValidator.lua +++ b/core/ConfigValidator.lua @@ -5,16 +5,50 @@ title = "string" }, theme = { - bgColor = "color", - headerBgColor = "color", - headerColor = "color" + formatting = { + headerAlign = "enum<'left' | 'center' | 'right'>: alignment", + productNameAlign = "enum<'left' | 'center' | 'right'>: alignment", + productTextSize = "enum<'small' | 'medium' | 'large' | 'auto'>: text size", + }, + colors = { + bgColor = "color", + headerBgColor = "color", + headerColor = "color", + productBgColor = "color", + outOfStockQtyColor = "color", + lowQtyColor = "color", + warningQtyColor = "color", + normalQtyColor = "color", + productNameColor = "color", + outOfStockNameColor = "color", + priceColor = "color", + addressColor = "color" + }, + palette = { + [colors.black] = "number", + [colors.blue] = "number", + [colors.purple] = "number", + [colors.green] = "number", + [colors.brown] = "number", + [colors.gray] = "number", + [colors.lightGray] = "number", + [colors.red] = "number", + [colors.orange] = "number", + [colors.yellow] = "number", + [colors.lime] = "number", + [colors.cyan] = "number", + [colors.magenta] = "number", + [colors.pink] = "number", + [colors.lightBlue] = "number", + [colors.white] = "number" + } }, currencies = { __type = "array", __min = 1, __entry = { id = "string", - endpoint = "string?", + node = "string?", host = [[regex<^\w{10}$>: address]], name = "string", pkey = "string", @@ -26,6 +60,10 @@ monitor = "string?", exchangeChest = "string?", outputChest = "string?", + }, + exchange = { + enabled = "boolean", + node = "string" } } @@ -37,6 +75,7 @@ address = "string", order = "number?", price = "number", + quantity = "number?", priceOverrides = { __type = "array?", __entry = { diff --git a/core/ShopRunner.lua b/core/ShopRunner.lua index 178001e..e1a789f 100644 --- a/core/ShopRunner.lua +++ b/core/ShopRunner.lua @@ -1,4 +1,4 @@ ---local ShopState = require("core.ShopState") +local ShopState = require("core.ShopState") local Animations = require("modules.hooks.animation") @@ -13,60 +13,60 @@ end local function launchShop(shopState, mainFunction) - --local shopCoroutine = coroutine.create(function() ShopState.runGame(shopState) end) - local mainCoroutine = coroutine.create(mainFunction) + parallel.waitForAny(function() ShopState.runShop(shopState) end, mainFunction) + -- local shopCoroutine = coroutine.create(function() ShopState.runShop(shopState) end) + -- local mainCoroutine = coroutine.create(mainFunction) - local stateFilter ---@type "animationFinished" | "waitForPlayerInput" - local uidFilter + -- local stateFilter ---@type "animationFinished" | "waitForPlayerInput" + -- local uidFilter - local eventFilter - local eventBacklog = {} + -- local eventFilter + -- local eventBacklog = {} - while true do - local e = (eventFilter == nil and #eventBacklog > 0) and table.remove(eventBacklog, 1) or { os.pullEvent() } + -- while true do + -- local e = (eventFilter == nil and #eventBacklog > 0) and table.remove(eventBacklog, 1) or { os.pullEvent() } - if eventFilter and e[1] ~= eventFilter then - eventBacklog[#eventBacklog+1] = e - else - local status, result = coroutine.resume(mainCoroutine, unpack(e)) - eventFilter = result - if not status then - error(result) - end - end + -- if eventFilter and e[1] ~= eventFilter then + -- eventBacklog[#eventBacklog+1] = e + -- else + -- local status, result = coroutine.resume(mainCoroutine, unpack(e)) + -- eventFilter = result + -- if not status then + -- error(result) + -- end + -- end - if coroutine.status(mainCoroutine) == "dead" then - break - end + -- if coroutine.status(mainCoroutine) == "dead" then + -- break + -- end - local canResume = true -- coroutine.status(gameCoroutine) ~= "dead" - if stateFilter == "animationFinished" then - canResume = areAnimationsFinished(uidFilter) - elseif stateFilter == "waitForPlayerInput" then - --canResume = isPlayerInputReady() - elseif stateFilter == "timer" then - canResume = e[1] == "timer" and e[2] == uidFilter - end + -- local canResume = coroutine.status(shopCoroutine) ~= "dead" -- true + -- if stateFilter == "animationFinished" then + -- canResume = areAnimationsFinished(uidFilter) + -- elseif stateFilter == "waitForPlayerInput" then + -- --canResume = isPlayerInputReady() + -- elseif stateFilter == "timer" then + -- canResume = e[1] == "timer" and e[2] == uidFilter + -- end - if canResume then - -- print("resuming...") - --status, stateFilter, uidFilter = coroutine.resume(shopCoroutine) - status = "alive" - if stateFilter then - print("new filter:", stateFilter) - end + -- if canResume then + -- -- print("resuming...") + -- status, stateFilter, uidFilter = coroutine.resume(shopCoroutine) + -- if stateFilter then + -- print("new filter:", stateFilter) + -- end - if not status then - error(stateFilter) - end + -- if not status then + -- error(stateFilter) + -- end - --[[if coroutine.status(shopCoroutine) == "dead" then - -- TODO: Reset game state - -- gameCoroutine = coroutine.create(GameState.runGame) - -- error("oops") - end]] - end - end + -- if coroutine.status(shopCoroutine) == "dead" then + -- -- TODO: Reset game state + -- -- gameCoroutine = coroutine.create(GameState.runGame) + -- -- error("oops") + -- end + -- end + -- end end return { diff --git a/core/ShopState.lua b/core/ShopState.lua new file mode 100644 index 0000000..26e9019 --- /dev/null +++ b/core/ShopState.lua @@ -0,0 +1,57 @@ +local Krypton = require("Krypton") + +---@class ShopState +---@field running boolean +local ShopState = {} +local ShopState_mt = { __index = ShopState } + +function ShopState.new(config, products) + local self = setmetatable({}, ShopState_mt) + + self.running = false + self.config = config + self.products = products + + return self +end + +local function waitForAnimation(uid) + coroutine.yield("animationFinished", uid) +end + +-- Anytime the shop state is resumed, animation should be finished instantly. (call animation finish hooks) +---@param state ShopState +local function runShop(state) + -- Shop is starting + state.running = true + state.currencies = {} + local kryptonListeners = {} + for _, currency in ipairs(state.config.currencies) do + local node = currency.node + if not node and currency.id == "krist" then + node = "https://krist.dev/" + elseif not node and currency.id == "tenebra" then + node = "https://tenebra.lil.gay/" + end + currency.krypton = Krypton.new({ + privateKey = currency.privateKey, + node = node, + id = currency.id, + }) + table.insert(state.currencies, currency) + local kryptonWs = currency.krypton:connect() + kryptonWs:subscribe("transactions") + table.insert(kryptonListeners, function() kryptonWs:listen() end) + end + parallel.waitForAny(unpack(kryptonListeners), function() + while true do + local event, transaction = os.pullEvent("transaction") + --print("Received transaction on " .. transaction.source) + end + end) +end + +return { + ShopState = ShopState, + runShop = runShop, +} \ No newline at end of file diff --git a/fonts/bigfont.lua b/fonts/bigfont.lua index be7a67a..e91014b 100644 --- a/fonts/bigfont.lua +++ b/fonts/bigfont.lua @@ -2,6 +2,6 @@ local createFont = require("modules.font") local bigFontSheet = loadRIF("res/cfont.rif") -local bigFont = createFont(bigFontSheet, " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/-,.\164!:\6") +local bigFont = createFont(bigFontSheet, " abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/-,.\164!:\6@") return bigFont diff --git a/fonts/smolfont.lua b/fonts/smolfont.lua new file mode 100644 index 0000000..ad017d4 --- /dev/null +++ b/fonts/smolfont.lua @@ -0,0 +1,7 @@ +local loadRIF = require("modules.rif") +local createFont = require("modules.font") + +local smolFontSheet = loadRIF("res/smolfont.rif") +local smolFont = createFont(smolFontSheet, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789:;<=>?[\\]^_{|}~\128` !\"#$%&'()*+,-./@") + +return smolFont diff --git a/modules/canvas.lua b/modules/canvas.lua index 7a56486..1bf2cc9 100644 --- a/modules/canvas.lua +++ b/modules/canvas.lua @@ -552,8 +552,8 @@ for i = #partition, 1, -1 do local other = partition[i] local otherCanvas, ox, oy = other[1], other[2], other[3] - -- if PixelCanvas.is(otherCanvas) then ---@cast other PixelCanvas - -- t1 = os.epoch("utc") + if PixelCanvas.is(otherCanvas) then ---@cast other PixelCanvas + t1 = os.epoch("utc") if otherCanvas.canvas[y-oy+1] then local otherPixel = (otherCanvas.canvas[y-oy+1] or {})[x-ox+1] if otherPixel then @@ -567,38 +567,38 @@ break end end - -- t2 = os.epoch("utc") - -- else ---@cast other TextCanvas - -- local otherRow = other.canvas[targetY] - -- local otherT = otherRow.t[targetX] - -- local otherC = otherRow.c[targetX] - -- local otherB = otherRow.b[targetX] - -- if otherT then - -- found = true - -- foundText = true + t2 = os.epoch("utc") + else ---@cast other TextCanvas + local otherRow = otherCanvas.canvas[targetY] + local otherT = otherRow.t[targetX] + local otherC = otherRow.c[targetX] + local otherB = otherRow.b[targetX] + if otherT then + found = true + foundText = true - -- local currRow = self.canvas[targetY] - -- local currT = currRow.t[targetX] - -- local currC = currRow.c[targetX] - -- local currB = currRow.b[targetX] + local currRow = self.canvas[targetY] + local currT = currRow.t[targetX] + local currC = currRow.c[targetX] + local currB = currRow.b[targetX] - -- if otherT ~= currT or otherC ~= currC or otherB ~= currB then - -- currRow.t[targetX] = otherT - -- currRow.c[targetX] = otherC - -- currRow.b[targetX] = otherB - -- currRow.direct[targetX] = true - -- self.dirty[targetY][targetX] = false -- Already processed + if otherT ~= currT or otherC ~= currC or otherB ~= currB then + currRow.t[targetX] = otherT + currRow.c[targetX] = otherC + currRow.b[targetX] = otherB + currRow.direct[targetX] = true + self.dirty[targetY][targetX] = false -- Already processed - -- local dirtyRow = self.dirtyRows[targetY] or {} - -- local minX, maxX = dirtyRow[1], dirtyRow[2] - -- minX = minX and min(minX, targetX) or targetX - -- maxX = maxX and max(maxX, targetX) or targetX - -- self.dirtyRows[targetY] = { minX, maxX } - -- end + local dirtyRow = self.dirtyRows[targetY] or {} + local minX, maxX = dirtyRow[1], dirtyRow[2] + minX = minX and min(minX, targetX) or targetX + maxX = maxX and max(maxX, targetX) or targetX + self.dirtyRows[targetY] = { minX, maxX } + end - -- break - -- end - -- end + break + end + end end if not found then diff --git a/modules/display.lua b/modules/display.lua index b024f10..cf37a89 100644 --- a/modules/display.lua +++ b/modules/display.lua @@ -22,35 +22,42 @@ SOFTWARE. ]] -local canvases = require("modules.canvas") -local PixelCanvas = canvases.PixelCanvas -local TeletextCanvas = canvases.TeletextCanvas -local TextCanvas = canvases.TextCanvas +local Display = {} +local Display_mt = { __index = Display } + +function Display.new(props) + local self = setmetatable({}, Display_mt) + + local canvases = require("modules.canvas") + local PixelCanvas = canvases.PixelCanvas + local TeletextCanvas = canvases.TeletextCanvas + local TextCanvas = canvases.TextCanvas -local mon = peripheral.find("monitor") -if not mon then - mon = term -else - mon.setTextScale(0.5) -end - --- Set Riko Palette -require("util.riko")(mon) - -local ccCanvas = TeletextCanvas(colors.green, mon.getSize()) -ccCanvas:outputFlush(mon) - -local bgCanvas = ccCanvas.pixelCanvas:newFromSize() -for y = 1, bgCanvas.height do - for x = 1, bgCanvas.width do - -- T-Piece - bgCanvas:setPixel(x, y, colors.lightGray) + self.mon = peripheral.find("monitor") + if not self.mon then + self.mon = term + else + self.mon.setTextScale(0.5) end + + -- Set Riko Palette + require("util.setPalette")(self.mon, props.theme.palette) + + self.ccCanvas = TeletextCanvas(props.theme.colors.bgColor, self.mon.getSize()) + self.ccCanvas:outputFlush(self.mon) + + self.textCanvas = TextCanvas(props.theme.colors.headerBgColor, self.mon.getSize()) + + self.bgCanvas = self.ccCanvas.pixelCanvas:newFromSize() + -- for y = 1, self.bgCanvas.height do + -- for x = 1, self.bgCanvas.width do + -- -- T-Piece + -- self.bgCanvas:setPixel(x, y, props.theme.colors.headerBgColor) + -- end + -- end + + return self end -return { - ccCanvas = ccCanvas, - bgCanvas = bgCanvas, - mon = mon, -} +return Display \ No newline at end of file diff --git a/modules/hooks/canvas.lua b/modules/hooks/canvas.lua index c82fe89..642e596 100644 --- a/modules/hooks/canvas.lua +++ b/modules/hooks/canvas.lua @@ -24,17 +24,16 @@ local Solyd = require("modules.solyd") local canvases = require("modules.canvas") -local display = require("modules.display") ---@return PixelCanvas -local function useCanvas(w,h) +local function useCanvas(display, w,h) local c = Solyd.useMemo(function() if w then return canvases.PixelCanvas(w, h) else return display.ccCanvas.pixelCanvas:newFromSize() end - end, {w, h}) + end, {display, w, h}) return c end diff --git a/modules/hooks/init.lua b/modules/hooks/init.lua index 29e9f6f..4de01e2 100644 --- a/modules/hooks/init.lua +++ b/modules/hooks/init.lua @@ -24,6 +24,7 @@ return { useCanvas = require("modules.hooks.canvas"), + useTextCanvas = require("modules.hooks.textCanvas"), useBoundingBox = require("modules.hooks.aabb").useBoundingBox, findNodeAt = require("modules.hooks.aabb").findNodeAt, useAnimation = require("modules.hooks.animation").useAnimation, diff --git a/modules/hooks/textCanvas.lua b/modules/hooks/textCanvas.lua new file mode 100644 index 0000000..bb1032d --- /dev/null +++ b/modules/hooks/textCanvas.lua @@ -0,0 +1,37 @@ +--[[ +MIT License + +Copyright (c) 2022 emmachase + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] + +local Solyd = require("modules.solyd") +local canvases = require("modules.canvas") + +---@return TextCanvas +local function useTextCanvas(display) + local c = Solyd.useMemo(function() + return display.textCanvas + end, {display}) + + return c +end + +return useTextCanvas diff --git a/modules/rif.lua b/modules/rif.lua index 51c0ba0..d560b35 100644 --- a/modules/rif.lua +++ b/modules/rif.lua @@ -66,7 +66,7 @@ local palMap = {} for i = 1, 16 do palMap[i] = 2^(i - 1) - colors[revPalMap[i]] = 2^(i - 1) + --colors[revPalMap[i]] = 2^(i - 1) end return function(filename) diff --git a/products.lua b/products.lua index 73888ba..05d05cc 100644 --- a/products.lua +++ b/products.lua @@ -4,6 +4,7 @@ name = "Lapis Lazuli", address = "lapis", price = 1.0, + quantity = 999, -- DEBUG ONLY priceOverrides = { { currency = "tenebra", @@ -12,15 +13,42 @@ }, }, { + modid = "minecraft:diamond", + name = "Diamond", + address = "dia", + price = 5.0, + quantity = 0, -- DEBUG ONLY + priceOverrides = { + { + currency = "tenebra", + price = 35.0 + } + }, + }, + { + modid = "minecraft:gold_ingot", + name = "Gold Ingot", + address = "gold", + price = 5.0, + quantity = 16, -- DEBUG ONLY + priceOverrides = { + { + currency = "tenebra", + price = 35.0 + } + }, + }, + { modid = "minecraft:diamond_pickaxe", name = "Diamond Pickaxe eff5", address = "dpick", price = 50.0, + quantity = 4, predicates = { enchantments = { { fullName = "Efficiency", - level = 4 + level = 5 } } } diff --git a/radon.lua b/radon.lua index 2b8180b..969e42b 100644 --- a/radon.lua +++ b/radon.lua @@ -1,15 +1,19 @@ --- Imports local _ = require("util.score") -local display = require("modules.display") +local Display = require("modules.display") local Solyd = require("modules.solyd") local hooks = require("modules.hooks") local useCanvas = hooks.useCanvas local BigText = require("components.BigText") +local bigFont = require("fonts.bigfont") +local SmolText = require("components.SmolText") +local smolFont = require("fonts.smolfont") +local BasicText = require("components.BasicText") local RenderCanvas = require("components.RenderCanvas") ---local Core = require("core.GameState") +local Core = require("core.ShopState") local ShopRunner = require("core.ShopRunner") local ConfigValidator = require("core.ConfigValidator") @@ -22,14 +26,91 @@ ConfigValidator.validateConfig(config) ConfigValidator.validateProducts(products) -local Main = Solyd.wrapComponent("Main", function(props) - local canvas = useCanvas() +local display = Display.new({theme=config.theme}) - return _.flat { - BigText { text="Radon Shop", x=1, y=1, bg=colors.red, width=display.bgCanvas.width }, - }, { +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 flatCanvas = { + header + } + + local maxAddrWidth = 0 + local maxQtyWidth = 0 + local maxPriceWidth = 0 + local shopProducts = props.shopState.products + local productsHeight = display.bgCanvas.height - 17 + local heightPerProduct = math.floor(productsHeight / #shopProducts) + local productTextSize + if theme.formatting.productTextSize == "auto" then + if heightPerProduct >= 15 then + productTextSize = "large" + elseif heightPerProduct >= 9 then + productTextSize = "medium" + else + productTextSize = "small" + end + else + productTextSize = theme.formatting.productTextSize + end + + for i = 1, #shopProducts do + local product = shopProducts[i] + 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) + 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) + 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) + end + end + for i = 1, #shopProducts do + local product = shopProducts[i] + -- Display products in format: + --
+ local qtyColor = theme.colors.normalQtyColor + if product.quantity == 0 then + qtyColor = theme.colors.outOfStockQtyColor + elseif product.quantity < 10 then + qtyColor = theme.colors.lowQtyColor + elseif product.quantity < 64 then + qtyColor = theme.colors.warningQtyColor + end + local productNameColor = theme.colors.productNameColor + if product.quantity == 0 then + 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="center", bg=theme.colors.productBgColor, color=theme.colors.addressColor, width=maxAddrWidth+6 }) + 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 }) + 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 }) + end + end + + return _.flat({ _.flat(flatCanvas) }), { canvas = {canvas, 1, 1}, - gameState = props.gameState or {} + config = props.config or {}, + shopState = props.shopState or {} } end) @@ -82,20 +163,19 @@ lastCanvasHash = newCanvasHash end ---local shopState = Core.ShopState.new() -local shopState = nil +local shopState = Core.ShopState.new(config, products) local deltaTimer = os.startTimer(0) ShopRunner.launchShop(shopState, function() while true do - tree = Solyd.render(tree, Main {t = t, gameState = shopState}) + tree = Solyd.render(tree, Main {t = t, config = config, shopState = shopState}) local context = Solyd.getTopologicalContext(tree, { "canvas", "aabb" }) diffCanvasStack(context.canvas) local t1 = os.epoch("utc") - display.ccCanvas:composite({display.bgCanvas, 1, 1}, unpack(context.canvas)) + display.ccCanvas:composite(unpack(context.canvas)) display.ccCanvas:outputDirty(display.mon) local t2 = os.epoch("utc") -- print("Render time: " .. (t2-t1) .. "ms") diff --git a/res/cfont.rif b/res/cfont.rif index 2e4b273..2a7def0 100644 --- a/res/cfont.rif +++ b/res/cfont.rif Binary files differ diff --git a/res/smolfont.rif b/res/smolfont.rif new file mode 100644 index 0000000..7c63e5a --- /dev/null +++ b/res/smolfont.rif Binary files differ diff --git a/util/riko.lua b/util/riko.lua deleted file mode 100644 index e67d4d6..0000000 --- a/util/riko.lua +++ /dev/null @@ -1,30 +0,0 @@ -require("modules.rif") -- Initialize colors - -return function(t) - local rikoPalette = { - {24, 24, 24}, -- black - {29, 43, 82}, -- blue - {126, 37, 83}, -- purple - {0, 134, 81}, -- green - {171, 81, 54}, -- brown - {86, 86, 86}, -- gray - {157, 157, 157}, -- lightGray - {255, 0, 76}, -- red - {255, 163, 0}, -- orange - {255, 240, 35}, -- yellow - {0, 231*0.7, 85*0.7}, -- lime - {41, 173, 255}, -- cyan - {130, 118, 156}, -- magenta - {255, 119, 169}, -- pink - {0, 231*0.5, 85*0.5}, -- darkGreen - {236, 236, 236}, -- white - } - - for i = 1, #rikoPalette do - for j = 1, 3 do - rikoPalette[i][j] = rikoPalette[i][j] / 255 - end - - t.setPaletteColor(2^(i - 1), unpack(rikoPalette[i])) - end -end diff --git a/util/setPalette.lua b/util/setPalette.lua new file mode 100644 index 0000000..a1dd816 --- /dev/null +++ b/util/setPalette.lua @@ -0,0 +1,7 @@ +require("modules.rif") -- Initialize colors + +return function(t, palette) + for k, v in pairs(palette) do + t.setPaletteColor(k, v) + end +end