···11+local content = schematicContainer.getContent()
22+for _,s in pairs(content) do
33+ schematicStorage["s" .. s.id] = s.quantity
44+end
+6
Master Program/source/system/onActionStart.lua
···11+if hud.actions.start[action] ~= nil then
22+ local success, message = pcall(hud.actions.start[action])
33+ if not success then
44+ system.print("Action Start '" .. action .. "' Error : " .. message)
55+ end
66+end
+6
Master Program/source/system/onActionStop.lua
···11+if hud.actions.stop[action] ~= nil then
22+ local success, message = pcall(hud.actions.stop[action])
33+ if not success then
44+ system.print("Action Stop '" .. action .. "' Error : " .. message)
55+ end
66+end
+13
Master Program/source/system/onInputText.lua
···11+if tonumber(text) then
22+ if #text > 8 then
33+ system.print("the value should not be higher than 99 999 999.")
44+ else
55+ craft_quantity_digits = {"0","0","0","0","0","0","0","0"}
66+ for i = #text, 1, -1 do
77+ local c = text:sub(i,i)
88+ craft_quantity_digits[9-(#text-(i-1))] = c
99+ end
1010+ end
1111+else
1212+ system.print("You must type a Number")
1313+end
+12
Master Program/source/system/onUpdate.lua
···11+--[[
22+ DU-Nested-Coroutines by Jericho
33+ Permit to easier avoid CPU Load Errors
44+ Source available here: https://github.com/Jericho1060/du-nested-coroutines
55+]]--
66+77+if coroutine.status(MainCoroutine) == "dead" then
88+ MainCoroutine = coroutine.create(runCoroutines)
99+end
1010+if coroutine.status(MainCoroutine) == "suspended" then
1111+ assert(coroutine.resume(MainCoroutine))
1212+end
+1022
Master Program/source/unit/onStart.lua
···11+version = "v4.2.1"
22+--[[
33+ DU Industry HUD By Jericho
44+]]
55+local log_split = "================================================="
66+--printing version in lua chat
77+system.print(log_split)local a=""local b=math.ceil((50-#version-2)/2)for c=1,b,1 do a=a..'='end;a=a.." "..version.." "for c=1,b,1 do a=a..'='end;system.print(a)system.print(log_split)
88+--[[
99+ Lua parameters
1010+]]
1111+fontSize = 12 --export: the fonct size
1212+showRecipeData = true --export: display the recipe ingredients in the machine detail
1313+1414+enableRefinerMonitoring = true --export: enable or disable the Refiners monitoring
1515+enableAssemblyMonitoring = true --export: enable or disable the Assembly lines monitoring
1616+enableSmelterMonitoring = true --export: enable or disable the Smelters monitoring
1717+enableChemicalMonitoring = true --export: enable or disable the Chemical industries monitoring
1818+enableElectronicsMonitoring = true --export: enable or disable the Electronics Industries monitoring
1919+enableGlassMonitoring = true --export: enable or disable the Glass Furnace monitoring
2020+enableHoneycombMonitoring = true --export: enable or disable the Honeycomb Refineries monitoring
2121+enableRecyclerMonitoring = true --export: enable or disable the Recyclers monitoring
2222+enableMetalworkMonitoring = true --export: enable or disable the Metalworks monitoring
2323+enable3DPrinterMonitoring = true --export: enable or disable the 3D Printers monitoring
2424+enableTransferMonitoring = true --export: enable or disable the transfer units monitoring
2525+enableRemoteControl = true --export: enable the HUD to control machines (start/stop/batch/maintain)
2626+elementsByPage = 20 --export: maximum amount of elements displayed on a single page
2727+dateFormat = "en" --export: the country code to format the date
2828+maxAmountOfElementsLoadedByFrame = 500 --export: if cpu load errors at start, lower that value
2929+machinesRefreshedByFrame = 100 --how many machines are refreshed by frame
3030+machineDetailDisplayDistance = 0 --export: the distance in meters to display the machine details
3131+displayType = 0 --export: the default display type: 0=ALL, 1=Table, 2=Augmented Reality
3232+displayMode = 0 --export: the default display type: 0=ALL, 1=Industry Only, 2=Storage Only, 3=None
3333+sortingType = 1 --export: the default sorting of elements in table: 1=Element Name, 2=Item in containers or product of industries, 3=Element ID
3434+3535+3636+--[[
3737+ receivers channels for each type of machine
3838+]]
3939+channel_for_refiner = "receiver_refiner" --export: receiver channel to send orders to refiners
4040+channel_for_assembly = "receiver_assembly" --export: receiver channel to send orders to assembly lines
4141+channel_for_smelter = "receiver_smelters" --export: receiver channel to send orders to smelters
4242+channel_for_chemical = "receiver_chemical" --export: receiver channel to send orders to chemical indutries
4343+channel_for_electronics = "receiver_electronics" --export: receiver channel to send orders to electronic industries
4444+channel_for_glass = "receiver_glass" --export: receiver channel to send orders to glass furnace
4545+channel_for_honeycomb = "receiver_honeycomb_recycler" --export: receiver channel to send orders to honeycomb refiniries
4646+channel_for_recycler = "receiver_honeycomb_recycler" --export: receiver channel to send orders to recylers
4747+channel_for_metalwork = "receiver_metalworks" --export: receiver channel to send orders to metalworks
4848+channel_for_3d_printer = "receiver_3dprinters" --export: receiver channel to send orders to 3d printers
4949+channel_for_transfer = "receiver_transfert" --export: receiver channel to send orders to tranfer units
5050+5151+--Storage options
5252+containerProficiencyLvl = 5 --export: Talent level for Container Proficiency
5353+containerOptimizationLvl = 0 --export: Talent level for Container Optimization
5454+storages_prefix_list = 's1,s2,s3,s4,s5,s6,s7,s8,s9' --export: the list of the prefixes used for the storage monitoring, comma separated values
5555+5656+--split a string on a delimiter By jericho
5757+function strSplit(a,b)result={}for c in(a..b):gmatch("(.-)"..b)do table.insert(result,c)end;return result end
5858+--formatting numbers by adding a space between thousands by Jericho
5959+function format_number(a)local b=a;while true do b,k=string.gsub(b,"^(-?%d+)(%d%d%d)",'%1 %2')if k==0 then break end end;return b end
6060+--Utility functions By jericho, see full source at https://github.com/Jericho1060/DualUniverse
6161+function removeDuplicatesInTable(a)local b={}local c={}for d,e in ipairs(a)do if not b[e]then c[#c+1]=e;b[e]=true end end;return c end;function strSplit(a,b)result={}for c in(a..b):gmatch("(.-)"..b)do table.insert(result,c)end;return result end;function SecondsToClockString(a)local a=tonumber(a)if a==nil or a<=0 then return"-"else days=string.format("%2.f",math.floor(a/(3600*24)))hours=string.format("%2.f",math.floor(a/3600-days*24))mins=string.format("%2.f",math.floor(a/60-hours*60-days*24*60))secs=string.format("%2.f",math.floor(a-hours*3600-days*24*60*60-mins*60))str=""if tonumber(days)>0 then str=str..days.."d "end;if tonumber(hours)>0 then str=str..hours.."h "end;if tonumber(mins)>0 then str=str..mins.."m "end;if tonumber(secs)>0 then str=str..secs.."s"end;return str end end;function removeQualityInName(a)if not a then return''end;return a:lower():gsub('basic ',''):gsub('uncommon ',''):gsub('advanced ',''):gsub('rare ',''):gsub('exotic ','')end;function has_value(a,b)for c,d in ipairs(a)do if d==b then return true end end;return false end
6262+--return RGB colors calculated from a gradient between two colors
6363+function getRGBGradient(a,b,c,d,e,f,g,h,i,j)a=-1*math.cos(a*math.pi)/2+0.5;local k=0;local l=0;local m=0;if a>=.5 then a=(a-0.5)*2;k=e-a*(e-h)l=f-a*(f-i)m=g-a*(g-j)else a=a*2;k=b-a*(b-e)l=c-a*(c-f)m=d-a*(d-g)end;return k,l,m end
6464+--time script to get client date and time by Jericho, see full source at https://github.com/Jericho1060/DualUniverse
6565+function DUCurrentDateTime(a)local b=system.getUtcTime()if not a then b=b+system.getUtcOffset()end;local c=24*60*60;local d=365*c;local e=d+c;local f=4*d+c;local g=4;local h=1970;local i={-1,30,58,89,119,150,180,211,242,272,303,333,364}local j={}for k=1,2 do j[k]=i[k]end;for k=3,13 do j[k]=i[k]+1 end;local l,m,n,o,p,q,r,s;local t=i;s=b;l=math.floor(s/f)s=s-l*f;l=l*4+h;if s>=d then l=l+1;s=s-d;if s>=d then l=l+1;s=s-d;if s>=e then l=l+1;s=s-e else t=j end end end;m=math.floor(s/c)s=s-m*c;local n=1;while t[n]<m do n=n+1 end;n=n-1;local o=m-t[n]p=(math.floor(b/c)+g)%7;if p==0 then p=7 end;q=math.floor(s/3600)s=s-q*3600;r=math.floor(s/60)function round(u,v)if v then return utils.round(u/v)*v end;return u>=0 and math.floor(u+0.5)or math.ceil(u-0.5)end;s=round(s-r*60)local w={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}local x={"Mon","Tue","Wed","Thu","Fri","Sat","Sun"}local y={"January","February","March","April","May","June","July","August","September","October","November","December"}local z={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}return l,n,o,q,r,s,p,w[p],x[p],y[n],z[n],m+1 end
6666+6767+--databank hub library By Jericho, see full source at https://github.com/Jericho1060/DualUniverse
6868+bankhub={}function bankhub:new(banks)o={}setmetatable(o,self)self.__index=self;o.banks=banks or{}function o.clear()return o:_clear()end;function o.getNbKeys()return o:_getNbKeys()end;function o.getKeys()return o:_getKeys()end;function o.hasKey(a)return o:_hasKey(a)end;function o.getStringValue(a)return o:_getStringValue(a)end;function o.getIntValue(a)return o:_getIntValue(a)end;function o.getFloatValue(a)return o:_getFloatValue(a)end;return o end;function bankhub:add(b)table.insert(self.banks,b)self.banks_size=#self.banks end;function bankhub:_clear()for c,d in pairs(self.banks)do d.clear()end end;function bankhub:_getNbKeys()local e=0;for c,d in pairs(self.banks)do e=e+d.getNbKeys()end;return e end;function bankhub:_getKeys()local e={}for c,d in pairs(self.banks)do local f=json.decode(d.getKeys())for c,g in pairs(f)do table.insert(e,g)end end;return json.encode(e)end;function bankhub:_hasKey(a)for c,d in pairs(self.banks)do if d.hasKey(a)==1 then return 1 end end;return 0 end;function bankhub:_getStringValue(a)for c,d in pairs(self.banks)do if d.hasKey(a)==1 then return d.getStringValue(a)end end;return nil end;function bankhub:_getIntValue(a)for c,d in pairs(self.banks)do if d.hasKey(a)==1 then return banks.getIntValue(a)end end;return nil end;function bankhub:_getFloatValue(a)for c,d in pairs(self.banks)do if d.hasKey(a)==1 then return banks.getFloatValue(a)end end;return nil end
6969+--time script to get client date and time by Jericho, see full source at https://github.com/Jericho1060/DualUniverse
7070+function DUCurrentDateTime(a)local b=system.getUtcTime()if not a then b=b+system.getUtcOffset()end;local c=24*60*60;local d=365*c;local e=d+c;local f=4*d+c;local g=4;local h=1970;local i={-1,30,58,89,119,150,180,211,242,272,303,333,364}local j={}for k=1,2 do j[k]=i[k]end;for k=3,13 do j[k]=i[k]+1 end;local l,m,n,o,p,q,r,s;local t=i;s=b;l=math.floor(s/f)s=s-l*f;l=l*4+h;if s>=d then l=l+1;s=s-d;if s>=d then l=l+1;s=s-d;if s>=e then l=l+1;s=s-e else t=j end end end;m=math.floor(s/c)s=s-m*c;local n=1;while t[n]<m do n=n+1 end;n=n-1;local o=m-t[n]p=(math.floor(b/c)+g)%7;if p==0 then p=7 end;q=math.floor(s/3600)s=s-q*3600;r=math.floor(s/60)function round(u,v)if v then return utils.round(u/v)*v end;return u>=0 and math.floor(u+0.5)or math.ceil(u-0.5)end;s=round(s-r*60)local w={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}local x={"Mon","Tue","Wed","Thu","Fri","Sat","Sun"}local y={"January","February","March","April","May","June","July","August","September","October","November","December"}local z={"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"}return l,n,o,q,r,s,p,w[p],x[p],y[n],z[n],m+1 end
7171+7272+statusList = {"STOPPED","RUNNING","MISSING INGREDIENT","OUTPUT FULL","NO OUTPUT CONTAINER","PENDING","MISSING SCHEMATIC","SERVER ERROR", "STOP REQUESTED"}
7373+function getIndustryStatusClass(status)
7474+ if status == 1 then
7575+ return "text-info"
7676+ elseif status == 2 then
7777+ return "text-success"
7878+ elseif ((status >= 3) and (status <= 5)) or (status == 7) then
7979+ return "text-danger"
8080+ elseif status == 6 then
8181+ return "text-primary"
8282+ elseif status >= 8 and status <= 9 then
8383+ return "text-warning"
8484+ end
8585+ return "" --default value for other status that can be added
8686+end
8787+function getIndustryStatusBgClass(status)
8888+ if status then
8989+ if status == 1 then
9090+ return "bg-info"
9191+ elseif status == 2 then
9292+ return "bg-success"
9393+ elseif ((status >= 3) and (status <= 5)) or (status == 7) then
9494+ return "bg-danger"
9595+ elseif status == 6 then
9696+ return "bg-primary"
9797+ elseif status >= 8 and status <= 9 then
9898+ return "bg-warning"
9999+ end
100100+ end
101101+ return "" --default value for other status that can be added
102102+end
103103+function getIndustryStatusBorderClass(status)
104104+ if status then
105105+ if status == 1 then
106106+ return "border-info"
107107+ elseif status == 2 then
108108+ return "border-success"
109109+ elseif ((status >= 3) and (status <= 5)) or (status == 7) then
110110+ return "border-danger"
111111+ elseif status == 6 then
112112+ return "border-primary"
113113+ elseif status >= 8 and status <= 9 then
114114+ return "border-warning"
115115+ end
116116+ end
117117+ return "" --default value for other status that can be added
118118+end
119119+function ConvertLocalToWorld(a,b,c,d,e)local f={a[1]*c[1],a[1]*c[2],a[1]*c[3]}local g={a[2]*d[1],a[2]*d[2],a[2]*d[3]}local h={a[3]*e[1],a[3]*e[2],a[3]*e[3]}return{f[1]+g[1]+h[1]+b[1],f[2]+g[2]+h[2]+b[2],f[3]+g[3]+h[3]+b[3]}end
120120+121121+122122+channels = {}
123123+channels['refiner'] = channel_for_refiner
124124+channels['assembly line'] = channel_for_assembly
125125+channels['smelter'] = channel_for_smelter
126126+channels['chemical industry'] = channel_for_chemical
127127+channels['electronics industry'] = channel_for_electronics
128128+channels['glass furnace'] = channel_for_glass
129129+channels['honeycomb refinery'] = channel_for_honeycomb
130130+channels['recycler'] = channel_for_recycler
131131+channels['metalwork industry'] = channel_for_metalwork
132132+channels['3d printer'] = channel_for_3d_printer
133133+channels['transfer unit'] = channel_for_transfer
134134+135135+bootstrap_css_colors = [[<style>.text-white {color: #fff !important;}.text-primary {color: #007bff !important;}.text-secondary {color: #6c757d !important;}.text-success {color: #28a745 !important;}.text-info {color: #17a2b8 !important;}.text-warning {color: #ffc107 !important;}.text-danger {color: #dc3545 !important;}.text-light {color: #f8f9fa !important;}.text-dark {color: #343a40 !important;}.text-body {color: #212529 !important;}.text-muted {color: #6c757d !important;}.text-black-50 {color: rgba(0, 0, 0, 0.5) !important;}.text-white-50 {color: rgba(255, 255, 255, 0.5) !important;}.bg-primary {background-color: #007bff !important;}.bg-secondary {background-color: #6c757d !important;}.bg-success {background-color: #28a745 !important;}.bg-info {background-color: #17a2b8 !important;}.bg-warning {background-color: #ffc107 !important;}.bg-danger {background-color: #dc3545 !important;}.bg-light {background-color: #f8f9fa !important;}.bg-dark {background-color: #343a40 !important;}.bg-white {background-color: #fff !important;}.bg-transparent {background-color: transparent !important;}.border-primary {border-color: #007bff !important;}.border-secondary {border-color: #6c757d !important;}.border-success {border-color: #28a745 !important;}.border-info {border-color: #17a2b8 !important;}.border-warning {border-color: #ffc107 !important;}.border-danger {border-color: #dc3545 !important;}.border-light {border-color: #f8f9fa !important;}.border-dark {border-color: #343a40 !important;}.border-white {border-color: #fff !important;}
136136+</style>]]
137137+base_css = [[<style>.ib{position:absolute;opacity:.75;transform: translate(-50%, -50%);}.tier0{font-weight:bold;padding:5px;margin-right:5px;color:black;background-color:#fff;}.tier1{font-weight:bold;padding:5px;margin-right:5px;color:black;background-color:#fff;}.tier2{font-weight:bold;padding:5px;margin-right:5px;color:white;background-color:green;}.tier3{font-weight:bold;padding:5px;margin-right:5px;color:white;background-color:blue;}.tier4{font-weight:bold;padding:5px;margin-right:5px;color:white;background-color:purple;}.tier5{font-weight:bold;padding:5px;margin-right:5px;color:white;background-color:orange;}hr{border-top:1px solid #343a40;border-collapse:collapse;}.hr-light{border-top:1px solid #f8f9fa;}.hud_container{z-index:1000;font-size:]] .. fontSize .. [[px;border: 1px solid black;border-radius:5px;background-color: rgba(15,24,29,.85);padding:10px;}.hud_time{position: absolute;bottom: ]] .. tostring((100/1080)*100) .. [[vh;right: ]] .. tostring((10/1920)*100) .. [[vw;text-transform: uppercase;font-weight: bold;}.hud_machine_types_container{position: absolute;top: ]] .. tostring((80/1080)*100) .. [[vh;left: ]] .. tostring((10/1920)*100) .. [[vw;text-transform: uppercase;font-weight: bold;}.hud_machine_status_container{position: absolute;top: ]] .. tostring((10/1080)*100) .. [[vh;left: ]] .. tostring((10/1920)*100) .. [[vw;text-transform: uppercase;font-weight: bold;}.hud_table_container{position: absolute;top:]] .. tostring((80/1080)*100) .. [[vh;left:]] .. tostring((225/1920)*100) .. [[vw;}.hud_table_container td,.hud_table_container th, .hud_machine_control th{border-bottom:1px solid #f8f9fa;}small{font-size:.75em;}.hud_table_container td.row_selected,.hud_table_container th.row_selected{border:2px solid green;}.hud_machine_control{position: absolute;top: ]] .. tostring((300/1080)*100) .. [[vh;right: ]] .. tostring((10/1920)*100) .. [[vw;text-transform: uppercase;font-weight: bold;}</style>]]
138138+139139+140140+databanks = {}
141141+core = nil
142142+emitter = nil
143143+industries_id = {}
144144+storages_id = {}
145145+industries = {}
146146+storages = {}
147147+elementsTypes = {}
148148+storagesTypes = {}
149149+schematicContainer = nil
150150+schematicContainerId = construct.getSchematicContainerId()
151151+schematicStorage = {}
152152+for slot_name, slot in pairs(unit) do
153153+ if
154154+ type(slot) == "table"
155155+ and type(slot.export) == "table"
156156+ and slot.getClass
157157+ then
158158+ if slot.getClass():lower() == 'databankunit' then
159159+ table.insert(databanks, slot)
160160+ elseif slot.getClass():lower():find("coreunit") then
161161+ core = slot
162162+ elseif slot.getClass():lower() == 'emitterunit' then
163163+ emitter = slot
164164+ elseif schematicContainerId ~= nil and slot.getClass():lower():find('container') then
165165+ if slot.getId() == schematicContainerId then
166166+ schematicContainer = slot
167167+ end
168168+ end
169169+ end
170170+end
171171+172172+if core == nil then
173173+ system.print("Connection to the core is missing")
174174+ unit.exit()
175175+end
176176+if schematicContainerId == nil then
177177+ system.print("No Schematic Container set on the Construct")
178178+end
179179+if schematicContainer == nil then
180180+ system.print("Schematic Container is not linked to the board")
181181+end
182182+183183+if emitter == nil then
184184+ enableRemoteControl = false
185185+ system.print("Connect an Emitter to enable machine control from industry")
186186+end
187187+if #databanks == 0 then
188188+ enableRemoteControl = false
189189+ system.print("No Databank linked")
190190+end
191191+192192+--init of bank hub
193193+Storage = bankhub:new(databanks)
194194+195195+--construct pos for AR data
196196+constructPos = construct.getWorldPosition()
197197+constructRight = construct.getWorldRight()
198198+constructForward = construct.getWorldForward()
199199+constructUp = construct.getWorldUp()
200200+201201+--script variables
202202+storage_prefix = strSplit(storages_prefix_list, ',')
203203+displayTypes = {"ALL", "Table", "Augmented Reality"}
204204+displayModes = {"ALL", "Industry", "Storage", "None"}
205205+sortingTypes = {"Element Name", "Item", "Element ID"}
206206+init = false
207207+initIndex = 1
208208+elementsIdList = {}
209209+machines_count = {}
210210+storages_count = {}
211211+machines_positions = {}
212212+storages_positions = {}
213213+system.showScreen(true)
214214+statusFilter = 0
215215+statusFilterType = 0
216216+maxFilterType = 0
217217+maxOnPage=1
218218+html_filters = ""
219219+html_filters_types = ""
220220+html_filter_storage = ""
221221+html_machines = ""
222222+html_storage = ""
223223+html_time = ""
224224+html_ar_selected = ""
225225+html_control = ""
226226+html_credits = [[<div class="hud_container" style="position:absolute;left:5px;bottom:5px;font-size:10px;padding:5px;">]] .. version .. [[ - Powered By jericho1060</div>]]
227227+228228+--pagination var
229229+page = 1
230230+maxPage = 1
231231+selectedRow = 1
232232+selected_machine=nil
233233+selected_container=nil
234234+235235+--control machine var
236236+command_1 = ""
237237+command_2 = ""
238238+command_3 = ""
239239+craft_quantity_digits = {"0","0","0","0","0","0","0","0"}
240240+241241+--keyPressed
242242+ctrlPressed = false
243243+244244+245245+--[[
246246+ DU INDUSTRY HUD By Jericho
247247+]]
248248+schematicRefreshTime = 0
249249+hud = {}
250250+hud.coroutines = {}
251251+if schematicContainer ~= nil then
252252+ hud.coroutines.getSchematics = function ()
253253+ schematicRefreshTime = math.ceil(schematicContainer.updateContent())
254254+ end
255255+end
256256+hud.coroutines.init = function()
257257+ if not init then
258258+ elementsTypes = {}
259259+ industries_id = {}
260260+ industries = {}
261261+262262+ storagesTypes = {}
263263+ storages_id = {}
264264+ storages = {}
265265+266266+ elementsIdList = core.getElementIdList()
267267+268268+ for index,id in ipairs(elementsIdList) do
269269+ elementType = core.getElementDisplayNameById(id):lower()
270270+ if (elementType:find("assembly line") and enableAssemblyMonitoring == true) or
271271+ (elementType:find("glass furnace") and enableGlassMonitoring == true) or
272272+ (elementType:find("3d printer") and enable3DPrinterMonitoring == true) or
273273+ (elementType:find("smelter") and enableSmelterMonitoring == true) or
274274+ (elementType:find("recycler") and enableRecyclerMonitoring == true) or
275275+ (elementType:find("refinery") and enableHoneycombMonitoring == true) or
276276+ (elementType:find("refiner") and enableRefinerMonitoring == true) or
277277+ (elementType:find("industry")
278278+ and (
279279+ (elementType:find("chemical") and enableChemicalMonitoring == true) or
280280+ (elementType:find("electronics") and enableElectronicsMonitoring == true) or
281281+ (elementType:find("metalwork") and enableMetalworkMonitoring == true)
282282+ )
283283+ ) or
284284+ (elementType == "transfer unit" and enableTransferMonitoring == true) or
285285+ elementType:lower():find("container")
286286+ then
287287+ local formatedType = removeQualityInName(elementType)
288288+ local pos = core.getElementPositionById(id)
289289+ if elementType:lower():find("container") then
290290+ if storages_count[formatedType] ~= nil then
291291+ storages_count[formatedType] = storages_count[formatedType] + 1
292292+ else
293293+ storages_count[formatedType] = 1
294294+ end
295295+ table.insert(storages_id, id)
296296+ storages_positions['i'..id] = pos
297297+ table.insert(storagesTypes, formatedType)
298298+ else
299299+ pos[3] = pos[3] + 1
300300+ if machines_count[formatedType] ~= nil then
301301+ machines_count[formatedType] = machines_count[formatedType] + 1
302302+ else
303303+ machines_count[formatedType] = 1
304304+ end
305305+ table.insert(industries_id, id)
306306+ machines_positions['i'..id] = pos
307307+ table.insert(elementsTypes, formatedType)
308308+ end
309309+ end
310310+311311+ if index % maxAmountOfElementsLoadedByFrame == 0 then
312312+ coroutine.yield(self.init)
313313+ end
314314+ end
315315+ elementsTypes = removeDuplicatesInTable(elementsTypes)
316316+ table.sort(elementsTypes, function(a,b) return a:lower() < b:lower() end)
317317+ selected_type = elementsTypes[1]
318318+ init = true
319319+ system.print("All elements loaded")
320320+ system.print(#storages_id .. " storage elements found")
321321+ system.print(#industries_id .. " industries found")
322322+ end
323323+end
324324+hud.coroutines.loadIndustries = function()
325325+ if init and (displayMode == 0 or displayMode == 1) then
326326+ for index,id in ipairs(industries_id) do
327327+ local pos = machines_positions['i'..id]
328328+ local data = core.getElementIndustryInfoById(id)
329329+ if industries["i"..tostring(id)] == nil then industries["i"..tostring(id)] = {} end
330330+ local mode = ""
331331+ local tableMode = ""
332332+ local maintainOrBatchQuantity = 0
333333+ if data.maintainProductAmount > 0 then
334334+ mode = "<div><strong>Mode:</strong> Maintain " .. math.floor(data.currentProductAmount) .."/" .. math.floor(data.maintainProductAmount) .. "</div>"
335335+ tableMode = "Maintain " .. math.floor(data.currentProductAmount) .."/" .. math.floor(data.maintainProductAmount)
336336+ maintainOrBatchQuantity=math.floor(data.maintainProductAmount)
337337+ elseif data.batchesRequested > 0 and data.batchesRequested <= 99999999 then
338338+ local current = data.batchesRequested - data.batchesRemaining
339339+ if data.batchesRemaining < 0 then current = 0 end
340340+ mode = "<div><strong>Mode:</strong> Produce " .. math.floor(current) .. "/" .. math.floor(data.batchesRequested) .. "</div>"
341341+ tableMode = "Produce " .. math.floor(current) .. "/" .. math.floor(data.batchesRequested)
342342+ maintainOrBatchQuantity=math.floor(data.batchesRequested)
343343+ end
344344+ local production = {locDisplayNameWithSize="No recipe selected", tier=0}
345345+ local schematicName = ""
346346+ local tableSchematicName = ""
347347+ local tableSchematicQuantity = ""
348348+ local schematicColor = "success"
349349+ local state = data.state
350350+ local time=""
351351+ local tableTime=""
352352+ local recipe=""
353353+ local industryType = core.getElementDisplayNameById(id)
354354+ local schematicId = data.requiredSchematicIds[1] or 0
355355+ if schematicId > 0 and not industryType:lower():find("transfer") then
356356+ local sch = system.getItem(schematicId)
357357+ local shematicQuantity = schematicStorage["s" .. schematicId] or 0
358358+ if shematicQuantity < data.requiredSchematicAmount then schematicColor = "danger" end
359359+ schematicName = '<div><strong>Schematic:</strong> ' .. format_number(shematicQuantity):gsub(".0", "") .. "/" .. format_number(data.requiredSchematicAmount) .. " " .. sch.locDisplayNameWithSize .. "</div>"
360360+ tableSchematicName = sch.locDisplayNameWithSize
361361+ tableSchematicQuantity = format_number(shematicQuantity):gsub(".0", "") .. "/" .. format_number(data.requiredSchematicAmount)
362362+ end
363363+ if #data.currentProducts > 0 then
364364+ local item_id = data.currentProducts[1].id
365365+ production = system.getItem(item_id)
366366+ if id ~= "947806142" and id ~= "1010524904" and not industryType:lower():find("transfer") then
367367+ if showRecipeData then
368368+ local rdata = system.getRecipes(item_id)
369369+ if #rdata > 0 then
370370+ local r = nil
371371+ for _,recipe in pairs(rdata) do
372372+ if recipe.products[1].id == item_id then
373373+ r = recipe
374374+ break
375375+ end
376376+ end
377377+ if r then
378378+ recipe = "<div><strong>Recipe:</strong>"
379379+ for _,v in pairs(r.ingredients) do
380380+ local item = system.getItem(v.id)
381381+ recipe = recipe .. "<br> - " .. v.quantity .. " x " .. item.locDisplayNameWithSize
382382+ end
383383+ recipe = recipe .. "</div>"
384384+ end
385385+ end
386386+ end
387387+ end
388388+ if data.remainingTime == 0 and data.state == 2 then
389389+ state = 8
390390+ end
391391+ if data.stopRequested and data.state ~= 1 then
392392+ state = 9
393393+ end
394394+ if data.remainingTime > 0 then
395395+ time = "<div><strong>Time:</strong> " .. SecondsToClockString(data.remainingTime) .. "</div>"
396396+ tableTime = SecondsToClockString(data.remainingTime)
397397+ end
398398+ end
399399+ local productionName = '<div><strong>Product</strong>: ' .. production.locDisplayNameWithSize .. '</div>'
400400+ local productId = ''
401401+ if production.id then
402402+ productId = "<div><strong>Main Product ID:</strong> " .. production.id .. "</div>"
403403+ end
404404+ local elementName = core.getElementNameById(id)
405405+ industries["i"..tostring(id)] = {
406406+ id=id,
407407+ screenPos=library.getPointOnScreen(ConvertLocalToWorld(pos, constructPos, constructRight, constructForward, constructUp)),
408408+ name="<div><strong>Industry Name:</strong> [" .. id .. "] " .. elementName .. "</div>",
409409+ tableName=elementName,
410410+ type="<div><strong>Industry Type:</strong> " .. industryType .. "</div>",
411411+ tableType=industryType,
412412+ typeFilter=removeQualityInName(industryType),
413413+ state=state,
414414+ production=production,
415415+ productId=productId,
416416+ productionName=productionName,
417417+ mode=mode,
418418+ tableMode=tableMode,
419419+ remainingTime=time,
420420+ tableTime=tableTime,
421421+ schematic=schematicName,
422422+ schematicColor=schematicColor,
423423+ maintainOrBatchQuantity=maintainOrBatchQuantity,
424424+ tableSchematic=tableSchematicName,
425425+ tableSchematicQuantity=tableSchematicQuantity,
426426+ recipe=recipe,
427427+ stopRequested=data.stopRequested
428428+ }
429429+ if index % machinesRefreshedByFrame == 0 then
430430+ coroutine.yield(self.loadIndustries)
431431+ end
432432+ end
433433+ end
434434+end
435435+hud.coroutines.loadStorage = function()
436436+ for index,id in ipairs(storages_id) do
437437+ local pos = storages_positions['i'..id]
438438+ local elementType = core.getElementDisplayNameById(id)
439439+ local base_name = core.getElementNameById(id)
440440+ local splitted = strSplit(base_name,'_')
441441+ local item_id = nil
442442+ local prefix = nil
443443+ if #splitted > 1 then
444444+ prefix = splitted[1]
445445+ item_id = splitted[2]
446446+ end
447447+ local container = {
448448+ id = id,
449449+ type=elementType,
450450+ name = "[" .. id .. "] " .. base_name,
451451+ tableName = base_name,
452452+ prefix = prefix,
453453+ item_id = item_id,
454454+ monitor = false,
455455+ screenPos=library.getPointOnScreen(ConvertLocalToWorld(pos, constructPos, constructRight, constructForward, constructUp)),
456456+ }
457457+ if item_id and prefix then
458458+ for _,sprefix in ipairs(storage_prefix) do
459459+ if prefix:lower() == sprefix:lower() then
460460+ container.monitor = true
461461+ container.item = system.getItem(item_id)
462462+ break
463463+ end
464464+ end
465465+ end
466466+ local container_size = "XS"
467467+ local container_empty_mass = 0
468468+ local container_volume = 0
469469+ local contentQuantity = 0
470470+ local percent_fill = 0
471471+ if not elementType:lower():find("hub") then
472472+ local containerMaxHP = core.getElementMaxHitPointsById(id)
473473+ if containerMaxHP > 68000 then
474474+ container_size = "XXL"
475475+ container_empty_mass = 88410
476476+ container_volume = 512000 * (containerProficiencyLvl * 0.1) + 512000
477477+ elseif containerMaxHP > 33000 then
478478+ container_size = "XL"
479479+ container_empty_mass = 44210
480480+ container_volume = 256000 * (containerProficiencyLvl * 0.1) + 256000
481481+ elseif containerMaxHP > 17000 then
482482+ container_size = "L"
483483+ container_empty_mass = 14842.7
484484+ container_volume = 128000 * (containerProficiencyLvl * 0.1) + 128000
485485+ elseif containerMaxHP > 7900 then
486486+ container_size = "M"
487487+ container_empty_mass = 7421.35
488488+ container_volume = 64000 * (containerProficiencyLvl * 0.1) + 64000
489489+ elseif containerMaxHP > 900 then
490490+ container_size = "S"
491491+ container_empty_mass = 1281.31
492492+ container_volume = 8000 * (containerProficiencyLvl * 0.1) + 8000
493493+ else
494494+ container_size = "XS"
495495+ container_empty_mass = 229.09
496496+ container_volume = 1000 * (containerProficiencyLvl * 0.1) + 1000
497497+ end
498498+ else
499499+ if splitted[3] then
500500+ container_size = splitted[3]
501501+ end
502502+ if splitted[4] then
503503+ container_amount = splitted[4]
504504+ end
505505+ local volume = 0
506506+ container_volume_list = {xxl=512000, xl=256000, l=128000, m=64000, s=8000, xs=1000}
507507+ container_size = container_size:lower()
508508+ if container_volume_list[container_size] then
509509+ volume = container_volume_list[container_size]
510510+ end
511511+ container_volume = (volume * containerProficiencyLvl * 0.1 + volume)
512512+ container_empty_mass = 55.8
513513+ end
514514+ local totalMass = core.getElementMassById(id)
515515+ local contentMassKg = totalMass - container_empty_mass
516516+ container.totalMass = totalMass
517517+ container.emptyMass = container_empty_mass
518518+ container.contentMass = contentMassKg
519519+ container.maxVolume = container_volume
520520+ container.size = container_size
521521+ if container.item == nil or container.item.name == "InvalidItem" then
522522+ container.percent = 0
523523+ container.quantity = 0
524524+ else
525525+ container.quantity = contentMassKg / (container.item.unitMass - (container.item.unitMass * (containerOptimizationLvl * 0.05)))
526526+ container.percent = utils.round((container.item.unitVolume * container.quantity) * 100 / container_volume)
527527+ end
528528+ storages['i'..id] = container
529529+ if index % machinesRefreshedByFrame == 0 then
530530+ coroutine.yield(self.loadStorage)
531531+ end
532532+ end
533533+end
534534+hud.coroutines.renderHelper = function()
535535+ local year, month, day, hour, minute, second = DUCurrentDateTime()
536536+ html_time = [[<div class="hud_time hud_container"><div style="text-align:center;">]] .. string.format("%02d/%02d/%04d %02d:%02d:%02d",day,month,year,hour,minute,second) .. [[</div><div style="text-align:right;"><hr class="hr-light"><style>.dm]] .. displayMode .. [[{border: 1px solid #28a745 !important;}.dp]] .. displayType .. [[{border: 1px solid #28a745 !important;}.sortType]] .. sortingType .. [[{border: 1px solid #28a745 !important;}</style><div>]]
537537+ for i,v in ipairs(sortingTypes) do
538538+ html_time = html_time .. '<span class="sortType' .. i ..'" style="margin:5px;">' .. v .. '</span>'
539539+ if i < #sortingTypes then html_time = html_time .. "|" end
540540+ end
541541+ html_time = html_time .. ' - Sorting Type - Alt + 6</div><div>'
542542+ for i,v in ipairs(displayTypes) do
543543+ html_time = html_time .. '<span class="dp' .. (i-1) .. '" style="margin:5px;">' .. v .. '</span>'
544544+ if i < #displayTypes then html_time = html_time .. "|" end
545545+ end
546546+ html_time = html_time .. ' - Display Type - Alt + 7</div><div>'
547547+ for i,v in ipairs(displayModes) do
548548+ html_time = html_time .. '<span class="dm' .. (i-1) .. '" style="margin:5px;">' .. v .. '</span>'
549549+ if i < #displayModes then html_time = html_time .. "|" end
550550+ end
551551+ html_time = html_time .. ' - Display Mode - Alt + 8</div>'
552552+ html_time = html_time .. '<div>Reload HUD - Alt + 9</div>'
553553+ if schematicContainer ~= nil then
554554+ html_time = html_time .. '<hr class="hr-light"><div>Schematic Storage Refresh in ' .. schematicRefreshTime .. 's</div>'
555555+ end
556556+ html_time = html_time .. '</div></div>'
557557+end
558558+hud.coroutines.renderIndustries = function()
559559+ local html = ""
560560+ local html_table = ""
561561+ local lastMachine = 0
562562+ local firstMachine = 0
563563+ local totalPage = 1
564564+ local selectedMachine = nil
565565+ if displayMode == 0 or displayMode == 1 then
566566+ local selectedIndustries = {}
567567+ for k,industry in pairs(industries) do
568568+ if (statusFilter == 0 or industry.state == statusFilter) and (statusFilterType == 0 or (statusFilterType <= #elementsTypes and industry.typeFilter:lower() == elementsTypes[statusFilterType])) then
569569+ if displayType == 0 then
570570+ table.insert(selectedIndustries, industry)
571571+ elseif displayType == 1 then
572572+ table.insert(selectedIndustries, industry)
573573+ elseif displayType == 2 then
574574+ if industry.screenPos[1] > 0 and industry.screenPos[1] < 1 and industry.screenPos[2] > 0 and industry.screenPos[2] < 1 then
575575+ table.insert(selectedIndustries, industry)
576576+ end
577577+ end
578578+ end
579579+ end
580580+ if (displayType == 0 or displayType == 1) and statusFilterType <= #elementsTypes then
581581+ totalPage = math.ceil(#selectedIndustries/elementsByPage)
582582+ firstMachine = (page - 1) * elementsByPage + 1
583583+ lastMachine = page * elementsByPage
584584+ if lastMachine > #selectedIndustries then
585585+ lastMachine = #selectedIndustries
586586+ end
587587+ html_table = html_table .. '<div class="hud_container hud_table_container"><div style="text-align:center;border-bottom:1px solid white;">↑ Arrow Up</div><table style="width:100%"><tr><th>← Arrow Left</th><th> Page ' .. page .. '/' .. totalPage .. ' (from ' .. firstMachine .. ' to ' .. lastMachine .. ' on ' .. #selectedIndustries .. ')</th><th>Arrow Right →</th></tr></table><table><tr><th>id</th><th>Machine Name & Type</th><th>Selected Recipe & schematic</th><th>Status</th><th>Mode</th><th>Time Remaining</th></tr>'
588588+ end
589589+ local count = 1
590590+ if sortingType == 1 then
591591+ table.sort(selectedIndustries, function(a,b) return a.tableName < b.tableName end)
592592+ elseif sortingType == 2 then
593593+ table.sort(selectedIndustries, function(a,b) return a.production.locDisplayNameWithSize < b.production.locDisplayNameWithSize end)
594594+ elseif sortingType == 3 then
595595+ table.sort(selectedIndustries, function(a,b) return a.id < b.id end)
596596+ end
597597+ for k,industry in pairs(selectedIndustries) do
598598+ if (displayType == 0 or displayType == 2) and industry.screenPos[1] > 0 and industry.screenPos[1] < 1 and industry.screenPos[2] > 0 and industry.screenPos[2] < 1 then
599599+ local showDetail = false
600600+ if industry.screenPos[3] <= machineDetailDisplayDistance then
601601+ showDetail = true
602602+ elseif industry.screenPos[1] < 0.55 and industry.screenPos[1] > 0.45 and industry.screenPos[2] < 0.55 and industry.screenPos[2] > 0.45 then
603603+ showDetail = true
604604+ end
605605+ local font_size = fontSize * ((200-industry.screenPos[3])/200)
606606+ local zindex = utils.round(1000 - industry.screenPos[3])
607607+ html = html .. [[<div class="ib" style="left:]] .. utils.round(industry.screenPos[1]*100) .. [[%;top:]] .. utils.round(industry.screenPos[2]*100) .. [[%;font-size:]] .. font_size .. [[px;z-index:]]..zindex..[[;"><div class="]] .. getIndustryStatusBgClass(industry.state) .. [[" style="padding:2px;text-transform:capitalize;"><span class="tier]] .. industry.production.tier .. [[">T]] .. industry.production.tier .. [[</span><strong>]] .. industry.production.locDisplayNameWithSize .. [[</strong></div>]]
608608+ if showDetail then
609609+ html = html .. [[<div class="bg-light text-dark" style="margin-top:2px;padding:2px;"><div class="]] .. getIndustryStatusClass(industry.state) .. [["><strong>]] .. statusList[industry.state] .. [[</strong></div><div>]] .. industry.productId .. industry.name .. industry.type .. industry.mode .. industry.schematic .. industry.remainingTime .. industry.recipe ..[[</div></div>]]
610610+ end
611611+ html = html .. [[</div>]]
612612+ end
613613+ if (displayType == 0 or displayType == 1) then
614614+ local selectedClass = ""
615615+ if selectedRow == (count + 1 - firstMachine)then
616616+ selectedClass = "row_selected"
617617+ selectedMachine = industry
618618+ end
619619+ if count >= firstMachine and count <= lastMachine then
620620+ html_table = html_table .. '<tr><th class="' .. selectedClass .. '">' .. industry.id .. '</th><th class="' .. selectedClass .. '"><span class="' .. getIndustryStatusClass(industry.state) .. '">' .. industry.tableName .. '</span><br><small>' .. industry.tableType .. '</small></th><th class="' .. selectedClass .. '">' .. industry.production.locDisplayNameWithSize .. '<br><small class="text-' .. industry.schematicColor .. '">' .. industry.tableSchematicQuantity .. " " .. industry.tableSchematic .. '</small></th><th class="' .. selectedClass .. '"><span class="' .. getIndustryStatusClass(industry.state) .. '">' .. statusList[industry.state] .. '</span></th><th class="' .. selectedClass .. '">' .. industry.tableMode .. '</th><th class="' .. selectedClass .. '">' .. industry.tableTime .. '</th></tr>'
621621+ end
622622+ end
623623+ count = count + 1
624624+ end
625625+ if (displayType == 0 or displayType == 1) and statusFilterType <= #elementsTypes then
626626+ html_table = html_table .. '</table><table style="width:100%"><tr><th>← Arrow Left</th><th> Page ' .. page .. '/' .. totalPage .. ' (from ' .. firstMachine .. ' to ' .. lastMachine .. ' on ' .. #selectedIndustries .. ')</th><th>Arrow Right →</th></tr></table><div style="text-align:center;border-bottom:1px solid white;">↓ Arrow Down</div></div>'
627627+ maxPage = totalPage
628628+ maxOnPage = lastMachine-firstMachine+1
629629+ end
630630+ end
631631+ html_machines = html .. html_table
632632+ selected_machine = selectedMachine
633633+end
634634+hud.coroutines.renderStorage = function()
635635+ local html = ""
636636+ local html_table = ""
637637+ local lastStorage = 0
638638+ local firstStorage = 0
639639+ local totalPage = 1
640640+ local selectedStorage = nil
641641+ if displayMode == 0 or displayMode == 2 then
642642+ local selectedStorages = {}
643643+ for k,storage in pairs(storages) do
644644+ if statusFilterType >= maxFilterType and storage.monitor then
645645+ if displayType == 0 then
646646+ table.insert(selectedStorages, storage)
647647+ elseif displayType == 1 then
648648+ table.insert(selectedStorages, storage)
649649+ end
650650+ end
651651+ end
652652+ if (displayType == 0 or displayType == 2) then
653653+ for k,storage in pairs(storages) do
654654+ if storage.screenPos[1] > 0 and storage.screenPos[1] < 1 and storage.screenPos[2] > 0 and storage.screenPos[2] < 1 then
655655+ local showDetail = false
656656+ if storage.screenPos[3] <= machineDetailDisplayDistance then
657657+ showDetail = true
658658+ elseif storage.screenPos[1] < 0.55 and storage.screenPos[1] > 0.45 and storage.screenPos[2] < 0.55 and storage.screenPos[2] > 0.45 then
659659+ showDetail = true
660660+ end
661661+ local font_size = fontSize * ((200-storage.screenPos[3])/200)
662662+ local zindex = utils.round(1000 - storage.screenPos[3])
663663+ local displayName = storage.name
664664+ local tierBox = ''
665665+ local itemBox = ''
666666+ local fillGauge = ''
667667+ if storage.item ~= nil and storage.item.name ~= "InvalidItem" then
668668+ displayName = format_number(utils.round(storage.quantity*100)/100) .. " " .. storage.item.locDisplayNameWithSize .. " (" .. storage.percent .. "%)"
669669+ tierBox = '<span class="tier' .. storage.item.tier .. '">T' .. storage.item.tier .. '</span>'
670670+ local i = storage.item
671671+ local gaugePercent = storage.percent
672672+ if storage.percent > 100 then
673673+ gaugePercent = 100
674674+ end
675675+ local r,g,b = getRGBGradient(gaugePercent/100,177,42,42,249,212,123,34,177,76)
676676+ r = utils.round(r)
677677+ g = utils.round(g)
678678+ b = utils.round(b)
679679+ itemBox = [[<hr><div><strong>Item ID:</strong> ]] .. i.id .. [[</div><div><strong>Item Name:</strong> ]] .. i.locDisplayNameWithSize .. [[</div><div><strong>Item Unit Mass:</strong> ]] .. i.unitMass .. [[ kg</div><div><strong>Item Unit Volume:</strong> ]] .. i.unitVolume .. [[ m<sup>3</sup></div>]]
680680+ fillGauge = '<div style="position:absolute;bottom:0;left:0px;width:' .. gaugePercent .. '%;background-color:rgb(' .. r .. ',' .. g ..',' .. b .. ');height:3px;z-index:' .. (zindex+1) .. ';"></div>'
681681+ end
682682+ html = html .. [[<div class="ib" style="left:]] .. utils.round(storage.screenPos[1]*100) .. [[%;top:]] .. utils.round(storage.screenPos[2]*100) .. [[%;font-size:]] .. font_size .. [[px;z-index:]]..zindex..[[;"><div class="bg-light text-dark" style="padding:2px;padding-bottom:5px;text-transform:capitalize;position:relative;">]] .. tierBox .. [[<strong>]] .. displayName .. [[</strong>]] .. fillGauge .. [[</div>]]
683683+ if showDetail then
684684+ html = html .. [[<div class="bg-light text-dark" style="margin-top:2px;padding:2px;"><div><strong>Container Type:</strong> ]] .. storage.type .. " " .. storage.size .. [[</div>]] .. itemBox .. [[</div></div>]]
685685+ end
686686+ html = html .. [[</div>]]
687687+ end
688688+ end
689689+ end
690690+ if (displayType == 0 or displayType == 1) and statusFilterType >= maxFilterType then
691691+ totalPage = math.ceil(#selectedStorages/elementsByPage)
692692+ firstStorage = (page - 1) * elementsByPage + 1
693693+ lastStorage = page * elementsByPage
694694+ if lastStorage > #selectedStorages then
695695+ lastStorage = #selectedStorages
696696+ end
697697+ html_table = html_table .. '<div class="hud_container hud_table_container"><div style="text-align:center;border-bottom:1px solid white;">↑ Arrow Up</div><table style="width:100%"><tr><th>← Arrow Left</th><th> Page ' .. page .. '/' .. totalPage .. ' (from ' .. firstStorage .. ' to ' .. lastStorage .. ' on ' .. #selectedStorages .. ')</th><th>Arrow Right →</th></tr></table><table><tr><th>id</th><th>Storage Name & Type</th><th>Item Name</th><th>Quantity</th><th style="width:100px;">Percent fill</th></tr>'
698698+ local count = 1
699699+ if sortingType == 1 then
700700+ table.sort(selectedStorages, function(a,b) return a.tableName < b.tableName end)
701701+ elseif sortingType == 2 then
702702+ table.sort(selectedStorages, function(a,b) return a.item.locDisplayNameWithSize < b.item.locDisplayNameWithSize end)
703703+ elseif sortingType == 3 then
704704+ table.sort(selectedStorages, function(a,b) return a.id < b.id end)
705705+ end
706706+ for k,storage in pairs(selectedStorages) do
707707+ if count >= firstStorage and count <= lastStorage then
708708+ local selectedClass = ""
709709+ if selectedRow == (count + 1 - firstStorage)then
710710+ selectedClass = "row_selected"
711711+ selectedStorage = storage
712712+ end
713713+ local item_name = ""
714714+ if storage.item ~= nil and storage.item.name ~= "InvalidItem" then
715715+ item_name = storage.item.locDisplayNameWithSize
716716+ end
717717+ local gaugePercent = storage.percent
718718+ if storage.percent > 100 then
719719+ gaugePercent = 100
720720+ end
721721+ local r,g,b = getRGBGradient(gaugePercent/100,177,42,42,249,212,123,34,177,76)
722722+ r = utils.round(r)
723723+ g = utils.round(g)
724724+ b = utils.round(b)
725725+ html_table = html_table .. '<tr><th class="' .. selectedClass .. '">' .. storage.id .. '</th><th class="' .. selectedClass .. '">' .. storage.tableName .. '<br><small>' .. storage.type .. ' ' .. storage.size .. '</small></th><th class="' .. selectedClass .. '">' .. item_name .. '</th><th class="' .. selectedClass .. '">' .. format_number(utils.round(storage.quantity*100)/100) .. '</th><th class="' .. selectedClass .. '">' .. storage.percent .. '%<br><div style="border:1px solid black;position:relative;height:5px;"><div style="position:absolute;left:0;top:0;width:' .. gaugePercent .. '%;background-color:rgb(' .. r .. ',' .. g ..',' .. b .. ');height:5px;"></div></div></th></tr>'
726726+ end
727727+ count = count + 1
728728+ end
729729+ html_table = html_table .. '</table><table style="width:100%"><tr><th>← Arrow Left</th><th> Page ' .. page .. '/' .. totalPage .. ' (from ' .. firstStorage .. ' to ' .. lastStorage .. ' on ' .. #selectedStorages .. ')</th><th>Arrow Right →</th></tr></table><div style="text-align:center;border-bottom:1px solid white;">↓ Arrow Down</div></div>'
730730+ maxPage = totalPage
731731+ maxOnPage = lastStorage-firstStorage+1
732732+ end
733733+ end
734734+ html_storage = html .. html_table
735735+ selected_storage = selectedStorage
736736+end
737737+hud.coroutines.renderFilterStatus = function()
738738+ html_filters = "<style>"
739739+ html_filters = html_filters .. ".bg{margin:5px;padding:2px;}.bg" .. (statusFilter+1) .. "{border: 1px solid #28a745 !important;}"
740740+ html_filters = html_filters .. "</style>"
741741+ html_filters = html_filters .. [[<div class="hud_container hud_machine_status_container"><div><span class="bg bg1" >ALL</span>|]]
742742+ for i,status in ipairs(statusList) do
743743+ html_filters = html_filters .. [[<span class="bg bg]] .. (i+1) .. [[ ]] .. getIndustryStatusClass(i) .. [[">]] .. statusList[i] .. [[</span>]]
744744+ if i < #statusList then html_filters = html_filters .. '|' end
745745+ end
746746+ html_filters = html_filters .. '</div>'
747747+ html_filters = html_filters .. [[<hr class="hr-light"><table style="width:100%;"><tr><td style="padding-left:5px;">← Ctrl+Arrow Left</td><td style="text-align:right;padding-right:5px;">Ctrl+Arrow Right →</td></tr></table></div>]]
748748+end
749749+hud.coroutines.renderFilterType = function()
750750+ maxFilterType = #elementsTypes
751751+ if displayMode == 0 then
752752+ maxFilterType = #elementsTypes + 1
753753+ elseif displayMode == 2 then
754754+ maxFilterType = 0
755755+ end
756756+ html_filters_types = "<style>"
757757+ html_filters_types = html_filters_types .. ".bgt" .. (statusFilterType+1) .. "{border: 2px solid #28a745 !important;}"
758758+ html_filters_types = html_filters_types .. "</style>"
759759+ html_filters_types = html_filters_types .. '<div class="hud_container hud_machine_types_container" style="text-align:center;"><div>↑ Ctrl+Arrow Up</div><hr class="hr-light">'
760760+ if displayMode == 0 or displayMode == 1 then
761761+ html_filters_types = html_filters_types .. '<div class="bgt1" style="margin:5px;">ALL Industries (' .. #industries_id .. ')</div>'
762762+ for i,v in ipairs(elementsTypes) do
763763+ html_filters_types = html_filters_types .. '<div class="bgt' .. (i+1) .. '" style="margin:5px;">' .. v .. ' (' .. machines_count[v] .. ')</div>'
764764+ end
765765+ end
766766+ if displayMode == 0 or displayMode == 2 then
767767+ local bgtclass = 'bgt' .. (#elementsTypes + 2)
768768+ if displayMode == 2 then
769769+ bgtclass = 'bgt1'
770770+ end
771771+ html_filters_types = html_filters_types .. '<div class="' .. bgtclass .. '" style="margin:5px;">Storage (' .. #storages_id .. ')</div>'
772772+ end
773773+ html_filters_types = html_filters_types .. '<hr class="hr-light"><div>↓ Ctrl+Arrow Down</div></div>'
774774+end
775775+hud.coroutines.renderSelectedAR = function()
776776+ local html = ""
777777+ if displayType == 0 or displayType == 1 then
778778+ if selected_machine then
779779+ local industry = selected_machine
780780+ html = html .. [[<div class="ib ]] .. getIndustryStatusBorderClass(industry.state) .. [[" style="border:5px solid white;left:]] .. utils.round(industry.screenPos[1]*100) .. [[%;top:]] .. utils.round(industry.screenPos[2]*100) .. [[%;font-size:]] .. fontSize .. [[px;z-index:1000;"><div class="]] .. getIndustryStatusBgClass(industry.state) .. [[" style="padding:2px;text-transform:capitalize;"><span class="tier]] .. industry.production.tier .. [[">T]] .. industry.production.tier .. [[</span><strong>]] .. industry.production.locDisplayNameWithSize .. [[</strong></div><div class="bg-light text-dark" style="margin-top:2px;padding:2px;"><div class="]] .. getIndustryStatusClass(industry.state) .. [["><strong>]] .. statusList[industry.state] .. [[</strong></div><div>]] .. industry.productId .. industry.name .. industry.type .. industry.mode .. industry.schematic .. industry.remainingTime .. industry.recipe ..[[</div></div></div>]]
781781+ elseif selected_storage then
782782+ local storage = selected_storage
783783+ local displayName = storage.name
784784+ local tierBox = ''
785785+ local itemBox = ''
786786+ local fillGauge = ''
787787+ local gaugePercent = storage.percent
788788+ if storage.percent > 100 then
789789+ gaugePercent = 100
790790+ end
791791+ local r,g,b = getRGBGradient(gaugePercent/100,177,42,42,249,212,123,34,177,76)
792792+ r = utils.round(r)
793793+ g = utils.round(g)
794794+ b = utils.round(b)
795795+ if storage.item ~= nil and storage.item.name ~= "InvalidItem" then
796796+ displayName = format_number(utils.round(storage.quantity*100)/100) .. " " .. storage.item.locDisplayNameWithSize .. " (" .. storage.percent .. "%)"
797797+ tierBox = '<span class="tier' .. storage.item.tier .. '">T' .. storage.item.tier .. '</span>'
798798+ local i = storage.item
799799+800800+ itemBox = [[<hr><div><strong>Item ID:</strong> ]] .. i.id .. [[</div><div><strong>Item Name:</strong> ]] .. i.locDisplayNameWithSize .. [[</div><div><strong>Item Unit Mass:</strong> ]] .. i.unitMass .. [[ kg</div><div><strong>Item Unit Volume:</strong> ]] .. i.unitVolume .. [[ m<sup>3</sup></div>]]
801801+ fillGauge = '<div style="position:absolute;bottom:0;left:0px;width:' .. gaugePercent .. '%;background-color:rgb(' .. r .. ',' .. g ..',' .. b .. ');height:3px;z-index:1000;"></div>'
802802+ end
803803+ html = html .. [[<div class="ib" style="border: 5px solid rgb(]] .. r .. [[,]] .. g ..[[,]] .. b .. [[);left:]] .. utils.round(storage.screenPos[1]*100) .. [[%;top:]] .. utils.round(storage.screenPos[2]*100) .. [[%;font-size:]] .. fontSize .. [[px;z-index:1000;"><div class="bg-light text-dark" style="padding:2px;padding-bottom:5px;text-transform:capitalize;position:relative;">]] .. tierBox .. [[<strong>]] .. displayName .. [[</strong>]] .. fillGauge .. [[</div><div class="bg-light text-dark" style="margin-top:2px;padding:2px;"><div><strong>Container Type:</strong> ]] .. storage.type .. " " .. storage.size .. [[</div>]] .. itemBox .. [[</div></div>]]
804804+ html = html .. [[</div>]]
805805+ end
806806+ end
807807+ html_ar_selected = html
808808+end
809809+hud.coroutines.renderControlSelectedIndustry = function()
810810+ local html = ""
811811+ if #databanks > 0 and emitter ~= nil and selected_machine ~= nil and (displayType == 0 or displayType == 1) then
812812+ local industry = selected_machine
813813+ html = '<div class="hud_container hud_machine_control" style="text-align:center;"><div class="' .. getIndustryStatusClass(industry.state) .. '">' .. industry.tableName .. '<br><small>' .. statusList[industry.state] .. '</small></div><hr class="hr-light"><div>' .. industry.production.locDisplayNameWithSize .. '</div>'
814814+ if industry.state == 1 or industry.state == (#statusList - 1) then
815815+ command_1 = "START"
816816+ command_2 = "BATCH"
817817+ command_3 = "MAINTAIN"
818818+ html = html .. '<table style="width:100%;"><tr><th style="text-align:left;width:100px;">START</th><th style="width:100px;"></th><th style="text-align:right;width:100px;">Alt+1</th></tr><tr><th style="text-align:left;">START BATCH</th><th rowspan="2">Quantity:<br>'
819819+ local has_quantity = false
820820+ for k,v in pairs(craft_quantity_digits) do
821821+ if tonumber(v) == nil then v = 0 end
822822+ if tonumber(v) > 0 then
823823+ has_quantity = true
824824+ end
825825+ end
826826+ if not has_quantity then
827827+ local value = tostring(math.floor(industry.maintainOrBatchQuantity))
828828+ for i = #value, 1, -1 do
829829+ local c = value:sub(i,i)
830830+ craft_quantity_digits[9-(#value-(i-1))] = c
831831+ end
832832+ end
833833+ for digit_index,digit in pairs(craft_quantity_digits) do
834834+ html = html .. digit
835835+ end
836836+ html = html .. '</th><th style="text-align:right;">ALT+2</th></tr><tr><th style="text-align:left;">MAINTAIN</th><th style="text-align:right;">ALT+3</th></tr></table>'
837837+ elseif industry.state < #statusList then
838838+ command_1 = "STOP"
839839+ command_2 = "SOFT_STOP"
840840+ command_3 = "HARD_STOP"
841841+ html = html .. [[<table><tr><th style="text-align:left;width:250px;">STOP</th><th style="text-align:right;width:50px;">ALT+1</th></tr><tr><th style="text-align:left;">FINISH AND STOP</th><th style="text-align:right;">ALT+2</th></tr><tr><th style="text-align:left;">STOP AND ALLOW MATERIAL LOSS</th><th style="text-align:right;">ALT+3</th></tr></table>]]
842842+ else
843843+ html = html .. '<div>Pending stop, please wait</div>'
844844+ end
845845+ html = html .. '</div>'
846846+ end
847847+ html_control = html
848848+end
849849+hud.coroutines.renderHud = function()
850850+ local html = bootstrap_css_colors .. base_css .. html_ar_selected
851851+ if displayMode ~= 3 then
852852+ html = html .. html_filters_types
853853+ if displayMode == 0 or displayMode == 1 then
854854+ html = html .. html_machines .. html_filters .. html_control
855855+ end
856856+ if displayMode == 0 or displayMode == 2 then
857857+ html = html .. html_storage
858858+ end
859859+ end
860860+ html = html .. html_time .. html_credits
861861+ system.setScreen(html)
862862+end
863863+hud.actions = {}
864864+hud.actions.start = {}
865865+hud.actions.stop = {}
866866+hud.actions.start.brake = function() ctrlPressed = true end
867867+hud.actions.stop.brake = function() ctrlPressed = false end
868868+hud.actions.start.down = function()
869869+ if ctrlPressed and displayMode < 3 then
870870+ statusFilterType = statusFilterType + 1
871871+ if statusFilterType > maxFilterType then statusFilterType = 0 end
872872+ onFilterChange()
873873+ elseif displayMode < 3 then
874874+ selectedRow = selectedRow + 1
875875+ if selectedRow > maxOnPage then selectedRow = 1 end
876876+ onRowChange()
877877+ end
878878+end
879879+hud.actions.start.up = function()
880880+ if ctrlPressed and displayMode < 3 then
881881+ statusFilterType = statusFilterType - 1
882882+ local max = #elementsTypes
883883+ if statusFilterType < 0 then statusFilterType = maxFilterType end
884884+ onFilterChange()
885885+ elseif displayMode < 3 then
886886+ selectedRow = selectedRow - 1
887887+ if selectedRow < 1 then selectedRow = maxOnPage end
888888+ onRowChange()
889889+ end
890890+end
891891+hud.actions.start.option1 = function()
892892+ if #databanks > 0 and emitter ~= nil and selected_machine ~= nil and (displayType == 0 or displayType == 1) then
893893+ --Send Command 1
894894+ for _,db in pairs(databanks) do
895895+ db.setStringValue(selected_machine.id, command_1)
896896+ end
897897+ emitter.send(channels[selected_machine.typeFilter:lower()], "")
898898+ end
899899+end
900900+hud.actions.start.option2 = function()
901901+ if #databanks > 0 and emitter ~= nil and selected_machine ~= nil and (displayType == 0 or displayType == 1) then
902902+ --Send Command 2
903903+ for _,db in pairs(databanks) do
904904+ if command_2:find("MAINTAIN") or command_2:find("BATCH") then
905905+ craft_quantity = ""
906906+ for _,digit in pairs(craft_quantity_digits) do
907907+ craft_quantity = craft_quantity .. digit
908908+ end
909909+ command_2 = command_2 .. "_" .. craft_quantity
910910+ end
911911+ db.setStringValue(selected_machine.id, command_2)
912912+ if command_2:find("MAINTAIN") then command_2 = "MAINTAIN" end
913913+ if command_2:find("BATCH") then command_2 = "BATCH" end
914914+ end
915915+ emitter.send(channels[selected_machine.typeFilter:lower()], "")
916916+ end
917917+end
918918+hud.actions.start.option3 = function()
919919+ if #databanks > 0 and emitter ~= nil and selected_machine ~= nil and (displayType == 0 or displayType == 1) then
920920+ --Send Command 3
921921+ for _,db in pairs(databanks) do
922922+ if command_3:find("MAINTAIN") or command_3:find("BATCH") then
923923+ craft_quantity = ""
924924+ for _,digit in pairs(craft_quantity_digits) do
925925+ craft_quantity = craft_quantity .. digit
926926+ end
927927+ command_3 = command_3 .. "_" .. craft_quantity
928928+ end
929929+ db.setStringValue(selected_machine.id, command_3)
930930+ if command_3:find("MAINTAIN") then command_3 = "MAINTAIN" end
931931+ if command_3:find("BATCH") then command_3 = "BATCH" end
932932+ end
933933+ emitter.send(channels[selected_machine.typeFilter:lower()], "")
934934+ end
935935+end
936936+hud.actions.start.option6 = function()
937937+ sortingType = sortingType + 1
938938+ if sortingType < 0 then sortingType = #sortingTypes end
939939+ if sortingType > (#sortingTypes) then sortingType = 1 end
940940+end
941941+hud.actions.start.option7 = function()
942942+ displayType = displayType + 1
943943+ if displayType < 0 then displayType = (#displayTypes-1) end
944944+ if displayType > (#displayTypes-1) then displayType = 0 end
945945+end
946946+hud.actions.start.option8 = function()
947947+ displayMode = displayMode + 1
948948+ if displayMode < 0 then displayMode = (#displayModes-1) end
949949+ if displayMode > (#displayModes-1) then displayMode = 0 end
950950+end
951951+hud.actions.start.option9 = function()
952952+ init = false
953953+end
954954+hud.actions.start.strafeleft = function()
955955+ if ctrlPressed and (displayMode == 0 or displayMode == 1) then
956956+ statusFilter = statusFilter - 1
957957+ if statusFilter < 0 then statusFilter = #statusList end
958958+ onFilterChange()
959959+ elseif (displayType == 0 or displayType == 1) then
960960+ page = page - 1
961961+ if page < 1 then page = maxPage end
962962+ onTablePageChange()
963963+ end
964964+end
965965+hud.actions.start.straferight = function()
966966+ if ctrlPressed and (displayMode == 0 or displayMode == 1) then
967967+ statusFilter = statusFilter + 1
968968+ if statusFilter > #statusList then statusFilter = 0 end
969969+ onFilterChange()
970970+ elseif (displayType == 0 or displayType == 1) then
971971+ page = page + 1
972972+ if page > maxPage then page = 1 end
973973+ onTablePageChange()
974974+ end
975975+end
976976+977977+--[[
978978+ DU-Nested-Coroutines by Jericho
979979+ Permit to easier avoid CPU Load Errors
980980+ Source available here: https://github.com/Jericho1060/du-nested-coroutines
981981+]]--
982982+983983+coroutinesTable = {}
984984+985985+function initCoroutines()
986986+ for k,f in pairs(hud.coroutines) do
987987+ coroutinesTable[k] = coroutine.create(f)
988988+ end
989989+end
990990+991991+initCoroutines()
992992+993993+for k,f in pairs(coroutinesTable) do system.print(k) end
994994+995995+runCoroutines = function()
996996+ for k,co in pairs(coroutinesTable) do
997997+ if coroutine.status(co) == "dead" then
998998+ coroutinesTable[k] = coroutine.create(hud.coroutines[k])
999999+ end
10001000+ if coroutine.status(co) == "suspended" then
10011001+ assert(coroutine.resume(co))
10021002+ end
10031003+ end
10041004+end
10051005+10061006+MainCoroutine = coroutine.create(runCoroutines)
10071007+10081008+unit.hideWidget()
10091009+10101010+function onTablePageChange()
10111011+ selectedRow = 1
10121012+ onRowChange()
10131013+end
10141014+10151015+function onFilterChange()
10161016+ onTablePageChange()
10171017+ page = 1
10181018+end
10191019+10201020+function onRowChange()
10211021+ craft_quantity_digits = {"0","0","0","0","0","0","0","0"}
10221022+end