2019-01-19 11:31:04 +03:00

416 lines
12 KiB
Lua

local GUI = require("GUI")
local screen = require("Screen")
local fs = require("Filesystem")
local color = require("Color")
local image = require("Image")
local system = require("System")
local paths = require("Paths")
local system = require("System")
local text = require("Text")
--------------------------------------------------------------------------------
local stack = {}
local port = 451
local maxInputs = 18
local scrollSpeed = 2
local from = 3
local maxActiveInputs = 4
local elementWidth = 40
local syncDelay = 4
local historyLimit = 30
local modem
local currentEffects = "{}"
local localization = system.getCurrentScriptLocalization()
local config = {
favourites = {},
}
local configPath = paths.user.applicationData .. "Nanomachines/Config.cfg"
if fs.exists(configPath) then
config = filesystem.readTable(configPath)
end
local function saveConfig()
filesystem.writeTable(configPath, config)
end
--------------------------------------------------------------------------------
local workspace, window = system.addWindow(GUI.filledWindow(1, 1, 80, 22, 0xF0F0F0))
local inputsPanel = window:addChild(GUI.panel(1, 1, 19, window.height, 0x2D2D2D))
window.backgroundPanel.localX = inputsPanel.width + 1
window.backgroundPanel.width = window.width - inputsPanel.width
local inputsContainer = window:addChild(GUI.container(1, 4, inputsPanel.width, inputsPanel.height - 3))
local layout = window:addChild(GUI.layout(window.backgroundPanel.localX, 1, window.backgroundPanel.width, window.height, 1, 1))
layout:setMargin(1, 1, 0, from)
layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
local textBox = window:addChild(GUI.textBox(window.backgroundPanel.localX, 1, window.backgroundPanel.width, 5, 0xFFFFFF, 0xB4B4B4, {}, 1, 1))
textBox.eventHandler = nil
textBox.localY = window.height - textBox.height + 1
local textBoxButton = window:addChild(GUI.adaptiveButton(1, 1, 2, 0, 0xFFFFFF, 0xD2D2D2, 0xFFFFFF, 0x2D2D2D, " "))
textBoxButton.localX = math.floor(textBox.localX + textBox.width / 2 - textBoxButton.width / 2)
local syncContainer = window:addChild(GUI.container(1, 1, window.width, window.height))
local syncPanel = syncContainer:addChild(GUI.panel(1, 1, syncContainer.width, syncContainer.height, 0xF0F0F0))
local syncLayout = syncContainer:addChild(GUI.layout(1, 1, syncContainer.width, syncContainer.height, 1, 1))
syncLayout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.syncWelcome))
local syncTextBox = syncLayout:addChild(GUI.textBox(1, 1, 44, 1, nil, 0xA5A5A5, {}, 1, 0, 0, true, true))
syncTextBox.eventHandler = nil
textBoxButton.onTouch = function()
textBox.hidden = not textBox.hidden
textBoxButton.localY = textBox.hidden and window.height or textBox.localY - 1
textBoxButton.text = textBox.hidden and "" or ""
end
local function addMessage(prefix, ...)
local message = {...}
for i = 1, #message do
message[i] = tostring(message[i])
end
message = text.wrap(prefix .. table.concat(message, ", "), textBox.width - 2)
for i = 1, #message do
table.insert(textBox.lines, message[i])
end
for i = 1, #textBox.lines - historyLimit do
table.remove(textBox.lines, 1)
end
textBox:scrollToEnd()
end
local function broadcast(...)
addMessage(localization.sent .. ": ", ...)
workspace:draw()
modem.broadcast(port, "nanomachines", ...)
end
local function broadcastPut(...)
table.insert(stack, {...})
end
local function broadcastNext()
if #stack > 0 then
broadcast(table.unpack(stack[1]))
table.remove(stack, 1)
end
end
local function getActiveSwitchCount()
local count = 0
for i = 1, maxInputs do
if inputsContainer.children[i].state then
count = count + 1
end
end
return count
end
local function checkSwitches()
local count = getActiveSwitchCount()
for i = 1, maxInputs do
inputsContainer.children[i].colors.active = count > 2 and 0xFF4940 or 0x66DB80
inputsContainer.children[i].disabled = not inputsContainer.children[i].state and count > 3
end
end
local spacing, width = 3, 6
local startX = math.floor(inputsContainer.width / 2 - width / 2 - spacing)
local x, y = startX, 1
for i = 1, maxInputs do
local input = inputsContainer:addChild(GUI.switch(x, y, width, 0x66DB80, 0x1E1E1E, 0xE1E1E1, false))
input.onStateChanged = function()
checkSwitches()
workspace:draw()
broadcastPut("setInput", i, input.state)
broadcastPut("getActiveEffects")
broadcastNext()
end
x = x + width + spacing
if x > inputsContainer.width - 1 then
x, y = startX, y + 2
end
end
for i = 1, maxInputs do
local child = inputsContainer.children[i]
inputsContainer:addChild(GUI.text(math.floor(child.localX + width / 2 - 1), child.localY + 1, 0x4B4B4B, tostring(i)))
end
layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.favourites))
local favouritesLayout = layout:addChild(GUI.layout(1, 1, elementWidth, 3, 1, 1))
favouritesLayout:setDirection(1, 1, GUI.DIRECTION_HORIZONTAL)
favouritesLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
local favouritesComboBox = favouritesLayout:addChild(GUI.comboBox(1, 1, favouritesLayout.width - 12, 3, 0xE1E1E1, 0x696969, 0xD2D2D2, 0xA5A5A5))
local favouritesAddButton = favouritesLayout:addChild(GUI.button(1, 1, 5, 3, 0xE1E1E1, 0x787878, 0x2D2D2D, 0xE1E1E1, "+"))
local favouritesRemoveButton = favouritesLayout:addChild(GUI.button(1, 1, 5, 3, 0xE1E1E1, 0x787878, 0x2D2D2D, 0xE1E1E1, "-"))
layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.activeEffects))
local effectsLayout = layout:addChild(GUI.layout(1, 1, elementWidth, 1, 1, 1))
effectsLayout:setSpacing(1, 1, 0)
effectsLayout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_LEFT, GUI.ALIGNMENT_VERTICAL_TOP)
layout:addChild(GUI.button(1, 1, elementWidth, 3, 0xE1E1E1, 0x787878, 0x2D2D2D, 0xE1E1E1, localization.saveConfiguration)).onTouch = function()
broadcastPut("saveConfiguration")
broadcastNext()
end
layout:addChild(GUI.text(1, 1, 0x2D2D2D, localization.instructionTitle))
local infoTextBox = layout:addChild(GUI.textBox(1, 1, elementWidth, 1, nil, 0xA5A5A5, {localization.instructionData}, 1, 0, 0, true, true))
infoTextBox.eventHandler = nil
local function parseEffects()
local variants = {}
for node in currentEffects:sub(2, -2):gmatch("[^%,]+") do
local type, name = node:match("([^%.]+)%.(.+)")
table.insert(variants, {
type = type or "effect",
name = name or node
})
end
table.sort(variants, function(a, b) return a.type < b.type end)
return variants
end
local function updateEffects(variants)
effectsLayout:removeChildren()
local function effectDraw(object)
screen.drawRectangle(object.x, object.y, object.width, object.height, object.backgroundColor, object.textColor, " ")
screen.drawText(object.x + 1, object.y + 1, object.textColor, object.text)
end
local step = false
local function add(text)
local object = effectsLayout:addChild(GUI.object(1, 1, elementWidth, 3))
object.draw = effectDraw
object.backgroundColor = step and 0xD2D2D2 or 0xE1E1E1
object.textColor = 0x787878
object.text = text
step = not step
end
if #variants > 0 then
local width = 14
for i = 1, #variants do
add(variants[i].type .. string.rep(" ", width - unicode.len(variants[i].type)) .. variants[i].name)
end
else
add(localization.nothingFound)
end
effectsLayout.height = #effectsLayout.children * 3
end
local function runtimeEventHandler(workspace, object, e1, e2, e3, e4, e5, e6, e7, e8, ...)
if e1 == "modem_message" and e6 == "nanomachines" then
if e7 == "input" then
local child = inputsContainer.children[e8]
if not child then
GUI.alert(localization.notSynced)
end
elseif e7 == "effects" then
currentEffects = e8
updateEffects(parseEffects())
elseif e7 == "saved" and e8 == false then
GUI.alert(localization.saveFailed)
end
addMessage(localization.received .. ": ", e7, e8, ...)
workspace:draw()
broadcastNext()
elseif e1 == "scroll" then
local cell = layout.cells[1][1]
local to = -math.floor(cell.childrenHeight / 2)
cell.verticalMargin = cell.verticalMargin + (e5 > 0 and scrollSpeed or -scrollSpeed)
if cell.verticalMargin > from then
cell.verticalMargin = from
elseif cell.verticalMargin < to then
cell.verticalMargin = to
end
workspace:draw()
end
end
local syncResult, syncDeadline, syncStarted
local function syncUpdate()
syncDeadline = computer.uptime() + syncDelay
end
local function setLines(text)
syncTextBox.lines = {text}
syncTextBox:update()
end
local function syncReset()
setLines(localization.syncInfo)
syncStarted = false
syncResult = {}
syncUpdate()
end
favouritesAddButton.onTouch = function()
local text, variants = "", parseEffects()
for i = 1, #variants do
text = text .. variants[i].name .. (i < #variants and ", " or "")
end
for i = 1, favouritesComboBox:count() do
local item = favouritesComboBox:getItem(i)
if item.text == text then
return
end
end
local inputs = {}
for i = 1, maxInputs do
inputs[i] = inputsContainer.children[i].state
end
favouritesComboBox:addItem(text)
table.insert(config.favourites, {
effects = currentEffects,
inputs = inputs,
text = text,
})
favouritesComboBox.selectedItem = favouritesComboBox:count()
saveConfig()
end
favouritesRemoveButton.onTouch = function()
if favouritesComboBox:count() > 0 then
table.remove(config.favourites, favouritesComboBox.selectedItem)
favouritesComboBox:removeItem(favouritesComboBox.selectedItem)
saveConfig()
end
end
favouritesComboBox.onItemSelected = function()
-- Оффаем текущие эффекты
for i = 1, maxInputs do
local child = inputsContainer.children[i]
if child.state then
child:setState(false)
broadcastPut("setInput", i, false)
end
end
-- Парисим эффекты конфига
currentEffects = config.favourites[favouritesComboBox.selectedItem].effects
updateEffects(parseEffects())
-- Врубаем новые эффекты
for i = 1, maxInputs do
if config.favourites[favouritesComboBox.selectedItem].inputs[i] then
inputsContainer.children[i]:setState(true)
broadcastPut("setInput", i, true)
end
end
checkSwitches()
workspace:draw()
broadcastNext()
end
for i = 1, #config.favourites do
favouritesComboBox:addItem(config.favourites[i].text)
end
--------------------------------------------------------------------------------
-- syncContainer.hidden = true
window.actionButtons:moveToFront()
if component.isAvailable("modem") then
modem = component.get("modem")
if modem.isWireless() then
modem.open(port)
textBoxButton.onTouch()
updateEffects(parseEffects())
syncReset()
layout.eventHandler = function(workspace, object, e1, e2, e3, e4, e5, e6, e7, e8, e9)
if not e1 then
if computer.uptime() >= syncDeadline then
syncReset()
setLines(localization.syncInfo)
workspace:draw()
end
if not syncStarted then
broadcast("setResponsePort", port)
end
elseif e1 == "modem_message" and e6 == "nanomachines" then
if e7 == "port" and not syncStarted then
syncStarted = true
for i = 1, maxInputs do
broadcastPut("getInput", i)
end
broadcastPut("getActiveEffects")
elseif e7 == "input" then
syncResult[e8] = e9
elseif e7 == "effects" then
currentEffects = e8
updateEffects(parseEffects())
syncContainer.hidden = true
for i = 1, #syncResult do
inputsContainer.children[i]:setState(syncResult[i])
end
checkSwitches()
workspace:draw()
layout.eventHandler = runtimeEventHandler
return
end
setLines(string.format(localization.syncProgress .. localization.syncContacts, #syncResult, maxInputs))
workspace:draw()
syncUpdate()
broadcastNext()
end
end
else
setLines(localization.notWireless)
end
else
setLines(localization.noModem)
end
workspace:draw()