Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local computer = require("computer")
- local unicode = require("unicode")
- local gpu = component.gpu
- local function getComponent(...)
- for _, name in ipairs({...}) do
- local address = component.list(name)()
- if address then
- return component.proxy(address)
- end
- end
- return nil
- end
- local chat = getComponent("chat_box")
- local opb = getComponent("openperipheral_bridge", "terminal_glasses_bridge", "terminal")
- local sensor = getComponent("sensor", "radar")
- local motionSensor = getComponent("motion_sensor")
- if chat and chat.setName then
- pcall(chat.setName, "§3[PRISMA CHAT]§r")
- end
- local w, h = gpu.getResolution()
- local minW, minH = 80, 24
- if w < minW or h < minH then
- pcall(gpu.setResolution, math.max(w, minW), math.max(h, minH))
- w, h = gpu.getResolution()
- end
- local C = {
- bg = 0x071018,
- panel = 0x0D1C28,
- panel2 = 0x102638,
- line = 0x1D5A73,
- cyan = 0x00D7FF,
- blue = 0x3A8DFF,
- white = 0xE9FBFF,
- dim = 0x7092A4,
- green = 0x39FFB6,
- yellow = 0xFFD166,
- red = 0xFF4E6A,
- btn = 0x163142,
- btnOn = 0x064B40,
- btnWarn = 0x5A1E2B
- }
- local mcColors = {
- ["0"] = 0x000000, ["1"] = 0x0000AA, ["2"] = 0x00AA00, ["3"] = 0x00AAAA,
- ["4"] = 0xAA0000, ["5"] = 0xAA00AA, ["6"] = 0xFFAA00, ["7"] = 0xAAAAAA,
- ["8"] = 0x555555, ["9"] = 0x5555FF, ["a"] = 0x55FF55, ["b"] = 0x55FFFF,
- ["c"] = 0xFF5555, ["d"] = 0xFF55FF, ["e"] = 0xFFFF55, ["f"] = 0xFFFFFF,
- ["r"] = C.white
- }
- local cfg = {
- scanInterval = 2,
- motionTimeout = 8,
- arEnabled = true,
- arChatEnabled = true,
- arRadarEnabled = true,
- arX = 2,
- arY = 2,
- arW = 230,
- arChatWidth = 38,
- playerConfig = {
- ignore = {"GintaRus"},
- prefixes = {
- ["111"] = "[Чёрт] ",
- ["Noney"] = "[Girl] "
- }
- }
- }
- local buttons = {}
- local chatLog = {}
- local arChatLog = {}
- local nearbyPlayers = {}
- local motionTargets = {}
- local showIgnoreMenu = false
- local needFullRedraw = true
- local chatUpdated = true
- local arError = nil
- local statusMessage = "Готово к работе"
- local lastScan = 0
- local leftW = 30
- local bottomH = 5
- local chatX = leftW + 2
- local chatW = w - leftW - 1
- local chatH = h - bottomH - 4
- local function clamp(v, a, b)
- if v < a then return a end
- if v > b then return b end
- return v
- end
- local function text(x, y, value, fg, bg)
- gpu.setForeground(fg or C.white)
- gpu.setBackground(bg or C.bg)
- gpu.set(x, y, value)
- end
- local function clearArea(x, y, bw, bh, bg)
- gpu.setBackground(bg or C.bg)
- gpu.fill(x, y, bw, bh, " ")
- end
- local function frame(x, y, bw, bh, title)
- clearArea(x, y, bw, bh, C.panel)
- gpu.setForeground(C.line)
- gpu.setBackground(C.panel)
- gpu.set(x, y, "+" .. string.rep("-", bw - 2) .. "+")
- for i = 1, bh - 2 do
- gpu.set(x, y + i, "|")
- gpu.set(x + bw - 1, y + i, "|")
- end
- gpu.set(x, y + bh - 1, "+" .. string.rep("-", bw - 2) .. "+")
- if title then
- local label = " " .. title .. " "
- text(x + 2, y, label, C.cyan, C.panel)
- end
- end
- local function drawButton(id, x, y, bw, label, bg, fg)
- buttons[id] = {x = x, y = y, w = bw, h = 1}
- clearArea(x, y, bw, 1, bg or C.btn)
- local lx = x + math.max(0, math.floor((bw - unicode.wlen(label)) / 2))
- text(lx, y, label, fg or C.white, bg or C.btn)
- end
- local function buttonHit(id, x, y)
- local b = buttons[id]
- return b and x >= b.x and x < b.x + b.w and y >= b.y and y < b.y + b.h
- end
- local function wrapText(value, maxW)
- local lines = {}
- local current = ""
- value = tostring(value or "")
- if maxW < 4 then maxW = 4 end
- for word in value:gmatch("%S+") do
- if current == "" then
- current = word
- elseif unicode.wlen(current) + unicode.wlen(word) + 1 <= maxW then
- current = current .. " " .. word
- else
- table.insert(lines, current)
- current = word
- end
- while unicode.wlen(current) > maxW do
- table.insert(lines, unicode.sub(current, 1, maxW))
- current = unicode.sub(current, maxW + 1)
- end
- end
- if current ~= "" then
- table.insert(lines, current)
- end
- if #lines == 0 then
- table.insert(lines, "")
- end
- return lines
- end
- local function drawColoredText(x, y, line, defaultColor, bg)
- local cx = x
- local color = defaultColor or C.white
- local safe = tostring(line or ""):gsub("§", "&")
- local pos = 1
- gpu.setBackground(bg or C.panel)
- while pos <= #safe do
- local mark = safe:find("&", pos, true)
- if not mark then
- gpu.setForeground(color)
- gpu.set(cx, y, safe:sub(pos))
- break
- end
- if mark > pos then
- local chunk = safe:sub(pos, mark - 1)
- gpu.setForeground(color)
- gpu.set(cx, y, chunk)
- cx = cx + unicode.wlen(chunk)
- end
- local code = safe:sub(mark + 1, mark + 1):lower()
- if mcColors[code] then
- color = mcColors[code]
- pos = mark + 2
- else
- gpu.setForeground(color)
- gpu.set(cx, y, "&")
- cx = cx + 1
- pos = mark + 1
- end
- end
- end
- local isIgnored
- local function decoratedName(name)
- local prefix = cfg.playerConfig.prefixes[name]
- if prefix then
- return prefix .. name
- end
- return name
- end
- local function formatDistance(value)
- if not value then
- return ""
- end
- return string.format(" %.1fm", value)
- end
- local function addNearbyPlayer(seen, name, distance)
- if not name or name == "" or isIgnored(name) then
- return
- end
- local shown = decoratedName(name)
- local key = shown:lower()
- if seen[key] then
- return
- end
- seen[key] = true
- table.insert(nearbyPlayers, shown .. formatDistance(distance))
- end
- local function handleMotionSignal(a, b, c, d, e)
- local dx, dy, dz, name
- if type(a) == "number" then
- dx, dy, dz, name = a, b, c, d
- elseif type(b) == "number" then
- dx, dy, dz, name = b, c, d, e
- else
- return
- end
- if type(dx) ~= "number" or type(dy) ~= "number" or type(dz) ~= "number" then
- return
- end
- name = tostring(name or "Движение")
- local distance = math.sqrt(dx * dx + dy * dy + dz * dz)
- motionTargets[name] = {
- distance = distance,
- time = computer.uptime()
- }
- statusMessage = "Движение: " .. name .. string.format(" %.1fm", distance)
- end
- function isIgnored(name)
- for _, ignored in ipairs(cfg.playerConfig.ignore) do
- if tostring(name):lower() == tostring(ignored):lower() then
- return true
- end
- end
- return false
- end
- local function addChatMessage(name, message)
- name = tostring(name or "Unknown")
- message = tostring(message or "")
- if message:sub(1, 1) == "!" then return end
- local shownName = decoratedName(name)
- local prefix = "&8> &b" .. shownName .. "&8: &f"
- local firstWidth = math.max(10, chatW - 8 - unicode.wlen(shownName) - 4)
- local restWidth = math.max(10, chatW - 8)
- local wrapped = wrapText(message, firstWidth)
- for i, line in ipairs(wrapped) do
- if i == 1 then
- table.insert(chatLog, prefix .. line)
- else
- for _, restLine in ipairs(wrapText(line, restWidth)) do
- table.insert(chatLog, "&f " .. restLine)
- end
- end
- end
- while #chatLog > chatH - 2 do
- table.remove(chatLog, 1)
- end
- table.insert(arChatLog, {type = "name", text = shownName})
- for _, line in ipairs(wrapText(message, cfg.arChatWidth)) do
- table.insert(arChatLog, {type = "msg", text = line})
- end
- while #arChatLog > 12 do
- table.remove(arChatLog, 1)
- end
- statusMessage = "Новое сообщение от " .. name
- chatUpdated = true
- end
- local function extractPlayerName(key, value)
- if type(value) == "string" then return value end
- if type(value) == "table" then
- return value.name or value.username or value.player or value[1]
- end
- if type(key) == "string" then return key end
- return nil
- end
- local function sortPlayers()
- table.sort(nearbyPlayers, function(a, b)
- return a:lower() < b:lower()
- end)
- end
- local function scanEnvironment()
- nearbyPlayers = {}
- local seen = {}
- if sensor then
- local ok, result = pcall(function()
- if sensor.getPlayers then
- return sensor.getPlayers()
- elseif sensor.getPlayerNames then
- return sensor.getPlayerNames()
- elseif sensor.players then
- return sensor.players()
- end
- end)
- if ok and type(result) == "table" then
- for key, value in pairs(result) do
- addNearbyPlayer(seen, extractPlayerName(key, value))
- end
- else
- statusMessage = "Сенсор списка не отвечает"
- end
- end
- local now = computer.uptime()
- for name, data in pairs(motionTargets) do
- if now - data.time <= cfg.motionTimeout then
- addNearbyPlayer(seen, name, data.distance)
- else
- motionTargets[name] = nil
- end
- end
- sortPlayers()
- if sensor then
- statusMessage = "Сканирование: " .. tostring(#nearbyPlayers) .. " игрок(ов)"
- elseif motionSensor then
- statusMessage = "Motion sensor: последние движения, радиус 8"
- else
- statusMessage = "Сенсор не найден"
- end
- end
- local function updateAR()
- if not opb or not cfg.arEnabled then return end
- local ok, err = pcall(function()
- opb.clear()
- local bx, by, bw = cfg.arX, cfg.arY, cfg.arW
- local radarH = cfg.arRadarEnabled and math.max(32, 18 + (#nearbyPlayers * 8)) or 0
- local chatLines = cfg.arChatEnabled and #arChatLog or 0
- local chatHpx = cfg.arChatEnabled and math.max(24, 12 + chatLines * 8) or 0
- local totalH = math.max(36, radarH + chatHpx + 12)
- opb.addBox(bx, by, bw, totalH, 0x06111A, 0.45)
- opb.addBox(bx, by, bw, 1, 0x00D7FF, 0.65)
- opb.addText(bx + 6, by + 4, "PRISMA CHAT", 0x00D7FF).setScale(0.8)
- local y = by + 16
- if cfg.arRadarEnabled then
- opb.addText(bx + 6, y, "ИГРОКИ РЯДОМ: " .. tostring(#nearbyPlayers), 0x39FFB6).setScale(0.7)
- y = y + 9
- if #nearbyPlayers == 0 then
- opb.addText(bx + 10, y, "никого нет", 0x7092A4).setScale(0.7)
- y = y + 9
- else
- for i, name in ipairs(nearbyPlayers) do
- if i <= 10 then
- opb.addText(bx + 10, y, "- " .. name, 0xE9FBFF).setScale(0.7)
- y = y + 8
- end
- end
- end
- y = y + 5
- end
- if cfg.arChatEnabled then
- opb.addText(bx + 6, y, "ЧАТ", 0x00D7FF).setScale(0.7)
- y = y + 9
- for _, item in ipairs(arChatLog) do
- if item.type == "name" then
- opb.addText(bx + 10, y, item.text .. ":", 0x00D7FF).setScale(0.7)
- else
- opb.addText(bx + 14, y, item.text, 0xE9FBFF).setScale(0.7)
- end
- y = y + 8
- end
- end
- opb.sync()
- end)
- if ok then
- arError = nil
- else
- arError = tostring(err)
- end
- end
- local function drawHeader()
- clearArea(1, 1, w, 2, C.bg)
- text(2, 1, "PRISMA CHAT // локальная связь и сенсор", C.cyan, C.bg)
- local clock = string.format("UP %ds", math.floor(computer.uptime()))
- text(w - unicode.wlen(clock) - 1, 1, clock, C.dim, C.bg)
- gpu.setBackground(C.bg)
- gpu.setForeground(C.line)
- gpu.fill(1, 2, w, 1, "-")
- end
- local function drawPlayers()
- frame(1, 3, leftW, h - bottomH - 2, "Игроки рядом")
- local status = "сенсор: не найден"
- local statusColor = C.red
- if sensor then
- status = "сенсор: список игроков"
- statusColor = C.green
- elseif motionSensor then
- status = "сенсор: движение"
- statusColor = C.yellow
- end
- text(3, 5, status, statusColor, C.panel)
- text(3, 6, "найдено: " .. tostring(#nearbyPlayers), C.white, C.panel)
- if motionSensor then
- text(3, 7, "радиус OC: 8 блоков", C.dim, C.panel)
- end
- local listY = 9
- local maxRows = h - bottomH - listY - 1
- clearArea(3, listY, leftW - 4, maxRows, C.panel)
- if #nearbyPlayers == 0 then
- text(4, listY, "Пусто", C.dim, C.panel)
- else
- for i = 1, math.min(#nearbyPlayers, maxRows) do
- local name = unicode.sub(nearbyPlayers[i], 1, leftW - 8)
- text(4, listY + i - 1, string.format("%02d ", i), C.dim, C.panel)
- text(7, listY + i - 1, name, C.white, C.panel)
- end
- end
- end
- local function drawChat()
- frame(chatX, 3, chatW, chatH + 2, "Чат")
- clearArea(chatX + 2, 4, chatW - 4, chatH, C.panel)
- if #chatLog == 0 then
- text(chatX + 3, 5, "Сообщений пока нет", C.dim, C.panel)
- return
- end
- local start = math.max(1, #chatLog - chatH + 1)
- local y = 4
- for i = start, #chatLog do
- drawColoredText(chatX + 2, y, unicode.sub(chatLog[i], 1, chatW - 4), C.white, C.panel)
- y = y + 1
- end
- end
- local function drawFooter()
- local y = h - bottomH + 1
- frame(1, y, w, bottomH, "Управление")
- drawButton("toggle_ar", 3, y + 2, 10, cfg.arEnabled and "AR вкл" or "AR выкл", cfg.arEnabled and C.btnOn or C.btn, cfg.arEnabled and C.green or C.dim)
- drawButton("toggle_ar_chat", 15, y + 2, 12, cfg.arChatEnabled and "чат AR" or "без чата", cfg.arChatEnabled and C.btnOn or C.btn, cfg.arChatEnabled and C.green or C.dim)
- drawButton("toggle_ar_radar", 29, y + 2, 14, cfg.arRadarEnabled and "сенсор AR" or "без сенс.", cfg.arRadarEnabled and C.btnOn or C.btn, cfg.arRadarEnabled and C.green or C.dim)
- drawButton("ignore", 45, y + 2, 12, "игнор", C.btn, C.white)
- drawButton("prefix", 59, y + 2, 12, "префикс", C.btn, C.cyan)
- local info = statusMessage
- if arError then
- info = "AR ошибка: " .. arError
- elseif not opb then
- info = info .. " | AR-мост не найден"
- end
- text(3, y + 3, unicode.sub(info, 1, w - 5), arError and C.red or C.dim, C.panel)
- end
- local function redraw()
- buttons = {}
- clearArea(1, 1, w, h, C.bg)
- drawHeader()
- drawPlayers()
- drawChat()
- drawFooter()
- needFullRedraw = false
- chatUpdated = false
- end
- local function redrawDynamic()
- drawHeader()
- drawPlayers()
- if chatUpdated then
- drawChat()
- chatUpdated = false
- end
- drawFooter()
- end
- local function readInput(title, prompt)
- while event.pull(0) do end
- local mw, mh = 54, 8
- local mx = math.floor((w - mw) / 2)
- local my = math.floor((h - mh) / 2)
- local value = ""
- local inputX = mx + unicode.wlen(prompt) + 5
- local inputW = mw - unicode.wlen(prompt) - 8
- local redrawInput = true
- while true do
- if redrawInput then
- clearArea(mx, my, mw, mh, C.panel2)
- frame(mx, my, mw, mh, title)
- text(mx + 3, my + 3, prompt .. ":", C.white, C.panel)
- clearArea(inputX, my + 3, inputW, 1, C.bg)
- text(inputX, my + 3, unicode.sub(value .. "_", -inputW + 1), C.cyan, C.bg)
- drawButton("input_ok", mx + 7, my + 5, 16, "OK", C.btnOn, C.green)
- drawButton("input_cancel", mx + 31, my + 5, 16, "Отмена", C.btnWarn, C.red)
- redrawInput = false
- end
- local ev, _, a1, a2 = event.pull()
- if ev == "key_down" then
- if a2 == 28 then
- break
- elseif a2 == 14 then
- if unicode.wlen(value) > 0 then
- value = unicode.sub(value, 1, -2)
- redrawInput = true
- end
- elseif a2 == 1 then
- value = nil
- break
- elseif a1 and a1 >= 32 and unicode.wlen(value) < 32 then
- local ok, ch = pcall(unicode.char, a1)
- if ok and ch then
- value = value .. ch
- redrawInput = true
- end
- end
- elseif ev == "touch" then
- local tx, ty = a1, a2
- if buttonHit("input_ok", tx, ty) then
- break
- elseif buttonHit("input_cancel", tx, ty) then
- value = nil
- break
- end
- end
- end
- buttons["input_ok"] = nil
- buttons["input_cancel"] = nil
- needFullRedraw = true
- return value
- end
- local function drawIgnoreMenu()
- local mw, mh = 48, 14
- local mx = math.floor((w - mw) / 2)
- local my = math.floor((h - mh) / 2)
- clearArea(mx, my, mw, mh, C.panel2)
- frame(mx, my, mw, mh, "Игнор-лист")
- if #cfg.playerConfig.ignore == 0 then
- text(mx + 4, my + 3, "Список пуст", C.dim, C.panel)
- else
- for i = 1, math.min(6, #cfg.playerConfig.ignore) do
- local name = unicode.sub(cfg.playerConfig.ignore[i], 1, 22)
- text(mx + 4, my + 2 + i, tostring(i) .. ". " .. name, C.white, C.panel)
- drawButton("unignore_" .. i, mx + 32, my + 2 + i, 12, "убрать", C.btnWarn, C.red)
- end
- end
- drawButton("ignore_add", mx + 4, my + mh - 2, 14, "добавить", C.btnOn, C.green)
- drawButton("ignore_close", mx + mw - 16, my + mh - 2, 12, "закрыть", C.btn, C.white)
- end
- local function openIgnoreMenu()
- showIgnoreMenu = true
- drawIgnoreMenu()
- end
- local function handleIgnoreTouch(x, y)
- if buttonHit("ignore_close", x, y) then
- showIgnoreMenu = false
- needFullRedraw = true
- return
- end
- if buttonHit("ignore_add", x, y) then
- local name = readInput("Добавить в игнор", "Ник")
- if name and name ~= "" and not isIgnored(name) then
- table.insert(cfg.playerConfig.ignore, name)
- scanEnvironment()
- updateAR()
- end
- showIgnoreMenu = true
- drawIgnoreMenu()
- return
- end
- for i = 1, 6 do
- if buttonHit("unignore_" .. i, x, y) then
- table.remove(cfg.playerConfig.ignore, i)
- scanEnvironment()
- updateAR()
- drawIgnoreMenu()
- return
- end
- end
- end
- local function handleTouch(x, y)
- if showIgnoreMenu then
- handleIgnoreTouch(x, y)
- return
- end
- if buttonHit("toggle_ar", x, y) then
- cfg.arEnabled = not cfg.arEnabled
- if opb and not cfg.arEnabled then
- pcall(opb.clear)
- pcall(opb.sync)
- end
- needFullRedraw = true
- elseif buttonHit("toggle_ar_chat", x, y) then
- cfg.arChatEnabled = not cfg.arChatEnabled
- updateAR()
- needFullRedraw = true
- elseif buttonHit("toggle_ar_radar", x, y) then
- cfg.arRadarEnabled = not cfg.arRadarEnabled
- updateAR()
- needFullRedraw = true
- elseif buttonHit("ignore", x, y) then
- openIgnoreMenu()
- elseif buttonHit("prefix", x, y) then
- local name = readInput("Префикс игрока", "Ник")
- if name and name ~= "" then
- local prefix = readInput("Префикс игрока", "Префикс")
- if prefix then
- if prefix == "" then
- cfg.playerConfig.prefixes[name] = nil
- else
- cfg.playerConfig.prefixes[name] = prefix .. " "
- end
- scanEnvironment()
- updateAR()
- end
- end
- needFullRedraw = true
- end
- end
- scanEnvironment()
- redraw()
- updateAR()
- while true do
- local ev, a, b, c, d, e = event.pull(0.5)
- if ev == "chat_message" then
- if c ~= nil then
- addChatMessage(b, c)
- else
- addChatMessage(a, b)
- end
- updateAR()
- elseif ev == "touch" then
- if type(b) == "number" then
- handleTouch(b, c)
- else
- handleTouch(a, b)
- end
- elseif ev == "motion" then
- handleMotionSignal(a, b, c, d, e)
- scanEnvironment()
- updateAR()
- needFullRedraw = true
- elseif ev == "interrupted" then
- clearArea(1, 1, w, h, 0x000000)
- if opb then
- pcall(opb.clear)
- pcall(opb.sync)
- end
- return
- end
- local now = computer.uptime()
- if now - lastScan >= cfg.scanInterval then
- scanEnvironment()
- updateAR()
- lastScan = now
- end
- if needFullRedraw then
- redraw()
- elseif not showIgnoreMenu then
- redrawDynamic()
- end
- end
Advertisement
Add Comment
Please, Sign In to add comment