diff --git a/config.lua b/config.lua index 00522bd..6e13efb 100644 --- a/config.lua +++ b/config.lua @@ -9,6 +9,7 @@ activityTimeout = 60, dropDirection = "forward", smallTextKristPayCompatability = true, + playSounds = true, }, lang = { footer = "/pay @%name% ", @@ -79,6 +80,18 @@ [colors.white] = 0xECECEC } }, + sounds = { + button = { + name = "minecraft:block.note_block.hat", + volume = 0.5, + pitch = 1.1 + }, + purchase = { + name = "minecraft:block.note_block.pling", + volume = 0.5, + pitch = 2 + }, + }, currencies = { { id = "krist", -- if not krist or tenebra, must supply endpoint @@ -102,13 +115,26 @@ }, }, peripherals = { - monitor = nil, -- Modem to display on, if not specified, will use the first monitor found - modem = nil, -- Modem for inventories, if not specified, will use the first modem found + monitor = nil, -- Monitor to display on, if not specified, will use the first monitor found + modem = nil, -- Modem for inventories, if not specified, will use the first wired modem found + speaker = nil, -- Speaker to play sounds on, if not specified, will use the first speaker found + shopSyncModem = nil, -- Modem for ShopSync, if not specified, will use the first wireless modem found exchangeChest = nil, outputChest = "self", -- Chest peripheral or self -- NOTE: Chest dropping is NYI in plethora 1.19, so do not use unless -- the output chest can be accessed }, + shopSync = { + enabled = false, + name = "Radon Shop", + description = "Shop for selling valuable items", + owner = "Allymonies", + location = { + coordinates = { 227, 70, -175 }, + description = "East of spawn, just passed the ISA", + dimension = "overworld" + } + }, exchange = { -- Not yet implemented enabled = true, diff --git a/core/ConfigValidator.lua b/core/ConfigValidator.lua index f9b2405..4fd9ddd 100644 --- a/core/ConfigValidator.lua +++ b/core/ConfigValidator.lua @@ -81,6 +81,10 @@ [colors.white] = "number" } }, + sounds = { + button = "sound", + purchase = "sound", + }, currencies = { __type = "array", __min = 1, @@ -96,10 +100,28 @@ }, peripherals = { monitor = "string?", + speaker = "speaker?", modem = "modem?", + shopSyncModem = "modem?", exchangeChest = "chest?", outputChest = "chest", }, + shopSync = { + enabled = "boolean?", + name = "string?", + description = "string?", + owner = "string?", + location = { + coordinates = { + __type = "array?", + __min = 3, + __max = 3, + __entry = "number" + }, + description = "string?", + dimension = "enum<'overworld' | 'nether' | 'end'>?: dimension" + } + }, exchange = { enabled = "boolean", node = "string" @@ -156,6 +178,14 @@ error("Config value " .. subpath .. " must refer to a modem") end end + if entryType == "speaker" then + if type(value) ~= "string" then + error("Config value " .. subpath .. " must be a speaker name") + end + if peripheral.getType(value) ~= "speaker" then + error("Config value " .. subpath .. " must refer to a speaker") + end + end if entryType == "chest" then if type(value) ~= "string" then error("Config value " .. subpath .. " must be a networked chest") @@ -183,6 +213,20 @@ end end end + if entryType == "sound" then + if type(value) ~= "table" then + error("Config value " .. subpath .. " must be a sound") + end + if not value.name or type(value.name) ~= "string" then + error("Config value " .. subpath .. " must have a name") + end + if not value.volume or type(value.volume) ~= "number" then + error("Config value " .. subpath .. " must have a volume") + end + if not value.pitch or type(value.pitch) ~= "number" then + error("Config value " .. subpath .. " must have a pitch") + end + end if entryType == "boolean" and type(value) ~= "boolean" then error("Config value " .. subpath .. " must be a boolean") end @@ -247,6 +291,9 @@ end end else + if not config then + config = {} + end for k,v in pairs(schema) do subpath = path .. "." .. k if type(v) == "table" then diff --git a/core/ShopState.lua b/core/ShopState.lua index 428fff8..7dd7040 100644 --- a/core/ShopState.lua +++ b/core/ShopState.lua @@ -1,19 +1,26 @@ local Krypton = require("Krypton") local ScanInventory = require("core.inventory.ScanInventory") local Pricing = require("core.Pricing") +local sound = require("util.sound") + +local shopSyncFrequency = 30 +local shopSyncChannel = 9773 ---@class ShopState ---@field running boolean local ShopState = {} local ShopState_mt = { __index = ShopState } -function ShopState.new(config, products, modem) +function ShopState.new(config, products, modem, shopSyncModem, speaker, version) local self = setmetatable({}, ShopState_mt) self.running = false self.config = config self.products = products self.modem = modem + self.shopSyncModem = shopSyncModem + self.speaker = speaker + self.version = version self.selectedCurrency = config.currencies[1] self.selectedCategory = 1 self.numCategories = 1 @@ -121,6 +128,9 @@ if refundAmount > 0 then refund(transactionCurrency, transaction.from, meta, refundAmount, state.config.lang.refundRemaining) end + if state.config.settings.playSounds then + sound.playSound(state.speaker, state.config.sounds.purchase) + end else refund(transactionCurrency, transaction.from, meta, transaction.value, state.config.lang.refundOutOfStock) end @@ -218,6 +228,68 @@ end sleep(math.min(1, state.config.settings.categoryCycleFrequency)) end + end, function() + while state.running do + sleep(shopSyncFrequency) + if state.config.shopSync and state.config.shopSync.enabled and state.shopSyncModem then + local items = {} + for i = 1, #state.products do + local product = state.products[i] + local prices = {} + local nbt = nil + local predicates = nil + if product.predicates then + nbt = "" + predicates = product.predicates + end + for j = 1, #state.config.currencies do + local currency = state.config.currencies[j] + local currencyName = "KST" + if currency.krypton and currency.krypton.currency and currency.krypton.currency.currency_symbol then + currencyName = currency.krypton.currency.currency_symbol + end + local address = currency.host + local requiredMeta = nil + if currency.name then + address = product.address .. "@" .. currency.name + else + requiredMeta = product.address + end + table.insert(prices, { + value = product.price / currency.value, + currency = currencyName, + address = address, + --requiredMeta = requiredMeta + }) + end + table.insert(items, { + price = prices, + item = { + name = product.modid, + displayName = product.name, + nbt = nbt, + --predicates = predicates + } + }) + end + state.shopSyncModem.transmit(shopSyncChannel, os.getComputerID(), { + type = "ShopSync", + info = { + name = state.config.shopSync.name, + description = state.config.shopSync.description, + owner = state.config.shopSync.owner, + software = { + name = "Radon", + version = state.version + }, + location = state.config.shopSync.location, + }, + items = { + + } + }) + end + end end, unpack(kryptonListeners)) end diff --git a/radon.lua b/radon.lua index 2034776..e394d43 100644 --- a/radon.lua +++ b/radon.lua @@ -1,10 +1,11 @@ local oldPullEvent = os.pullEvent os.pullEvent = os.pullEventRaw -local version = "1.1.3" +local version = "1.1.4" --- Imports local _ = require("util.score") +local sound = require("util.sound") local Display = require("modules.display") @@ -38,10 +39,36 @@ local modem if config.peripherals.modem then modem = peripheral.wrap(config.peripherals.modem) -elseif peripheral.find("modem") then - modem = peripheral.find("modem") else - error("No modem found") + modem = peripheral.find("modem", function(pName) + return not peripheral.wrap(pName).isWireless() + end) + if not modem then + error("No modem found") + end +end + +local shopSyncModem +if config.peripherals.shopSyncModem then + shopSyncModem = peripheral.wrap(config.peripherals.shopSyncModem) +else + shopSyncModem = peripheral.find("modem", function(pName) + return peripheral.wrap(pName).isWireless() + end) + if not shopSyncModem and config.shopSync and config.shopSync.enabled then + error("No wireless modem found but ShopSync is enabled!") + end +end + +local speaker +if config.peripherals.speaker then + speaker = peripheral.wrap(config.peripherals.speaker) +else + speaker = peripheral.find("speaker") +end + +if config.shopSync and config.shopSync.enabled and not config.shopSync.force then + error("ShopSync is not yet finalized, please update Radon to use this feature, or set config.shopSync.force to true to use current ShopSync spec") end local display = Display.new({theme=config.theme, monitor=config.peripherals.monitor}) @@ -482,6 +509,9 @@ onClick = function() props.shopState.selectedCurrency = props.config.currencies[i] props.shopState.lastTouched = os.epoch("utc") + if config.settings.playSounds then + sound.playSound(speaker, config.sounds.button) + end end }) currencyX = currencyX + symbolSize + 2 @@ -515,6 +545,9 @@ onClick = function() props.shopState.selectedCategory = i props.shopState.lastTouched = os.epoch("utc") + if config.settings.playSounds then + sound.playSound(speaker, config.sounds.button) + end -- canvas:markRect(1, 16, canvas.width, canvas.height-16) end }) @@ -587,7 +620,7 @@ lastCanvasHash = newCanvasHash end -local shopState = Core.ShopState.new(config, products, modem) +local shopState = Core.ShopState.new(config, products, modem, shopSyncModem, speaker, version) local Profiler = require("profile") @@ -621,7 +654,7 @@ deltaTimer = os.startTimer(0) hooks.tickAnimations(dt) - elseif name == "monitor_touch" then + elseif name == "monitor_touch" and e[2] == peripheral.getName(display.mon) then local x, y = e[3], e[4] local node = hooks.findNodeAt(context.aabb, x, y) if node then diff --git a/util/sound.lua b/util/sound.lua new file mode 100644 index 0000000..cfb7a97 --- /dev/null +++ b/util/sound.lua @@ -0,0 +1,11 @@ +local function playSound(speaker, sound) + if not speaker then + return + end + + speaker.playSound(sound.name, sound.volume, sound.pitch) +end + +return { + playSound = playSound +} \ No newline at end of file