Advertisement
dadragon84

Colony

Mar 4th, 2025
163
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. -- RSWarehouse.lua
  2.  
  3. -- Author: Chuck Burgess
  4.  
  5. -- Updated: 2024-01-15
  6.  
  7.  
  8.  
  9. local logFile = "RSWarehouse.log"
  10.  
  11. local time_between_runs = 30
  12.  
  13.  
  14.  
  15. -- Initialize Monitor
  16.  
  17. -- see: https://www.computercraft.info/wiki/Advanced_Monitor
  18.  
  19. local monitor = peripheral.find("monitor")
  20.  
  21. if not monitor then error("Monitor not found.") end
  22.  
  23. monitor.setTextScale(0.5)
  24.  
  25. monitor.clear()
  26.  
  27. monitor.setCursorPos(1, 1)
  28.  
  29. monitor.setCursorBlink(false)
  30.  
  31. print("Monitor initialized.")
  32.  
  33.  
  34.  
  35. -- Initialize RS Bridge
  36.  
  37. -- see: https://advancedperipherals.madefor.cc/peripherals/rs_bridge/
  38.  
  39. local bridge = peripheral.find("rsBridge")
  40.  
  41. if not bridge then error("RS Bridge not found.") end
  42.  
  43. print("RS Bridge initialized.")
  44.  
  45.  
  46.  
  47. -- Initialize Colony Integrator
  48.  
  49. -- see: https://docs.advanced-peripherals.de/peripherals/colony_integrator/
  50.  
  51. local colony = peripheral.find("colonyIntegrator")
  52.  
  53. if not colony then error("Colony Integrator not found.") end
  54.  
  55. if not colony.isInColony then error("Colony Integrator is not in a colony.") end
  56.  
  57. print("Colony Integrator initialized.")
  58.  
  59.  
  60.  
  61. -- Establish the direction to transport the items into the Warehouse based on
  62.  
  63. -- where the entnglement block is sitting. Default to empty string.
  64.  
  65. local storage = peripheral.find("entangled:tile")
  66.  
  67. if not storage then error("Warehouse storage not found.") end
  68.  
  69. local direction = ""
  70.  
  71. local names = peripheral.getNames()
  72.  
  73. for _, pos in ipairs(names) do
  74.  
  75.   if peripheral.getType(pos) == "entangled:tile" then
  76.  
  77.     direction = pos
  78.  
  79.     end
  80.  
  81. end
  82.  
  83. print("Warehouse storage initialized.")
  84.  
  85.  
  86.  
  87. ----------------------------------------------------------------------------
  88.  
  89. -- FUNCTIONS
  90.  
  91. ----------------------------------------------------------------------------
  92.  
  93. --[[
  94.  
  95.   Table.Empty
  96.  
  97.   @desc     check to see if a table contains any data
  98.  
  99.   @return   boolean
  100.  
  101. ]]
  102.  
  103. function table.empty (self)
  104.  
  105.     for _, _ in pairs(self) do
  106.  
  107.         return false
  108.  
  109.     end
  110.  
  111.     return true
  112.  
  113. end
  114.  
  115.  
  116.  
  117. --[[
  118.  
  119.     Write To Log
  120.  
  121.     @desc   Write the specified `table` to the file surrounded by the `blockTop` and `blockBottom`
  122.  
  123.     @return void
  124.  
  125. ]]
  126.  
  127. function writeToLog(data, blockTop, blockBottom)
  128.  
  129.   file.write("\n")
  130.  
  131.   file.write(blockTop)
  132.  
  133.   file.write("\n")
  134.  
  135.   file.write(textutils.serialize(data, { allow_repetitions = true }))
  136.  
  137.   file.write("\n")
  138.  
  139.   file.write(blockBottom)
  140.  
  141.   file.write("\n")
  142.  
  143. end
  144.  
  145.  
  146.  
  147. --[[
  148.  
  149.     Process Work Request Item
  150.  
  151.     @desc Determine if this item can be delivered to the warehouse from the storage
  152.  
  153.     @return boolean
  154.  
  155. ]]
  156.  
  157. function processWorkRequestItem(request)
  158.  
  159.   if string.find(request.desc, "Tool of class") then return false end
  160.  
  161.   if string.find(request.name, "Hoe") then return false end
  162.  
  163.   if string.find(request.name, "Shovel") then return false end
  164.  
  165.   if string.find(request.name, "Axe") then return false end
  166.  
  167.   if string.find(request.name, "Pickaxe") then return false end
  168.  
  169.   if string.find(request.name, "Bow") then return false end
  170.  
  171.   if string.find(request.name, "Sword") then return false end
  172.  
  173.   if string.find(request.name, "Shield") then return false end
  174.  
  175.   if string.find(request.name, "Helmet") then return false end
  176.  
  177.   if string.find(request.name, "Leather Cap") then return false end
  178.  
  179.   if string.find(request.name, "Chestplate") then return false end
  180.  
  181.   if string.find(request.name, "Tunic") then return false end
  182.  
  183.   if string.find(request.name, "Pants") then return false end
  184.  
  185.   if string.find(request.name, "Leggings") then return false end
  186.  
  187.   if string.find(request.name, "Boots") then return false end
  188.  
  189.   if request.name == "Rallying Banner" then return false end --bugged in alpha versions
  190.  
  191.   if request.name == "Crafter" then return false end
  192.  
  193.   if request.name == "Compostable" then return false end
  194.  
  195.   if request.name == "Fertilizer" then return false end
  196.  
  197.   if request.name == "Flowers" then return false end
  198.  
  199.   if request.name == "Food" then return false end
  200.  
  201.   if request.name == "Fuel" then return false end
  202.  
  203.   if request.name == "Smeltable Ore" then return false end
  204.  
  205.   if request.name == "Stack List" then return false end
  206.  
  207.   -- you can add any new items here if they are found
  208.  
  209.   return true
  210.  
  211. end
  212.  
  213.  
  214.  
  215. --[[
  216.  
  217.     Monitor Print Row Justified
  218.  
  219.     @desc   Print a line of data to the in-game monitor
  220.  
  221.     @return void
  222.  
  223. ]]
  224.  
  225. function mPrintRowJustified(mon, y, pos, text, textcolor)
  226.  
  227.     w, h = mon.getSize()
  228.  
  229.     fg = colors.white
  230.  
  231.     bg = colors.black
  232.  
  233.  
  234.  
  235.     if pos == "left" then x = 1 end
  236.  
  237.     if pos == "center" then x = math.floor((w - #text) / 2) end
  238.  
  239.     if pos == "right" then x = w - #text end
  240.  
  241.  
  242.  
  243.     mon.setTextColor(textcolor)
  244.  
  245.     mon.setCursorPos(x, y)
  246.  
  247.     mon.write(text)
  248.  
  249.     mon.setTextColor(fg)
  250.  
  251.     mon.setBackgroundColor(bg)
  252.  
  253. end
  254.  
  255.  
  256.  
  257. --[[
  258.  
  259.     Display Timer
  260.  
  261.     @desc   Update the time on the monitor
  262.  
  263.     @return void
  264.  
  265. ]]
  266.  
  267. function displayTimer(mon, t)
  268.  
  269.     now = os.time()
  270.  
  271.     cycle = "day"
  272.  
  273.     cycle_color = colors.orange
  274.  
  275.     if now >= 4 and now < 6 then
  276.  
  277.         cycle = "sunrise"
  278.  
  279.         cycle_color = colors.yellow
  280.  
  281.     elseif now >= 6 and now < 18 then
  282.  
  283.         cycle = "day"
  284.  
  285.         cycle_color = colors.lightBlue
  286.  
  287.     elseif now >= 18 and now < 19.5 then
  288.  
  289.         cycle = "sunset"
  290.  
  291.         cycle_color = colors.magenta
  292.  
  293.     elseif now >= 19.5 or now < 5 then
  294.  
  295.         cycle = "night"
  296.  
  297.         cycle_color = colors.red
  298.  
  299.     end
  300.  
  301.  
  302.  
  303.     timer_color = colors.green
  304.  
  305.     if t < 15 then timer_color = colors.yellow end
  306.  
  307.     if t < 5 then timer_color = colors.orange end
  308.  
  309.  
  310.  
  311.     mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s]    ", textutils.formatTime(now, false), cycle), cycle_color)
  312.  
  313.     if cycle ~= "night" then
  314.  
  315.       mPrintRowJustified(mon, 1, "right", string.format("    Remaining: %ss", t), timer_color)
  316.  
  317.     else
  318.  
  319.       mPrintRowJustified(mon, 1, "right", "    Remaining: PAUSED", colors.red)
  320.  
  321.     end
  322.  
  323. end
  324.  
  325.  
  326.  
  327. --[[
  328.  
  329.     Create Colonist Data
  330.  
  331.     @desc   Build a table of Colonist making the request
  332.  
  333.     @return table
  334.  
  335. ]]
  336.  
  337. function createColonistData(colonist)
  338.  
  339.   title_words = {}
  340.  
  341.   words_in_name = 0
  342.  
  343.   colonist_job = ""
  344.  
  345.   word_count = 1
  346.  
  347.  
  348.  
  349.   for word in colonist:gmatch("%S+") do
  350.  
  351.     table.insert(title_words, word)
  352.  
  353.     words_in_name = words_in_name + 1
  354.  
  355.   end
  356.  
  357.  
  358.  
  359.   if words_in_name >= 3 then colonist_name = title_words[words_in_name-2] .. " " .. title_words[words_in_name]
  360.  
  361.   else colonist_name = colonist end
  362.  
  363.  
  364.  
  365.   repeat
  366.  
  367.     if colonist_job ~= "" then colonist_job = colonist_job .. " " end
  368.  
  369.     colonist_job = colonist_job .. title_words[word_count]
  370.  
  371.     word_count = word_count + 1
  372.  
  373.   until word_count > words_in_name - 3
  374.  
  375.  
  376.  
  377.   return  { fullName = colonist, titleWords = title_words, job = colonist_job, name = colonist_name, wordsInName = words_in_name }
  378.  
  379. end
  380.  
  381.  
  382.  
  383. --[[
  384.  
  385.     Get Work Request List (from colony)
  386.  
  387.     @desc   Build a table of the work request data from the colony
  388.  
  389.     @return table
  390.  
  391. ]]
  392.  
  393. function getWorkRequestList(colony)
  394.  
  395.     requestList = {}
  396.  
  397.     workRequests = colony.getRequests()
  398.  
  399.     file = fs.open(logFile, "w")
  400.  
  401.    
  402.  
  403.     for w in pairs(workRequests) do
  404.  
  405.         writeToLog(workRequests[w], "--- Request start ---", "--- Request end ---");
  406.  
  407.         name = workRequests[w].name -- the name of the count/item being requested
  408.  
  409.         colonist = createColonistData(workRequests[w].target)
  410.  
  411.         desc = workRequests[w].desc -- the request description
  412.  
  413.         item = {}
  414.  
  415.         -- create the filter item for the transfer request through the bridge
  416.  
  417.         if workRequests[w].items and workRequests[w].items[1] then
  418.  
  419.           if not workRequests[w].items[1].nbt or table.empty(workRequests[w].items[1].nbt) then
  420.  
  421.             item = { name = workRequests[w].items[1].name, count =  workRequests[w].count, displayName = workRequests[w].items[1].displayName}
  422.  
  423.           else
  424.  
  425.             item = { name = workRequests[w].items[1].name, count = workRequests[w].count, displayName = workRequests[w].items[1].displayName, nbt =  workRequests[w].items[1].nbt}
  426.  
  427.           end
  428.  
  429.         end
  430.  
  431.         -- how many items are needed to fulfill this request?
  432.  
  433.         needed = workRequests[w].count
  434.  
  435.  
  436.  
  437.         local newRecord = {}
  438.  
  439.         newRecord.name = name
  440.  
  441.         newRecord.desc = desc
  442.  
  443.         newRecord.needed = needed
  444.  
  445.         newRecord.item = item
  446.  
  447.         newRecord.colonist = colonist
  448.  
  449.         table.insert(requestList, newRecord)
  450.  
  451.         writeToLog(newRecord, "--- Record start ---", "--- Record end ---");
  452.  
  453.       end
  454.  
  455.       file.close()
  456.  
  457.   return requestList
  458.  
  459. end
  460.  
  461.  
  462.  
  463. --[[
  464.  
  465.     Display List
  466.  
  467.     @desc   Update the monitor with the work request items currently in the system
  468.  
  469.     @return void
  470.  
  471. ]]
  472.  
  473. function displayList(mon, listName, itemList)
  474.  
  475.   -- show the list header first
  476.  
  477.   mPrintRowJustified(mon, row, "center", listName, colors.white)
  478.  
  479.   row = row + 1
  480.  
  481.   for e in pairs(itemList) do
  482.  
  483.       record = itemList[e]
  484.  
  485.       text = string.format("%d %s", record.provided , record.name)
  486.  
  487.       mPrintRowJustified(mon, row, "left", text, record.color)
  488.  
  489.       mPrintRowJustified(mon, row, "right", " " .. record.colonist, record.color)
  490.  
  491.       row = row + 1
  492.  
  493.   end
  494.  
  495.   -- add a space at the end of the list
  496.  
  497.   row = row + 1
  498.  
  499. end
  500.  
  501.  
  502.  
  503. -- Color References:
  504.  
  505. -- RED:     work order can't be satisfied by Refined Storage (lack of pattern or lack of
  506.  
  507. --            required crafting ingredients).
  508.  
  509. -- YELLOW:  order partially filled and a crafting job was scheduled for the rest.
  510.  
  511. -- GREEN:   order fully filled.
  512.  
  513. -- BLUE:    the Player needs to manually fill the work order. This includes some equipment as well as generic requests ike Compostables, Fuel, Food, Flowers, etc.
  514.  
  515. --[[
  516.  
  517.     Scan Work Requests
  518.  
  519.     @desc   Manages all of the open work requests in the system and attempts to fulfill them from the inventory
  520.  
  521.     @desc   Not called at night (as determined by the server) since requests cannot be fulfilled anyway
  522.  
  523.     @return void
  524.  
  525. ]]
  526.  
  527. function scanWorkRequests(mon, bridge, direction)
  528.  
  529.    
  530.  
  531.     print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  532.  
  533.     builder_list = {}
  534.  
  535.     nonbuilder_list = {}
  536.  
  537.     equipment_list = {}
  538.  
  539.     requestList = getWorkRequestList(colony)
  540.  
  541.    
  542.  
  543.     for j, data in ipairs(requestList) do
  544.  
  545.         color = colors.blue
  546.  
  547.         provided = 0
  548.  
  549.  
  550.  
  551.         if processWorkRequestItem(data) then
  552.  
  553.             provided = bridge.exportItemToPeripheral(data.item, direction)
  554.  
  555.             color = colors.lightGray
  556.  
  557.             if provided >= data.needed then
  558.  
  559.               color = colors.green
  560.  
  561.             end
  562.  
  563.             -- only handle the Non-NBT data items or empty nbt table item records
  564.  
  565.             if provided < data.needed then
  566.  
  567.                 if bridge.isItemCrafting(data.item) then
  568.  
  569.                     color = colors.yellow
  570.  
  571.                     print("[Crafting]", data.name)
  572.  
  573.                 else
  574.  
  575.                     if bridge.craftItem(data.item) then
  576.  
  577.                       color = colors.yellow
  578.  
  579.                       print("[Scheduled]", data.item.count, "x", data.name)
  580.  
  581.                     else
  582.  
  583.                       color = colors.red
  584.  
  585.                       print("[Failed]", data.name)
  586.  
  587.                     end
  588.  
  589.                 end
  590.  
  591.             end
  592.  
  593.         else
  594.  
  595.            nameString = data.name .. " [" .. data.colonist.fullName .. "]"
  596.  
  597.            print("[Skipped]", nameString)
  598.  
  599.         end
  600.  
  601.         -- ---------------------------------------------------------------------
  602.  
  603.         -- Build the newList data
  604.  
  605.         -- ---------------------------------------------------------------------
  606.  
  607.         -- create the target text
  608.  
  609.         expectedList = "Builder"
  610.  
  611.         colonist = data.colonist.name
  612.  
  613.         if not string.find(data.colonist.fullName, "Builder") then
  614.  
  615.             expectedList = ""
  616.  
  617.             colonist = data.colonist.job .. " " .. data.colonist.name
  618.  
  619.             if data.colonist.wordsInName < 3 then
  620.  
  621.                 colonist = data.colonist.name
  622.  
  623.             end
  624.  
  625.         end
  626.  
  627.          
  628.  
  629.         -- create the name
  630.  
  631.         listName = data.name
  632.  
  633.         if string.find(data.desc, "level") then
  634.  
  635.             expectedList = "Equipment"
  636.  
  637.             level = "Any Level"
  638.  
  639.             if string.find(data.desc, "with maximal level: Leather") then level = "Leather" end
  640.  
  641.             if string.find(data.desc, "with maximal level: Gold") then level = "Gold" end
  642.  
  643.             if string.find(data.desc, "with maximal level: Chain") then level = "Chain" end
  644.  
  645.             if string.find(data.desc, "with maximal level: Wood or Gold") then level = "Wood or Gold" end
  646.  
  647.             if string.find(data.desc, "with maximal level: Stone") then level = "Stone" end
  648.  
  649.             if string.find(data.desc, "with maximal level: Iron") then level = "Iron" end
  650.  
  651.             if string.find(data.desc, "with maximal level: Diamond") then level = "Diamond" end
  652.  
  653.             listName = level .. " " .. data.name
  654.  
  655.             if level == "Any Level" then listName = data.name .. " of any level" end
  656.  
  657.         end
  658.  
  659.          
  660.  
  661.         -- create the new list table defining what is inserted into a specific list
  662.  
  663.         newList = { name=listName, colonist=colonist, needed=data.needed, provided=provided, color=color}
  664.  
  665.        
  666.  
  667.         if expectedList == "Equipment" then
  668.  
  669.             table.insert(equipment_list, newList)
  670.  
  671.         elseif expectedList == "Builder" then
  672.  
  673.             table.insert(builder_list, newList)
  674.  
  675.         else
  676.  
  677.             table.insert(nonbuilder_list, newList)
  678.  
  679.         end
  680.  
  681.         -- ---------------------------------------------------------------------
  682.  
  683.     end
  684.  
  685.  
  686.  
  687.   -- Show the various lists on the attached monitor.
  688.  
  689.   mon.clear()
  690.  
  691.   row = 3
  692.  
  693.   if not table.empty(builder_list) then displayList(mon, "Builder Requests", builder_list) end
  694.  
  695.   if not table.empty(nonbuilder_list) then displayList(mon, "Nonbuilder Requests", nonbuilder_list) end
  696.  
  697.   if not table.empty(equipment_list) then displayList(mon, "Equipment", equipment_list) end
  698.  
  699.  
  700.  
  701.   -- no requests
  702.  
  703.   if row == 3 then
  704.  
  705.     mPrintRowJustified(mon, row, "center", "No Open Requests", colors.white)
  706.  
  707.   end
  708.  
  709.   print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  710.  
  711. end
  712.  
  713.  
  714.  
  715.  
  716.  
  717. --[[
  718.  
  719.     MAIN
  720.  
  721.     @desc   establish the run times and execute the work request management
  722.  
  723.     @return void
  724.  
  725. ]]
  726.  
  727. local current_run = time_between_runs
  728.  
  729. scanWorkRequests(monitor, bridge, direction)
  730.  
  731. displayTimer(monitor, current_run)
  732.  
  733. local TIMER = os.startTimer(1)
  734.  
  735.  
  736.  
  737. while true do
  738.  
  739.   local e = {os.pullEvent()}
  740.  
  741.   if e[1] == "timer" and e[2] == TIMER then
  742.  
  743.     now = os.time()
  744.  
  745.     if now >= 5 and now < 19.5 then
  746.  
  747.       current_run = current_run - 1
  748.  
  749.       if current_run <= 0 then
  750.  
  751.         scanWorkRequests(monitor, bridge, direction)
  752.  
  753.         current_run = time_between_runs
  754.  
  755.       end
  756.  
  757.     end
  758.  
  759.     displayTimer(monitor, current_run)
  760.  
  761.     TIMER = os.startTimer(1)
  762.  
  763.   elseif e[1] == "monitor_touch" then
  764.  
  765.     os.cancelTimer(TIMER)
  766.  
  767.     scanWorkRequests(monitor, bridge, direction)
  768.  
  769.     current_run = time_between_runs
  770.  
  771.     displayTimer(monitor, current_run)
  772.  
  773.     TIMER = os.startTimer(1)
  774.  
  775.   end
  776.  
  777. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement