Advertisement
Jishh_

Untitled

Jul 20th, 2024 (edited)
10
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.85 KB | None | 0 0
  1. -- RSWarehouse.lua
  2. -- Author: Scott Adkins <adkinss@gmail.com> (Zucanthor)
  3. -- Published: 2021-09-21
  4. --
  5. -- This program monitors work requests for the Minecolonies Warehouse.
  6.  
  7. -- The following is required for setup:
  8. -- * 1 ComputerCraft Computer
  9. -- * 1 or more ComputerCraft Monitors (recommend 3x3 monitors)
  10. -- * 1 Advanced Peripheral Colony Integrator
  11.  
  12. ----------------------------------------------------------------------------
  13. -- INITIALIZATION
  14. ----------------------------------------------------------------------------
  15.  
  16. -- Initialize Monitor
  17. -- A future update may allow for multiple monitors. This would allow one
  18. -- monitor to be used for logging and another to be used for work requests.
  19. local monitor = peripheral.find("monitor")
  20. if not monitor then error("Monitor not found.") end
  21. monitor.setTextScale(0.5)
  22. monitor.clear()
  23. monitor.setCursorPos(1, 1)
  24. monitor.setCursorBlink(false)
  25. print("Monitor initialized.")
  26.  
  27. -- Initialize Colony Integrator
  28. local colony = peripheral.find("colonyIntegrator")
  29. if not colony then error("Colony Integrator not found.") end
  30. if not colony.isInColony then error("Colony Integrator is not in a colony.") end
  31. print("Colony Integrator initialized.")
  32.  
  33. -- Point to location of chest or storage container
  34. -- A future update may autodetect where the storage container is and error
  35. -- out if no storage container is found.
  36. local storage = "right"
  37. print("Storage initialized.")
  38.  
  39. -- Name of log file to capture JSON data from the open requests. The log can
  40. -- be too big to edit within CC, which may require a "pastebin put" if you want
  41. -- to look at it. Logging could be improved to only capture Skipped items,
  42. -- which in turn will make log files smaller and edittable in CC directly.
  43. local logFile = "RSWarehouse.log"
  44.  
  45. ----------------------------------------------------------------------------
  46. -- FUNCTIONS
  47. ----------------------------------------------------------------------------
  48.  
  49. -- Prints to the screen one row after another, scrolling the screen when
  50. -- reaching the bottom. Acts as a normal display where text is printed in
  51. -- a standard way. Long lines are not wrapped and newlines are printed as
  52. -- spaces, both to be addressed in a future update.
  53. -- NOTE: No longer used in this program.
  54. function mPrintScrollable(mon, ...)
  55. w, h = mon.getSize()
  56. x, y = mon.getCursorPos()
  57.  
  58. -- Blink the cursor like a normal display.
  59. mon.setCursorBlink(true)
  60.  
  61. -- For multiple strings, append them with a space between each.
  62. for i = 2, #arg do t = t.." "..arg[i] end
  63. mon.write(arg[1])
  64. if y >= h then
  65. mon.scroll(1)
  66. mon.setCursorPos(1, y)
  67. else
  68. mon.setCursorPos(1, y+1)
  69. end
  70. end
  71.  
  72. -- Prints strings left, centered, or right justified at a specific row and
  73. -- specific foreground/background color.
  74. function mPrintRowJustified(mon, y, pos, text, ...)
  75. w, h = mon.getSize()
  76. fg = mon.getTextColor()
  77. bg = mon.getBackgroundColor()
  78.  
  79. if pos == "left" then x = 1 end
  80. if pos == "center" then x = math.floor((w - #text) / 2) end
  81. if pos == "right" then x = w - #text end
  82.  
  83. if #arg > 0 then mon.setTextColor(arg[1]) end
  84. if #arg > 1 then mon.setBackgroundColor(arg[2]) end
  85. mon.setCursorPos(x, y)
  86. mon.write(text)
  87. mon.setTextColor(fg)
  88. mon.setBackgroundColor(bg)
  89. end
  90.  
  91. -- Utility function that returns true if the provided character is a digit.
  92. -- Yes, this is a hack and there are better ways to do this. Clearly.
  93. function isdigit(c)
  94. if c == "0" then return true end
  95. if c == "1" then return true end
  96. if c == "2" then return true end
  97. if c == "3" then return true end
  98. if c == "4" then return true end
  99. if c == "5" then return true end
  100. if c == "6" then return true end
  101. if c == "7" then return true end
  102. if c == "8" then return true end
  103. if c == "9" then return true end
  104. return false
  105. end
  106.  
  107. -- Utility function that displays current time and remaining time on timer.
  108. -- For time of day, yellow is day, orange is sunset/sunrise, and red is night.
  109. -- The countdown timer is orange over 15s, yellow under 15s, and red under 5s.
  110. -- At night, the countdown timer is red and shows PAUSED insted of a time.
  111. function displayTimer(mon, t)
  112. now = os.time()
  113.  
  114. cycle = "day"
  115. cycle_color = colors.orange
  116. if now >= 4 and now < 6 then
  117. cycle = "sunrise"
  118. cycle_color = colors.orange
  119. elseif now >= 6 and now < 18 then
  120. cycle = "day"
  121. cycle_color = colors.yellow
  122. elseif now >= 18 and now < 19.5 then
  123. cycle = "sunset"
  124. cycle_color = colors.orange
  125. elseif now >= 19.5 or now < 5 then
  126. cycle = "night"
  127. cycle_color = colors.red
  128. end
  129.  
  130. timer_color = colors.orange
  131. if t < 15 then timer_color = colors.yellow end
  132. if t < 5 then timer_color = colors.red end
  133.  
  134. mPrintRowJustified(mon, 1, "left", string.format("Time: %s [%s] ", textutils.formatTime(now, false), cycle), cycle_color)
  135. if cycle ~= "night" then mPrintRowJustified(mon, 1, "right", string.format(" Remaining: %ss", t), timer_color)
  136. else mPrintRowJustified(mon, 1, "right", " Remaining: PAUSED", colors.red) end
  137. end
  138.  
  139. -- Scan all open work requests from the Warehouse and attempt to satisfy those
  140. -- requests. Display all activity on the monitor, including time of day and the
  141. -- countdown timer before next scan. This function is not called at night to
  142. -- save on some ticks, as the colonists are in bed anyways. Items in red mean
  143. -- work order can't be satisfied by Refined Storage (lack of pattern or lack of
  144. -- required crafting ingredients). Yellow means order partially filled and a
  145. -- crafting job was scheduled for the rest. Green means order fully filled.
  146. -- Blue means the Player needs to manually fill the work order. This includes
  147. -- equipment (Tools of Class), NBT items like armor, weapons and tools, as well
  148. -- as generic requests ike Compostables, Fuel, Food, Flowers, etc.
  149. function scanWorkRequests(mon, chest)
  150. -- Before we do anything, prep the log file for this scan.
  151. -- The log file is truncated each time this function is called.
  152. file = fs.open(logFile, "w")
  153. print("\nScan starting at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  154.  
  155. -- We want to keep three different lists so that they can be
  156. -- displayed on the monitor in a more intelligent way. The first
  157. -- list is for the Builder requests. The second list is for the
  158. -- non-Builder requests. The third list is for any armor, tools
  159. -- and weapons requested by the colonists.
  160. builder_list = {}
  161. nonbuilder_list = {}
  162. equipment_list = {}
  163.  
  164. -- Scan the Warehouse for all open work requests. For each item, try to
  165. -- provide as much as possible from RS, then craft whatever is needed
  166. -- after that. Green means item was provided entirely. Yellow means item
  167. -- is being crafted. Red means item is missing crafting recipe.
  168. workRequests = colony.getRequests()
  169. for w in pairs(workRequests) do
  170. name = workRequests[w].name
  171. item = workRequests[w].items[1].name
  172. target = workRequests[w].target
  173. desc = workRequests[w].desc
  174. needed = workRequests[w].count
  175. provided = 0
  176.  
  177. target_words = {}
  178. target_length = 0
  179. for word in target:gmatch("%S+") do
  180. table.insert(target_words, word)
  181. target_length = target_length + 1
  182. end
  183.  
  184. if target_length >= 3 then target_name = target_words[target_length-2] .. " " .. target_words[target_length]
  185. else target_name = target end
  186.  
  187. target_type = ""
  188. target_count = 1
  189. repeat
  190. if target_type ~= "" then target_type = target_type .. " " end
  191. target_type = target_type .. target_words[target_count]
  192. target_count = target_count + 1
  193. until target_count > target_length - 3
  194.  
  195. color = colors.blue
  196. if string.find(desc, "of class") then
  197. level = "Any Level"
  198. if string.find(desc, "with maximal level:Leather") then level = "Leather" end
  199. if string.find(desc, "with maximal level:Gold") then level = "Gold" end
  200. if string.find(desc, "with maximal level:Chain") then level = "Chain" end
  201. if string.find(desc, "with maximal level:Wood or Gold") then level = "Wood or Gold" end
  202. if string.find(desc, "with maximal level:Stone") then level = "Stone" end
  203. if string.find(desc, "with maximal level:Iron") then level = "Iron" end
  204. if string.find(desc, "with maximal level:Diamond") then level = "Diamond" end
  205. new_name = level .. " " .. name
  206. if level == "Any Level" then new_name = name .. " of any level" end
  207. new_target = target_type .. " " .. target_name
  208. equipment = { name=new_name, target=new_target, needed=needed, provided=provided, color=color}
  209. table.insert(equipment_list, equipment)
  210. elseif string.find(target, "Builder") then
  211. builder = { name=name, item=item, target=target_name, needed=needed, provided=provided, color=color }
  212. table.insert(builder_list, builder)
  213. else
  214. new_target = target_type .. " " .. target_name
  215. if target_length < 3 then
  216. new_target = target
  217. end
  218. nonbuilder = { name=name, target=new_target, needed=needed, provided=provided, color=color }
  219. table.insert(nonbuilder_list, nonbuilder)
  220. end
  221. end
  222.  
  223. -- Show the various lists on the attached monitor.
  224. row = 3
  225. mon.clear()
  226.  
  227. header_shown = 0
  228. for e in pairs(equipment_list) do
  229. equipment = equipment_list[e]
  230. if header_shown == 0 then
  231. mPrintRowJustified(mon, row, "center", "Equipment")
  232. header_shown = 1
  233. row = row + 1
  234. end
  235. text = string.format("%d %s", equipment.needed, equipment.name)
  236. mPrintRowJustified(mon, row, "left", text, equipment.color)
  237. mPrintRowJustified(mon, row, "right", " " .. equipment.target, equipment.color)
  238. row = row + 1
  239. end
  240.  
  241. header_shown = 0
  242. for b in pairs(builder_list) do
  243. builder = builder_list[b]
  244. if header_shown == 0 then
  245. if row > 1 then row = row + 1 end
  246. mPrintRowJustified(mon, row, "center", "Builder Requests")
  247. header_shown = 1
  248. row = row + 1
  249. end
  250. text = string.format("%d/%s", builder.provided, builder.name)
  251. mPrintRowJustified(mon, row, "left", text, builder.color)
  252. mPrintRowJustified(mon, row, "right", " " .. builder.target, builder.color)
  253. row = row + 1
  254. end
  255.  
  256. header_shown = 0
  257. for n in pairs(nonbuilder_list) do
  258. nonbuilder = nonbuilder_list[n]
  259. if header_shown == 0 then
  260. if row > 1 then row = row + 1 end
  261. mPrintRowJustified(mon, row, "center", "Nonbuilder Requests")
  262. header_shown = 1
  263. row = row + 1
  264. end
  265. text = string.format("%d %s", nonbuilder.needed, nonbuilder.name)
  266. if isdigit(nonbuilder.name:sub(1,1)) then
  267. text = string.format("%d/%s", nonbuilder.provided, nonbuilder.name)
  268. end
  269. mPrintRowJustified(mon, row, "left", text, nonbuilder.color)
  270. mPrintRowJustified(mon, row, "right", " " .. nonbuilder.target, nonbuilder.color)
  271. row = row + 1
  272. end
  273.  
  274. if row == 3 then mPrintRowJustified(mon, row, "center", "No Open Requests") end
  275. print("Scan completed at", textutils.formatTime(os.time(), false) .. " (" .. os.time() ..").")
  276. file.close()
  277. end
  278.  
  279. ----------------------------------------------------------------------------
  280. -- MAIN
  281. ----------------------------------------------------------------------------
  282.  
  283. -- Scan for requests periodically. This will catch any updates that were
  284. -- triggered from the previous scan. Right-clicking on the monitor will
  285. -- trigger an immediate scan and reset the timer. Unfortunately, there is
  286. -- no way to capture left-clicks on the monitor.
  287. local time_between_runs = 30
  288. local current_run = time_between_runs
  289. scanWorkRequests(monitor, storage)
  290. displayTimer(monitor, current_run)
  291. local TIMER = os.startTimer(1)
  292.  
  293. while true do
  294. local e = {os.pullEvent()}
  295. if e[1] == "timer" and e[2] == TIMER then
  296. now = os.time()
  297. if now >= 5 and now < 19.5 then
  298. current_run = current_run - 1
  299. if current_run <= 0 then
  300. scanWorkRequests(monitor, storage)
  301. current_run = time_between_runs
  302. end
  303. end
  304. displayTimer(monitor, current_run)
  305. TIMER = os.startTimer(1)
  306. elseif e[1] == "monitor_touch" then
  307. os.cancelTimer(TIMER)
  308. scanWorkRequests(monitor, storage)
  309. current_run = time_between_runs
  310. displayTimer(monitor, current_run)
  311. TIMER = os.startTimer(1)
  312. end
  313. end
  314.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement