Advertisement
drpepper240

utils_0_3

Apr 20th, 2025 (edited)
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 55.40 KB | None | 0 0
  1. --  Utils library v0.3 by DrPepper
  2. --
  3. --
  4. -------------------------------------------------------------------
  5.  
  6. local Widgets = require("widgets_0_3")
  7. -------------------------------------------------------------------
  8. -- Tries to read table from the file
  9. -- Returns table or (nil, error message)
  10. function ReadTableFromFile(filename)
  11.   local resTable
  12.   local f = fs.open(filename, "r")
  13.   if f then
  14.     local fstr = f.readAll()
  15.     f.close()
  16.     if fstr then
  17.       local resTable = textutils.unserialize(fstr)
  18.       if resTable then
  19.         return resTable
  20.       else
  21.         return nil, "Can't unserialize "..filename
  22.       end
  23.     else
  24.       return nil, "Can't read "..filename
  25.     end
  26.   else
  27.     return nil, "Can't open "..filename
  28.   end
  29. end
  30.  
  31. -------------------------------------------------------------------
  32. -- Tries to write table to the file
  33. -- Returns true or (nil, error message)
  34. function WriteTableToFile(t, filename)
  35.   local f = fs.open(filename, "w")
  36.   if f then
  37.     local resString = textutils.serialize(t)
  38.     if resString then
  39.       f.write(resString)
  40.       f.close()
  41.       return true
  42.     else
  43.       return nil, "Can't serialize "..filename
  44.     end
  45.   else
  46.     return nil, "Can't open "..filename
  47.   end
  48. end
  49.  
  50. -------------------------------------------------------------------
  51. -- Wraps text, returns an array of strings, respects line endings
  52. function string:wrap(len)
  53.   if len < 1 then len=#self end
  54.   local strings = {}
  55.   for line in self:gmatch("([^\n]+)") do
  56.     while #line > 0 do
  57.       table.insert(strings, line:sub(1, len))
  58.       line = line:sub(len+1)
  59.     end
  60.   end
  61.   return strings
  62. end
  63.  
  64. -------------------------------------------------------------------
  65. -- Backport of the same function from newer CC:Tweaked
  66. -- Colour to hex lookup table for toBlit
  67. local color_hex_lookup = {}
  68. for i = 0, 15 do
  69.     color_hex_lookup[2 ^ i] = string.format("%x", i)
  70. end
  71.  
  72. local function colorToBlit(color)
  73.     local hex = color_hex_lookup[color]
  74.     if hex then return hex end
  75.  
  76.     if color < 0 or color > 0xffff then error("Colour out of range", 2) end
  77.     return string.format("%x", math.floor(math.log(color, 2)))
  78. end
  79.  
  80. -------------------------------------------------------------------
  81. -- For given key-value table returns an indexed array of strings
  82. -- ready for output and a table of index-key pairs.
  83. local function ConvertTableToArrayStrings(t)
  84.   local arr, ind = {}, {}
  85.     for k, v in pairs(t) do
  86.         if type(v)=="table" then
  87.             local s = ""
  88.             for kk, vv in pairs(v) do
  89.                 if type(kk)=="number" then kk = ""
  90.                 else kk = tostring(kk).."="
  91.                 end
  92.                 s = s..kk..tostring(vv).."; "
  93.             end
  94.             v=s
  95.         end
  96.         table.insert(arr, string.format("%s: %s", k, tostring(v)))
  97.     table.insert(ind, #arr, k)
  98.     end
  99.   return arr, ind
  100. end
  101.  
  102. -------------------------------------------------------------------
  103. -- Same as above but with textutils.serialize()
  104. local function ConvertTableToArraySerialize(t)
  105.   local TS = textutils.serialize
  106.   local arr, ind = {}, {}
  107.     for k, v in pairs(t) do
  108.         if type(v)=="table" then
  109.             local s = ""
  110.             for kk, vv in pairs(v) do
  111.                 if type(kk)=="number" then kk = ""
  112.                 else kk = TS(kk).."="
  113.                 end
  114.                 s = s..kk..TS(vv).."; "
  115.             end
  116.       table.insert(arr, string.format("%s: %s", TS(k), s))
  117.         else
  118.           table.insert(arr, string.format("%s: %s", TS(k), TS(v)))
  119.     end
  120.     table.insert(ind, #arr, k)
  121.     end
  122.   return arr, ind
  123. end
  124.  
  125. -------------------------------------------------------------------
  126. -- Peripheral manager
  127. -- Contains peripherals table with names of every peripheral
  128. -- currently connected or saved in peripheral.cfg with its state:
  129. -- peripherals = {peripheral_name = { wrapped = {table},
  130. --  widgets = {array}, callable = true, lastUpdated = epoch,
  131. --  lg="GRP1", lgm = true, pos = nil or {x, y, z}, eu = 3000,
  132. --  euMax = 10000, type = warpdriveLaser } }
  133. -- Contains laser group table with names of each laser peripheral
  134. -- and lock state (nil means unlocked, number means targeter id that
  135. -- locked the group):
  136. -- laserGroups = {GRP1 = {main = "laser3", aux = {"laser1",
  137. -- "laser2", ...}, lock = 2}}
  138. local function CreatePeripheralManager()
  139.   local self = { peripherals = {},
  140.     updaterPeriod = 1,        -- seconds
  141.     peripheralPeriod = 2000,  -- ms
  142.     logger = nil,             -- optional external logger
  143.     widgetsToUpdate = {},     -- widgets to call :draw() on after each update cycle
  144.     laserGroups = {},         --
  145.     freq = 30000,             -- laser freq
  146.     scRedThr = 0.1,           -- fraction of max energy below which the status is red
  147.     scOrangeThr = 0.9         -- fraction of max energy below which the status is red
  148.   }
  149.  
  150.   --forward declarations
  151.   local function connectPeripheral(side) end
  152.   local function fillLaserGroups() end
  153.   local function updatePeripheral(side) end
  154.   local function ReadPeripheralTable() end
  155.   local function SetStatusLaser(p) end
  156.  
  157.   local function init(initLasers)
  158.     --try reading the config into peripherals table
  159.     local res = ReadPeripheralTable(self.peripherals)
  160.     if initLasers then fillLaserGroups() end
  161.     --connect all peripherals
  162.     local connected = peripheral.getNames()
  163.     for _,v in ipairs(connected) do
  164.       connectPeripheral(v)
  165.     end
  166.   end
  167.  
  168.   -- populates laser group table with data from peripheral table
  169.   fillLaserGroups = function()
  170.     for name, p in pairs(self.peripherals) do
  171.       if p.lg then
  172.         if not self.laserGroups[p.lg] then
  173.           self.laserGroups[p.lg] = {aux = {} }
  174.         end
  175.         if p.lgm then -- peripheral marked as main laser
  176.           if self.laserGroups[p.lg].main and self.logger then
  177.             self.logger.log("ERROR",
  178.               string.format("Two main lasers %s and %s in %s",
  179.               self.laserGroups[p.lg].main, name, p.lg),
  180.               "PeripheralManager")
  181.           end
  182.           self.laserGroups[p.lg].main = name
  183.         else
  184.           table.insert(self.laserGroups[p.lg].aux, name)
  185.         end
  186.       end
  187.     end
  188.   end
  189.  
  190.  
  191.   -- update the in-mem table with connected peripheral
  192.   connectPeripheral = function (side)
  193.     if not self.peripherals[side] then self.peripherals[side] = {} end
  194.     cp = self.peripherals[side]
  195.     cp.wrapped = peripheral.wrap(side)
  196.     cp.type = peripheral.getType(side)
  197.     updatePeripheral(side)
  198.     if cp.widgets then
  199.       for i,w in ipairs(cp.widgets) do w:draw() end
  200.     end
  201.   end
  202.  
  203.   -- mark peripheral as disconnected
  204.   local function disconnectPeripheral(side)
  205.     local dp = self.peripherals[side]
  206.     if not dp then return end
  207.     dp.wrapped = nil
  208.     dp.callable = false
  209.     if dp.widgets then
  210.       for i,w in ipairs(cp.widgets) do w:draw() end
  211.     end
  212.   end
  213.  
  214.   -- update peripheral
  215.   updatePeripheral = function (side)
  216.     local now = os.epoch("utc")
  217.     local p = self.peripherals[side]
  218.     if not p then return end
  219.     if not p.wrapped then
  220.       if peripheral.isPresent(side) then connectPeripheral(side)
  221.       else return
  222.       end
  223.     end
  224.     if p.wrapped.isInterfaced then
  225.       p.callable = p.wrapped.isInterfaced()
  226.     else
  227.       p.callable = true
  228.     end
  229.  
  230.     if p.callable then
  231.       --Energy
  232.       if p.wrapped.getEnergyStatus then
  233.         p.eu, p.euMax = p.wrapped.getEnergyStatus()
  234.       end
  235.       --Position
  236.       if p.wrapped.getLocalPosition and not p.pos then
  237.         p.pos = table.pack(p.wrapped.getLocalPosition())
  238.       end
  239.     end
  240.      --laser-specific stuff
  241.     if p.type and p.type == "warpdriveLaser" then
  242.       if not p.frequency or p.frequency ~= self.freq then
  243.         if p.wrapped and p.wrapped.beamFrequency and p.callable then
  244.           p.frequency = p.wrapped.beamFrequency(self.freq)
  245.           if not p.frequency or p.frequency == -1 then
  246.             self.logger.log("ERROR", "freq == -1", "updatePeripheral")
  247.           end
  248.         end
  249.       end
  250.       SetStatusLaser(p)
  251.     end
  252.     --TODO further updates here
  253.     p.lastUpdated = os.epoch("utc")
  254.   end
  255.  
  256.   -- coroutine to process peripheral events
  257.   local function managerCoroutine()
  258.     while true do
  259.       local p1, p2, p3, p4, p5 = os.pullEvent()
  260.       if p1 == "peripheral" then
  261.         connectPeripheral(p2)
  262.       elseif p1 == "peripheral_detach" then
  263.         disconnectPeripheral(p2)
  264.       end
  265.     end
  266.   end
  267.  
  268.   local function updaterCoroutine()
  269.     local tid = os.startTimer(self.updaterPeriod)
  270.     while true do
  271.       local p1, p2--[[, p3, p4, p5--]] = os.pullEvent()
  272.       if (p1 == "timer" and p2 == tid)
  273.           or p1 == "updatePeripheralsNow" then
  274.         local sMs = os.epoch("utc")
  275.         for name, per in pairs(self.peripherals) do
  276.           if os.epoch("utc") > (sMs + 40) then break end
  277.           if not per.lastUpdated
  278.               or sMs >= per.lastUpdated + self.peripheralPeriod then
  279.             updatePeripheral(name)
  280.           end
  281.         end
  282.         local uMs = os.epoch("utc")
  283.         if self.widgetsToUpdate then
  284.           for i,w in ipairs(self.widgetsToUpdate) do w:draw() end
  285.         end
  286.         local dMs = os.epoch("utc")
  287.         if self.logger then
  288.           self.logger.log("PERF",
  289.             "Took "..(uMs - sMs).."ms to update, "..(dMs - uMs)
  290.             .."ms to redraw", "updaterCoroutine")
  291.         end
  292.         tid = os.startTimer(self.updaterPeriod)
  293.       end
  294.     end
  295.   end
  296.  
  297.   local function setLogger(logger)
  298.     self.logger = logger
  299.   end
  300.  
  301.   --loads table deserialized from peripheral.cfg into t
  302.   ReadPeripheralTable = function(t)
  303.     --try reading the config into peripherals table
  304.     local res, err = ReadTableFromFile("peripheral.cfg")
  305.     if not res and self.logger then
  306.       self.logger.log("ERROR", err, "ReadPeripheralTable")
  307.       return nil
  308.     end
  309.     --copy data from it
  310.     if res then
  311.       for k,v in pairs(res) do
  312.         t[k] = v
  313.       end
  314.     end
  315.     return true
  316.   end
  317.  
  318.   -- rewrites peripheral.cfg with the supplied table
  319.   local WritePeripheralTable = function(t)
  320.     local res, err = WriteTableToFile(t, "peripheral.cfg")
  321.     if not res and self.logger then
  322.       self.logger.log("ERROR", err, "WritePeripheralTable")
  323.       return nil
  324.     end
  325.     return true
  326.   end
  327.  
  328.   -- assigns a color representing the status
  329.   SetStatusLaser = function (perTableEntry)
  330.     if perTableEntry.wrapped and perTableEntry.callable
  331.         and perTableEntry.eu and perTableEntry.euMax
  332.         and perTableEntry.pos then
  333.       local en = perTableEntry.eu / perTableEntry.euMax
  334.       if en < self.scRedThr or perTableEntry.eu == 0 then
  335.         perTableEntry.sc = colors.red
  336.       elseif en < self.scOrangeThr then
  337.         perTableEntry.sc = colors.orange
  338.       else
  339.         perTableEntry.sc = colors.green
  340.       end
  341.     else
  342.       perTableEntry.sc = colors.gray
  343.     end
  344.   end
  345.  
  346.   local function GetDefaultParamsByType(perType)
  347.     if perType == "warpdriveLaser" then
  348.       return {lg = "LG", lgm = false, offset = {0,-1.5,0}}
  349.       --for bottom main emitter position
  350.       --return {lg = "LG", lgm = false, offset = {0,-1.5,0}}
  351.       --for top main emitter position
  352.       --return {lg = "LG", lgm = false, offset = {0,1.5,0}}
  353.     else
  354.       return {}
  355.     end
  356.   end
  357.  
  358.   -- Returns first found peripheral of given type from
  359.   -- self.peripherals (might not be wrapped) with "param" = val if
  360.   -- val is defined, with "param" if val is nil, or returns nil
  361.   local function GetPeripheralByParamAndType(param, val, type)
  362.     for _, p in pairs(self.peripherals) do
  363.       if p.type and p.type == type then
  364.         if p[param] and (val == nil or p[param] == val) then
  365.           return p
  366.         end
  367.       end
  368.     end
  369.   end
  370.  
  371.   return {
  372.     ManagerCoroutine = managerCoroutine,
  373.     UpdaterCoroutine = updaterCoroutine,
  374.     peripherals = self.peripherals,
  375.     SetLogger = setLogger,
  376.     widgetsToUpdate = self.widgetsToUpdate,
  377.     Init = init,
  378.     laserGroups = self.laserGroups,
  379.     ReadPeripheralTable = ReadPeripheralTable,
  380.     WritePeripheralTable = WritePeripheralTable,
  381.     GetDefaultParamsByType = GetDefaultParamsByType,
  382.     GetPeripheralByParamAndType = GetPeripheralByParamAndType
  383.   }
  384. end
  385.  
  386. local PeripheralManager = CreatePeripheralManager()
  387.  
  388. -------------------------------------------------------------------
  389. -- Basic targeter
  390. -- has local array of laser groups copied from the PeripheralManager
  391. -- [1] = {name="LG1", p={mainRef, aux1ref, aux2ref, ...},
  392. --   canHit = {true, false, nil, ...}}}
  393. local function CreateTargeter(targeterId, logger)
  394.   local self = {
  395.     manager = PeripheralManager,
  396.     isFiring = false,
  397.     isAborted = false,
  398.     id = targeterId,  -- to identify fire requests and lock lasers
  399.     logger = logger,
  400.     target = {nil, nil, nil}, --currently assigned target
  401.     groups = {},
  402.     selected = {},  --indices from groups array, "selected" means true,
  403.                     --"not selected" means nil
  404.     modeBoost = true,
  405.     attemptsMax = 30, --tries to find a group to fire
  406.     minDstSq = 900, --won't shoot closer than this (squared)
  407.     repeats = 3, --times to fire in succession
  408.     repeatDelay = 1.0, --delay between repeats in seconds
  409.     statusWidgetRef = nil, --has to have .text, .textColor and :draw()
  410.     TargeterCoroutine = function() end,
  411.     DisplayGroup = function() end,
  412.     IsTargetAllowed = function() end,
  413.     OnGroupSelect = function() end,
  414.     SetTarget = function() end,
  415.     GetTarget = function() end,
  416.     ModeBoost = function() end,
  417.     SetStatusWidget = function() end,
  418.     FiringSequence = function() end,
  419.     UpdateStatus = function() end
  420.   }
  421.  
  422.   -- getter/setter
  423.   self.ModeBoost = function (val)
  424.     if val == nil then
  425.       return self.modeBoost
  426.     else
  427.       if val == true then self.modeBoost = true return end
  428.       if val == false then self.modeBoost = false return end
  429.     end
  430.   end
  431.  
  432.   self.SetStatusWidget = function(ref)
  433.     self.statusWidgetRef = ref
  434.   end
  435.  
  436.   -- updates status message (have to set the widget first)
  437.   -- textColor is optional
  438.   self.UpdateStatus = function(msg, textColor)
  439.     if not self.statusWidgetRef or not self.statusWidgetRef.text
  440.       or not self.statusWidgetRef.draw then return end
  441.     self.statusWidgetRef.text = msg
  442.     if textColor and self.statusWidgetRef.textColor then
  443.       self.statusWidgetRef.textColor = textColor
  444.     end
  445.     self.statusWidgetRef:draw()
  446.   end
  447.  
  448.   -- fills laser group array
  449.   local function fillGroups()
  450.     self.groups = {}
  451.     for n, g in pairs(self.manager.laserGroups) do
  452.       tgr = {name = n, p = {}, canHit = {}}
  453.       if g.main and self.manager.peripherals[g.main] then
  454.         tgr.p[1] = self.manager.peripherals[g.main]
  455.         if g.aux then
  456.           for _, auxName in ipairs(g.aux) do
  457.             auxRef = self.manager.peripherals[auxName]
  458.             if auxRef then tgr.p[#tgr.p+1] = auxRef end
  459.           end
  460.         end
  461.         self.groups[#self.groups+1] = tgr
  462.       else
  463.         if self.logger then
  464.           self.logger.log("ERROR", "No main laser in "..n, "fillGroups")
  465.         end
  466.       end
  467.     end
  468.     --TODO optional sorting
  469.   end
  470.  
  471.   self.GetTarget = function()
  472.     return self.target[1], self.target[2], self.target[3]
  473.   end
  474.  
  475.   --Sets new target, updates canHit values for each group
  476.   self.SetTarget = function(x, y, z)
  477.     self.target[1], self.target[2], self.target[3] = x, y, z
  478.     self.logger.log("DEBUG", string.format("Set %s,%s,%s",  x, y, z),
  479.         "setTarget")
  480.     for i_g, g in ipairs(self.groups) do
  481.       for i_l, l in ipairs(g.p) do
  482.         local res = nil
  483.         if l and l.pos then --laser and its position are ok
  484.           local relCoords = table.pack(x - l.pos[1],
  485.                                       y - l.pos[2],
  486.                                       z - l.pos[3])
  487.           -- TODO distance check here
  488.           res = self.IsTargetAllowed(l, relCoords)
  489.         end
  490.         g.canHit[i_l] = res
  491.       end
  492.     end
  493.   end
  494.  
  495.   --blits laser group through the supplied widget (designed for list)
  496.   --cuts group name after 4 symbols
  497.   self.DisplayGroup = function(w, index, length, isSelected)
  498.     --NWU1ooooo____ --like this
  499.     local textColor = colorToBlit(colors.white)
  500.     local bgColor = colorToBlit(colors.blue)
  501.     if isSelected then bgColor = colorToBlit(colors.cyan) end
  502.     local group = self.groups[index]
  503.     if not group or not group.name then
  504.       if self.logger then
  505.         self.logger.log("ERROR", "No group #"..index, "DisplayGroup")
  506.       end
  507.       return
  508.     end
  509.     --group in the manager
  510.     local managerGroup = self.manager.laserGroups[group.name]
  511.     if not managerGroup then
  512.       self.logger.log("ERROR",
  513.         "No group "..tostring(group.name).." in the manager",
  514.         "CheckFireGroup")
  515.     elseif managerGroup.lock then
  516.       if managerGroup.lock ~= self.id then
  517.         textColor = colorToBlit(colors.gray) --locked by other
  518.       else
  519.         textColor = colorToBlit(colors.yellow) --locked by self
  520.       end
  521.     end
  522.     --group name blit
  523.     local str=string.format("%-4.4s", group.name)
  524.     local col=textColor:rep(4)
  525.     local bg=bgColor:rep(4)
  526.     --lasers
  527.     for i_p = 1, length-4 do
  528.       if group.p[i_p] then
  529.         --symbol
  530.         if i_p == 1 then
  531.           str = str.."\4"
  532.         else
  533.           str = str.."\7"
  534.         end
  535.         --status
  536.         if group.p[i_p].sc then
  537.           col = col..colorToBlit(group.p[i_p].sc)
  538.         else
  539.           col = col..colorToBlit(colors.gray)
  540.         end
  541.         --bg
  542.         if group.canHit[i_p] then
  543.           bg = bg..colorToBlit(colors.green)
  544.         else
  545.           bg = bg..bgColor
  546.         end
  547.       else
  548.         str = str.." "
  549.         col = col..colorToBlit(colors.black)
  550.         bg = bg..bgColor
  551.       end
  552.     end
  553.     if not w or not w.window then
  554.       if self.logger then
  555.         self.logger.log("ERROR", "Widget error", "displayGroup")
  556.       end
  557.       return
  558.     end
  559.     w.window.blit(str, col, bg)
  560.   end
  561.  
  562.   -- overload for WidgetListView:OnSelect()
  563.   self.OnGroupSelect = function (w)
  564.     if not w then return end
  565.     for i_g, g in ipairs(self.groups) do
  566.       local mg = self.manager.laserGroups[g.name]
  567.       if not mg then
  568.         self.logger.log("ERROR", "No group "..tostring(g.name)
  569.           .." in the manager", "CheckFireGroup")
  570.         return
  571.       end
  572.       if not mg.lock or mg.lock == self.id then
  573.         if not w.selected then  --no widget selection data
  574.           if self.selected[i_g] then
  575.             mg.lock = nil
  576.             self.selected[i_g] = nil
  577.             self.logger.log("DEBUG", "unlocked "..g.name, "OGS")
  578.           end
  579.         else
  580.           if w.selected[i_g] then --selected in the widget
  581.             mg.lock = self.id
  582.             self.selected[i_g] = true
  583.             self.logger.log("DEBUG", "locked "..g.name, "OGS")
  584.           else  --not selected in the widget (nil or false)
  585.             mg.lock = nil
  586.             self.selected[i_g] = nil
  587.             self.logger.log("DEBUG", "unlocked "..g.name, "OGS")
  588.           end
  589.         end
  590.       end
  591.     end
  592.     if not w.selected then
  593.       self.selected = {}
  594.     end
  595.   end
  596.  
  597.   -- input: ref to laser peripheral, relative {x,y,z} of the target
  598.   self.IsTargetAllowed = function(laserPerRef, relXyzT)
  599.     --TODO sanity checks
  600.     local x, y, z = table.unpack(relXyzT)
  601.     self.logger.log("DEBUG", string.format("%s,%s,%s",
  602.         x, y, z ), "IsTargetAllowed")
  603.     if x*x + y*y + z*z < self.minDstSq then
  604.       self.logger.log("DEBUG", "Too close", "IsTargetAllowed")
  605.       return false
  606.     end
  607.     ax, ay, az = math.abs(x), math.abs(y), math.abs(z)
  608.     if laserPerRef.N and z < 0 and az >= ax and az >= ay then
  609.       self.logger.log("DEBUG", "N", "IsTargetAllowed")
  610.       return true
  611.     end
  612.     if laserPerRef.S and z > 0 and az >= ax and az >= ay then
  613.       self.logger.log("DEBUG", "S", "IsTargetAllowed")
  614.       return true
  615.     end
  616.     if laserPerRef.W and x < 0 and ax >= ay and ax >= az then
  617.       self.logger.log("DEBUG", "W", "IsTargetAllowed")
  618.       return true
  619.     end
  620.     if laserPerRef.E and x > 0 and ax >= ay and ax >= az then
  621.       self.logger.log("DEBUG", "E", "IsTargetAllowed")
  622.       return true
  623.     end
  624.     if laserPerRef.D and y < 0 and ay >= ax and ay >= az then
  625.       self.logger.log("DEBUG", "D", "IsTargetAllowed")
  626.        return true
  627.     end
  628.     if laserPerRef.U and y > 0 and ay >= ax and ay >= az then
  629.       self.logger.log("DEBUG", "U", "IsTargetAllowed")
  630.       return true
  631.     end
  632.     self.logger.log("DEBUG", "false", "IsTargetAllowed")
  633.     return false
  634.   end
  635.  
  636.   -- simple fire condition function to fire once
  637.   local function CheckFireCondition()
  638.     if not self.target[1] or not self.target[2]
  639.         or not self.target[3] then
  640.       self.UpdateStatus("NO TARG", colors.red)
  641.       return false
  642.     end
  643.     if self.isAborted then
  644.       self.UpdateStatus("ABORTED", colors.red)
  645.       return false
  646.     end
  647.     return true --TODO
  648.   end
  649.  
  650.   -- returns true if the laser is ready to fire sans direction check
  651.   local function CheckLaser(laserPerRef)
  652.     if not laserPerRef.sc then return false end
  653.     return laserPerRef.sc == colors.green
  654.   end
  655.  
  656.   -- returns true if the entire laser group is ready to fire
  657.   -- in boosted mode, or at least one laser is ready otherwise.
  658.   -- Checks if the group is locked to the current targeter or no
  659.   -- groups are locked to it at all
  660.   -- Locks the group
  661.   local function CheckFireGroup(g)
  662.     -- lock check
  663.     local groupM = self.manager.laserGroups[g.name]
  664.     if not groupM then
  665.       self.logger.log("ERROR",
  666.         "No group "..tostring(g.name).." in the manager",
  667.         "CheckFireGroup")
  668.       return false
  669.     end
  670.     --locked by some other targeter
  671.     if groupM.lock and groupM.lock ~= self.id then
  672.       self.logger.log("DEBUG",g.name.." locked by othr","CheckFG")
  673.       return false
  674.     end
  675.     --some groups are selected (self.selected not empty),
  676.     --but this one does not have a lock
  677.     if self.selected and next(self.selected) ~= nil
  678.         and not groupM.lock then
  679.       return false
  680.     end
  681.     -- direction check
  682.     if self.ModeBoost then
  683.       if not g.canHit[1] then
  684.         self.logger.log("DEBUG",g.name.." can't hit","CheckFG")
  685.         return false
  686.       end
  687.       -- individual laser check
  688.       for i_l=1, #g.p do
  689.         if not CheckLaser(g.p[i_l]) then
  690.           self.logger.log("DEBUG",g.name.." has false on laser "..
  691.             tostring(i_l),"CheckFG")
  692.           return false
  693.         end
  694.       end
  695.     else  --at least one can hit
  696.       for i_l=1, #g.p do
  697.         if g.canHit[i_l] and CheckLaser(g.p[i_l]) then
  698.           break
  699.         end
  700.       end
  701.     end
  702.     -- locking to self
  703.     groupM.lock = self.id
  704.     return true
  705.   end
  706.  
  707.   -- returns an array of suitable laser groups ready to fire
  708.   -- this one tries attemptsMax times to find a single group with 1s
  709.   -- cooldown if none are available
  710.   local function GetSuitableGroups()
  711.     for attempt = 1, self.attemptsMax do
  712.       if not CheckFireCondition() then
  713.         return {}
  714.       end
  715.       for i_g, g in ipairs(self.groups) do
  716.         if CheckFireGroup(g)==true then return {g} end
  717.       end
  718.       self.UpdateStatus("WAIT #"..attempt, colors.orange)
  719.       sleep(1)
  720.     end
  721.     self.UpdateStatus("NO GROUPS", colors.red)
  722.     return {}
  723.   end
  724.  
  725.   --returns number of groups which opened fire successfully
  726.   local function FireBoost(groupsArray)
  727.     local fired = 0
  728.     for _,g in ipairs(groupsArray) do
  729.       local xm, ym, zm = table.unpack(g.p[1].pos)
  730.       local xt, yt, zt = table.unpack(self.target)
  731.       for i_l=2, #g.p do
  732.         local xo, yo, zo = 0, 0, 0
  733.         if g.p[i_l].offset then
  734.           xo, yo, zo = table.unpack(g.p[i_l].offset)
  735.         end
  736.         --TODO check if callable
  737.         local xl, yl, zl = table.unpack(g.p[i_l].pos)
  738.         g.p[i_l].wrapped.emitBeam(xm-xl+xo, ym-yl+yo, zm-zl+zo)
  739.         --mark lasers red
  740.         g.p[i_l].sc = colors.red
  741.         self.logger.log("DEBUG", string.format("%s[%s].emitBeam(%s,%s,%s)",
  742.           g.name, i_l, xm-xl+xo, ym-yl+yo, zm-zl+zo), "FireBoost")
  743.       end
  744.       sleep(0.1) --boost sleep
  745.       if not CheckFireCondition() then
  746.         return fired
  747.       end
  748.       g.p[1].wrapped.emitBeam(xt-xm, yt-ym, zt-zm)
  749.       --mark lasers red
  750.       g.p[1].sc = colors.red
  751.       self.logger.log("DEBUG", string.format("%s[1].emitBeam(%s,%s,%s)",
  752.       g.name, xt-xm, yt-ym, zt-zm), "FireBoost")
  753.       fired = fired+1
  754.       --UpdateStatus("FIRED".."".."B", colors.green)
  755.     end
  756.     return fired
  757.   end
  758.  
  759.   --TODO
  760.   local function FireAll(groupsArray)
  761.     local count = 0
  762.     for _,g in ipairs(groupsArray) do
  763.       local xt, yt, zt = table.unpack(self.target)
  764.       for i_l=1, #g.p do
  765.         if g.canHit[i_l] and CheckLaser(g.p[i_l]) then
  766.           local xl, yl, zl = table.unpack(g.p[i_l].pos)
  767.           g.p[i_l].wrapped.emitBeam(xt-xl, yt-yl, zt-zl)
  768.           count = count+1
  769.         end
  770.       end
  771.     end
  772.     self.UpdateStatus("FIRED #"..count, colors.green)
  773.   end
  774.  
  775.   --unlocks every group except selected
  776.   local function UnlockGroups()
  777.     for i_g, g in ipairs(self.groups) do
  778.       local mg = self.manager.laserGroups[g.name]
  779.       if not mg then
  780.         self.logger.log("ERROR", "No group "..tostring(g.name)
  781.           .." in the manager", "UnlockGroups")
  782.         return
  783.       end
  784.       if not self.selected then
  785.         self.logger.log("ERROR", "nil", "UG")
  786.         return
  787.       end
  788.       if mg.lock and mg.lock == self.id then
  789.         if not self.selected[i_g] then
  790.           mg.lock = nil
  791.           self.logger.log("DEBUG", "unlocked "..g.name, "UG")
  792.         end
  793.       end
  794.     end
  795.   end
  796.  
  797.   self.FiringSequence = function()
  798.     self.isFiring = true
  799.     for i_r=1, self.repeats do
  800.       if CheckFireCondition() then
  801.         --find suitable laser groups
  802.         local groupsArray = GetSuitableGroups()
  803.         if self.modeBoost then
  804.           FireBoost(groupsArray)
  805.           self.UpdateStatus("FIRED #"..i_r, colors.green)
  806.         else
  807.           FireAll(groupsArray)
  808.         end
  809.         --unlock laser groups
  810.         UnlockGroups(groupsArray)
  811.       else
  812.         break
  813.       end
  814.       if i_r < self.repeats then sleep(self.repeatDelay) end
  815.     end
  816.     self.isFiring = false
  817.   end
  818.  
  819.   self.TargeterCoroutine = function()
  820.     self.UpdateStatus("STARTED", colors.green)
  821.     while true do
  822.       local p1, p2, p3, p4, p5 = os.pullEvent()
  823.       if p1 == "fire" and p2 == self.id then
  824.         self.logger.log("DEBUG", "fire event: id="..tostring(p2), "TC")
  825.         self.FiringSequence()
  826.         --UpdateStatus("STANDBY", colors.green)
  827.       end
  828.     end
  829.   end
  830.  
  831.   fillGroups()
  832.  
  833.   return self
  834. end
  835.  
  836. -- Repeating targeter --now included in the basic
  837. --local function CreateTargeterR(targeterId, logger)
  838. --  local self = table.pack(CreateTargeter(targeterId, logger))
  839. --  basicFireSequence = self.FiringSequence()
  840. --end
  841.  
  842. -- Automatic targeter (to work with laser cameras)
  843. local function CreateTargeterAutoCam(targeterId, logger)
  844.   local self = CreateTargeter(targeterId, logger)
  845.   self.TargeterCoroutine = function()
  846.     self.UpdateStatus("LISTEN", colors.green)
  847.     while true do
  848.       local p1, p2, p3, p4, p5 = os.pullEvent()
  849.       self.logger.log("DEBUG", tostring(p1)..","..
  850.         tostring(p2)..","..tostring(p3).."...", "ACAM")
  851.       self.UpdateStatus(p1, colors.pink)
  852.     end
  853.   end
  854.   return self
  855. end
  856.  
  857.  
  858. -------------------------------------------------------------------
  859. -- Basic targeter tab
  860. -- fills 18x51 tab with widgets for basic targeter
  861. -- Input:
  862. -- targeterId (1, 2, ...)
  863. -- refTabWidget - empty 18x51 tab
  864. -- refTextInput - InputLine widget reference
  865. -- refTargetArray - target array reference
  866. -- refTargetArrDrawRow - function drawing target array elements
  867. -- Output:
  868. -- t1ListT - target list ref (for updates)
  869. -- TargeterCoroutine
  870. local function CreateTargeterTab(targeterId, refTabWidget,
  871.       refTextInput, refTargetArray, refTargetArrDrawRow)
  872.   local targeter1 = CreateTargeter(targeterId, Widgets.Logger)
  873.   local t1l1 = Widgets.Label:new()
  874.   t1l1:init(refTabWidget, 1, 1, 13, 1, true)
  875.   t1l1.text = "Lasers:"
  876.   local t1ListLg = Widgets.ListView:new()
  877.   t1ListLg:init(refTabWidget, 1, 2, 10, 16, true)
  878.   t1ListLg.isSelectable = true
  879.   t1ListLg.isMultiSelectable = true
  880.   t1ListLg.dataArray = targeter1.groups
  881.   t1ListLg.drawRow = targeter1.DisplayGroup
  882.   t1ListLg.onSelect = targeter1.OnGroupSelect
  883.   table.insert(PeripheralManager.widgetsToUpdate, t1ListLg)
  884.   local t1l2 = Widgets.Label:new()
  885.   t1l2:init(refTabWidget, 12, 1, 7, 1, true)
  886.   t1l2.text = "Target:"
  887.   local t1l3 = Widgets.Label:new()
  888.   t1l3:init(refTabWidget, 12, 2, 20, 1, true)
  889.   t1l3.bgColor = colors.white
  890.   t1l3.text = string.format("%7s,%4s,%7s", targeter1.GetTarget())
  891.   local t1b1 = Widgets.Button:new()
  892.   t1b1:init(refTabWidget, 32, 2, 5, 1, true)
  893.   t1b1.caption = "ENTER"
  894.   t1b1.refTextInput = refTextInput
  895.   t1b1.refTargeter = targeter1
  896.   t1b1.refDrawable = refTabWidget
  897.   t1b1.onClick = function(self)
  898.     if not self.refTextInput or not self.refTargeter
  899.         or not self.refDrawable then
  900.       Widgets.Logger.log("ERROR", "ref not set", "TargetSetBn")
  901.       return
  902.     end
  903.     local str = self.refTextInput:processText("x, y, z: ", "")
  904.     local tab = textutils.unserialize("{"..str.."}")
  905.     if tab == nil then return end
  906.     local x, y, z = table.unpack(tab)
  907.     if x and type(x) == "number" and y and type(y) == "number"
  908.       and z and type(z) == "number" then
  909.         self.refTargeter.SetTarget(x, y, z)
  910.         t1l3.text = string.format("%7s,%4s,%7s", self.refTargeter.GetTarget())
  911.         self.refDrawable:draw()
  912.       else
  913.         Widgets.Logger.log("DEBUG", string.format("x,y,z=%s,%s,%s",
  914.           x, y, z), "TargetSetBn")
  915.       end
  916.   end
  917.   local t1b2 = Widgets.ButtonT:new()
  918.   t1b2:init(refTabWidget, 40, 4, 7, 1, true)
  919.   t1b2.caption = " BOOST"
  920.   t1b2.fn = targeter1.ModeBoost
  921.   local t1l6 = Widgets.Label:new()
  922.   t1l6:init(refTabWidget, 40, 5, 7, 1, true)
  923.   t1l6.text = "Repeats:"
  924.   local t1b6 = Widgets.Button:new()
  925.   t1b6:init(refTabWidget, 40, 6, 7, 1, true)
  926.   t1b6.caption = tostring(targeter1.repeats)
  927.   t1b6.refTargeter = targeter1
  928.   t1b6.refTextInput = refTextInput
  929.   t1b6.refDrawable = refTabWidget
  930.   t1b6.onClick = function(self)
  931.     if not self.refTextInput or not self.refTargeter
  932.         or not self.refDrawable then
  933.       Widgets.Logger.log("ERROR", "ref not set", "RepeatsBn")
  934.       return
  935.     end
  936.     local val = tonumber(self.refTextInput:processText(
  937.       "Number of repeats: ", tostring(self.refTargeter.repeats)))
  938.     if not val then return end
  939.     val = math.floor(math.abs(val))
  940.     if val <= 0 then return end
  941.     self.refTargeter.repeats = val
  942.     self.caption = tostring(val)
  943.     self.refDrawable:draw()
  944.   end
  945.   local t1l7 = Widgets.Label:new()
  946.   t1l7:init(refTabWidget, 40, 7, 7, 1, true)
  947.   t1l7.text = "Delay:"
  948.   local t1b7 = Widgets.Button:new()
  949.   t1b7:init(refTabWidget, 40, 8, 7, 1, true)
  950.   t1b7.caption = tostring(targeter1.repeatDelay.." s")
  951.   t1b7.refTargeter = targeter1
  952.   t1b7.refTextInput = refTextInput
  953.   t1b7.refDrawable = refTabWidget
  954.   t1b7.onClick = function(self)
  955.     if not self.refTextInput or not self.refTargeter
  956.         or not self.refDrawable then
  957.       Widgets.Logger.log("ERROR", "ref not set", "RepeatDelayBn")
  958.       return
  959.     end
  960.     local val = tonumber(self.refTextInput:processText(
  961.       "Seconds between repeats: ",
  962.       tostring(self.refTargeter.repeatDelay)))
  963.     if not val then return end
  964.     val = math.floor(math.abs(val)*20)/20
  965.     if val < 0.05 then return end
  966.     self.refTargeter.repeatDelay = val
  967.     self.caption = tostring(val).." s"
  968.     self.refDrawable:draw()
  969.   end
  970.   local t1b3 = Widgets.ButtonM:new()
  971.   t1b3:init(refTabWidget, 45, 14, 6, 2, true)
  972.   t1b3.text = " \n FIRE \n"
  973.   t1b3.refTargeter = targeter1
  974.   t1b3.onClick = function(self)
  975.     if not self.refTargeter then
  976.       Widgets.Logger.log("ERROR", "ref not set", "FireBn")
  977.       return
  978.     end
  979.     self.refTargeter.isAborted = false
  980.     if not self.refTargeter.isFiring then
  981.       os.queueEvent("fire", targeterId)
  982.     end
  983.   end
  984.   local t1l4 = Widgets.Label:new()
  985.   t1l4:init(refTabWidget, 38, 1, 7, 1, true)
  986.   t1l4.text = "Status:"
  987.   local t1LStatus = Widgets.Label:new()
  988.   t1LStatus:init(refTabWidget, 38, 2, 10, 1, true)
  989.   t1LStatus.bgColor = colors.blue
  990.   targeter1.SetStatusWidget(t1LStatus)
  991.   local t1l5 = Widgets.Label:new()
  992.   t1l5:init(refTabWidget, 12, 3, 13, 1, true)
  993.   t1l5.text = "Target list:"
  994.   -- target list viewer/selector
  995.   local t1ListT = Widgets.ListView:new()
  996.   t1ListT:init(refTabWidget, 12, 4, 25, 14, true)
  997.   t1ListT.isSelectable = true
  998.   t1ListT.dataArray = refTargetArray
  999.   t1ListT.drawRow = refTargetArrDrawRow
  1000.   table.insert(ttB1.refDrawableArr, t1ListT)
  1001.   local t1b4 = Widgets.Button:new()
  1002.   t1b4:init(refTabWidget, 27, 3, 3, 1, true)
  1003.   t1b4.caption = " \30 "
  1004.   t1b4.refListT = t1ListT
  1005.   t1b4.refTargeter = targeter1
  1006.   t1b4.refTargetLabel = t1l3
  1007.   t1b4.refDrawable = refTabWidget
  1008.   t1b4.onClick = function(self)
  1009.     if not self.refListT or not self.refTargeter
  1010.         or not self.refDrawable or not self.refTargetLabel then
  1011.       Widgets.Logger.log("ERROR", "ref not set", "t1b4")
  1012.       return
  1013.     end
  1014.     Widgets.Logger.log("ERROR",
  1015.         "selected="..tostring(self.refListT.selected), "t1b4")
  1016.     if self.refListT.selected then
  1017.       local ts = self.refListT.dataArray[self.refListT.selected]
  1018.       self.refTargeter.SetTarget(ts[1], ts[2], ts[3])
  1019.       self.refTargetLabel.text = string.format("%7s,%4s,%7s",
  1020.           targeter1.GetTarget())
  1021.       self.refDrawable:draw()
  1022.     end
  1023.   end
  1024.   t1b5 = Widgets.ButtonM:new()
  1025.   t1b5:init(refTabWidget, 45, 17, 6, 1, true)
  1026.   t1b5.text = "ABORT "
  1027.   t1b5.textColor = colors.red
  1028.   t1b5.refTargeter = targeter1
  1029.   t1b5.onClick = function(self)
  1030.     if not self.refTargeter then
  1031.       Widgets.Logger.log("ERROR", "ref not set", "FireBn")
  1032.       return
  1033.     end
  1034.     self.refTargeter.isAborted = true
  1035.   end
  1036.  
  1037.   return t1ListT, targeter1.TargeterCoroutine
  1038. end
  1039.  
  1040.  
  1041. -------------------------------------------------------------------
  1042. -- Automatic targeter tab for laser cameras
  1043. -- fills 18x51 tab with widgets for automatic targeter
  1044. -- Input:
  1045. -- targeterId (1, 2, ...) - should be unique
  1046. -- refTabWidget - empty 18x51 tab
  1047. -- refTextInput - InputLine widget reference
  1048. -- refTargetArray - target array reference
  1049. -- refTargetArrDrawRow - function drawing target array elements
  1050. -- Output:
  1051. -- t1ListT - target list ref (for updates)
  1052. -- TargeterCoroutine
  1053. local function CreateTargeterAutoCamTab(targeterId, refTabWidget,
  1054.   refTextInput, refTargetArray, refTargetArrDrawRow)
  1055. local targeter1 = CreateTargeterAutoCam(targeterId, Widgets.Logger)
  1056. local t1l1 = Widgets.Label:new()
  1057. t1l1:init(refTabWidget, 1, 1, 13, 1, true)
  1058. t1l1.text = "Lasers:"
  1059. local t1ListLg = Widgets.ListView:new()
  1060. t1ListLg:init(refTabWidget, 1, 2, 10, 16, true)
  1061. t1ListLg.isSelectable = true
  1062. t1ListLg.isMultiSelectable = true
  1063. t1ListLg.dataArray = targeter1.groups
  1064. t1ListLg.drawRow = targeter1.DisplayGroup
  1065. t1ListLg.onSelect = targeter1.OnGroupSelect
  1066. table.insert(PeripheralManager.widgetsToUpdate, t1ListLg)
  1067. local t1l2 = Widgets.Label:new()
  1068. t1l2:init(refTabWidget, 12, 1, 7, 1, true)
  1069. t1l2.text = "Target:"
  1070. local t1l3 = Widgets.Label:new()
  1071. t1l3:init(refTabWidget, 12, 2, 20, 1, true)
  1072. t1l3.bgColor = colors.white
  1073. t1l3.text = string.format("%7s,%4s,%7s", targeter1.GetTarget())
  1074. local t1b1 = Widgets.Button:new()
  1075. t1b1:init(refTabWidget, 32, 2, 5, 1, true)
  1076. t1b1.caption = "ENTER"
  1077. t1b1.refTextInput = refTextInput
  1078. t1b1.refTargeter = targeter1
  1079. t1b1.refDrawable = refTabWidget
  1080. t1b1.onClick = function(self)
  1081. if not self.refTextInput or not self.refTargeter
  1082.     or not self.refDrawable then
  1083.   Widgets.Logger.log("ERROR", "ref not set", "TargetSetBn")
  1084.   return
  1085. end
  1086. local str = self.refTextInput:processText("x, y, z: ", "")
  1087. local tab = textutils.unserialize("{"..str.."}")
  1088. if tab == nil then return end
  1089. local x, y, z = table.unpack(tab)
  1090. if x and type(x) == "number" and y and type(y) == "number"
  1091.   and z and type(z) == "number" then
  1092.     self.refTargeter.SetTarget(x, y, z)
  1093.     t1l3.text = string.format("%7s,%4s,%7s", self.refTargeter.GetTarget())
  1094.     self.refDrawable:draw()
  1095.   else
  1096.     Widgets.Logger.log("DEBUG", string.format("x,y,z=%s,%s,%s",
  1097.       x, y, z), "TargetSetBn")
  1098.   end
  1099. end
  1100. local t1b2 = Widgets.ButtonT:new()
  1101. t1b2:init(refTabWidget, 40, 4, 7, 1, true)
  1102. t1b2.caption = " BOOST"
  1103. t1b2.fn = targeter1.ModeBoost
  1104. local t1l6 = Widgets.Label:new()
  1105. t1l6:init(refTabWidget, 40, 5, 7, 1, true)
  1106. t1l6.text = "Repeats:"
  1107. local t1b6 = Widgets.Button:new()
  1108. t1b6:init(refTabWidget, 40, 6, 7, 1, true)
  1109. t1b6.caption = tostring(targeter1.repeats)
  1110. t1b6.refTargeter = targeter1
  1111. t1b6.refTextInput = refTextInput
  1112. t1b6.refDrawable = refTabWidget
  1113. t1b6.onClick = function(self)
  1114. if not self.refTextInput or not self.refTargeter
  1115.     or not self.refDrawable then
  1116.   Widgets.Logger.log("ERROR", "ref not set", "RepeatsBn")
  1117.   return
  1118. end
  1119. local val = tonumber(self.refTextInput:processText(
  1120.   "Number of repeats: ", tostring(self.refTargeter.repeats)))
  1121. if not val then return end
  1122. val = math.floor(math.abs(val))
  1123. if val <= 0 then return end
  1124. self.refTargeter.repeats = val
  1125. self.caption = tostring(val)
  1126. self.refDrawable:draw()
  1127. end
  1128. local t1l7 = Widgets.Label:new()
  1129. t1l7:init(refTabWidget, 40, 7, 7, 1, true)
  1130. t1l7.text = "Delay:"
  1131. local t1b7 = Widgets.Button:new()
  1132. t1b7:init(refTabWidget, 40, 8, 7, 1, true)
  1133. t1b7.caption = tostring(targeter1.repeatDelay.." s")
  1134. t1b7.refTargeter = targeter1
  1135. t1b7.refTextInput = refTextInput
  1136. t1b7.refDrawable = refTabWidget
  1137. t1b7.onClick = function(self)
  1138. if not self.refTextInput or not self.refTargeter
  1139.     or not self.refDrawable then
  1140.   Widgets.Logger.log("ERROR", "ref not set", "RepeatDelayBn")
  1141.   return
  1142. end
  1143. local val = tonumber(self.refTextInput:processText(
  1144.   "Seconds between repeats: ",
  1145.   tostring(self.refTargeter.repeatDelay)))
  1146. if not val then return end
  1147. val = math.floor(math.abs(val)*20)/20
  1148. if val < 0.05 then return end
  1149. self.refTargeter.repeatDelay = val
  1150. self.caption = tostring(val).." s"
  1151. self.refDrawable:draw()
  1152. end
  1153. local t1b3 = Widgets.ButtonM:new()
  1154. t1b3:init(refTabWidget, 45, 14, 6, 2, true)
  1155. t1b3.text = " \n FIRE \n"
  1156. t1b3.refTargeter = targeter1
  1157. t1b3.onClick = function(self)
  1158. if not self.refTargeter then
  1159.   Widgets.Logger.log("ERROR", "ref not set", "FireBn")
  1160.   return
  1161. end
  1162. self.refTargeter.isAborted = false
  1163. if not self.refTargeter.isFiring then
  1164.   os.queueEvent("fire", targeterId)
  1165. end
  1166. end
  1167. local t1l4 = Widgets.Label:new()
  1168. t1l4:init(refTabWidget, 38, 1, 7, 1, true)
  1169. t1l4.text = "Status:"
  1170. local t1LStatus = Widgets.Label:new()
  1171. t1LStatus:init(refTabWidget, 38, 2, 10, 1, true)
  1172. t1LStatus.bgColor = colors.blue
  1173. targeter1.SetStatusWidget(t1LStatus)
  1174. local t1l5 = Widgets.Label:new()
  1175. t1l5:init(refTabWidget, 12, 3, 13, 1, true)
  1176. t1l5.text = "Target list:"
  1177. -- target list viewer/selector
  1178. local t1ListT = Widgets.ListView:new()
  1179. t1ListT:init(refTabWidget, 12, 4, 25, 14, true)
  1180. t1ListT.isSelectable = true
  1181. t1ListT.dataArray = refTargetArray
  1182. t1ListT.drawRow = refTargetArrDrawRow
  1183. table.insert(ttB1.refDrawableArr, t1ListT)
  1184. local t1b4 = Widgets.Button:new()
  1185. t1b4:init(refTabWidget, 27, 3, 3, 1, true)
  1186. t1b4.caption = " \30 "
  1187. t1b4.refListT = t1ListT
  1188. t1b4.refTargeter = targeter1
  1189. t1b4.refTargetLabel = t1l3
  1190. t1b4.refDrawable = refTabWidget
  1191. t1b4.onClick = function(self)
  1192. if not self.refListT or not self.refTargeter
  1193.     or not self.refDrawable or not self.refTargetLabel then
  1194.   Widgets.Logger.log("ERROR", "ref not set", "t1b4")
  1195.   return
  1196. end
  1197. Widgets.Logger.log("ERROR",
  1198.     "selected="..tostring(self.refListT.selected), "t1b4")
  1199. if self.refListT.selected then
  1200.   local ts = self.refListT.dataArray[self.refListT.selected]
  1201.   self.refTargeter.SetTarget(ts[1], ts[2], ts[3])
  1202.   self.refTargetLabel.text = string.format("%7s,%4s,%7s",
  1203.       targeter1.GetTarget())
  1204.   self.refDrawable:draw()
  1205. end
  1206. end
  1207. t1b5 = Widgets.ButtonM:new()
  1208. t1b5:init(refTabWidget, 45, 17, 6, 1, true)
  1209. t1b5.text = "ABORT "
  1210. t1b5.textColor = colors.red
  1211. t1b5.refTargeter = targeter1
  1212. t1b5.onClick = function(self)
  1213. if not self.refTargeter then
  1214.   Widgets.Logger.log("ERROR", "ref not set", "FireBn")
  1215.   return
  1216. end
  1217. self.refTargeter.isAborted = true
  1218. end
  1219.  
  1220. return t1ListT, targeter1.TargeterCoroutine
  1221. end
  1222.  
  1223.  
  1224. -------------------------------------------------------------------
  1225. -- Peripheral manager tab
  1226. -- fills 18x51 tab with a GUI to manage all connected peripherals
  1227. -- uses existing PeripheralManager from this file
  1228. -- Input:
  1229. -- refTabWidget - empty 18x51 tab
  1230. -- refTextInput - InputLine widget reference
  1231. local function CreatePeripheralManagerTab(refTabWidget,
  1232.   refTextInput)
  1233.   local tpml1 = Widgets.Label:new()
  1234.   tpml1:init(refTabWidget, 1, 1, 12, 1, true)
  1235.   tpml1.text = "Peripherals:"
  1236.   -- List of every connected peripheral side/name
  1237.   local tpmList1 = Widgets.ListView:new()
  1238.   tpmList1:init(refTabWidget, 1, 2, 20, 15, true)
  1239.   tpmList1.isSelectable = true
  1240.   tpmList1.includeCfg = function() return true end
  1241.   tpmList1.refPerManTable = PeripheralManager.peripherals
  1242.   tpmList1.onSelect = function(self)
  1243.     if not self.refMethods or not self.refPerData then
  1244.       Widgets.Logger.log("ERROR", "ref not set", "tpmList1")
  1245.       return
  1246.     end
  1247.     self.refMethods.dataArray = nil
  1248.     if self.selected and self.dataArray then
  1249.       local perSide = self.dataArray[self.selected]
  1250.       if peripheral.isPresent(perSide) then
  1251.         self.refMethods.dataArray = peripheral.getMethods(perSide)
  1252.       end
  1253.     end
  1254.     self:drawRefs()
  1255.   end
  1256.   tpmList1.updateData = function(self)
  1257.     self.dataArray = peripheral.getNames()
  1258.     if self.includeCfg() and self.refPerManTable then
  1259.       for k, _ in pairs(self.refPerManTable) do
  1260.         if not peripheral.isPresent(k) then
  1261.           table.insert(self.dataArray, k)
  1262.         end
  1263.       end
  1264.     end
  1265.     table.sort(self.dataArray)
  1266.   end
  1267.   local tpmList1fn = function(newVal)
  1268.     local value = true --default
  1269.     return function(newVal)
  1270.       if newVal==nil then return value end
  1271.       if newVal==true then value = true end
  1272.       if newVal==false then value = false end
  1273.     end
  1274.   end
  1275.   tpmList1.includeCfg = tpmList1fn()
  1276.   tpmList1.drawRow = function(self, index, length, isSelected)
  1277.     local bgColor = self.bgColor
  1278.     local textColor = self.textColor
  1279.     local val = self.dataArray[index] --peripheral name
  1280.     local shortName = val:gsub("warpdrive", "wd", 1) --shorten prefix
  1281.     if shortName:len() > 20 then  --we're limited to 20 chars
  1282.       local numStart, numEnd = shortName:find("_%d+", 3)
  1283.       if numStart and numEnd then
  1284.         shortName = shortName:sub(1, 20-(numEnd-numStart+1)) .. "~"
  1285.           .. shortName:sub(numStart+1, numEnd)
  1286.       end
  1287.     end
  1288.     if self.refPerManTable and val
  1289.       and (not self.refPerManTable[val]
  1290.       or not self.refPerManTable[val].callable) then
  1291.       textColor = colors.gray
  1292.       end
  1293.     if isSelected then
  1294.       bgColor = self.bgColorSelected
  1295.     end
  1296.     local rowStr = string.format("%-"..length.."."..length.."s",
  1297.       shortName)
  1298.     self.window.blit(rowStr,
  1299.         colorToBlit(textColor):rep(#rowStr),
  1300.         colorToBlit(bgColor):rep(#rowStr))--]]
  1301.   end
  1302.   -- Button to refresh the list of peripherals
  1303.   local tpmb1 = Widgets.Button:new()
  1304.   tpmb1:init(refTabWidget, 1, 17, 7, 1, true)
  1305.   tpmb1.caption = "REFRESH"
  1306.   tpmb1.refListPeripherals = tpmList1
  1307.   tpmb1.onClick = function(self)
  1308.     if not self.refListPeripherals then
  1309.       Widgets.Logger.log("ERROR", "refListPeripherals not set", "tpmb1")
  1310.       return
  1311.     end
  1312.     self.refListPeripherals:draw()
  1313.   end
  1314.   -- Button to include/exclude disconnected peripherals from .cfg
  1315.   local tpmBT1 = Widgets.ButtonT:new()
  1316.   tpmBT1:init(refTabWidget, 9, 17, 5, 1, true)
  1317.   tpmBT1.caption = ".cfg"
  1318.   tpmBT1.refListPeripherals = tpmList1
  1319.   tpmBT1.fn = tpmList1.includeCfg
  1320.  
  1321.  
  1322.   -- Methods and data tabs
  1323.   local tpmTabs1 = Widgets.Tabs:new()
  1324.   tpmTabs1:init(refTabWidget, 22, 1, 31, 49, true)
  1325.   -- Methods tab
  1326.   local tpmTab1 = tpmTabs1:addTab(" Methods ")
  1327.   -- List of all methods of selected peripheral
  1328.   local tpmList2 = Widgets.ListView:new()
  1329.   tpmList2:init(tpmTab1, 1, 2, 20, 8, true)
  1330.   tpmList2.isSelectable = true
  1331.   tpmList1.refMethods = tpmList2
  1332.   table.insert(tpmList1.refsDraw, tpmList2)
  1333.   -- Function to perform all the checks and make the call
  1334.   local tpmBtnCallFunc = function(self, askParams)
  1335.     if not self.refMethods or not self.refListPeripherals
  1336.         or not self.refLabelResult then
  1337.       Widgets.Logger.log("ERROR", "reference not set", "tpmBtnCallFunc")
  1338.       return
  1339.     end
  1340.     self.refLabelResult.text = ""
  1341.     local selectedIndexMethod = self.refMethods.selected
  1342.     local selectedIndexName = self.refListPeripherals.selected
  1343.     if selectedIndexMethod and selectedIndexName
  1344.         and self.refMethods.dataArray
  1345.         and self.refListPeripherals.dataArray then
  1346.       local selectedMethod = self.refMethods.dataArray[selectedIndexMethod]
  1347.       local selectedName = self.refListPeripherals.dataArray[selectedIndexName]
  1348.       if selectedMethod and selectedName then
  1349.         --user params
  1350.         local parTable = {}
  1351.         if askParams then
  1352.           if not self.refTextInput then
  1353.             Widgets.Logger.log("ERROR", "refTextInput not set", "tpmBtnCallFunc")
  1354.           else
  1355.             local parStr = self.refTextInput:processText("Params: ", "")
  1356.             parTable = textutils.unserialize("{"..parStr.."}")
  1357.             if parTable == nil then return end
  1358.           end
  1359.         end
  1360.         --call method + params
  1361.         local res = table.pack(pcall(function()
  1362.           return peripheral.call(selectedName, selectedMethod,
  1363.             table.unpack(parTable)) end))
  1364.         --show what has been called
  1365.         self.refLabelResult.text = selectedName.."."..selectedMethod.."("
  1366.         if (parTable) then
  1367.           for i_p = 1, #parTable do
  1368.             self.refLabelResult.text = self.refLabelResult.text..tostring(parTable[i_p])
  1369.             if i_p < #parTable then
  1370.               self.refLabelResult.text = self.refLabelResult.text..", "
  1371.             end
  1372.           end
  1373.         end
  1374.         self.refLabelResult.text = self.refLabelResult.text..")\n"
  1375.         --process call results
  1376.         res.n = nil
  1377.         if res[1] then
  1378.           self.refLabelResult.textColor = colors.white
  1379.           for i_r = 2, #res do
  1380.             self.refLabelResult.text = self.refLabelResult.text..tostring(res[i_r])
  1381.             if i_r < #res then
  1382.               self.refLabelResult.text = self.refLabelResult.text..", "
  1383.             end
  1384.           end
  1385.         else
  1386.           self.refLabelResult.textColor = colors.red
  1387.           self.refLabelResult.text = self.refLabelResult.text..tostring(res[2])
  1388.         end
  1389.       end
  1390.     end
  1391.     self.refLabelResult:draw()
  1392.   end
  1393.   -- Button to call the selected method
  1394.   local tpmB2 = Widgets.Button:new()
  1395.   tpmB2:init(tpmTab1, 23, 2, 6, 1, true)
  1396.   tpmB2.caption = " CALL "
  1397.   tpmB2.refMethods = tpmList2
  1398.   tpmB2.refListPeripherals = tpmList1
  1399.   tpmB2.onClick = function(self)
  1400.     tpmBtnCallFunc(self)
  1401.   end
  1402.   -- Button to call the selected method with user params
  1403.   local tpmB3 = Widgets.ButtonM:new()
  1404.   tpmB3:init(tpmTab1, 23, 4, 6, 3, true)
  1405.   tpmB3.text = " CALL \n WITH \nPARAMS"
  1406.   tpmB3.refMethods = tpmList2
  1407.   tpmB3.refListPeripherals = tpmList1
  1408.   tpmB3.refTextInput = refTextInput
  1409.   tpmB3.onClick = function(self)
  1410.     tpmBtnCallFunc(self, true)
  1411.   end
  1412.   -- Label to display the results of the call
  1413.   local tpmL2 = Widgets.Label:new()
  1414.   tpmL2:init(tpmTab1, 1, 11, 29, 5, true)
  1415.   tpmL2.bgColor = colors.gray
  1416.   tpmL2.text = "Method call results\nshould appear here"
  1417.   tpmB2.refLabelResult = tpmL2
  1418.   tpmB3.refLabelResult = tpmL2
  1419.   -- Data tab
  1420.   local tpmTab2 = tpmTabs1:addTab(" Data ")
  1421.   local tpmL3 = Widgets.Label:new()
  1422.   tpmL3:init(tpmTab2, 1, 1, 10, 1, true)
  1423.   tpmL3.text = "In memory:"
  1424.   local tpmListPerData = Widgets.ListView:new()
  1425.   tpmListPerData:init(tpmTab2, 1, 2, 29, 14, true)
  1426.   tpmListPerData.refPerManTable = PeripheralManager.peripherals
  1427.   tpmListPerData.refListPeripherals = tpmList1
  1428.   tpmList1.refPerData = tpmListPerData
  1429.   table.insert(tpmList1.refsDraw, tpmListPerData)
  1430.   tpmListPerData.updateData = function(self)
  1431.     self.dataArray = {}
  1432.     if not self.refPerManTable or not self.refListPeripherals then
  1433.       Widgets.Logger.log("ERROR", "ref not set", "tpmListPerData")
  1434.       return
  1435.     end
  1436.     local sel = self.refListPeripherals.selected
  1437.     local da = self.refListPeripherals.dataArray
  1438.     if not sel or not da then return end
  1439.     local selName = da[sel]
  1440.     if not selName or not self.refPerManTable[selName] then return end
  1441.     local res = ConvertTableToArrayStrings(
  1442.       self.refPerManTable[selName])
  1443.     if res then self.dataArray = res end
  1444.   end
  1445.   table.insert(PeripheralManager.widgetsToUpdate, tpmListPerData)
  1446.   -- .cfg tab
  1447.   local tpmTab3 = tpmTabs1:addTab(".cfg ")
  1448.   local tpmL4 = Widgets.Label:new()
  1449.   tpmL4:init(tpmTab3, 1, 1, 10, 1, true)
  1450.   tpmL4.text = "On disk:"
  1451.   local tpmListPerCfg = Widgets.ListView:new()
  1452.   tpmListPerCfg:init(tpmTab3, 1, 2, 30, 14, true)
  1453.   tpmListPerCfg.refListPeripherals = tpmList1
  1454.   tpmListPerCfg.isSelectable = true
  1455.   table.insert(tpmList1.refsDraw, tpmListPerCfg)
  1456.   tpmListPerCfg.getSelPeripheral = function(self)
  1457.     if not self.refListPeripherals then
  1458.       Widgets.Logger.log("ERROR", "ref not set", "tpmListPerCfg")
  1459.       return
  1460.     end
  1461.     local sel = self.refListPeripherals.selected
  1462.     local da = self.refListPeripherals.dataArray
  1463.     if not sel or not da then return end
  1464.     self.selectedPeripheral = da[sel]
  1465.   end
  1466.   tpmListPerCfg.updateData = function(self)
  1467.     self.dataArray = {}
  1468.     self:getSelPeripheral()
  1469.     if not self.selectedPeripheral or not self.cfgTable then
  1470.       return
  1471.     end
  1472.     if self.cfgTable[self.selectedPeripheral] then
  1473.       local arr, ind = ConvertTableToArraySerialize(
  1474.         self.cfgTable[self.selectedPeripheral])
  1475.       if arr then self.dataArray = arr else return end
  1476.       if ind then self.dataInd = ind else return end
  1477.     end
  1478.   end
  1479.   --Button to reload .cfg file to memory for edit
  1480.   local tpmB4 = Widgets.Button:new()
  1481.   tpmB4:init(tpmTab3, 19, 16, 6, 1, true)
  1482.   tpmB4.caption = "RELOAD"
  1483.   tpmB4.refList = tpmListPerCfg
  1484.   tpmB4.refTextInput = refTextInput
  1485.   tpmB4.onClick = function(self)
  1486.     if not self.refList or not self.refTextInput then
  1487.       Widgets.Logger.log("ERROR", "ref not set", "RELOAD")
  1488.       return
  1489.     end
  1490.     self.refList.cfgTable = {}
  1491.     if not PeripheralManager.ReadPeripheralTable(
  1492.         self.refList.cfgTable) then
  1493.       self.refList.cfgTable = nil
  1494.       refTextInput:showMessage("["..os.date("%T")
  1495.         .."] Failed to load config")
  1496.       return
  1497.     end
  1498.     refTextInput:showMessage("["..os.date("%T")
  1499.         .."] Config loaded")
  1500.     self.refList:draw()
  1501.   end
  1502.   -- Button to write new .cfg from memory (if there's a table in there)
  1503.   local tpmB5 = Widgets.Button:new()
  1504.   tpmB5:init(tpmTab3, 26, 16, 5, 1, true)
  1505.   tpmB5.caption = "WRITE"
  1506.   tpmB5.refList = tpmListPerCfg
  1507.   tpmB5.refTextInput = refTextInput
  1508.   tpmB5.onClick = function(self)
  1509.     if not self.refList or not tpmB5.refTextInput then
  1510.       Widgets.Logger.log("ERROR", "ref not set", "WRITE")
  1511.       return
  1512.     end
  1513.     if not self.refList.cfgTable
  1514.         or type(self.refList.cfgTable)~="table" then
  1515.       Widgets.Logger.log("ERROR", "broken cfgTable", "WRITE")
  1516.       refTextInput:showMessage("["..os.date("%T")
  1517.         .."] In-mem config is broken")
  1518.       return
  1519.     end
  1520.     if not PeripheralManager.WritePeripheralTable(
  1521.         self.refList.cfgTable) then
  1522.       refTextInput:showMessage("["..os.date("%T")
  1523.         .."] Failed to write config")
  1524.       return
  1525.     end
  1526.     refTextInput:showMessage("["..os.date("%T").."] Config written")
  1527.   end
  1528.   -- Button to add new key=true for the selected peripheral
  1529.   local tpmB6 = Widgets.Button:new()
  1530.   tpmB6:init(tpmTab3, 1, 16, 3, 1, true)
  1531.   tpmB6.caption = "ADD"
  1532.   tpmB6.refList = tpmListPerCfg
  1533.   tpmB6.refTextInput = refTextInput
  1534.   tpmB6.onClick = function(self)
  1535.     if not self.refList or not self.refTextInput then
  1536.       Widgets.Logger.log("ERROR", "ref not set", "ADD")
  1537.       return
  1538.     end
  1539.     if not self.refList.cfgTable then return end
  1540.     local selName = self.refList.selectedPeripheral
  1541.     if not selName then return end
  1542.     local key = textutils.unserialize(self.refTextInput:processText(
  1543.         "New key: ", ""))
  1544.     if key == nil then
  1545.       Widgets.Logger.log("DEBUG", "key == nil", "ADD")
  1546.       return end
  1547.     if not self.refList.cfgTable[selName] then
  1548.       self.refList.cfgTable[selName] = {}
  1549.     end
  1550.     local cfgSelected = self.refList.cfgTable[selName]
  1551.     cfgSelected[key] = true
  1552.     self.refList:draw()
  1553.   end
  1554.   -- Button to remove selected kv pair for the selected peripheral
  1555.   local tpmB7 = Widgets.Button:new()
  1556.   tpmB7:init(tpmTab3, 10, 16, 3, 1, true)
  1557.   tpmB7.caption = "REM"
  1558.   tpmB7.refList = tpmListPerCfg
  1559.   tpmB7.onClick = function(self)
  1560.     if not self.refList then
  1561.       Widgets.Logger.log("ERROR", "ref not set", "REM")
  1562.       return
  1563.     end
  1564.     if not self.refList.cfgTable then return end
  1565.     local cfgTable = self.refList.cfgTable
  1566.     if not self.refList.selectedPeripheral then return end
  1567.     local selectedPeripheral = self.refList.selectedPeripheral
  1568.     if not self.refList.selected then return end
  1569.     local selIndex = self.refList.selected
  1570.     if not self.refList.dataInd then return end
  1571.     local di = self.refList.dataInd
  1572.     if not di[selIndex] then return end
  1573.     if not cfgTable[selectedPeripheral] then return end
  1574.     cfgTable[selectedPeripheral][di [selIndex] ] = nil
  1575.     self.refList:draw()
  1576.   end
  1577.   -- Button to edit value for the selected kv pair
  1578.   local tpmB8 = Widgets.Button:new()
  1579.   tpmB8:init(tpmTab3, 5, 16, 4, 1, true)
  1580.   tpmB8.caption = "EDIT"
  1581.   tpmB8.refList = tpmListPerCfg
  1582.   tpmB8.refTextInput = refTextInput
  1583.   tpmB8.onClick = function(self)
  1584.     if not self.refList or not self.refTextInput then
  1585.       Widgets.Logger.log("ERROR", "ref not set", "EDIT")
  1586.       return
  1587.     end
  1588.     if not self.refList.cfgTable then return end
  1589.     local cfgTable = self.refList.cfgTable
  1590.     if not self.refList.selectedPeripheral then return end
  1591.     local selectedPeripheral = self.refList.selectedPeripheral
  1592.     if not self.refList.selected then return end
  1593.     local selIndex = self.refList.selected
  1594.     if not self.refList.dataInd then return end
  1595.     local di = self.refList.dataInd
  1596.     if not di[selIndex] then return end
  1597.     if not cfgTable[selectedPeripheral] then return end
  1598.     local curVal = cfgTable[selectedPeripheral][di [selIndex] ]
  1599.     curVal = string.gsub(textutils.serialize(curVal), "\n", "")
  1600.     local str = self.refTextInput:processText(tostring(di[selIndex])
  1601.       ..": ", curVal)
  1602.     local val = textutils.unserialize(str)
  1603.     if val == nil then return end
  1604.     cfgTable[selectedPeripheral][di [selIndex] ] = val
  1605.     self.refList:draw()
  1606.   end
  1607.   -- Button to set default values for the selected peripheral
  1608.   local tpmB9 = Widgets.Button:new()
  1609.   tpmB9:init(tpmTab3, 14, 16, 1, 1, true)
  1610.   tpmB9.caption = "D"
  1611.   tpmB9.refList = tpmListPerCfg
  1612.   tpmB9.onClick = function(self)
  1613.     if not self.refList then
  1614.       Widgets.Logger.log("ERROR", "ref not set", "D")
  1615.       return
  1616.     end
  1617.     if not self.refList.cfgTable then return end
  1618.     local selName = self.refList.selectedPeripheral
  1619.     if not selName then return end
  1620.     local t = peripheral.getType(selName)
  1621.     if not t then return end
  1622.     self.refList.cfgTable[selName] =
  1623.       PeripheralManager.GetDefaultParamsByType(t)
  1624.     self.refList:draw()
  1625.   end
  1626.  
  1627.   tpmTabs1:selectTab(" Data ")
  1628. end
  1629.  
  1630.  
  1631.  
  1632. local ExportedUtils =
  1633. {
  1634.     ReadTableFromFile = ReadTableFromFile,
  1635.   WriteTableToFile = WriteTableToFile,
  1636.     PeripheralManager = PeripheralManager,
  1637.   CreateTargeter = CreateTargeter,
  1638.   CreateTargeterTab = CreateTargeterTab,
  1639.   CreateTargeterAutoCamTab = CreateTargeterAutoCamTab,
  1640.   CreatePeripheralManagerTab = CreatePeripheralManagerTab,
  1641.   ColorToBlit = colorToBlit,
  1642.   ConvertTableToArrayStrings = ConvertTableToArrayStrings,
  1643.   ConvertTableToArraySerialize = ConvertTableToArraySerialize
  1644. }
  1645.  
  1646. return ExportedUtils
  1647.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement