hunter3216

Untitled

Aug 24th, 2024
29
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 35.48 KB | None | 0 0
  1. --[[
  2. TODO: Change how understocked items are displayed
  3. display status (crafting, queued, out of missing mats)
  4. display batch and how many more batches to go (stretch goal)
  5.  
  6.  
  7. ]]
  8.  
  9.  
  10. -- Import libraries
  11. local GUI = require("GUI")
  12. local system = require("System")
  13. local screen = require("Screen")
  14. local event = require("Event")
  15. local component = require("component")
  16. local fs = require("Filesystem")
  17. ---------------------------------------------------------------------------------
  18. -- misc globals
  19. local lvmList -- list of all level maintainers we have
  20. local nLvms = 0 -- how many level maintainers we have
  21. local stockedItems = {} -- table of all items currently being stocked
  22. local iStocked = {} -- list of all items currently being stocked; generated from stockedItems
  23. local openStockers = {} -- list of all open level maintainers' slots
  24. local watchList = {} -- list of items that have dipped below their stock qty
  25. local lvmSlots = {0,1,2,3,4};
  26. local qtySuffixes = {"K", "M", "G", "T",
  27. K=1000,
  28. M=1000000,
  29. G=1000000000,
  30. T=1000000000000
  31. };
  32. local currentI = 0;
  33.  
  34. ---------------------------------------------------------------------------------
  35. -- default config options
  36. local defaultOptions = {
  37. -- time between inventory checks and updates, in seconds. If zero, never automatically checks item quantities.
  38. checkFrequency = 2,
  39. -- time between additional AE2 pulls of 'watched' items, in cycles. min 1, whole numbers only
  40. watchFrequency = 12,
  41. -- uses this address as the proxy for the AE2 me interface. needed if there are several me interface components connected.
  42. allocatedCPUs = #component.me_interface.getCpus(),
  43. -- main program colors
  44. programColor = {
  45. ["header"]=0x2D2D2D,
  46. ["headerText"]=0xFFFFFF,
  47. ["windowFill"]=0xE1E1E1,
  48. ["textbox"]=0xEEEEEE,
  49. ["textboxFocused"]=0xFFFFFF,
  50. ["textboxText"]=0x2D2D2D,
  51. ["textboxTextFaint"]=0x555555,
  52. ["textboxTextHighlight"]=0x880000
  53. },
  54. -- colored button colors
  55. buttonColor = {
  56. ["save"]=0x00EE00,
  57. ["savePressed"]=0x008800,
  58. ["cancel"]=0xEE4444,
  59. ["cancelPressed"]=0x880000,
  60. ["text"]=0x555555,
  61. ["textPressed"]=0xFFFFFF
  62. }
  63. }
  64.  
  65. ---------------------------------------------------------------------------------
  66. -- other files
  67. local currentFolder = fs.path(system.getCurrentScript())
  68. local pseudonymFilePath = currentFolder.."/pseudonyms.txt"
  69. local configFilePath = currentFolder.."/config.txt"
  70. local stockedItemsFilePath = currentFolder.."/stockedItems.txt"
  71. ---- if we have one, open the saved list of config options
  72. local config;
  73. if (fs.exists(configFilePath)) then
  74. config = fs.readTable(configFilePath);
  75. else
  76. config = defaultOptions;
  77. end
  78.  
  79. ---- if we have one, open the saved list of pseudonyms (so user can rename items in the GUI)
  80. local pseudonyms;
  81. if (fs.exists(pseudonymFilePath)) then
  82. pseudonyms = fs.readTable(pseudonymFilePath);
  83. else
  84. pseudonyms = {};
  85. end
  86.  
  87. local itemDBStockedItems = {}
  88. -- Creates the file if one doesn't already exist
  89. if not (fs.exists(stockedItemsFilePath)) then
  90. local file = fs.open(stockedItemsFilePath, "w")
  91. file:close()
  92. end
  93.  
  94. function saveItems()
  95. fs.writeTable(stockedItemsFilePath, stockedItems)
  96. end
  97.  
  98. function loadItems()
  99. stockedItems = fs.readTable(stockedItemsFilePath)
  100. if stockedItems == nil then
  101. stockedItems = {}
  102. end
  103. end
  104.  
  105. function removeStockedItem(item)
  106. key = itemID(item)
  107. stockedItems[key] = nil
  108. saveItems()
  109. sortStocked()
  110. end
  111.  
  112. function addStockedItem(item)
  113. saveItems()
  114. end
  115.  
  116.  
  117. ---------------------------------------------------------------------------------
  118. -- Add a new window to MineOS workspace
  119. local workspace, window, menu = system.addWindow(GUI.filledWindow(1, 1, screen.getWidth(), screen.getHeight()-1, config.programColor.windowFill))
  120. --local workspace, window, menu = system.addWindow(GUI.Window(1, 1, screen.getWidth(), screen.getHeight()-1))
  121. --window:addChild(GUI.panel(1, 1, window.width, window.height, config.programColor.windowFill))
  122. window:maximize()
  123.  
  124. window.actionButtons.close.onTouch = function()
  125. stopCheckingStock()
  126. window:remove();
  127. end
  128.  
  129. menu:addItem("Add Item").onTouch = function()
  130. addAutoStock();
  131. end
  132.  
  133. menu:addItem("Update All Quantities").onTouch = function()
  134. updateAll();
  135. end
  136.  
  137. menu:addItem("Edit Configs").onTouch = function()
  138. editConfigs();
  139. end
  140.  
  141.  
  142. local layout = window:addChild(GUI.layout(1, 1, window.width, window.height, 2, 1))
  143. --:setSpacing(int column, int row, int spacing)
  144. layout:setSpacing(1, 1, 0);
  145. --:setDirection(int column, int row, enum direction)
  146. layout:setDirection(1, 1, GUI.DIRECTION_VERTICAL);
  147. --:setMargin(int column, int row, int horizontalMargin, int verticalMargin)
  148. layout:setMargin(1, 1, 1, 3);
  149. --:setAlignment(int column, int row, enum horizontalAlignment, enum verticalAlignment)
  150. layout:setAlignment(1, 1, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP);
  151. --:setFitting(int column, int row, int horizontalFitting, int verticalFitting[, int horizontalOffset, int verticalOffset] )
  152. layout:setFitting(1, 1, true, true, 4, 6);
  153.  
  154. --:setColumnWidth(int column, enum sizePolicy, float size)
  155. layout:setColumnWidth(1, GUI.SIZE_POLICY_ABSOLUTE, 95);
  156. layout:setColumnWidth(2, GUI.SIZE_POLICY_ABSOLUTE, 65);
  157.  
  158. local layoutRight = layout:setPosition(2,1,layout:addChild(GUI.layout(1,1,layout.columnSizes[2].size,layout.height,1,2)));
  159. for row=1,2 do
  160. --:setSpacing(int column, int row, int spacing)
  161. layoutRight:setSpacing(1, row, 0);
  162. --:setDirection(int column, int row, enum direction)
  163. layoutRight:setDirection(1, row, GUI.DIRECTION_VERTICAL);
  164. --:setMargin(int column, int row, int horizontalMargin, int verticalMargin)
  165. layoutRight:setMargin(1, row, 1, 3)
  166. --:setAlignment(int column, int row, enum horizontalAlignment, enum verticalAlignment)
  167. layoutRight:setAlignment(1, row, GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
  168. --:setFitting(int column, int row, int horizontalFitting, int verticalFitting[, int horizontalOffset, int verticalOffset] )
  169. layoutRight:setFitting(1, row, true, true, 5, 5)
  170. end
  171. layoutRight:setRowHeight(1, GUI.SIZE_POLICY_RELATIVE, 0.65);
  172.  
  173. window.onResize = function(newWidth, newHeight)
  174. window.backgroundPanel.width, window.backgroundPanel.height = newWidth, newHeight
  175. layout.width, layout.height = newWidth, newHeight
  176. end
  177.  
  178. ---------------------------------------------------------------------------------
  179. function prelim()
  180. -- need an ME Interface; warns user on startup if there is none
  181. getAe2();
  182.  
  183. loadAllLvms();
  184.  
  185. -- start functionality
  186. doInventory()
  187. if config.checkFrequency > 0 then
  188. startCheckingStock(config.checkFrequency)
  189. end
  190. end
  191.  
  192. function getComponent(componentName)
  193. if next(component.list(componentName)) then
  194. return component[componentName];
  195. else
  196. return nil;
  197. end
  198. end
  199.  
  200. function getAe2()
  201. local ae2;
  202. ae2 = getComponent("me_interface");
  203.  
  204. if ae2 == nil then
  205. GUI.alert("No ME Interface available!");
  206. end
  207.  
  208. return ae2;
  209. end
  210.  
  211. ---------------------------------------------------------------------------------
  212. -- data structure functions
  213.  
  214. -- to store items as a lookuptable instead of a list
  215. function itemID(item)
  216. return item.name ..":".. item.maxDamage ..":".. item.label;
  217. end
  218.  
  219. -- to query AE2 with
  220. function itemFilter(item, useIsCraftable)
  221. return {name=item.name, maxDamage=item.maxDamage, label=item.label, isCraftable=((useIsCraftable == nil) or useIsCraftable)};
  222. end
  223.  
  224. -- turns quantities into strings
  225. function qtyStr(qtyNbr)
  226. if qtyNbr == nil then
  227. return " ";
  228. elseif qtyNbr < 10000 then
  229. return string.format(" %4d", qtyNbr);
  230. else
  231. local e = math.floor(math.log(qtyNbr, 1000));
  232. return string.format("%4d %s",math.floor(qtyNbr/math.pow(1000,e)),qtySuffixes[e]);
  233. end
  234. end
  235.  
  236. -- turns strings into quantities
  237. function strQty(qtyStr)
  238. local match = string.match(qtyStr, " ?([KMGTkmgt])");
  239. qtyStr = string.gsub(qtyStr, " ?([KMGTkmgt])", "");
  240.  
  241. local n = tonumber(qtyStr);
  242. if n ~= nil then
  243. if match ~= nil then
  244. n = n * qtySuffixes[string.upper(match)];
  245. end
  246. if n < 1 then
  247. return nil;
  248. else
  249. return math.floor(n);
  250. end
  251. else
  252. return nil
  253. end
  254. end
  255.  
  256. -- pseudonym handling for itmes
  257. function dispName(item)
  258. return pseudonyms[itemID(item)] or item.label;
  259. end
  260.  
  261. ---------------------------------------------------------------------------------
  262. -- level maintainer functions
  263.  
  264. -- loads data from a single level maintainer
  265. function loadLvm(lvmAddress)
  266. local lvm = component.proxy(lvmAddress);
  267. local outInfo = {nil, nil, nil, nil, nil};
  268.  
  269. for i=0, #lvmSlots-1 do
  270. local stockedItem = lvm.getRequestItem(i);
  271. if stockedItem ~= nil and lvm.isRequestValid(i) then
  272. -- stockedItems[itemID(stockedItem)] = {
  273. -- name = stockedItem.name,
  274. -- label = stockedItem.label,
  275. -- maxDamage = stockedItem.maxDamage,
  276. -- isDone = stockedItem.isDone,
  277. -- quantity= lvm.getRequestQuantity(i),
  278. -- batch = lvm.getRequestBatchSize(i),
  279. -- address = lvmAddress,
  280. -- slot = i
  281. -- };
  282. else
  283. table.insert( openStockers, {
  284. address = lvmAddress,
  285. slot = i
  286. });
  287. end
  288. end
  289. end
  290.  
  291. -- resets globals, and loads all level maintainers
  292. function loadAllLvms()
  293. stockedItems = {};
  294. openStockers = {};
  295. currentI = 0;
  296. nLvms = 0;
  297. lvmList = component.list("level_maintainer");
  298.  
  299. loadItems()
  300.  
  301. for k,v in pairs(lvmList) do
  302. loadLvm(k);
  303. nLvms = nLvms+1;
  304. end
  305. sortStocked();
  306. end
  307.  
  308. -- creates a sorted version of stockedItems, to print
  309. function sortStocked()
  310. iStocked = {};
  311. for k, v in pairs(stockedItems) do
  312. table.insert(iStocked, v);
  313. end
  314.  
  315. if #iStocked > 0 then
  316. -- sort by name, id, and label. good enough.
  317. table.sort(iStocked, function(a, b) return
  318. (a.name > b.name) or
  319. (a.name==b.name and a.maxDamage > b.maxDamage) or
  320. (a.name==b.name and a.maxDamage== b.maxDamage and a.label > b.label)
  321. end);
  322. end
  323. end
  324.  
  325.  
  326. -- returns address and slot of an open levelmaintainer, or nil/nil if none exist
  327. function getOpenSlot()
  328. if next(openStockers) then
  329. return 0, 0
  330. -- return openStockers[1].address, openStockers[1].slot;
  331. else
  332. return 0, 0
  333. -- return nil, nil;
  334. end
  335. end
  336.  
  337. function consumeOpenSlot()
  338. table.remove(openStockers, 1);
  339. end
  340.  
  341. -- stops stocking the given item
  342. function stopStockingItem(item)
  343. local stockedItem = stockedItems[itemID(item)];
  344.  
  345. if stockedItem ~= nil then
  346. -- disable in level maintainer
  347. --component.proxy(stockedItem.address).clearRequest(stockedItem.slot);
  348. -- mark as available
  349. table.insert( openStockers, {
  350. address = stockedItem.address,
  351. slot = stockedItem.slot
  352. });
  353. -- remove pseudonym, if any
  354. removePseudonym(itemID(item));
  355. -- remove watch, if any
  356. watchList[itemID(item)] = nil;
  357. -- remove from stockedItems
  358. removeStockedItem(item)
  359. end
  360. end
  361.  
  362. function setLvm(item, quantity, batch)
  363. -- is it currently being stocked?
  364. local id = itemID(item);
  365. if stockedItems[id] then
  366. -- local address, slot = stockedItems[id].address, stockedItems[id].slot;
  367. -- component.invoke(address, "setRequestQuantity", slot, quantity)
  368. -- component.invoke(address, "setRequestBatchSize", slot, batch)
  369. stockedItems[id].quantity = quantity;
  370. stockedItems[id].batch = batch;
  371. saveItems()
  372. else
  373. local address, slot = getOpenSlot();
  374. -- local db = getComponent("database");
  375. local ae2 = getAe2();
  376.  
  377. -- if address == nil then
  378. -- GUI.alert("No open slots!");
  379. -- elseif db == nil then
  380. -- GUI.alert("No database available!");
  381. if ae2 ~= nil then
  382. -- reuse database slot 1
  383. -- db.clear(1);
  384. -- ae2.store( itemFilter(item), db.address, 1 )
  385. if ae2.getCraftables({["name"]=item.name, ["label"]=item.label, ["maxDamage"]=item.maxDamage})[1] then
  386. -- component.invoke(address, "setRequestItem", slot, db.address)
  387. -- component.invoke(address, "setRequestQuantity", slot, quantity)
  388. -- component.invoke(address, "setRequestBatchSize", slot, batch)
  389. local newItem = item;
  390. newItem.quantity = quantity;
  391. newItem.batch = batch;
  392. newItem.address = address;
  393. newItem.slot = slot;
  394. stockedItems[id] = newItem;
  395. saveItems()
  396. --consumeOpenSlot();
  397. sortStocked();
  398. else
  399. GUI.alert("Could not store item into database?");
  400. end
  401. end
  402. end
  403. end
  404.  
  405. ---------------------------------------------------------------------------------
  406. -- primary window
  407.  
  408. local primaryContainer = layout:setPosition(1,1,layout:addChild(GUI.container(0, 0, layout.columnSizes[1].size - 5, layout.height - 3)))
  409. primaryContainer:addChild(GUI.panel(1,1,primaryContainer.width,2,config.programColor.header))
  410. primaryContainer:addChild(GUI.label(1,2,primaryContainer.width,1,config.programColor.headerText," Item Name Quantity Stock Batch "))
  411.  
  412. local stockingTextbox = primaryContainer:addChild(
  413. -- .textBox(x, y, width, height, backgroundColor, textColor, lines, currentLine, horizontalOffset, verticalOffset[, autoWrap, autoHeight])
  414. GUI.textBox(1, 3, primaryContainer.width, primaryContainer.height - 3, config.programColor.textbox, config.programColor.textboxText, {}, 1, 0, 0)
  415. )
  416.  
  417. -- add an invisible overlay panel to allow clicking on an entry to edit or delete its rule
  418. local overlayPanel = primaryContainer:addChild(GUI.object(0,3,stockingTextbox.width-2,stockingTextbox.height))
  419. overlayPanel.passScreenEvents = true;
  420. overlayPanel.eventHandler = function(workspace, object, e1, e2, e3, e4)
  421. if e1 == "touch" then
  422. local lineNumber = stockingTextbox.currentLine-stockingTextbox.currentLine+e4-overlayPanel.y + 1
  423. if #stockingTextbox.lines >= lineNumber and lineNumber > 0 then
  424. itemContextMenu(stockedItems[stockingTextbox.lines[lineNumber].id], e3, e4)
  425. end
  426. end
  427. end
  428. --
  429. function itemContextMenu(stockedItem, x, y)
  430. if (stockedItem ~= nil) then
  431. local contextMenu = GUI.addContextMenu(workspace, x, y+1)
  432. contextMenu:addItem("Craft "..dispName(stockedItem)).onTouch = function()
  433. craft(stockedItem, stockedItem.batch);
  434. updateScreens();
  435. end
  436. contextMenu:addItem("Edit rule for "..dispName(stockedItem)).onTouch = function()
  437. GUI_editAutoStock(stockedItem, true);
  438. updateScreens();
  439. end
  440. contextMenu:addItem("Show details for "..dispName(stockedItem)).onTouch = function()
  441. debugDetails(stockedItem, x, y);
  442. updateScreens();
  443. end
  444. contextMenu:addItem("Refresh data for "..dispName(stockedItem)).onTouch = function()
  445. updateItem(stockedItem);
  446. updateScreens();
  447. end
  448. contextMenu:addSeparator()
  449. contextMenu:addItem("Delete rule for "..dispName(stockedItem)).onTouch = function()
  450. stopStockingItem(stockedItem);
  451. updateScreens();
  452. end
  453. workspace:draw()
  454. end
  455. end
  456.  
  457. -- watchlist
  458. local watchContainer = layoutRight:setPosition(1,1,layoutRight:addChild(
  459. GUI.container(0, 0, layoutRight.width, layoutRight.height * layoutRight.rowSizes[1].size)
  460. ))
  461. watchContainer:addChild(GUI.panel(1,1,watchContainer.width,2,config.programColor.header))
  462. watchContainer:addChild(GUI.label(1,2,watchContainer.width,1,config.programColor.headerText,string.format("%45s", "Understocked Items")))
  463. local watchTextbox = watchContainer:addChild(
  464. GUI.textBox(1, 3, watchContainer.width, watchContainer.height - 3, config.programColor.textbox, config.programColor.textboxText, {}, 1, 0, 0)
  465. )
  466. -- add another invisible overlay panel to allow clicking on an entry to edit or delete its rule
  467. local overlayPanelWatch = watchContainer:addChild(GUI.object(0,3,watchTextbox.width-2,watchTextbox.height))
  468. overlayPanelWatch.passScreenEvents = true;
  469. overlayPanelWatch.eventHandler = function(workspace, object, e1, e2, e3, e4)
  470. if e1 == "touch" then
  471. local lineNumber = watchTextbox.currentLine+e4-overlayPanelWatch.y
  472. if #watchTextbox.lines >= lineNumber then
  473. itemContextMenu(stockedItems[watchTextbox.lines[lineNumber].id], e3, e4)
  474. end
  475. end
  476. end
  477.  
  478. -- searchbar; located on top of Primary container, uses both primary and watch
  479. local searchBar = primaryContainer:addChild(
  480. GUI.input(1,1,primaryContainer.width,1,config.programColor.header,config.programColor.headerText,config.programColor.textboxTextFaint,config.programColor.header,config.programColor.headerText, "", "Search", true)
  481. )
  482. searchBar.onInputFinished = function()
  483. updateScreens();
  484. stockingTextbox:scrollToStart();
  485. watchTextbox:scrollToStart();
  486. end
  487.  
  488. -- info panel
  489. local infoContainer = layoutRight:setPosition(1,2,layoutRight:addChild(
  490. GUI.container(0, 0, layoutRight.width, layoutRight.height * layoutRight.rowSizes[2].size)
  491. ))
  492. infoContainer:addChild(GUI.panel(1, 1,infoContainer.width,2,config.programColor.header))
  493. infoContainer:addChild(GUI.label(1, 2,infoContainer.width,1,config.programColor.headerText,string.format("%45s", "Program Information")))
  494. infoContainer:addChild(GUI.panel(1, 3,infoContainer.width, infoContainer.height - 3, config.programColor.textbox))
  495. infoContainer:addChild(GUI.text( 1, 4,config.programColor.textboxText, string.format("%45s", "CPUs Total:")))
  496. infoContainer:addChild(GUI.text( 1, 5,config.programColor.textboxText, string.format("%45s", "CPUs Allocated:")))
  497. infoContainer:addChild(GUI.text( 1, 6,config.programColor.textboxText, string.format("%45s", "CPUs In Use:")))
  498. infoContainer:addChild(GUI.text( 1, 7,config.programColor.textboxText, string.format("%45s", "CPUs Available:")))
  499. infoContainer:addChild(GUI.text( 1, 9,config.programColor.textboxText, string.format("%45s", "Items Stocked:")))
  500. local infoText = {}
  501. infoText.CPUTotal = infoContainer:addChild(GUI.text(48, 4, config.programColor.textboxText, "-"));
  502. infoText.CPUAllocated = infoContainer:addChild(GUI.text(48, 5, config.programColor.textboxText, "-"));
  503. infoText.CPUInUse = infoContainer:addChild(GUI.text(48, 6, config.programColor.textboxText, "-"));
  504. infoText.CPUAvail = infoContainer:addChild(GUI.text(48, 7, config.programColor.textboxText, "-"));
  505. infoText.ItemsTot = infoContainer:addChild(GUI.text(48, 9, config.programColor.textboxText, "-"));
  506.  
  507. ---------------------------------------------------------------------------------
  508. -- event handling
  509. local checkingEvent
  510. function startCheckingStock(interval)
  511. checkingEvent = event.addHandler(doInventory, interval)
  512. end
  513. function stopCheckingStock()
  514. if (checkingEvent ~= nil) then
  515. event.removeHandler(checkingEvent)
  516. checkingEvent = nil
  517. end
  518. end
  519.  
  520. ---------------------------------------------------------------------------------
  521. -- Main loop
  522.  
  523. function doInventory()
  524. if next(stockedItems) then
  525. -- each cycle, update ONE item's quantity in AE2
  526. currentI = currentI + 1;
  527. if iStocked[currentI] == nil then
  528. currentI = 1;
  529. end
  530. updateItem(iStocked[currentI]);
  531.  
  532. -- each cycle, check Lvm status (and AE2 quantity?) of all watched items
  533. watchTextbox.lines = {};
  534. for k,v in pairs(watchList) do
  535. checkWatched(k, v);
  536. end
  537.  
  538. updateScreens();
  539. else
  540. stockingTextbox.lines = {};
  541. end
  542. end
  543.  
  544. function updateScreens()
  545. printMisc();
  546. printStocking();
  547. end
  548.  
  549. function is_crafting(itemID)
  550. if watchList[itemID] == nil then
  551. return false
  552. else
  553. return not watchList[itemID].isDone()
  554. end
  555. end
  556.  
  557. function checkWatched(itemID, watchObj)
  558. local stockedItem = stockedItems[itemID];
  559.  
  560. ae2 = getAe2()
  561.  
  562. if watchList[itemID] ~= nil then
  563. watchList[itemID].cycles = watchList[itemID].cycles + 1
  564. end
  565.  
  566. if (config.watchFrequency > 0) and (watchList[itemID].cycles % config.watchFrequency == 0) then
  567. updateItem(stockedItem)
  568. end
  569.  
  570. if watchList[itemID] ~= nil then
  571. if (searchBar.text == nil or string.find(string.lower(dispName(stockedItem)), string.lower(searchBar.text))) then
  572. table.insert(watchTextbox.lines, {
  573. text = string.format("%45s", dispName(stockedItem)),
  574. color = (not is_crafting(itemID) and watchList[itemID].cycles > 3) and config.programColor.textboxTextHighlight or config.programColor.textboxText,
  575. id = itemID
  576. })
  577. end
  578. end
  579. end
  580.  
  581. function listStockedItem(stockedItem, textbox, mustMatchStr, i)
  582. if (mustMatchStr == nil or string.find(string.lower(dispName(stockedItem)), string.lower(mustMatchStr))) then
  583. table.insert(textbox.lines, {
  584. text = string.format("%3s %45s %6s / %6s %6s ",
  585. (i == currentI) and "-->" or "",
  586. dispName(stockedItem),
  587. qtyStr(stockedItem.size),
  588. qtyStr(stockedItem.quantity),
  589. qtyStr(stockedItem.batch)),
  590. color = (watchList[itemID(stockedItem)] and (not is_crafting(watchList[itemID(stockedItem)])) and watchList[itemID(stockedItem)].cycles > 3)
  591. and config.programColor.textboxTextHighlight or config.programColor.textboxText,
  592. id = itemID(stockedItem)
  593. });
  594. end
  595. end
  596.  
  597. function printStocking()
  598. stockingTextbox.lines = {}
  599. for i=1, #iStocked do
  600. listStockedItem(iStocked[i], stockingTextbox, searchBar.text, i);
  601. end
  602. end
  603.  
  604.  
  605. function printMisc()
  606. local cpuData = CPUInfo();
  607. infoText.CPUTotal.text = cpuData.total;
  608. infoText.CPUAllocated.text = config.allocatedCPUs
  609. infoText.CPUInUse.text = cpuData.inuse;
  610. infoText.CPUAvail.text = cpuData.available;
  611. infoText.ItemsTot.text = #iStocked;
  612. end
  613.  
  614. function updateItem(item)
  615. local id = itemID(item);
  616. local ae2 = getAe2();
  617. if ae2 ~= nil then
  618. local ae2Query = ae2.getItemsInNetwork(itemFilter(item));
  619. if ae2Query[1] ~= nil then
  620. stockedItems[id].size = ae2Query[1].size;
  621. end
  622.  
  623. if stockedItems[id].size < item.quantity then
  624. recipe = ae2.getCraftables({["name"]=item.name, ["label"]=item.label, ["maxDamage"]=item.maxDamage})[1]
  625.  
  626. if watchList[id] == nil or watchList[id].isDone() then
  627. local crafting_data = recipe.request(stockedItems[id].batch)
  628. watchList[id] = {
  629. isDone = crafting_data.isDone,
  630. isCanceled = crafting_data.isCanceled,
  631. cycles = 1
  632. }
  633. elseif watchList[id].isCanceled() then
  634. watchList[id] = nil
  635. end
  636. else
  637. watchList[id] = nil;
  638. end
  639. end
  640. end
  641.  
  642. function debugDetails(item, x, y)
  643. -- make a subwindow
  644. local itemInfoMenu = GUI.addContextMenu(workspace, x, y);
  645.  
  646. itemInfoMenu:addItem("Debug Details for "..dispName(item));
  647. itemInfoMenu:addSeparator();
  648. for k, v in pairs(item) do
  649. itemInfoMenu:addItem(string.format("%-10s: %40s", k, v), true);
  650. end
  651. itemInfoMenu:addItem(string.format("%-10s: %40s", "pseudonym", pseudonyms[itemID(item)]), true);
  652. workspace:draw();
  653. end
  654.  
  655. -- queries AE2 for all currently stocked items.
  656. -- warning: tps intensive
  657. function updateAll()
  658. for i=1,#iStocked do
  659. updateItem(iStocked[i]);
  660. end
  661. updateScreens();
  662. end
  663.  
  664. function CPUInfo()
  665. local ae2 = getAe2();
  666. if ae2 ~= nil then
  667. local cpus = ae2.getCpus()
  668. local openCpus = 0
  669. for i=1, #cpus do
  670. if (cpus[i].busy == false) then
  671. openCpus = openCpus + 1
  672. end
  673. end
  674. available = openCpus - #cpus + config.allocatedCPUs
  675. if available < 0 then
  676. available = 0
  677. end
  678. return {
  679. total = #cpus,
  680. available = available,
  681. inuse = #cpus - openCpus
  682. };
  683. else
  684. return {
  685. total = 0,
  686. available = 0,
  687. inuse = 0
  688. }
  689. end
  690. end
  691.  
  692. function craft(item, amt)
  693. if (CPUInfo().available > 0) then
  694. local recipe = getAe2().getCraftables(itemFilter(item, false))[1];
  695. if recipe then
  696. local order = recipe.request(amt);
  697. if (order.isCanceled()) then
  698. GUI.alert("Insufficient resources for craft!");
  699. end
  700. else
  701. GUI.alert("No crafting recipe!");
  702. end
  703. else
  704. GUI.alert("CPU not available!");
  705. end
  706. end
  707.  
  708. ---------------------------------------------------------------------------------
  709. function addAutoStock()
  710. local invController = getComponent("inventory_controller");
  711.  
  712. if (invController == nil) then
  713. GUI.alert("No inventory controller detected on network!");
  714. elseif (invController.getInventoryName(1) == nil) then
  715. GUI.alert("Place an inventory on top of the inventory controller.");
  716. else
  717. for itemIt in invController.getAllStacks(1) do
  718. if next(itemIt) then
  719. item = itemIt
  720. item.tag = nil
  721. item.hasTag = nil
  722. local stockedItem = stockedItems[itemID(item)];
  723. if (stockedItem ~= nil) then
  724. GUI_editAutoStock(stockedItem, true);
  725. else
  726. local ae2 = getAe2();
  727. if ae2 ~= nil then
  728. if ae2.getCraftables({["name"]=item.name, ["label"]=item.label, ["maxDamage"]=item.maxDamage})[1] then
  729. if getOpenSlot() then
  730. GUI_editAutoStock(item, false);
  731. addStockedItem(item)
  732. else
  733. GUI.alert("No open slots!");
  734. end
  735. else
  736. GUI.alert("Item '" .. item.label .."' is not craftable.");
  737. end
  738. end
  739. end
  740. end
  741. end
  742. end
  743. end
  744.  
  745. function GUI_editAutoStock(stockedItem, alreadystocking)
  746. local defaultQuan
  747. local defaultSize
  748. if (alreadystocking) then
  749. defaultQuan = stockedItem.quantity
  750. defaultSize = stockedItem.batch
  751. else
  752. defaultQuan = 1
  753. defaultSize = 1
  754. end
  755.  
  756. local topstr = {[true]="Editing Stocked Item",[false]="Stocking New Item"}
  757. local savestr = {[true]="Save Changes",[false]="Stock Item"}
  758. local cancelstr = {[true]="Discard Changes",[false]="Cancel"}
  759.  
  760. -- make a subwindow
  761. local subwindow = window:addChild(GUI.filledWindow(20,10,60,20,config.programColor.windowFill))
  762. subwindow.actionButtons.close.onTouch = function()
  763. subwindow:remove()
  764. workspace:draw()
  765. end
  766. subwindow:addChild(GUI.panel(1,1,subwindow.width,2,config.programColor.header))
  767. subwindow:addChild(GUI.label(1,2,subwindow.width,1,config.programColor.headerText,topstr[alreadystocking])):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP)
  768.  
  769. local sublayout = subwindow:addChild(GUI.layout(1,3,subwindow.width,subwindow.height-2,1,3))
  770. for row=1,3 do
  771. sublayout:setSpacing(1, row, 0)
  772. end
  773.  
  774. -- 1st layer: label
  775. sublayout:setPosition(1,1,
  776. sublayout:addChild(
  777. GUI.label(0, 0, 4, 1, config.programColor.textboxText,"Name"))
  778. :setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP))
  779. local nameinput = sublayout:setPosition(1,1,
  780. sublayout:addChild(
  781. GUI.input(0, 0, sublayout.width-4, 3, config.programColor.textbox, config.programColor.textboxTextFaint, 0x0, config.programColor.textboxFocused, config.programColor.textboxText,
  782. dispName(stockedItem), "Enter display name", true)))
  783. nameinput.validator = function()
  784. return (nameinput.text ~= "")
  785. end
  786. -- 2nd layer: stocking quantity and group size
  787. local stocksizelayer = sublayout:setPosition(1,2,
  788. sublayout:addChild(
  789. GUI.layout(0,0,sublayout.width,sublayout.height/3,2,1)
  790. ))
  791. for col=1,2 do
  792. stocksizelayer:setSpacing(col,1,0)
  793. end
  794. -- stock quantity
  795. stocksizelayer:setPosition(1,1,
  796. stocksizelayer:addChild(
  797. GUI.label(0, 0, 8, 1, config.programColor.headerText,"Quantity")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP))
  798. local quantityinput = stocksizelayer:setPosition(1,1,
  799. stocksizelayer:addChild(
  800. GUI.input(0, 0, stocksizelayer.width/2-4, 3, config.programColor.textbox, config.programColor.textboxTextFaint, 0x0, config.programColor.textboxFocused, config.programColor.textboxText,
  801. defaultQuan, "Enter stocking quantity", true)))
  802. quantityinput.validator = function(text)
  803. local n = strQty(text);
  804. if n then
  805. quantityinput.text = n;
  806. end
  807. return (n ~= nil);
  808. end
  809. -- batch size
  810. stocksizelayer:setPosition(2,1,
  811. stocksizelayer:addChild(
  812. GUI.label(0, 0, 9, 1, config.programColor.headerText,"Batch")):setAlignment(GUI.ALIGNMENT_HORIZONTAL_CENTER, GUI.ALIGNMENT_VERTICAL_TOP))
  813. local batchinput = stocksizelayer:setPosition(2,1,
  814. stocksizelayer:addChild(
  815. GUI.input(0, 0, stocksizelayer.width/2-4, 3, config.programColor.textbox, config.programColor.textboxTextFaint, 0x0, config.programColor.textboxFocused, config.programColor.textboxText,
  816. defaultSize, "Enter batch quantity", true)))
  817. batchinput.validator = function(text)
  818. local n = strQty(text);
  819. if n then
  820. batchinput.text = math.floor(n)
  821. end
  822. return (n ~= nil)
  823. end
  824. -- 3rd layer: save/discard buttons
  825. local buttonlayer = sublayout:setPosition(1,3,
  826. sublayout:addChild(
  827. GUI.layout(0,0,sublayout.width,sublayout.height/3,2,1)
  828. ))
  829. -- Save Changes
  830. buttonlayer:setPosition(1,1,
  831. buttonlayer:addChild(
  832. GUI.roundedButton(0, 0, buttonlayer.width/2-4, 3, config.buttonColor.save, config.buttonColor.text, config.buttonColor.savePressed, config.buttonColor.textPressed, savestr[alreadystocking])
  833. )).onTouch = function()
  834. if (nameinput.text ~= dispName(stockedItem)) then
  835. addPseudonym(itemID(stockedItem), nameinput.text);
  836. end
  837.  
  838. if (quantityinput.text ~= defaultQuan or batchinput.text ~= defaultSize or not alreadystocking) then
  839. setLvm(stockedItem, quantityinput.text, batchinput.text);
  840. end
  841.  
  842. updateScreens();
  843. subwindow:remove();
  844. workspace:draw();
  845. end
  846. -- Discard Changes
  847. buttonlayer:setPosition(2,1,
  848. buttonlayer:addChild(
  849. GUI.roundedButton(0, 0, buttonlayer.width/2-4, 3, config.buttonColor.cancel, config.buttonColor.text, config.buttonColor.cancelPressed, config.buttonColor.textPressed, cancelstr[alreadystocking])
  850. )).onTouch = function()
  851. subwindow:remove();
  852. workspace:draw();
  853. end
  854. end
  855.  
  856. function addPseudonym(id, pseudonym)
  857. pseudonyms[id] = pseudonym;
  858. fs.writeTable(pseudonymFilePath, pseudonyms);
  859. end
  860.  
  861. function removePseudonym(id)
  862. pseudonyms[id] = nil;
  863. fs.writeTable(pseudonymFilePath, pseudonyms);
  864. end
  865.  
  866. ---------------------------------------------------------------------------------
  867. -- edit config options
  868. function editConfigs()
  869. -- make a subwindow
  870. local subwindow = window:addChild(GUI.filledWindow(40,20,60,34,config.programColor.windowFill));
  871. subwindow.actionButtons.close.onTouch = function()
  872. subwindow:remove();
  873. workspace:draw();
  874. end
  875.  
  876. subwindow:addChild(GUI.text(10, 2, config.programColor.textboxText, "EDITING CONFIG"));
  877.  
  878. subwindow:addChild(GUI.text(3, 4, config.programColor.textboxText, "Time between inventory checks and updates, in seconds."));
  879. subwindow:addChild(GUI.text(3, 5, config.programColor.textboxText, "If zero, never automatically checks item quantities."));
  880. local checkFrequency = subwindow:addChild(
  881. GUI.input(5, 6, subwindow.width-10, 1, config.programColor.textbox, config.programColor.textboxTextFaint, 0x0, config.programColor.textboxFocused, config.programColor.textboxText,
  882. config.checkFrequency, "Cycle time", true));
  883. checkFrequency.validator = function(text)
  884. local n = strQty(text);
  885. if n then
  886. checkFrequency.text = math.max(0, math.floor(n));
  887. end
  888. return (n ~= nil);
  889. end
  890.  
  891. subwindow:addChild(GUI.text(3, 8, config.programColor.textboxText, "Cycles between additional AE2 pulls of 'watched' items."));
  892. local watchFrequency = subwindow:addChild(
  893. GUI.input(5, 9, subwindow.width-10, 1, config.programColor.textbox, config.programColor.textboxTextFaint, 0x0, config.programColor.textboxFocused, config.programColor.textboxText,
  894. config.watchFrequency, "Cycle time", true));
  895. watchFrequency.validator = function(text)
  896. local n = strQty(text);
  897. if n then
  898. watchFrequency.text = math.max(1, math.floor(n));
  899. end
  900. return (n ~= nil);
  901. end
  902.  
  903. subwindow:addChild(GUI.text(3, 11, config.programColor.textboxText, "Optional: Number of allocated CPUs."));
  904. local allocatedCPUs = subwindow:addChild(
  905. GUI.input(5, 12, subwindow.width-10, 1, config.programColor.textbox, config.programColor.textboxTextFaint, 0x0, config.programColor.textboxFocused, config.programColor.textboxText,
  906. config.allocatedCPUs, "Cycle time", true));
  907. allocatedCPUs.validator = function(text)
  908. local n = strQty(text);
  909. if n then
  910. allocatedCPUs.text = math.max(0, math.floor(n));
  911. end
  912. return (n ~= nil);
  913. end
  914.  
  915. subwindow:addChild(GUI.text(3, 14, config.programColor.textboxText, "Program Colors"));
  916. programColorheader = subwindow:addChild(GUI.colorSelector( 5, 15, 35, 1, config.programColor.header, "header"))
  917. programColorheaderText = subwindow:addChild(GUI.colorSelector( 5, 16, 35, 1, config.programColor.headerText, "headerText"))
  918. programColorwindowFill = subwindow:addChild(GUI.colorSelector( 5, 17, 35, 1, config.programColor.windowFill, "windowFill"))
  919. programColortextbox = subwindow:addChild(GUI.colorSelector( 5, 18, 35, 1, config.programColor.textbox, "textbox"))
  920. programColortextboxFocused = subwindow:addChild(GUI.colorSelector( 5, 19, 35, 1, config.programColor.textboxFocused, "textboxFocused"))
  921. programColortextboxText = subwindow:addChild(GUI.colorSelector( 5, 20, 35, 1, config.programColor.textboxText, "textboxText"))
  922. programColortextboxTextFaint = subwindow:addChild(GUI.colorSelector( 5, 21, 35, 1, config.programColor.textboxTextFaint, "textboxTextFaint"))
  923. programColortextboxTextHighlight = subwindow:addChild(GUI.colorSelector( 5, 22, 35, 1, config.programColor.textboxTextHighlight, "textboxTextHighlight"))
  924.  
  925. subwindow:addChild(GUI.text(3, 24, config.programColor.textboxText, "Button Colors"));
  926. buttonColorsave = subwindow:addChild(GUI.colorSelector( 5, 25, 35, 1, config.buttonColor.save, "save"))
  927. buttonColorsavePressed = subwindow:addChild(GUI.colorSelector( 5, 26, 35, 1, config.buttonColor.savePressed, "savePressed"))
  928. buttonColorcancel = subwindow:addChild(GUI.colorSelector( 5, 27, 35, 1, config.buttonColor.cancel, "cancel"))
  929. buttonColorcancelPressed = subwindow:addChild(GUI.colorSelector( 5, 28, 35, 1, config.buttonColor.cancelPressed, "cancelPressed"))
  930. buttonColortext = subwindow:addChild(GUI.colorSelector( 5, 29, 35, 1, config.buttonColor.text, "text"))
  931. buttonColortextPressed = subwindow:addChild(GUI.colorSelector( 5, 30, 35, 1, config.buttonColor.textPressed, "textPressed"))
  932.  
  933. -- Apply Changes
  934. subwindow:addChild(
  935. GUI.roundedButton(3, 32, subwindow.width/3-2, 3, config.buttonColor.save, config.buttonColor.text, config.buttonColor.savePressed, config.buttonColor.textPressed, "Apply changes")
  936. ).onTouch = function()
  937.  
  938. config.checkFrequency = checkFrequency.text;
  939. config.watchFrequency = watchFrequency.text;
  940. config.allocatedCPUs = allocatedCPUs.text;
  941.  
  942. config.programColor.header = programColorheader.color;
  943. config.programColor.headerText = programColorheaderText.color;
  944. config.programColor.windowFill = programColorwindowFill.color;
  945. config.programColor.textbox = programColortextbox.color;
  946. config.programColor.textboxFocused = programColortextboxFocused.color;
  947. config.programColor.textboxText = programColortextboxText.color;
  948. config.programColor.textboxTextFaint = programColortextboxTextFaint.color;
  949. config.programColor.textboxTextHighlight = programColortextboxTextHighlight.color;
  950. config.buttonColor.save = buttonColorsave.color;
  951. config.buttonColor.savePressed = buttonColorsavePressed.color;
  952. config.buttonColor.cancel = buttonColorcancel.color;
  953. config.buttonColor.cancelPressed = buttonColorcancelPressed.color;
  954. config.buttonColor.text = buttonColortext.color;
  955. config.buttonColor.textPressed = buttonColortextPressed.color;
  956.  
  957. -- write to file
  958. fs.writeTable(configFilePath, config);
  959.  
  960. GUI.alert("Config updated -- restart program.");
  961. stopCheckingStock();
  962. subwindow:remove();
  963. window:remove();
  964. workspace:draw();
  965. end
  966. -- Discard Changes
  967. subwindow:addChild(
  968. GUI.roundedButton(1*subwindow.width/3+2, 32, subwindow.width/3-2, 3, config.buttonColor.cancel, config.buttonColor.text, config.buttonColor.cancelPressed, config.buttonColor.textPressed, "Discard changes")
  969. ).onTouch = function()
  970. subwindow:remove();
  971. workspace:draw();
  972. end
  973. -- Reset Config
  974. subwindow:addChild(
  975. GUI.roundedButton(2*subwindow.width/3+2, 32, subwindow.width/3-2, 3, config.buttonColor.cancel, config.buttonColor.text, config.buttonColor.cancelPressed, config.buttonColor.textPressed, "Reset all")
  976. ).onTouch = function()
  977. local needRestart = (table.concat(config.programColor) .. table.concat(config.buttonColor)) ~= (table.concat(defaultOptions.programColor) .. table.concat(defaultOptions.buttonColor));
  978. config = defaultOptions;
  979. fs.writeTable(configFilePath, config);
  980. GUI.alert("Config reset -- restart program.");
  981. stopCheckingStock();
  982. subwindow:remove();
  983. window:remove();
  984. workspace:draw();
  985. end
  986. end
  987.  
  988. -- Run preliminary setup
  989. prelim();
  990. -- Draw changes on screen after customizing your window
  991. workspace:draw();
Add Comment
Please, Sign In to add comment