Programmes intégrés

devices
carte des appareils reliés
Tapez ceci à l'invite d'un ordinateur pour le copier sur le support :
> examples devices > edit devices
Le programme
-- Tout ce que cet ordinateur peut piloter, dessiné comme une carte : le bloc
-- de chacune de ses six faces, les appareils de son câble de données, ses
-- radios, les réseaux Wi-Fi et les ordinateurs joignables. Cliquez un
-- appareil pour voir ses mesures et les fonctions qu'un programme peut
-- appeler. Tab ou les flèches changent de page, Q quitte.
local C = term.colors
local size = term.get_size()
local W = size.w
local H = size.h
local COLOR = os.tier() >= 4 -- les écrans plus anciens n'ont qu'une couleur
local FACES = 1
local CABLE = 2
local RADIO = 3
local DETAIL = 4
-- Icônes de 8 x 8 : une couleur hexa par pixel (celles de term.colors), "." vide
local I_PC = {"77777777", "7ffffff7", "7f5ffff7", "7ff5fff7", "7f5f55f7", "7ffffff7", "77777777", "..7777.."}
local I_CHEST = {"cccccccc", "c111111c", "c111111c", "ccc88ccc", "c118811c", "c111111c", "c111111c", "cccccccc"}
local I_COG = {"...11...", ".1.11.1.", "..1cc1..", "11c77c11", "11c77c11", "..1cc1..", ".1.11.1.", "...11..."}
local I_TANK = {"88888888", "8......8", "8......8", "8bbbbbb8", "8b3bbbb8", "8bbbb3b8", "8bbbbbb8", "88888888"}
local I_SCREEN = {"77777777", "7bbbbbb7", "7b33bbb7", "7b3bbbb7", "7bbbbbb7", "77777777", "...77...", ".777777."}
local I_ROUTER = {".a....a.", ".a....a.", ".a....a.", "aaaaaaaa", "a5a5a5aa", "aaaaaaaa", ".7....7.", "........"}
local I_RADIO = {"a..7..a.", ".a.7.a..", "...7....", "..777...", "...7....", "..7.7...", ".7...7..", "7777777."}
local I_WIFI = {"..2222..", ".2....2.", "2..22..2", "..2..2..", "........", "...22...", "...22...", "........"}
local I_LIGHT = {"..7777..", "..7ee7..", "..7777..", "..7117..", "..7777..", "..7557..", "..7777..", "...77..."}
local I_LOOP = {"77777777", "7eeeeee7", "7e7777e7", "7e7777e7", "7e7777e7", "7e7777e7", "7eeeeee7", "77777777"}
local I_LINK = {"...ee...", "..e66e..", "...ee...", "...cc...", "...cc...", ".777777.", "7e7777e7", "77777777"}
local I_SENSOR = {"..8888..", ".8....8.", "8.....58", "8....5.8", "8..55..8", "8..55..8", ".8....8.", "..8888.."}
local I_DUST = {"...ee...", "...ee...", "...ee...", "eeeeeeee", "eeeeeeee", "...ee...", "...ee...", "...ee..."}
local I_CABLE = {"...777..", "...797..", "...797..", "7777977.", "9999997.", "7777777.", "........", "........"}
-- Couleur et icône de chaque type d'appareil (peripheral.type)
local KIND = {
traffic_light = {color = C.green, icon = I_LIGHT},
inductive_loop = {color = C.pink, icon = I_LOOP},
monitor = {color = C.cyan, icon = I_SCREEN},
wifi_router = {color = C.magenta, icon = I_ROUTER},
radio_modem = {color = C.purple, icon = I_RADIO},
redstone_link = {color = C.red, icon = I_LINK},
kinetic = {color = C.orange, icon = I_COG},
inventory = {color = C.yellow, icon = I_CHEST},
tank = {color = C.light_blue, icon = I_TANK},
}
local SENSOR = {color = C.lime, icon = I_SENSOR} -- les capteurs de Create Aeronautics
-- Des noms assez courts pour une carte (13 lettres)
local NAMES = {
traffic_light = "Feu tricolore", inductive_loop = "Boucle détec.", monitor = "Moniteur",
wifi_router = "Routeur Wi-Fi", radio_modem = "Modem radio", redstone_link = "Redstone Link",
kinetic = "Bloc Create", inventory = "Inventaire", tank = "Réservoir", altitude_sensor = "Altimètre",
gimbal_sensor = "Gyroscope", velocity_sensor = "Vélocimètre", optical_sensor = "Capteur opt.",
navigation_table = "Table de nav.", swivel_bearing = "Roulement", torsion_spring = "Ressort",
hot_air_burner = "Brûleur",
}
local SIDE_NAME = {top = "Dessus", bottom = "Dessous", left = "Gauche", right = "Droite", front = "Devant", back = "Derrière"}
local ASPECTS = {off = "éteint", red = "rouge", red_amber = "rouge + orange", amber = "orange", green = "vert", flashing = "clignotant"}
local TABS = {"Faces", "Câble", "Sans fil"}
local T = {
title = "Périphériques", back = "< Retour", this = "Ordinateur #", cable = "Câble", radio = "Sans fil",
empty = "vide", noLabel = "sans nom", more = "détails >", wifi = "Wi-Fi : ", blocks = " blocs",
device = "appareil", devices = "appareils", computer = "ordinateur", computers = "ordinateurs",
antenna = "antenne", antennas = "antennes", item = "objet", items = "objets", slot = "case", slots = "cases",
router = "routeur", routers = "routeurs", rpm = " tr/min", overstressed = "SURCHARGÉ", range = "portée ",
noSsid = "sans SSID", receiver = "récepteur", transmitter = "émetteur", onIt = " dessus", nothing = "rien dessus",
yours = "le vôtre", alsoInv = "+ inventaire", alsoTank = "+ réservoir", alsoKinetic = "+ Create", tank = "cuve ",
name = "Nom", readings = "Mesures", contents = "Contenu", functions = "Fonctions", noReading = "Aucune mesure.",
needMini = {"Disponible dès", "le mini-", "ordinateur"}, needPc = {"Disponible dès", "le micro-", "ordinateur"},
noAntenna = {"Aucune antenne", "posez un modem", "ou un routeur"},
cableDevice = "appareil sur le câble", cableDevices = "appareils sur le câble",
cableComputer = "ordinateur sur le câble", cableComputers = "ordinateurs sur le câble",
myAntenna = "antenne sur cet ordinateur", myAntennas = "antennes sur cet ordinateur",
network = "réseau Wi-Fi joignable", networks = "réseaux Wi-Fi joignables",
inReach = "ordinateur joignable", inReachs = "ordinateurs joignables",
noCable = "Les câbles de données demandent un mini-ordinateur ou mieux.",
noRadio = "La radio et le Wi-Fi demandent un micro-ordinateur ou mieux.",
noneCable = "Aucun pour l'instant : reliez des blocs à l'ordinateur avec du câble de données.", none = "Aucun.",
noneAntenna = "Aucune : posez un modem radio ou un routeur Wi-Fi contre l'ordinateur ou sur son câble.",
noneNetwork = "Aucun : un routeur Wi-Fi reçoit un nom de réseau avec set_ssid ; un modem à portée d'un routeur rejoint son réseau.",
gone = "Déconnecté : l'appareil n'est plus là.",
hintFaces = "Clic : détails Tab : page Q : quitter",
hintList = "Clic : détails Haut/Bas : défiler Q : quitter",
hintDetail = "Clic ou Retour arr. : revenir Haut/Bas : défiler",
}
-- La page des faces est une grille de 3 x 3 cartes, l'ordinateur au milieu. La
-- profondeur est en diagonale, comme un cube vu de face : l'arrière en haut à
-- droite, l'avant en bas à gauche.
local GX = 1 -- colonnes entre deux cartes
if W >= 60 then
GX = 2
end
local CW = (W - 2 * GX) // 3 -- taille d'une carte en caractères
local CH = math.min(5, (H - 2) // 3)
local GY = math.min(2, (H - 2 - 3 * CH) // 2)
local OX = (W - 3 * CW - 2 * GX) // 2 + 1
local OY = 2 + (H - 2 - 3 * CH - 2 * GY) // 2
local GRID = {
{col = 2, row = 1, side = "top"},
{col = 3, row = 1, side = "back"},
{col = 1, row = 2, side = "left"},
{col = 3, row = 2, side = "right"},
{col = 1, row = 3, side = "front"},
{col = 2, row = 3, side = "bottom"},
}
local A = (W - 5) * 2 // 5 -- colonnes des listes : nom, mesure, emplacement
local B = (W - 5) // 4
local KEY = math.min(18, W // 3)
-- Fonctions qui ne font que lire, appelées sur la page de détail ; celles qui veulent des arguments échouent et sont sautées
local READERS = {"get", "count", "detected", "size", "speed", "stress", "capacity", "overstressed",
"range", "get_ssid", "is_receiver", "frequency", "height", "pressure", "angle_x", "angle_z",
"velocity", "hit", "distance", "block", "angle", "target_angle", "limit", "running"}
local page = FACES
local back = FACES -- la page d'où vient le détail
local target = nil -- nom de l'appareil montré en détail
local scroll = 0
local faces = {}
local cable = {}
local antennas = {}
local wired = {}
local remote = {}
local networks = {}
local found = 0
local rows = {}
local hits = {} -- zones cliquables de l'écran
local tabs = {}
local shown = {} -- ce que montre chaque partie de l'écran, pour ne redessiner que les changements
local counts = {}
local running = true
local scanned = -1
-- Texte coupé ou complété d'espaces jusqu'à n caractères
local function fit(s, n)
if #s > n then
return string.sub(s, 1, n)
end
return s .. string.rep(" ", n - #s)
end
local function right(s, n)
if #s > n then
return string.sub(s, 1, n)
end
return string.rep(" ", n - #s) .. s
end
local function put(x, y, s, fg)
term.set_cursor(x, y)
term.set_fg(fg)
term.write(s)
end
local function num(v)
if v == math.floor(v) then
return tostring(v)
end
return string.format("%.1f", v)
end
local function plural(n, one, many)
if n <= 1 then
return num(n) .. " " .. one
end
return num(n) .. " " .. many
end
-- "minecraft:water" -> "water"
local function short(id)
local at = string.find(id, ":", 1, true)
if at then
return string.sub(id, at + 1)
end
return id
end
-- "inventory@12,64,-3" -> "12,64,-3"
local function coords(name)
local at = string.find(name, "@", 1, true)
if at then
return string.sub(name, at + 1)
end
return name
end
-- Appelle une fonction d'un appareil ; nil si elle échoue (elle veut des arguments, le bloc est parti...)
local function try(p, method)
local f = p[method]
if f == nil then
return nil
end
local r = pcall(f)
if r.ok then
return r.value
end
return nil
end
local function show(v)
if type(v) == "number" then
return num(v)
elseif type(v) == "table" then
if v.w then
return num(v.w) .. " x " .. num(v.h)
elseif v.a then
return short(v.a) .. " + " .. short(v.b)
end
return "{...}"
end
return tostring(v)
end
-- Découpe un texte en lignes d'au plus n caractères, entre les mots
local function wrap(text, n)
local lines = {}
local line = ""
for _, word in ipairs(string.split(text, " ")) do
if line == "" then
line = word
elseif #line + 1 + #word <= n then
line = line .. " " .. word
else
table.insert(lines, line)
line = word
end
end
if line ~= "" then
table.insert(lines, line)
end
return lines
end
-- La redstone reçue et émise sur une face, en quelques mots
local function redstone(power, output)
if power > 0 and output > 0 then return "reçu " .. power .. " émis " .. output end
if power > 0 then return "reçoit " .. power end
if output > 0 then return "émet " .. output end
return ""
end
local function who(c)
local s = "#" .. num(c.id)
if c.label then
s = s .. " " .. c.label
end
return s
end
-- Un appareil, prêt à dessiner : son nom, sa couleur, son icône et deux mesures courtes
local function describe(name, p)
local t = p.type()
local k = KIND[t] or SENSOR
local d = {name = name, type = t, label = NAMES[t] or t, color = k.color, icon = k.icon, s1 = "", s2 = ""}
if t == "traffic_light" then
d.s1 = ASPECTS[try(p, "get") or "off"] or ""
elseif t == "inductive_loop" then
local n = try(p, "count") or 0
if n > 0 then
d.s1 = num(n) .. T.onIt
else
d.s1 = T.nothing
end
elseif t == "monitor" then
local s = try(p, "size")
if s then
d.s1 = num(s.w) .. " x " .. num(s.h)
end
elseif t == "wifi_router" then
local ssid = try(p, "get_ssid") or ""
if ssid == "" then
d.s1 = T.noSsid
else
d.s1 = "SSID " .. ssid
end
d.s2 = T.range .. num(try(p, "range") or 0)
elseif t == "radio_modem" then
d.s1 = T.range .. num(try(p, "range") or 0)
elseif t == "redstone_link" then
if try(p, "is_receiver") then
d.s1 = T.receiver
else
d.s1 = T.transmitter
end
local f = try(p, "frequency")
if f then
d.s2 = short(f.a) .. "+" .. short(f.b)
end
elseif t == "kinetic" then
d.s1 = num(math.abs(try(p, "speed") or 0)) .. T.rpm
if try(p, "overstressed") then
d.s2 = T.overstressed
else
d.s2 = num(try(p, "stress") or 0) .. " / " .. num(try(p, "capacity") or 0) .. " su"
end
elseif t == "inventory" then
d.s1 = plural(try(p, "count") or 0, T.item, T.items)
d.s2 = plural(try(p, "size") or 0, T.slot, T.slots)
elseif t == "tank" then
local list = try(p, "tanks")
if list and list[1] then
if list[1].fluid then
d.s1 = short(list[1].fluid)
else
d.s1 = T.empty
end
d.s2 = num(list[1].amount) .. " / " .. num(list[1].capacity) .. " mB"
end
else
-- un capteur : ses deux premières mesures
for key, value in pairs(try(p, "read") or {}) do
if d.s1 == "" then
d.s1 = key .. " " .. show(value)
elseif d.s2 == "" then
d.s2 = key .. " " .. show(value)
end
end
end
-- un bloc peut avoir plusieurs types : une meule tourne et contient des objets
if t ~= "inventory" and p.push then
d.s2 = T.alsoInv
elseif t ~= "tank" and p.tanks then
d.s2 = T.alsoTank
elseif t ~= "kinetic" and p.overstressed then
d.s2 = T.alsoKinetic
end
return d
end
local function isAntenna(d)
return d.type == "radio_modem" or d.type == "wifi_router"
end
-- Regarde tout ce qui est joignable. Les faces sont vues face à l'écran.
local function scan()
faces = {}
antennas = {}
found = 0
for _, side in ipairs(rs.sides()) do
local f = {side = side, power = rs.get(side), output = rs.get_output(side)}
local p = peripheral.wrap(side)
if p then
f.device = describe(side, p)
f.device.where = SIDE_NAME[side]
found = found + 1
if isAntenna(f.device) then
table.insert(antennas, f.device)
end
end
faces[side] = f
end
-- les appareils du câble s'appellent type@x,y,z
cable = {}
for _, name in ipairs(peripheral.list()) do
if string.find(name, "@", 1, true) then
local p = peripheral.wrap(name)
if p then
local d = describe(name, p)
d.where = coords(name)
table.insert(cable, d)
if isAntenna(d) then
table.insert(antennas, d)
end
end
end
end
found = found + #cable
wired = {}
remote = {}
networks = {}
if net then -- nil avant le mini-ordinateur
for _, c in ipairs(net.computers()) do
if c.via == "cable" then
table.insert(wired, c)
else
table.insert(remote, c)
end
end
networks = net.networks()
end
end
local function addHead(text, color)
table.insert(rows, {kind = "head", text = text, color = color})
end
local function addItem(icon, color, a, b, c, link)
table.insert(rows, {kind = "item", icon = icon, color = color, a = a, b = b, c = c, link = link})
end
local function addPair(key, value)
table.insert(rows, {kind = "pair", a = key, b = value})
end
local function addText(text, color)
for _, line in ipairs(wrap(text, W - 4)) do
table.insert(rows, {kind = "text", text = line, color = color})
end
end
local function addBlank()
table.insert(rows, {kind = "text", text = "", color = C.white})
end
local function buildCable()
rows = {}
if net == nil then
addText(T.noCable, C.light_gray)
return
end
addHead(plural(#cable, T.cableDevice, T.cableDevices), C.light_gray)
for _, d in ipairs(cable) do
addItem(d.icon, d.color, d.label, d.s1, d.where, d.name)
end
if #cable == 0 then
addText(T.noneCable, C.light_gray)
end
addBlank()
addHead(plural(#wired, T.cableComputer, T.cableComputers), C.white)
for _, c in ipairs(wired) do
addItem(I_PC, C.white, who(c), num(c.distance or 0) .. T.blocks, "", nil)
end
if #wired == 0 then
addText(T.none, C.light_gray)
end
end
local function buildRadio()
rows = {}
if net == nil or os.tier() < 4 then
addText(T.noRadio, C.light_gray)
return
end
addHead(plural(#antennas, T.myAntenna, T.myAntennas), C.purple)
for _, d in ipairs(antennas) do
addItem(d.icon, d.color, d.label, d.s1, d.where, d.name)
end
if #antennas == 0 then
addText(T.noneAntenna, C.light_gray)
end
addBlank()
addHead(plural(#networks, T.network, T.networks), C.magenta)
for _, n in ipairs(networks) do
local place = num(n.distance) .. T.blocks
if n.own then
place = T.yours
end
addItem(I_WIFI, C.magenta, n.ssid, plural(n.routers, T.router, T.routers), place, nil)
end
if #networks == 0 then
addText(T.noneNetwork, C.light_gray)
end
addBlank()
addHead(plural(#remote, T.inReach, T.inReachs), C.white)
for _, c in ipairs(remote) do
local via = c.via
if via == "wifi" then
via = "Wi-Fi"
end
addItem(I_PC, C.white, who(c), via, num(c.distance or 0) .. T.blocks, nil)
end
if #remote == 0 then
addText(T.none, C.light_gray)
end
end
local function byCount(a, b)
return counts[a] > counts[b]
end
-- La page de détail : toutes les mesures de l'appareil, son contenu, ses fonctions
local function buildDetail()
rows = {}
local p = peripheral.wrap(target)
if p == nil then
addHead(target, C.gray)
addText(T.gone, C.light_gray)
return
end
local d = describe(target, p)
addHead(d.label, d.color)
addPair(T.name, target)
if SIDE_NAME[target] then
local text = redstone(rs.get(target), rs.get_output(target))
if text == "" then
text = "0"
end
addPair("Redstone", text)
end
addText('p = peripheral.wrap("' .. target .. '")', C.lime)
addBlank()
addHead(T.readings, C.gray)
local before = #rows
for _, m in ipairs(READERS) do
if p[m] then
local r = pcall(p[m])
if r.ok and r.value ~= nil then
addPair(m .. "()", show(r.value))
end
end
end
if p.tanks then
for i, tank in ipairs(try(p, "tanks") or {}) do
local fluid = T.empty
if tank.fluid then
fluid = short(tank.fluid)
end
addPair(T.tank .. i, fluid .. " " .. num(tank.amount) .. " / " .. num(tank.capacity) .. " mB")
end
end
if #rows == before then
addText(T.noReading, C.light_gray)
end
if p.push then
-- les mêmes objets de plusieurs cases sont comptés ensemble, les plus nombreux d'abord
counts = {}
local order = {}
for _, stack in pairs(try(p, "list") or {}) do
if counts[stack.display] == nil then
counts[stack.display] = 0
table.insert(order, stack.display)
end
counts[stack.display] = counts[stack.display] + stack.count
end
table.sort(order, byCount)
addBlank()
addHead(T.contents, C.gray)
for _, item in ipairs(order) do
addPair(item, num(counts[item]))
end
if #order == 0 then
addText(T.empty, C.light_gray)
end
end
local names = {}
for key, value in pairs(p) do
table.insert(names, key .. "()")
end
table.sort(names)
addBlank()
addHead(T.functions, C.gray)
addText(table.concat(names, " "), C.white)
end
-- Pixel d'une case de caractère : une case fait 6 x 9 pixels
local function px(x)
return (x - 1) * 6 + 1
end
local function py(y)
return (y - 1) * 9 + 1
end
local function cellX(col)
return OX + (col - 1) * (CW + GX)
end
local function cellY(row)
return OY + (row - 1) * (CH + GY)
end
-- Vrai si cette partie de l'écran doit changer ; ne dessiner que les changements évite le scintillement
local function changed(key, sig)
if shown[key] == sig then
return false
end
shown[key] = sig
return true
end
-- Une carte : en couleur, un bandeau de titre et un bord colorés. Les écrans d'une
-- seule couleur allument tout pixel dessiné : un bandeau plein cacherait son texte.
local function card(c)
local x = cellX(c.col)
local y = cellY(c.row)
table.insert(hits, {x1 = x, y1 = y, x2 = x + CW - 1, y2 = y + CH - 1, link = c.link, page = c.page})
if not changed("card" .. c.col .. c.row, tostring(c.color) .. c.head .. table.concat(c.lines, "|")) then
return
end
local left = px(x)
local top = py(y)
gfx.rect(left, top, CW * 6, CH * 9, "none", true)
if COLOR then
gfx.rect(left, top, CW * 6, 9, c.color, true)
gfx.rect(left, top, CW * 6, CH * 9, c.color)
else
gfx.rect(left, top, CW * 6, CH * 9, "white")
gfx.line(left, top + 9, left + CW * 6 - 1, top + 9, "white")
end
put(x + 1, y, fit(c.head, CW - 2), c.ink or C.black)
for i = 1, CH - 1 do
put(x + 1, y + i, fit(c.lines[i] or "", CW - 2), c.colors[i] or C.white)
end
if c.icon then
gfx.image(left + 2, top + 9, c.icon) -- sous la première colonne de la première ligne
end
end
-- Le trait de l'ordinateur vers une face, de la couleur de ce qui s'y trouve
local function connect(g)
local f = faces[g.side]
local color = "none"
if f.device then
color = f.device.color
elseif f.power > 0 or f.output > 0 then
color = C.red
elseif COLOR then
color = C.gray
end
if not changed("line" .. g.side, tostring(color)) then
return
end
local pl = px(cellX(2))
local pt = py(cellY(2))
local pr = pl + CW * 6 - 1
local pb = pt + CH * 9 - 1
local fl = px(cellX(g.col))
local ft = py(cellY(g.row))
local fr = fl + CW * 6 - 1
local fb = ft + CH * 9 - 1
local mx = (pl + pr) // 2
local my = (pt + pb) // 2
if g.side == "left" then
gfx.line(fr + 1, my, pl - 2, my, color, 2)
elseif g.side == "right" then
gfx.line(pr + 1, my, fl - 2, my, color, 2)
elseif GY == 0 then
return -- pas de place entre les lignes
elseif g.side == "top" then
gfx.line(mx, fb + 1, mx, pt - 2, color, 2)
elseif g.side == "bottom" then
gfx.line(mx, pb + 1, mx, ft - 2, color, 2)
elseif g.side == "back" then
gfx.line(pr + 1, pt - 2, fl - 2, fb + 1, color, 2)
else
gfx.line(pl - 2, pb + 1, fr + 1, ft - 2, color, 2)
end
end
local function faceCard(g)
local f = faces[g.side]
local d = f.device
local signal = redstone(f.power, f.output)
local head = SIDE_NAME[g.side]
if string.lower(head) ~= g.side and #head + 1 + #g.side <= CW - 2 then
head = fit(head, CW - 3 - #g.side) .. " " .. g.side -- le nom pour les programmes
end
local c = {col = g.col, row = g.row, head = head, color = C.gray, ink = C.light_gray}
local a = T.empty
local b = ""
local e = ""
local first = C.gray
if d then
c.color = d.color
c.ink = C.black
c.icon = d.icon
c.link = g.side
a = " " .. d.label
b = d.s1
e = d.s2
first = d.color
elseif signal ~= "" then
c.color = C.red
c.ink = C.black
c.icon = I_DUST
a = " Redstone"
b = signal
signal = ""
first = C.red
end
c.colors = {first, C.white, C.white, C.red}
if CH >= 5 then
c.lines = {a, b, e, signal}
elseif signal == "" then
c.lines = {a, b, e}
elseif CH == 4 then
c.lines = {a, b, signal}
c.colors[3] = C.red
else
c.lines = {a, signal}
c.colors[2] = C.red
end
card(c)
end
local function computerCard()
local lines = {" " .. (os.label() or T.noLabel), plural(found, T.device, T.devices), ""}
if net then
lines[3] = plural(#wired + #remote, T.computer, T.computers)
end
card({col = 2, row = 2, head = T.this .. num(os.id()), color = C.white, icon = I_PC, lines = lines,
colors = {C.white, C.white, C.white}})
end
local function cableCard()
local c = {col = 1, row = 1, head = T.cable, color = C.light_gray, icon = I_CABLE, page = CABLE,
colors = {C.white, C.white, C.white, C.light_gray}}
if net == nil then
c.lines = T.needMini
c.icon = nil
c.colors = {C.light_gray, C.light_gray, C.light_gray}
else
c.lines = {" " .. plural(#cable, T.device, T.devices), plural(#wired, T.computer, T.computers), "", T.more}
end
card(c)
end
local function radioCard()
local c = {col = 3, row = 3, head = T.radio, color = C.purple, page = RADIO,
colors = {C.light_gray, C.light_gray, C.light_gray}}
if net == nil or os.tier() < 4 then
c.lines = T.needPc
elseif #antennas == 0 then
c.lines = T.noAntenna
else
c.icon = I_WIFI
c.lines = {" " .. plural(#antennas, T.antenna, T.antennas), T.wifi .. #networks,
plural(#remote, T.computer, T.computers), T.more}
c.colors = {C.white, C.white, C.white, C.light_gray}
end
card(c)
end
local function drawFaces()
hits = {}
for _, g in ipairs(GRID) do
connect(g)
end
computerCard()
for _, g in ipairs(GRID) do
faceCard(g)
end
cableCard()
radioCard()
end
-- Une ligne d'une liste ; les icônes sont dessinées sous les deux espaces laissés pour elles
local function drawRow(y, r)
local sig = ""
if r then
sig = r.kind .. "|" .. (r.text or "") .. "|" .. (r.a or "") .. "|" .. (r.b or "") .. "|" .. (r.c or "") .. "|" .. tostring(r.color)
end
if not changed("row" .. y, sig) then
return
end
gfx.rect(1, py(y), W * 6, 9, "none", true)
if r == nil then
put(1, y, fit("", W), C.white)
elseif r.kind == "head" and COLOR then
gfx.rect(1, py(y), W * 6, 9, r.color, true)
put(1, y, fit(" " .. r.text, W), C.black)
elseif r.kind == "head" then
put(1, y, fit("-- " .. r.text .. " --", W), C.white)
elseif r.kind == "item" then
put(1, y, " " .. fit(r.a, A) .. " ", r.color)
put(6 + A, y, fit(r.b, B) .. " ", C.white)
put(7 + A + B, y, right(r.c, W - 6 - A - B), C.light_gray)
gfx.image(px(2) + 2, py(y), r.icon)
elseif r.kind == "pair" then
put(1, y, fit(" " .. r.a, KEY + 2), C.light_gray)
put(KEY + 3, y, fit(r.b, W - KEY - 2), C.white)
else
put(1, y, fit(" " .. r.text, W), r.color)
end
end
local function drawRows()
local view = H - 2
scroll = math.max(0, math.min(scroll, #rows - view))
hits = {}
for i = 1, view do
local r = rows[scroll + i]
drawRow(i + 1, r)
if r and r.link then
table.insert(hits, {x1 = 1, y1 = i + 1, x2 = W, y2 = i + 1, link = r.link})
end
end
end
local function drawTabs()
local active = page
if page == DETAIL then
active = back
end
if not changed("tabs", page .. "/" .. active) then
return
end
if COLOR then
gfx.rect(1, 1, W * 6, 9, "blue", true)
end
local x = W + 1
for i = 1, 3 do
x = x - #TABS[i] - 2
end
local title = " " .. T.title
if page == DETAIL then
title = " " .. T.back
end
put(1, 1, fit(title, x - 1), C.white)
for i = 1, 3 do
local label = " " .. TABS[i] .. " "
local ink = C.white
if i == active then
ink = C.black
if COLOR then
gfx.rect(px(x), 1, #label * 6, 9, "white", true)
else
label = "[" .. TABS[i] .. "]"
end
end
put(x, 1, label, ink)
tabs[i] = {x1 = x, x2 = x + #label - 1}
x = x + #label
end
end
local function draw()
drawTabs()
if page == FACES then
drawFaces()
else
drawRows()
end
local hint = T.hintFaces
if page == DETAIL then
hint = T.hintDetail
elseif page ~= FACES then
hint = T.hintList
end
if changed("footer", hint) then
put(1, H, fit(" " .. hint, W), C.light_gray)
end
end
local function refresh()
if page == DETAIL then
buildDetail()
else
scan()
if page == CABLE then
buildCable()
elseif page == RADIO then
buildRadio()
end
end
scanned = os.clock()
draw()
end
local function setPage(p)
page = p
scroll = 0
shown = {}
term.set_bg(C.black)
term.clear()
gfx.clear()
refresh()
end
local function open(name)
if page ~= DETAIL then
back = page
end
target = name
setPage(DETAIL)
end
-- e.x, e.y : le caractère cliqué, dans le terminal ou sur un moniteur
local function onClick(e)
if e.y == 1 then
for i = 1, 3 do
if e.x >= tabs[i].x1 and e.x <= tabs[i].x2 then
setPage(i)
return
end
end
if page == DETAIL then
setPage(back)
end
return
end
for _, h in ipairs(hits) do
if e.x >= h.x1 and e.x <= h.x2 and e.y >= h.y1 and e.y <= h.y2 then
if h.link then
open(h.link)
return
elseif h.page then
setPage(h.page)
return
end
end
end
if page == DETAIL then
setPage(back)
end
end
local function onKey(k)
if page == DETAIL and (k == "backspace" or k == "left" or k == "right" or k == "tab") then
setPage(back)
elseif k == "tab" or k == "right" then
setPage(page % 3 + 1)
elseif k == "left" then
setPage((page + 1) % 3 + 1)
elseif page ~= FACES and k == "up" then
scroll = scroll - 1
drawRows()
elseif page ~= FACES and k == "down" then
scroll = scroll + 1
drawRows()
end
end
setPage(FACES)
local timer = os.start_timer(2)
while running do
local e = os.pull_event()
if e.name == "timer" and e.id == timer then
refresh() -- les mesures changent : toutes les 2 s, chaque seconde en détail
if page == DETAIL then
timer = os.start_timer(1)
else
timer = os.start_timer(2)
end
elseif e.name == "redstone" and os.clock() - scanned >= 0.5 then
refresh()
elseif e.name == "click" then
onClick(e)
elseif e.name == "key" then
onKey(e.key)
elseif e.name == "char" and string.lower(e.char) == "q" then
running = false
elseif e.name == "char" and tonumber(e.char) and tonumber(e.char) >= 1 and tonumber(e.char) <= 3 then
setPage(tonumber(e.char))
end
end
term.set_bg(C.black)
term.set_fg(C.white)
term.clear()
gfx.clear()
term.set_cursor(1, 1)