Advertisement
jacojazz

RSWarehouse.lua

Mar 10th, 2023
1,563
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.67 KB | None | 0 0
  1. -- RSWarehouse.lua
  2. -- Author: Scott Adkins <[email protected]> (Zucanthor)
  3. -- Published: 2021-09-21
  4. -- Greene5300: i am new to programming in general, i love this script but would still like to allow for
  5. -- userinput to decide for .nbt items and the like without needing to edit the code directly.
  6. --
  7. --
  8. -- This program monitors work requests for the Minecolonies Warehouse and
  9. -- tries to fulfill requests from the Refined Storage network. If the
  10. -- RS network doesn't have enough items and a crafting pattern exists, a
  11. -- crafting job is scheduled to restock the items in order to fulfill the
  12. -- work request.  The script will continuously loop, monitoring for new
  13. -- requests and checking on crafting jobs to fulfill previous requests.
  14.  
  15. -- The following is required for setup:
  16. --   * 1 ComputerCraft Computer
  17. --   * 1 or more ComputerCraft Monitors (recommend 3x3 monitors)
  18. --   * 1 Advanced Peripheral Colony Integrator
  19. --   * 1 Advanced Peripheral RS Bridge
  20. --   * 1 Chest or other storage container
  21. -- Attach an RS Cable from the RS network to the RS Bridge. Connect the
  22. -- storage container to the Minecolonies Warehouse Hut block. One idea is
  23. -- to set up a second RS network attached to the Warehouse Hut using an
  24. -- External Storage connector and then attach an Importer for that network
  25. -- to the storage container.
  26.  
  27. -- THINGS YOU CAN CUSTOMIZE IN THIS PROGRAM:
  28. -- Line 59: Specify the side storage container is at.
  29. -- Line 66: Name of log file for storing JSON data of all open requests.
  30. -- Lines 231+: Any items you find that should be manually provided.
  31. -- Line 373: Time in seconds between work order scans.
  32.  
  33. ----------------------------------------------------------------------------
  34. -- INITIALIZATION
  35. ----------------------------------------------------------------------------
  36.  
  37. -- Initialize Monitor
  38. -- A future update may allow for multiple monitors. This would allow one
  39. -- monitor to be used for logging and another to be used for work requests.
  40. local monitor = peripheral.find("monitor")
  41. if not monitor then error("Monitor not found.") end
  42. monitor.setTextScale(0.5)
  43. monitor.clear()
  44. monitor.setCursorPos(1, 1)
  45. monitor.setCursorBlink(false)
  46. print("Monitor initialized.")
  47.  
  48. -- Initialize RS Bridge
  49. local bridge = peripheral.find("rsBridge")
  50. if not bridge then error("RS Bridge not found.") end
  51. print("RS Bridge initialized.")
  52.  
  53. -- Initialize Colony Integrator
  54. local colony = peripheral.find("colonyIntegrator")
  55. if not colony then error("Colony Integrator not found.") end
  56. if not colony.isInColony then error("Colony Integrator is not in a colony.") end
  57. print("Colony Integrator initialized.")
  58.  
  59. -- Point to location of chest or storage container
  60. -- A future update may autodetect where the storage container is and error
  61. -- out if no storage container is found.
  62. local storage = "left"
  63. print("Storage initialized.")
  64.  
  65. -- Name of log file to capture JSON data from the open requests.  The log can
  66. -- be too big to edit within CC, which may require a "pastebin put" if you want
  67. -- to look at it.  Logging could be improved to only capture Skipped items,
  68. -- which in turn will make log files smaller and edittable in CC directly.
  69.  
  70. -- Greene5300 start of madnesss If you are reading this mess tell me how to optimize this I'm new
  71.  
  72. local logFile = "RSWarehouse.log"
  73. local useNBT = false
  74. print ("Do you want to use NBT items? This includes armor,tools, and enchantments. Answer true or false")
  75. useNBT = read()
  76. local trustMeBro = false
  77. print "Do you want to allow all items to be accessed by your colony? Answer true for yes, or false to go through each item type and decide individually.")
  78. trustMeBro = read()
  79.  
  80. If trustMeBro == true then
  81.  --list all item variables found on 230ih set to 1
  82.  
  83.  
  84. -- list all variables convert to 1 or 0
  85.  
  86.  
  87. ----------------------------------------------------------------------------
  88. -- FUNCTIONS----------------------------------------------------------------------------
  89.  
  90. -- Prints to the screen one row after another, scrolling the screen when
  91. -- reaching the bottom. Acts as a normal display where text is printed in
  92. -- a standard way. Long lines are not wrapped and newlines are printed as
  93. -- spaces, both to be addressed in a future update.
  94. -- NOTE: No longer used in this program.
  95.  
  96. function resolveInput(userInput,item)
  97.   while userInput == nil do
  98.     print ("do you want them to be able to pull out " ..item.."")
  99.     userInput = read()
  100.     if userInput == "yes" or userInput == "true" or userInput == "1" then
  101.       yay = 1
  102.       return 1
  103.     elseif userInput == "no" or userInput == "false" or userInput == "0" then
  104.       userInput = 0
  105.       return 0
  106.     else
  107.       userInput = nil
  108.     end
  109.   end
  110. end
  111.  
  112. function resolveItem(item)
  113. local userInput = nil
  114. return resolveInput(userInput, item)
  115. end
  116.  
  117. function mPrintScrollable(mon, ...)
  118.     w, h = mon.getSize()
  119.     x, y = mon.getCursorPos()
  120.  
  121.     -- Blink the cursor like a normal display.
  122.     mon.setCursorBlink(true)
  123.  
  124.     -- For multiple strings, append them with a space between each.
  125.     for i = 2, #arg do t = t.." "..arg[i] end
  126.     mon.write(arg[1])
  127.     if y >= h then
  128.         mon.scroll(1)
  129.         mon.setCursorPos(1, y)
  130.     else
  131.         mon.setCursorPos(1, y+1)
  132.     end
  133. end
  134.  
  135. -- Prints strings left, centered, or right justified at a specific row and
  136. -- specific foreground/background color.
  137. function mPrintRowJustified(mon, y, pos, text, ...)
  138.     w, h = mon.getSize()
  139.     fg = mon.getTextColor()
  140.     bg = mon.getBackgroundColor()
  141.  
  142.     if pos == "left" then x = 1 end
  143.     if pos == "center" then x = math.floor((w - #text) / 2) end
  144.     if pos == "right" then x = w - #text end
  145.  
  146.     if #arg > 0 then mon.setTextColor(arg[1]) end
  147.     if #arg > 1 then mon.setBackgroundColor(arg[2]) end
  148.     mon.setCursorPos(x, y)
  149.     mon.write(text)
  150.     mon.setTextColor(fg)
  151.     mon.setBackgroundColor(bg)
  152. end
  153.  
  154. -- Utility function that returns true if the provided character is a digit.
  155. -- Yes, this is a hack and there are better ways to do this.  Clearly.
  156. function isdigit(c)
  157.     if c == "0" then return true end
  158.     if c == "1" then return true end
  159.     if c == "2" then return true end
  160.     if c == "3" then return true end
  161.     if c == "4" then return true end
  162.     if c == "5" then return true end
  163.     if c == "6" then return true end
  164.     if c == "7" then return true end
  165.     if c == "8" then return true end
  166.     if c == "9" then return true end
  167.     return false
  168. end
  169.  
  170. -- Utility function that displays current time and remaining time on timer.
  171. -- For time of day, yellow is day, orange is sunset/sunrise, and red is night.
  172. -- The countdown timer is orange over 15s, yellow under 15s, and red under 5s.
  173. -- At night, the countdown timer is red and shows PAUSED insted of a time.
  174. function displayTimer(mon, t)
  175.     now = os.time()
  176.  
  177.     cycle = "day"
  178.     cycle_color = colors.orange
  179.     if now >= 4 and now < 6 then
  180.         cycle = "sunrise"
  181.         cycle_color = colors.orange
  182.     elseif now >= 6 and now < 18 then
  183.         cycle = "day"
  184.         cycle_color = colors.yellow
  185.     elseif now >= 18 and now < 19.5 then
  186.         cycle = "sunset"
  187.         cycle_color = colors.orange
  188.     elseif now >= 19.5 or now < 5 then
  189.         cycle = "night"
  190.         cycle_color = colors.red
  191.     end
  192.  
  193.     timer_color = colors.orange
  194.     if t < 15 then timer_color = colors.yellow end
  195.     if t < 5 then timer_color = colors.red end
  196.  
  197.     mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s]    ", textutils.formatTime(now, false), cycle), cycle_color)
  198.     if cycle ~= "night" then mPrintRowJustified(mon, 1, "right", string.format("    Remaining: %ss", t), timer_color)
  199.     else mPrintRowJustified(mon, 1, "right", "    Remaining: PAUSED", colors.red) end
  200. end
  201.  
  202. -- Scan all open work requests from the Warehouse and attempt to satisfy those
  203. -- requests.  Display all activity on the monitor, including time of day and the
  204. -- countdown timer before next scan.  This function is not called at night to
  205. -- save on some ticks, as the colonists are in bed anyways.  Items in red mean
  206. -- work order can't be satisfied by Refined Storage (lack of pattern or lack of
  207. -- required crafting ingredients).  Yellow means order partially filled and a
  208. -- crafting job was scheduled for the rest.  Green means order fully filled.
  209. -- Blue means the Player needs to manually fill the work order.  This includes
  210. -- equipment (Tools of Class), NBT items like armor, weapons and tools, as well
  211. -- as generic requests ike Compostables, Fuel, Food, Flowers, etc.
  212. function scanWorkRequests(mon, rs, chest)
  213.     -- Before we do anything, prep the log file for this scan.
  214.     -- The log file is truncated each time this function is called.
  215.     file = fs.open(logFile, "w")
  216.     print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  217.  
  218.     -- We want to keep three different lists so that they can be
  219.     -- displayed on the monitor in a more intelligent way.  The first
  220.     -- list is for the Builder requests.  The second list is for the
  221.     -- non-Builder requests.  The third list is for any armor, tools
  222.     -- and weapons requested by the colonists.
  223.     builder_list = {}
  224.     nonbuilder_list = {}
  225.     equipment_list = {}
  226.  
  227.     -- Scan RS for all items in its network. Ignore items with NBT data.
  228.     -- If a Builder needs any items with NBT data, this function will need
  229.     -- to be updated to not ignore those items.
  230.     items = rs.listItems()
  231.     item_array = {}
  232.  
  233.    if useNBT == 0 then
  234.      for index, item in ipairs(items) do
  235.           if not item.nbt then
  236.                item_array[item.name] = item.amount
  237.      end
  238.           elseif useNBT == 1 then
  239.      for index, item in ipairs(items) do
  240.           item_array[item.name] = item.amount
  241.            end
  242.        end
  243.     end
  244.     -- Scan the Warehouse for all open work requests. For each item, try to
  245.     -- provide as much as possible from RS, then craft whatever is needed
  246.     -- after that. Green means item was provided entirely. Yellow means item
  247.     -- is being crafted. Red means item is missing crafting recipe.
  248.     workRequests = colony.getRequests()
  249.     file.write(textutils.serialize(workRequests))
  250.     for w in pairs(workRequests) do
  251.         name = workRequests[w].name
  252.         item = workRequests[w].items[1].name
  253.         target = workRequests[w].target
  254.         desc = workRequests[w].desc
  255.         needed = workRequests[w].count
  256.         provided = 0
  257.  
  258.         target_words = {}
  259.         target_length = 0
  260.         for word in target:gmatch("%S+") do
  261.             table.insert(target_words, word)
  262.             target_length = target_length + 1
  263.         end
  264.  
  265.         if target_length >= 3 then target_name = target_words[target_length-2] .. " " .. target_words[target_length]
  266.         else target_name = target end
  267.  
  268.         target_type = ""
  269.         target_count = 1
  270.         repeat
  271.             if target_type ~= "" then target_type = target_type .. " " end
  272.             target_type = target_type .. target_words[target_count]
  273.             target_count = target_count + 1
  274.         until target_count > target_length - 3
  275.         list = {hoe, shovel, axe, pickaxe, bow, sword, shield, helmet, leatherCap, chestplate, tunic, pants, leggings, boots, rallyingBanner, allowAutoCraft,compostable, fertilizer, flowers, food, fuel, smeltableOre }
  276.         useRS = 1
  277.         ResolveInput
  278.         local input = read()
  279.         if string.find(name, "Hoe") then useRS = list.hoe end
  280.         if string.find(name, "Shovel") then useRS = list.shovel end
  281.         if string.find(name, "Axe") then useRS = list.axe end
  282.         if string.find(name, "Pickaxe") then useRS = list.pickaxe end
  283.         if string.find(name, "Bow") then useRS = list.bow end
  284.         if string.find(name, "Sword") then useRS = list.sword end
  285.         if string.find(name, "Shield") then useRS = list.shield end
  286.         if string.find(name, "Helmet") then useRS = list.helmet end
  287.         if string.find(name, "Leather Cap") then useRS = list.leatherCap end
  288.         if string.find(name, "Chestplate") then useRS = list.chestplate end
  289.         if string.find(name, "Tunic") then useRS = list.tunic end
  290.         if string.find(name, "Pants") then useRS = list.pants end
  291.         if string.find(name, "Leggings") then useRS = list.leggings end
  292.         if string.find(name, "Boots") then useRS = list.boots end
  293.         if name == "Rallying Banner" then useRS = list.rallyingBanner end --bugged in alpha versions
  294.         if name == "Crafter" then useRS = list.allowAutoCraft end
  295.         if name == "Compostable" then useRS = list.compostable end
  296.         if name == "Fertilizer" then useRS = list.fertilizer end
  297.         if name == "Flowers" then useRS = list.flowers end
  298.         if name == "Food" then useRS = list.food end
  299.         if name == "Fuel" then useRS = list.fuel end
  300.         if name == "Smeltable Ore" then useRS = list.smeltableOre end
  301.         if name == "Stack List" then useRS = 1 end
  302.  
  303.         color = colors.blue
  304.         if useRS == 1 then
  305.             if item_array[item] then
  306.                 provided = rs.exportItemToPeripheral({name=item, count=needed}, chest)
  307.             end
  308.  
  309.             color = colors.green
  310.             if provided < needed then
  311.                 if rs.isItemCrafting(item) then
  312.                     color = colors.yellow
  313.                     print("[Crafting]", item)
  314.                 else
  315.                     if rs.craftItem({name=item, count=needed}) then
  316.                         color = colors.yellow
  317.                         print("[Scheduled]", needed, "x", item)
  318.                     else
  319.                         color = colors.red
  320.                         print("[Failed]", item)
  321.                     end
  322.                 end
  323.             end
  324.         else
  325.             nameString = name .. " [" .. target .. "]"
  326.             print("[Skipped]", nameString)
  327.         end
  328.  
  329.         if string.find(desc, "of class") then
  330.             level = "Any Level"
  331.             if string.find(desc, "with maximal level:Leather") then level = "Leather" end
  332.             if string.find(desc, "with maximal level:Gold") then level = "Gold" end
  333.             if string.find(desc, "with maximal level:Chain") then level = "Chain" end
  334.             if string.find(desc, "with maximal level:Wood or Gold") then level = "Wood or Gold" end
  335.             if string.find(desc, "with maximal level:Stone") then level = "Stone" end
  336.             if string.find(desc, "with maximal level:Iron") then level = "Iron" end
  337.             if string.find(desc, "with maximal level:Diamond") then level = "Diamond" end
  338.             new_name = level .. " " .. name
  339.             if level == "Any Level" then new_name = name .. " of any level" end
  340.             new_target = target_type .. " " .. target_name
  341.             equipment = { name=new_name, target=new_target, needed=needed, provided=provided, color=color}
  342.             table.insert(equipment_list, equipment)
  343.         elseif string.find(target, "Builder") then
  344.             builder = { name=name, item=item, target=target_name, needed=needed, provided=provided, color=color }
  345.             table.insert(builder_list, builder)
  346.         else
  347.             new_target = target_type .. " " .. target_name
  348.             if target_length < 3 then
  349.                 new_target = target
  350.             end
  351.             nonbuilder = { name=name, target=new_target, needed=needed, provided=provided, color=color }
  352.             table.insert(nonbuilder_list, nonbuilder)
  353.         end
  354.     end
  355.  
  356.     -- Show the various lists on the attached monitor.
  357.     row = 3
  358.     mon.clear()
  359.  
  360.     header_shown = 0
  361.     for e in pairs(equipment_list) do
  362.         equipment = equipment_list[e]
  363.         if header_shown == 0 then
  364.             mPrintRowJustified(mon, row, "center", "Equipment")
  365.             header_shown = 1
  366.             row = row + 1
  367.         end
  368.         text = string.format("%d %s", equipment.needed, equipment.name)
  369.         mPrintRowJustified(mon, row, "left", text, equipment.color)
  370.         mPrintRowJustified(mon, row, "right", " " .. equipment.target, equipment.color)
  371.         row = row + 1
  372.     end
  373.  
  374.     header_shown = 0
  375.     for b in pairs(builder_list) do
  376.         builder = builder_list[b]
  377.         if header_shown == 0 then
  378.             if row > 1 then row = row + 1 end
  379.             mPrintRowJustified(mon, row, "center", "Builder Requests")
  380.             header_shown = 1
  381.             row = row + 1
  382.         end
  383.         text = string.format("%d/%s", builder.provided, builder.name)
  384.         mPrintRowJustified(mon, row, "left", text, builder.color)
  385.         mPrintRowJustified(mon, row, "right", " " .. builder.target, builder.color)
  386.         row = row + 1
  387.     end
  388.  
  389.     header_shown = 0
  390.     for n in pairs(nonbuilder_list) do
  391.         nonbuilder = nonbuilder_list[n]
  392.         if header_shown == 0 then
  393.             if row > 1 then row = row + 1 end
  394.             mPrintRowJustified(mon, row, "center", "Nonbuilder Requests")
  395.             header_shown = 1
  396.             row = row + 1
  397.         end
  398.         text = string.format("%d %s", nonbuilder.needed, nonbuilder.name)
  399.         if isdigit(nonbuilder.name:sub(1,1)) then
  400.             text = string.format("%d/%s", nonbuilder.provided, nonbuilder.name)
  401.         end
  402.         mPrintRowJustified(mon, row, "left", text, nonbuilder.color)
  403.         mPrintRowJustified(mon, row, "right", " " .. nonbuilder.target, nonbuilder.color)
  404.         row = row + 1
  405.     end
  406.  
  407.     if row == 3 then mPrintRowJustified(mon, row, "center", "No Open Requests") end
  408.     print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  409.     file.close()
  410. end
  411.  
  412. ----------------------------------------------------------------------------
  413. -- MAIN
  414. ----------------------------------------------------------------------------
  415.  
  416. -- Scan for requests periodically. This will catch any updates that were
  417. -- triggered from the previous scan. Right-clicking on the monitor will
  418. -- trigger an immediate scan and reset the timer. Unfortunately, there is
  419. -- no way to capture left-clicks on the monitor.
  420. local time_between_runs = 30
  421. local current_run = time_between_runs
  422. scanWorkRequests(monitor, bridge, storage)
  423. displayTimer(monitor, current_run)
  424. local TIMER = os.startTimer(1)
  425.  
  426. while true do
  427.     local e = {os.pullEvent()}
  428.     if e[1] == "timer" and e[2] == TIMER then
  429.         now = os.time()
  430.         if now >= 5 and now < 19.5 then
  431.             current_run = current_run - 1
  432.             if current_run <= 0 then
  433.                 scanWorkRequests(monitor, bridge, storage)
  434.                 current_run = time_between_runs
  435.             end
  436.         end
  437.         displayTimer(monitor, current_run)
  438.         TIMER = os.startTimer(1)
  439.     elseif e[1] == "monitor_touch" then
  440.         os.cancelTimer(TIMER)
  441.         scanWorkRequests(monitor, bridge, storage)
  442.         current_run = time_between_runs
  443.         displayTimer(monitor, current_run)
  444.         TIMER = os.startTimer(1)
  445.     end
  446. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement