· 3 years ago · Nov 14, 2021, 06:00 PM
1Filenames = {}
2Filenames.configFolder = "config"
3Filenames.tankContents = Filenames.configFolder.."/tank_contents.json"
4Filenames.potionTypes = Filenames.configFolder.."/potion_types.json"
5
6ItemNames = {
7 redstone = "minecraft:redstone",
8 glowstone = "minecraft:glowstone_dust",
9 gunpowder = "minecraft:gunpowder",
10 dragonBreath = "minecraft:dragon_breath",
11 netherwart = "minecraft:nether_wart"
12}
13
14term.clear()
15term.setCursorPos(1,1)
16term.setTextColor(colors.white)
17if json == nil then
18 json = {}
19end
20if not fs.exists("apis/json") then
21 print("Downloading json api...")
22 shell.run("pastebin","get","4nRg9CHU","apis/json")
23end
24if not fs.exists(Filenames.potionTypes) then
25 print("Downloading potion types config...")
26 shell.run("pastebin","get","YBSC0sGp",Filenames.potionTypes)
27end
28
29UNKNOWN = "unknown"
30
31PotionTypes = {
32 definitions=nil,
33 aliases={},
34 initialize=function()
35 PotionTypes.definitions = {}
36 PotionTypes.aliases = {}
37 potionTypes = {}
38 if not pcall(function()
39 potionTypes = json.decodeFromFile(Filenames.potionTypes)
40 end) then
41 --failed to decode file and load potion types
42 clearTerm()
43 printError("An error occurred while reading potion types config")
44 return false
45 end
46 for typeName,typeData in pairs(potionTypes) do
47 PotionTypes.addDefinition(typeName,typeData)
48 end
49 return true
50 end,
51 addDefinition=function(name,definition)
52 PotionTypes.definitions[name] = definition
53 for _,displayName in ipairs(definition.names) do
54 PotionTypes.aliases[string.lower(displayName)] = name
55 end
56 end,
57 getInternalName=function(name)
58 if name == nil then return nil end
59 name = string.lower(name)
60 if PotionTypes.definitions[name] ~= nil then return name end
61 if PotionTypes.aliases[name] ~= nil then return PotionTypes.aliases[name] end
62 end,
63 getDefinition=function(name)
64 local internalName = PotionTypes.getInternalName(name)
65 if internalName == nil then return nil end
66 return PotionTypes.definitions[internalName]
67 end
68}
69PotionModifiers = {
70 normal="Normal",
71 extended="Extended",
72 levelTwo="Level II",
73 getList=function()
74 return {
75 PotionModifiers.normal,
76 PotionModifiers.extended,
77 PotionModifiers.levelTwo
78 }
79 end,
80 isValid=function(value)
81 if value == PotionModifiers.normal then return true end
82 if value == PotionModifiers.extended then return true end
83 if value == PotionModifiers.levelTwo then return true end
84 end,
85 getItem=function(modifier)
86 if modifier == PotionModifiers.extended then
87 return ItemNames.redstone
88 elseif modifier == PotionModifiers.levelTwo then
89 return ItemNames.glowstone
90 end
91 return nil
92 end
93}
94PotionStyles = {
95 normal="Normal",
96 splash="Splash",
97 lingering="Lingering",
98 getList=function()
99 return {
100 PotionStyles.normal,
101 PotionStyles.splash,
102 PotionStyles.lingering
103 }
104 end,
105 isValid=function(value)
106 if value == PotionStyles.normal then return true end
107 if value == PotionStyles.splash then return true end
108 if value == PotionStyles.lingering then return true end
109 end,
110 getItem=function(style)
111 if style == PotionStyles.splash then
112 return ItemNames.gunpowder
113 elseif style == PotionStyles.lingering then
114 return ItemNames.dragonBreath
115 end
116 return nil
117 end
118}
119PotionDefinition = {
120 new=function(self,data)
121 local o = {}
122 o.names = List:new(data.names)
123 o.recipes = RecipeList:new()
124 for _,recipeData in ipairs(data.recipes) do
125 o.recipes:addRecipe(Recipe:new(recipeData))
126 end
127 setmetatable(o,self)
128 self.__index = self
129 return o
130 end
131}
132RecipeList = {
133 new=function(self)
134 local o = List:new()
135 setmetatable(o,self)
136 self.__index = self
137 return o
138 end,
139 addRecipe=function(self,recipe)
140 self:insert(recipe)
141 end,
142 --- Gets a list of recipes and the number of times each should be crafted
143 --
144 -- first return is a bool for whether or not the desired qty can be satisfied
145 --
146 -- second return is the list of recipes and quantities
147 getCraftPlan=function(self,qty)
148 local plan = List:new()
149 for _,recipe in ipairs(self) do
150 local maxCrafts = recipe:getMaxCrafts(qty)
151 if maxCrafts > 0 then
152 qty = qty - maxCrafts
153 plan:insert({maxCrafts,recipe})
154 if qty == 0 then
155 break
156 end
157 end
158 end
159
160 end
161}
162Recipe = {
163 new=function(self,items)
164 local o = {}
165 o.items = List:new(items)
166 setmetatable(o,self)
167 self.__index = self
168 return o
169 end,
170 getMaxCrafts=function(self,targetQty)
171 local maxCrafts = targetQty
172 for _,itemName in ipairs(self.items) do
173 local available = Inventories.getItemCount(itemName)
174 if available == 0 then return 0 end
175 if maxCrafts == nil or available < maxCrafts then maxCrafts = available end
176 end
177 return maxCrafts
178 end,
179 checkMissingItems=function(self,qty,missingItems)
180 for _,itemName in ipairs(self.items) do
181 print("Recipe:checkMissingItems")
182 print(qty)
183 Inventories.checkMissingItem(itemName,qty,missingItems)
184 end
185 end
186}
187
188string.startsWith = function(self,str)
189 return self:find("^"..str)
190end
191string.split = function(self,sep)
192 if sep == nil then
193 sep = "%s"
194 end
195 local t = {}
196 for str in self:gmatch("([^"..sep.."]+)") do
197 table.insert(t,str)
198 end
199 return t
200end
201table.contains = function(self,value)
202 for _, v in ipairs(self) do
203 if v == value then
204 return true
205 end
206 end
207 return false
208end
209
210StorageTank = {
211 new=function(self,peripheralName)
212 perip = peripheral.wrap(peripheralName)
213 perip.name = peripheralName
214 perip.contents=nil
215 setmetatable(perip,self)
216 self.__index = self
217 return perip
218 end,
219 hasContents=function(self)
220 if #self.tanks() == 0 then
221 if self.contents ~= nil then
222 self:setContents(nil)
223 end
224 return false
225 else
226 if self.contents == nil then
227 self:setContents(Potion:new(UNKNOWN))
228 end
229 return true
230 end
231 end,
232 isUnknown=function(self)
233 if self.contents == nil then
234 if #self.tanks() ~= 0 then
235 self:setContents(Potion:new(UNKNOWN))
236 return true
237 end
238 return false
239 else
240 if #self.tanks() == 0 then
241 self.contents = nil
242 self:setContents(nil)
243 return false
244 end
245 return self.contents:isUnknown()
246 end
247 end,
248 contentsMatch=function(self,potionDef)
249 if self.contents == nil then
250 return potionDef == nil
251 elseif potionDef == nil then
252 return false
253 end
254 return self.contents:matches(potionDef)
255 end,
256 setContents=function(self,potionDef)
257 self.contents = potionDef
258 StorageTanks:writeTankContents()
259 end
260}
261Potion = {
262 new=function(self,type,modifier,style)
263 local contents = {}
264 contents.type = type
265 contents.modifier = modifier or PotionStyles.normal
266 contents.style = style or PotionStyles.normal
267 setmetatable(contents,self)
268 self.__index = self
269 return contents
270 end,
271 matches=function(self,def)
272 if def == nil then return false end
273 if self.type ~= def.type then return false end
274 if self.type == UNKNOWN then return true end
275 return self.modifier == def.modifier and self.style == def.style
276 end,
277 canCreate=function(self,def)
278 if def == nil or self.type == UNKNOWN then return false end
279 if self.type ~= def.type then return false end
280 if self.modifier ~= def.modifier then
281 if def.modifier == PotionModifiers.normal then return false end
282 if self.modifier ~= PotionModifiers.normal then return false end
283 end
284 if self.style ~= def.style then
285 if def.style == PotionModifiers.normal then return false end
286 if self.modifier ~= PotionModifiers.normal then return false end
287 end
288 return true
289 end,
290 isUnknown=function(self)
291 return self.type == UNKNOWN
292 end,
293 getDisplayName=function(self)
294 local fullPotionName = "Potion of "..PotionTypes.getDefinition(self.type).names[1]
295 if self.style ~= PotionStyles.normal then
296 fullPotionName = self.style.." "..fullPotionName
297 end
298 if self.modifier == PotionModifiers.extended then
299 fullPotionName = PotionModifiers.extended.." "..fullPotionName
300 elseif self.modifier == PotionModifiers.levelTwo then
301 fullPotionName = fullPotionName.." II"
302 end
303 return fullPotionName
304 end
305}
306List = {
307 new=function(self,t)
308 local list = t or {}
309 setmetatable(list,self)
310 self.__index = self
311 return list
312 end,
313 insert=table.insert,
314 remove=table.remove,
315 contains=table.contains
316}
317
318StorageTanks = {
319 list=List:new(),
320 findStorage=function(potion)
321 local matchingTanks = List:new()
322 local unknownTanks = List:new()
323 local emptyTanks = List:new()
324 for _,tank in ipairs(StorageTanks.list) do
325 if tank:hasContents() then
326 if tank:contentsMatch(potion) then
327 matchingTanks:insert(tank)
328 elseif tank:isUnknown() then
329 unknownTanks:insert(tank)
330 end
331 else
332 emptyTanks:insert(tank)
333 end
334 end
335 return matchingTanks, unknownTanks, emptyTanks
336 end,
337 writeTankContents=function()
338 local contents = {}
339 for _,tank in ipairs(StorageTanks.list) do
340 if tank.hasContents() and not tank.isUnknown() then
341 contents[tank.name] = tank.contents
342 end
343 end
344 if not pcall(function()
345 local h = fs.open(Filenames.tankContents,"w")
346 h.write(json.encode(contents))
347 h.close()
348 end) then
349 printError("Error writing tank contents file")
350 waitForKey()
351 end
352 end,
353 initializeTankContents=function()
354 if fs.exists(Filenames.tankContents) then
355 local contents = nil
356 if not pcall(function()
357 contents = json.decodeFromFile(Filenames.tankContents)
358 for _,tank in ipairs(StorageTanks.list) do
359 if tank.hasContents() ~= nil then
360 if contents[tank.name] ~= nil then
361 tank.contents = Potion:new(contents.type,contents.modifiers,contents.style)
362 end
363 end
364 end
365 end) then
366 --failed to decode file and set tank contents
367 clearTerm()
368 printError("An error occurred while reading previous tank contents")
369 waitForKey()
370 end
371 StorageTanks:writeTankContents()
372 end
373 end,
374 storePotion=function(potion)
375 local matchingTanks, unknownTanks, emptyTanks = StorageTanks.findStorage(potion)
376 local basin = Peripherals.basin
377 if #matchingTanks > 0 then
378 for _,t in ipairs(matchingTanks) do
379 if t.pullFluid(basin.name) > 0 then
380 if not basin:hasFluid() then
381 return true
382 end
383 end
384 end
385 end
386 if #unknownTanks > 0 then
387 for _,t in ipairs(unknownTanks) do
388 if t.pullFluid(basin.name) > 0 then
389 t:setContents(potion)
390 if not basin:hasFluid() then
391 break
392 end
393 end
394 end
395 end
396 if basin:hasFluid() and #emptyTanks > 0 then
397 for _,t in ipairs(emptyTanks) do
398 if t.pullFluid(basin.name) > 0 then
399 t:setContents(potion)
400 if not basin:hasFluid() then
401 break
402 end
403 end
404 end
405 end
406 return basin:hasFluid()
407 end
408}
409Inventories = {
410 list=List:new(),
411 indexedItems={},
412 indexInventories=function()
413 local totalSlots = 0
414 for _,inv in ipairs(Inventories.list) do
415 totalSlots = totalSlots + inv.size()
416 end
417 Inventories.indexedItems = {}
418 local currentSlot = 1
419 clearTerm()
420 for _,inv in ipairs(Inventories.list) do
421 local inventorySize = inv.size()
422 for slot=1, inventorySize do
423 term.setCursorPos(1,1)
424 term.write("Indexing Inventories... Slot "..currentSlot.." of "..totalSlots)
425 local itemDetail = inv.getItemDetail(slot)
426 if itemDetail ~= nil then
427 if not Inventories.hasItem(itemDetail.name) then
428 Inventories.indexedItems[itemDetail.name] = IndexedItem:new(itemDetail.name)
429 end
430 Inventories.indexedItems[itemDetail.name]:addLocation(ItemLocation:new(inv,slot,itemDetail.count))
431 end
432 currentSlot = currentSlot + 1
433 end
434 end
435 clearTerm()
436 end,
437 hasItem=function(itemName)
438 return Inventories.indexedItems[itemName] ~= nil
439 end,
440 getItemCount=function(itemName)
441 if Inventories.indexedItems[itemName] == nil then return 0 end
442 return Inventories.indexedItems[itemName].total
443 end,
444 getItemLocations=function(itemName)
445 if not Inventories.hasItem(itemName) then return nil end
446 return Inventories.indexedItems[itemName].locations
447 end,
448 checkMissingItem=function(itemName,qty,missingItems)
449 local count = Inventories.getItemCount(itemName)
450 if count < qty then
451 missingItems:insert({itemName,qty - count})
452 end
453 end,
454 --- Returns true if the operation was successful
455 --
456 -- Can trigger issue if item is not found, after which user can add more items or cancel the operation
457 take=function(itemName,toInventory,qty)
458 if Inventories.getItemCount(itemName) < qty then
459 if not Issues.itemMissingInInventory(itemName,qty) then
460 return false
461 end
462 end
463 while qty > 0 do
464 local satisfied, remainingQty = Inventories.indexedItems[itemName]:take(toInventory,qty)
465 if not satisfied then
466 qty = remainingQty
467 if not Issues.itemMissingInInventory(itemName,qty) then
468 return false
469 end
470 else
471 qty = 0
472 end
473 end
474 return true
475 end
476}
477IndexedItem={
478 new=function(self,name)
479 local o = {}
480 o.name = name
481 o.total = 0
482 o.locations = List:new()
483 setmetatable(o,self)
484 self.__index = self
485 return o
486 end,
487 addLocation=function(self,location)
488 self.locations:insert(location)
489 self.total = self.total + location.stackSize
490 end,
491 --- Takes an item from the index
492 --
493 -- first return is true if the qty was satisfied, otherwise false
494 --
495 -- second return is the remaining qty that was not satisfied
496 take=function(self,toInventory,qty)
497 qty = qty or 1
498 if self.total == 0 or #self.locations == 0 then return false end
499 if qty > self.total then return false end
500 while qty > 0 and self.total > 0 do
501 local moveCount = toInventory.pullItems(self.locations[1].inventory.name,self.locations[1].slot,qty)
502 if moveCount > 0 then
503 qty = qty - moveCount
504 self.locations[1].stackSize = self.locations[1].stackSize - moveCount
505 self.total = self.total - moveCount
506 self.validateFirstLocation()
507 else
508 if self:validateFirstLocation() then
509 --stored location is still valid
510 --destination must have an issue
511 break
512 end
513 end
514 end
515 return qty == 0, qty
516 end,
517 --- returns true if location still exists after being validated, else false
518 validateFirstLocation=function(self)
519 local count, difference = self.locations[1].validateCount(self.name)
520 if difference ~= 0 then
521 self.total = self.total + difference
522 end
523 if count == 0 then
524 self.locations:remove(1)
525 return false
526 end
527 return true
528 end
529}
530ItemLocation={
531 new=function(self,inventory,slot,stackSize)
532 local o = {}
533 o.inventory = inventory
534 o.slot = slot
535 o.stackSize = stackSize
536 setmetatable(o,self)
537 self.__index = self
538 return o
539 end,
540 --- checks the location to validate that the expected item still exists, and updates the stored qty if it differs
541 --
542 -- first return is the count of the item that exists in the stack
543 --
544 -- second return is the difference between the actual and the stored value
545 validateCount=function(self,itemName)
546 local detail = self.inventory.getItemDetail(self.slot)
547 if detail == nil then return 0, -1 * self.stackSize end
548 if detail.name ~= itemName then return 0, -1 * self.stackSize end
549 local diff = detail.count - self.stackSize
550 if diff ~= 0 then self.stackSize = detail.count end
551 return detail.count, diff
552 end
553}
554Peripherals={
555 waterTank=nil,
556 basin=nil,
557 spout=nil,
558 depot=nil,
559 workbench=nil,
560 wrapPeripherals=function()
561 while true do
562 Peripherals.waterTank = nil
563 StorageTanks.list = List:new()
564 Peripherals.basin = nil
565 Inventories.list = List:new()
566 Monitors.list = List:new()
567 Peripherals.spout = nil
568 Peripherals.depot = nil
569 Peripherals.workbench = nil
570 for _,v in ipairs(peripheral.getNames()) do
571 if v:startsWith("create:fluid_tank") then
572 local t = peripheral.wrap(v)
573 local tanks = t.tanks()
574 if #tanks > 0 and tanks[1].name == "minecraft:water" then
575 t.name = v
576 Peripherals.waterTank = t
577 else
578 t = StorageTank:new(v)
579 StorageTanks.list:insert(t)
580 end
581 elseif v:startsWith("thermal:tinker_bench") then
582 Peripherals.workbench = peripheral.wrap(v)
583 Peripherals.workbench.name = v
584 elseif v:startsWith("create:basin") then
585 Peripherals.basin = Basin:new(v)
586 elseif v:startsWith("monitor") then
587 Monitors.list:insert(peripheral.wrap(v))
588 elseif v:startsWith("create:spout") then
589 Peripherals.spout = peripheral.wrap(v)
590 Peripherals.spout.name = v
591 elseif v:startsWith("create:depot") then
592 Peripherals.depot = peripheral.wrap(v)
593 Peripherals.depot.name = v
594 else
595 local sideNames = List:new({"top","front","left","right","back","bottom"})
596 if not sideNames:contains(v) then
597 local p = peripheral.wrap(v)
598 if p.pullItems ~= nil then
599 p.name = v
600 Inventories.list:insert(p)
601 end
602 end
603 end
604 end
605 local messages = List:new()
606 if Peripherals.waterTank == nil then
607 messages:insert("No water tank found")
608 end
609 if #StorageTanks.list == 0 then
610 messages:insert("No storage tanks found")
611 end
612 if Peripherals.basin == nil then
613 messages:insert("No Basin found")
614 end
615 if #Inventories.list == 0 then
616 messages:insert("No item Inventories.list found")
617 end
618 if #messages > 0 then
619 Monitors.error("Could not find necessary peripherals")
620 clearTerm()
621 printError("Could not find necessary peripherals")
622 for _,msg in ipairs(messages) do
623 printError("- "..msg)
624 end
625 printLine()
626 print("Press X to quit. Press any other key to retry.")
627 local _, key = waitForKey()
628 if key == keys.x then
629 return false
630 end
631 else
632 return true
633 end
634 end
635 end
636}
637Basin={
638 new=function(self,peripheralName)
639 local basin = peripheral.wrap(peripheralName)
640 basin.name = peripheralName
641 setmetatable(basin,self)
642 self.__index = self
643 return basin
644 end,
645 hasFluid=function(self)
646 if #self.tanks() > 0 then
647 return true
648 end
649 if self.tanks()[3] ~= nil then
650 return true
651 end
652 return false
653 end,
654 hasItem=function(self)
655 return self.getItemDetail(1) ~= nil
656 end,
657 insertItem=function(self,itemName)
658 if self.hasItem() then
659 if not Issues.itemStuckInBasin() then
660 return false
661 end
662 end
663 if not Inventories.take(itemName,self,1) then
664 return false
665 end
666 return true
667 end
668}
669
670Monitors = {
671 lineNum=1,
672 list=List:new(),
673 setLineNum=function(lineNum)
674 if #Monitors.list > 0 then
675 for _,v in ipairs(Monitors.list) do
676 v.setCursorPos(1,lineNum)
677 end
678 Monitors.lineNum=lineNum
679 end
680 end,
681 clear=function()
682 if #Monitors.list > 0 then
683 for _,v in ipairs(Monitors.list) do
684 v.clear()
685 v.setCursorPos(1,1)
686 end
687 Monitors.lineNum=1
688 end
689 end,
690 clearLine=function(line)
691 line = line or Monitors.lineNum
692 if #Monitors.list > 0 then
693 for _,v in ipairs(Monitors.list) do
694 v.setCursorPos(1,line)
695 v.clearLine()
696 end
697 Monitors.lineNum=line
698 end
699 end,
700 write=function(text,line)
701 if #Monitors.list > 0 then
702 for _,v in ipairs(Monitors.list) do
703 if line ~= nil then
704 v.setCursorPos(1,line)
705 else
706 v.setCursorPos(1,Monitors.lineNum)
707 end
708 v.clearLine()
709 v.write(text)
710 end
711 if line == nil then
712 Monitors.lineNum = Monitors.lineNum+1
713 end
714 end
715 end,
716 error=function(text)
717 if #Monitors.list > 0 then
718 for _,v in ipairs(Monitors.list) do
719 v.setTextColor(colors.red)
720 v.setCursorPos(1,Monitors.lineNum)
721 v.write(text)
722 v.setTextColor(colors.white)
723 end
724 Monitors.lineNum = Monitors.lineNum+1
725 end
726 end,
727 back=function()
728 if #Monitors.list > 0 then
729 if Monitors.lineNum > 1 then
730 Monitors.lineNum = Monitors.lineNum-1
731 end
732 for _,mon in pairs(Monitors.list) do
733 mon.setCursorPos(1,Monitors.setLineNum)
734 end
735 end
736 end
737}
738
739function clearTerm()
740 term.clear()
741 term.setCursorPos(1,1)
742end
743function printError(text)
744 term.setTextColor(colors.red)
745 print(text)
746 term.setTextColor(colors.white)
747end
748function printInstruction(text)
749 term.setTextColor(colors.lightBlue)
750 print(text)
751 term.setTextColor(colors.white)
752end
753function printLine()
754 print()
755end
756function waitForKey()
757 return os.pullEvent("key")
758end
759
760Crafting = {
761 craft=function(potion,qty)
762 local craftPlan = nil
763 while craftPlan == nil do
764 local result, missing, recipes = Crafting.canCraftPotion(potion,qty)
765 if not result then
766 if not Issues.multipleItemsMissing(missing) then
767 return false
768 end
769 else
770 craftPlan = recipes
771 end
772 end
773
774 clearTerm()
775
776 local remainingQty = qty
777 local fullPotionName = potion:getDisplayName()
778
779 for _,recipePlan in ipairs(craftPlan) do
780 local recipeCount = recipePlan[1]
781 local recipe = recipePlan[2]
782 for i=1, recipeCount do
783 Monitors.write("Crafting "..(qty-remainingQty+1).." of "..qty)
784 Monitors.write(fullPotionName)
785 Crafting.refillWater()
786 Crafting.mix(ItemNames.netherwart)
787 for _,itemName in ipairs(recipe.items) do
788 Crafting.mix(itemName)
789 end
790 local modItem = PotionModifiers.getItem(potion.modifier)
791 if modItem ~= nil then
792 Crafting.mix(modItem)
793 end
794 local styleItem = PotionStyles.getItem(potion.style)
795 if styleItem ~= nil then
796 Crafting.mix(styleItem)
797 end
798 while true do
799 if StorageTanks.storePotion(potion) then
800 break
801 else
802 Issues.cannotStorePotion()
803 end
804 end
805 remainingQty = remainingQty - 1
806 end
807 end
808 Monitors.clear()
809 clearTerm()
810 return true
811 end,
812 canCraftPotion=function(potion,qty)
813 local potionDef = PotionTypes.getDefinition(potion.type)
814
815 local missingItems = List:new()
816
817 print("Crafting.canCraftPotion")
818 print(qty)
819 Inventories.checkMissingItem(ItemNames.netherwart,qty,missingItems)
820 local modItem = PotionModifiers.getItem(potion.modifier)
821 if modItem ~= nil then
822 Inventories.checkMissingItem(modItem,qty,missingItems)
823 end
824 local styleItem = PotionStyles.getItem(potion.style)
825 if styleItem ~= nil then
826 Inventories.checkMissingItem(styleItem,qty,missingItems)
827 end
828
829 local canCraft, craftPlan = potionDef.recipes:getCraftPlan(qty)
830 if not canCraft then
831 potionDef.recipes[1]:checkMissingItems(qty,missingItems)
832 end
833
834 if #missingItems > 0 then
835 return false, missingItems, nil
836 end
837 return true, nil, craftPlan
838 end,
839 refillWater=function()
840 local basin = Peripherals.basin
841 if basin:hasFluid() then
842 Issues.fluidStuckInBasin()
843 end
844 Basin.pullFluid(Peripherals.waterTank.name)
845 end,
846 mix=function(itemName)
847 if Peripherals.basin:hasItem() ~= nil then
848 Issues.itemStuckInBasin()
849 end
850 if Peripherals.basin:insertItem(itemName) then
851 while true do
852 sleep(1)
853 if not Peripherals.basin:hasItem() then
854 break
855 end
856 end
857 else
858 return false
859 end
860 return true
861 end
862}
863
864Issues = {
865 itemStuckInBasin=function()
866 Monitors.error("Item stuck in Basin!")
867 while true do
868 sleep(10)
869 if not Peripherals.basin:hasItem() then
870 Monitors.setLineNum(Monitors.lineNum - 1)
871 Monitors.clearLine()
872 return
873 end
874 end
875 end,
876 fluidStuckInBasin=function()
877 Monitors.error("Fluid stuck in Basin!")
878 while true do
879 sleep(10)
880 if not Peripherals.basin:hasFluid() then
881 Monitors.setLineNum(Monitors.lineNum - 1)
882 Monitors.clearLine()
883 return
884 end
885 end
886 end,
887 itemMissingInInventory=function(itemName,qty)
888 Monitors.error("Item could not be found in inventory! "..itemName)
889 while true do
890 clearTerm()
891 printError("Item could not be found in inventory! "..itemName)
892 printError("Needed quantity: "..qty)
893 printInstruction("Press X to cancel")
894 printInstruction("Press any other key after adding item")
895 local _,key = waitForKey()
896 if key == keys.x then
897 clearTerm()
898 Monitors.clear()
899 return false
900 end
901 Inventories.indexedItems()
902 if Inventories.getItemCount(itemName) > 0 then
903 clearTerm()
904 Monitors.back()
905 Monitors.clearLine()
906 return true
907 end
908 end
909 end,
910 multipleItemsMissing=function(missingItems)
911 Monitors.error("Insufficient items in inventory!")
912 clearTerm()
913 printError("The following items could not be found:")
914 for _,itemData in ipairs(missingItems) do
915 print(" "..itemData[2].."x "..itemData[1])
916 end
917 printInstruction("Press x to cancel")
918 printInstruction("Press any other key after adding items")
919 local _,key = waitForKey()
920 if key == keys.x then
921 clearTerm()
922 Monitors.clear()
923 return false
924 end
925 Monitors.back()
926 Monitors.clearLine()
927 clearTerm()
928 return true
929 end,
930 cannotStorePotion=function()
931 Monitors.error("Potion in Basin could not be stored!")
932 waitForKey()
933 end
934}
935
936CommandLine = {
937 read=function()
938 while true do
939 Monitors.clear()
940 Monitors.write("Waiting for input...")
941 clearTerm()
942 printInstruction("Please enter command (use help for more info)")
943 local com = tostring(read()):split(",")
944 if #com > 0 then
945 if com[1] == "craft" then
946 CommandLine.craft(com[2],com[3],com[4],com[5])
947 elseif com[1] == "bottle" then
948
949 elseif com[1] == "help" then
950
951 elseif com[1] == "index" then
952 Inventories.indexInventories()
953 elseif com[1] == "exit" then
954 return
955 end
956 end
957 end
958 end,
959 craft=function(potionName,modifier,style,qty)
960 local potionType = CommandLine.validatePotionType(potionName)
961 if potionType == nil then return end
962 modifier = CommandLine.validateModifier(modifier)
963 if modifier == nil then return end
964 style = CommandLine.validateStyle(style)
965 if style == nil then return end
966 qty = CommandLine.validateQty(qty,"buckets")
967 if qty == nil then return end
968 Crafting.craft(Potion:new(potionType,modifier,style),qty)
969 end,
970 validatePotionType=function(potionName)
971 local potionType = nil
972 if potionName ~= nil then
973 potionType = PotionTypes.getInternalName(potionName)
974 if potionType == nil then
975 printError("Invalid potion name provided")
976 printInstruction("Press any key to continue")
977 waitForKey()
978 end
979 else
980 potionType = CommandLine.promptPotionType()
981 end
982 return potionType
983 end,
984 promptPotionType=function()
985 local names = List:new()
986 for internalName,_ in pairs(PotionTypes.definitions) do
987 names:insert(internalName)
988 end
989 while true do
990 printInstruction("Which potion type do you want?")
991 for i,internalName in ipairs(names) do
992 print(" "..i..") "..PotionTypes.definitions[internalName].names[1])
993 end
994 printError(" "..(#names+1)..") Cancel")
995
996 local i = tonumber(read())
997 if i < 1 or i > #names+1 then
998 printError("Invalid value provided")
999 elseif i == #names+1 then
1000 return nil
1001 else
1002 return names[i]
1003 end
1004 end
1005 end,
1006 validateModifier=function(modifier)
1007 if modifier ~= nil then
1008 if tonumber(modifier) ~= nil then
1009 modifier = tonumber(modifier)
1010 if modifier > 0 and modifier <= #PotionModifiers.getList() then
1011 modifier = PotionModifiers.getList()[modifier]
1012 else
1013 modifier = nil
1014 end
1015 else
1016 if not PotionModifiers.isValid(modifier) then
1017 modifier = nil
1018 end
1019 end
1020 if modifier == nil then
1021 printError("Invalid value provided for potion modifier")
1022 printInstruction("Press any key to continue")
1023 waitForKey()
1024 return nil
1025 end
1026 return modifier
1027 end
1028 return CommandLine.promptModifier()
1029 end,
1030 promptModifier=function()
1031 local mods = PotionModifiers.getList()
1032 while true do
1033 printInstruction("Which modifier do you want?")
1034 for m=1, #mods do
1035 printInstruction(" "..m..") "..mods[m])
1036 end
1037 printInstruction(" "..(#mods+1)..") Cancel")
1038 local i = tonumber(read())
1039 if i == nil or i < 1 or i > #mods+1 then
1040 printError("Invalid value provided")
1041 elseif i == #mods+1 then
1042 return nil
1043 else
1044 return mods[i]
1045 end
1046 end
1047 end,
1048 validateStyle=function(style)
1049 if style ~= nil then
1050 if tonumber(style) ~= nil then
1051 style = tonumber(style)
1052 if style > 0 and style <= #PotionStyles.getList() then
1053 style = PotionStyles.getList()[style]
1054 else
1055 style = nil
1056 end
1057 else
1058 if not PotionStyles.isValid(style) then
1059 style = nil
1060 end
1061 end
1062 if style == nil then
1063 printError("Invalid value provided for potion style")
1064 printInstruction("Press any key to continue")
1065 waitForKey()
1066 return nil
1067 end
1068 return style
1069 end
1070 return CommandLine.promptStyle()
1071 end,
1072 promptStyle=function()
1073 local mods = PotionStyles.getList()
1074 while true do
1075 printInstruction("Which style do you want?")
1076 for m=1, #mods do
1077 printInstruction(" "..m..") "..mods[m])
1078 end
1079 printInstruction(" "..(#mods+1)..") Cancel")
1080 local i = tonumber(read())
1081 if i == nil or i < 1 or i > #mods+1 then
1082 printError("Invalid value provided")
1083 elseif i == #mods+1 then
1084 return nil
1085 else
1086 return mods[i]
1087 end
1088 end
1089 end,
1090 validateQty=function(qty,type)
1091 if qty ~= nil then
1092 qty = tonumber(qty)
1093 if qty == nil then
1094 printError("Invalid value provided for number of "..type)
1095 printInstruction("Press any key to continue")
1096 waitForKey()
1097 end
1098 return qty
1099 end
1100 return CommandLine.promptQty
1101 end,
1102 promptQty=function(qtyType)
1103 while true do
1104 printInstruction("How many "..qtyType.." do you want? (Enter \"cancel\" to quit)")
1105 local i = read()
1106 if i == "cancel" then
1107 return nil
1108 else
1109 i = tonumber(i)
1110 if i == nil or i <= 0 then
1111 printError("Invalid value provided")
1112 else
1113 return i
1114 end
1115 end
1116 end
1117 end
1118}
1119
1120function run()
1121 if not Peripherals.wrapPeripherals() then
1122 return
1123 end
1124
1125 Monitors.clear()
1126 clearTerm()
1127
1128 if not os.loadAPI("apis/json") then
1129 Monitors.error("Could not load json api")
1130 printError("Could not load json api")
1131 return
1132 end
1133
1134 StorageTanks.initializeTankContents()
1135 if not PotionTypes.initialize() then
1136 return
1137 end
1138
1139 Inventories.indexInventories()
1140
1141 CommandLine.read()
1142end
1143
1144run()