Guest User

Untitled

a guest
Apr 12th, 2023
199
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 18.69 KB | Gaming | 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(monitor, bridge, storage)
  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.     end
  239.           elseif useNBT == 1 then
  240.      for index, item in ipairs(items) do
  241.           item_array[item.name] = item.amount
  242.            end
  243.        end
  244.     end
  245.     -- Scan the Warehouse for all open work requests. For each item, try to
  246.     -- provide as much as possible from RS, then craft whatever is needed
  247.     -- after that. Green means item was provided entirely. Yellow means item
  248.     -- is being crafted. Red means item is missing crafting recipe.
  249.     workRequests = colony.getRequests()
  250.     file.write(textutils.serialize(workRequests))
  251.     for w in pairs(workRequests) do
  252.         name = workRequests[w].name
  253.         item = workRequests[w].items[1].name
  254.         target = workRequests[w].target
  255.         desc = workRequests[w].desc
  256.         needed = workRequests[w].count
  257.         provided = 0
  258.  
  259.         target_words = {}
  260.         target_length = 0
  261.         for word in target:gmatch("%S+") do
  262.             table.insert(target_words, word)
  263.             target_length = target_length + 1
  264.         end
  265.  
  266.         if target_length >= 3 then target_name = target_words[target_length-2] .. " " .. target_words[target_length]
  267.         else target_name = target end
  268.  
  269.         target_type = ""
  270.         target_count = 1
  271.         repeat
  272.             if target_type ~= "" then target_type = target_type .. " " end
  273.             target_type = target_type .. target_words[target_count]
  274.             target_count = target_count + 1
  275.         until target_count > target_length - 3
  276.         list = {hoe, shovel, axe, pickaxe, bow, sword, shield, helmet, leatherCap, chestplate, tunic, pants, leggings, boots, rallyingBanner, allowAutoCraft,compostable, fertilizer, flowers, food, fuel, smeltableOre }
  277.         useRS = 1
  278.         ResolveInput ()
  279.         local input = read()
  280.         if string.find(name, "Hoe") then useRS = list.hoe end
  281.         if string.find(name, "Shovel") then useRS = list.shovel end
  282.         if string.find(name, "Axe") then useRS = list.axe end
  283.         if string.find(name, "Pickaxe") then useRS = list.pickaxe end
  284.         if string.find(name, "Bow") then useRS = list.bow end
  285.         if string.find(name, "Sword") then useRS = list.sword end
  286.         if string.find(name, "Shield") then useRS = list.shield end
  287.         if string.find(name, "Helmet") then useRS = list.helmet end
  288.         if string.find(name, "Leather Cap") then useRS = list.leatherCap end
  289.         if string.find(name, "Chestplate") then useRS = list.chestplate end
  290.         if string.find(name, "Tunic") then useRS = list.tunic end
  291.         if string.find(name, "Pants") then useRS = list.pants end
  292.         if string.find(name, "Leggings") then useRS = list.leggings end
  293.         if string.find(name, "Boots") then useRS = list.boots end
  294.         if name == "Rallying Banner" then useRS = list.rallyingBanner end --bugged in alpha versions
  295.         if name == "Crafter" then useRS = list.allowAutoCraft end
  296.         if name == "Compostable" then useRS = list.compostable end
  297.         if name == "Fertilizer" then useRS = list.fertilizer end
  298.         if name == "Flowers" then useRS = list.flowers end
  299.         if name == "Food" then useRS = list.food end
  300.         if name == "Fuel" then useRS = list.fuel end
  301.         if name == "Smeltable Ore" then useRS = list.smeltableOre end
  302.         if name == "Stack List" then useRS = 1 end
  303.  
  304.         color = colors.blue
  305.         if useRS == 1 then
  306.             if item_array[item] then
  307.                 provided = rs.exportItemToPeripheral({name=item, count=needed}, chest)
  308.             end
  309.  
  310.             color = colors.green
  311.             if provided < needed then
  312.                 if rs.isItemCrafting(item) then
  313.                     color = colors.yellow
  314.                     print("[Crafting]", item)
  315.                 else
  316.                     if rs.craftItem({name=item, count=needed}) then
  317.                         color = colors.yellow
  318.                         print("[Scheduled]", needed, "x", item)
  319.                     else
  320.                         color = colors.red
  321.                         print("[Failed]", item)
  322.                     end
  323.                 end
  324.             end
  325.         else
  326.             nameString = name .. " [" .. target .. "]"
  327.             print("[Skipped]", nameString)
  328.         end
  329.  
  330.         if string.find(desc, "of class") then
  331.             level = "Any Level"
  332.             if string.find(desc, "with maximal level:Leather") then level = "Leather" end
  333.             if string.find(desc, "with maximal level:Gold") then level = "Gold" end
  334.             if string.find(desc, "with maximal level:Chain") then level = "Chain" end
  335.             if string.find(desc, "with maximal level:Wood or Gold") then level = "Wood or Gold" end
  336.             if string.find(desc, "with maximal level:Stone") then level = "Stone" end
  337.             if string.find(desc, "with maximal level:Iron") then level = "Iron" end
  338.             if string.find(desc, "with maximal level:Diamond") then level = "Diamond" end
  339.             new_name = level .. " " .. name
  340.             if level == "Any Level" then new_name = name .. " of any level" end
  341.             new_target = target_type .. " " .. target_name
  342.             equipment = { name=new_name, target=new_target, needed=needed, provided=provided, color=color}
  343.             table.insert(equipment_list, equipment)
  344.         elseif string.find(target, "Builder") then
  345.             builder = { name=name, item=item, target=target_name, needed=needed, provided=provided, color=color }
  346.             table.insert(builder_list, builder)
  347.         else
  348.             new_target = target_type .. " " .. target_name
  349.             if target_length < 3 then
  350.                 new_target = target
  351.             end
  352.             nonbuilder = { name=name, target=new_target, needed=needed, provided=provided, color=color }
  353.             table.insert(nonbuilder_list, nonbuilder)
  354.         end
  355.     end
  356.  
  357.     -- Show the various lists on the attached monitor.
  358.     row = 3
  359.     mon.clear()
  360.  
  361.     header_shown = 0
  362.     for e in pairs(equipment_list) do
  363.         equipment = equipment_list[e]
  364.         if header_shown == 0 then
  365.             mPrintRowJustified(mon, row, "center", "Equipment")
  366.             header_shown = 1
  367.             row = row + 1
  368.         end
  369.         text = string.format("%d %s", equipment.needed, equipment.name)
  370.         mPrintRowJustified(mon, row, "left", text, equipment.color)
  371.         mPrintRowJustified(mon, row, "right", " " .. equipment.target, equipment.color)
  372.         row = row + 1
  373.     end
  374.  
  375.     header_shown = 0
  376.     for b in pairs(builder_list) do
  377.         builder = builder_list[b]
  378.         if header_shown == 0 then
  379.             if row > 1 then row = row + 1 end
  380.             mPrintRowJustified(mon, row, "center", "Builder Requests")
  381.             header_shown = 1
  382.             row = row + 1
  383.         end
  384.         text = string.format("%d/%s", builder.provided, builder.name)
  385.         mPrintRowJustified(mon, row, "left", text, builder.color)
  386.         mPrintRowJustified(mon, row, "right", " " .. builder.target, builder.color)
  387.         row = row + 1
  388.     end
  389.  
  390.     header_shown = 0
  391.     for n in pairs(nonbuilder_list) do
  392.         nonbuilder = nonbuilder_list[n]
  393.         if header_shown == 0 then
  394.             if row > 1 then row = row + 1 end
  395.             mPrintRowJustified(mon, row, "center", "Nonbuilder Requests")
  396.             header_shown = 1
  397.             row = row + 1
  398.         end
  399.         text = string.format("%d %s", nonbuilder.needed, nonbuilder.name)
  400.         if isdigit(nonbuilder.name:sub(1,1)) then
  401.             text = string.format("%d/%s", nonbuilder.provided, nonbuilder.name)
  402.         end
  403.         mPrintRowJustified(mon, row, "left", text, nonbuilder.color)
  404.         mPrintRowJustified(mon, row, "right", " " .. nonbuilder.target, nonbuilder.color)
  405.         row = row + 1
  406.     end
  407.  
  408.     if row == 3 then mPrintRowJustified(mon, row, "center", "No Open Requests") end
  409.     print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  410.     file.close()
  411. end
  412.  
  413. ----------------------------------------------------------------------------
  414. -- MAIN
  415. ----------------------------------------------------------------------------
  416.  
  417. -- Scan for requests periodically. This will catch any updates that were
  418. -- triggered from the previous scan. Right-clicking on the monitor will
  419. -- trigger an immediate scan and reset the timer. Unfortunately, there is
  420. -- no way to capture left-clicks on the monitor.
  421. local time_between_runs = 30
  422. local current_run = time_between_runs
  423. scanWorkRequests(monitor, bridge, storage)
  424. displayTimer(monitor, current_run)
  425. local TIMER = os.startTimer(1)
  426.  
  427. while true do
  428.     local e = {os.pullEvent()}
  429.     if e[1] == "timer" and e[2] == TIMER then
  430.         now = os.time()
  431.         if now >= 5 and now < 19.5 then
  432.             current_run = current_run - 1
  433.             if current_run <= 0 then
  434.                 scanWorkRequests(monitor, bridge, storage)
  435.                 current_run = time_between_runs
  436.             end
  437.         end
  438.         displayTimer(monitor, current_run)
  439.         TIMER = os.startTimer(1)
  440.     elseif e[1] == "monitor_touch" then
  441.         os.cancelTimer(TIMER)
  442.         scanWorkRequests(monitor, bridge, storage)
  443.         current_run = time_between_runs
  444.         displayTimer(monitor, current_run)
  445.         TIMER = os.startTimer(1)
  446.     end
  447. end
Advertisement
Add Comment
Please, Sign In to add comment