diff --git a/README.md b/README.md index c97199a..24d8722 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ For a Hub, you must use that pattern: `___`: the prefix that enable monitoring, by default `MONIT`, see options to customize it - ``: The ID of the item in the game database, you can search the ID of item here: https://du-lua.dev/#/items -- ``: if a hub, the size of the containers linked (default to XS) +- ``: if a hub, the size of the containers linked (default to XS), valid options are `xs`, `s`, `m`, `l`, `xl`, `xxl` - ``: if a hub, the amount of containers linked By default, the script is grouping all containers or hub that contains the same items on a single line and add the values. See option if you want to disable it. @@ -84,7 +84,8 @@ By rightclicking on the board, advanced, edit lua parameters, you can customize - `showRed`: if not enable, line with red gauge will be hidden - `maxAmountOfElementsLoadedByTick`: the maximum number of element loaded by tick of the coroutine on script startup (lower that value if you encounter cpu load errors on startup, default to 5000) - `maxAmountOfElementsRefreshedByTick`: the maximum number of element refreshed by tick of the coroutine when refreshing values (lower that value if you have cpu load errors after all emlements are loaded, default to 200) -- `verticalMode`: rotate the screen 90deg (bottom on right), disabled by default +- `verticalMode`: rotate the screen 90deg (bottom on right), disabled by default +- `verticalModeBottomSide`: when vertical mode is enabled, on which side the bottom of the screen is positioned (`left` or `right`) # Support or donation diff --git a/config.json b/config.json index 24c028d..0e727c6 100644 --- a/config.json +++ b/config.json @@ -1 +1 @@ -{"events":[],"handlers":[{"code":"if coroutine.status(MainCoroutine) == \"dead\" then\n MainCoroutine = coroutine.create(runCoroutines)\nend\nif coroutine.status(MainCoroutine) == \"suspended\" then\n assert(coroutine.resume(MainCoroutine))\nend","filter":{"args":[],"signature":"onUpdate()","slotKey":"-4"},"key":"2"},{"code":"if databank ~= nil then\n databank.setStringValue(\"options\", json.encode(options))\nend","filter":{"args":[],"signature":"onStop()","slotKey":"-1"},"key":"0"},{"code":"--[[\n\tLUA PARAMETERS\n]]\nuseDatabankValues = false --export: if checked and if values were saved in databank, parmaters will be loaded from the databank, if not, following ones will be used\n\nPrefixScreen1 = \"s1_\" --export: the prefix used to enable container monitoring and display on the 1st screen\nPrefixScreen2 = \"s2_\" --export: the prefix used to enable container monitoring and display on the 2nd screen\nPrefixScreen3 = \"s3_\" --export: the prefix used to enable container monitoring and display on the 3rd screen\nPrefixScreen4 = \"s4_\" --export: the prefix used to enable container monitoring and display on the 4th screen\nPrefixScreen5 = \"s5_\" --export: the prefix used to enable container monitoring and display on the 5th screen\nPrefixScreen6 = \"s6_\" --export: the prefix used to enable container monitoring and display on the 6th screen\nPrefixScreen7 = \"s7_\" --export: the prefix used to enable container monitoring and display on the 7th screen\nPrefixScreen8 = \"s8_\" --export: the prefix used to enable container monitoring and display on the 8th screen\nPrefixScreen9 = \"s9_\" --export: the prefix used to enable container monitoring and display on the 9th screen\n\nscreenTitle1 = \"-\" --export: the title display on the 1st screen, not displayed if empty or equal to \"-\"\nscreenTitle2 = \"-\" --export: the title display on the 2nd screen, not displayed if empty or equal to \"-\"\nscreenTitle3 = \"-\" --export: the title display on the 3rd screen, not displayed if empty or equal to \"-\"\nscreenTitle4 = \"-\" --export: the title display on the 4th screen, not displayed if empty or equal to \"-\"\nscreenTitle5 = \"-\" --export: the title display on the 5th screen, not displayed if empty or equal to \"-\"\nscreenTitle6 = \"-\" --export: the title display on the 6th screen, not displayed if empty or equal to \"-\"\nscreenTitle7 = \"-\" --export: the title display on the 7th screen, not displayed if empty or equal to \"-\"\nscreenTitle8 = \"-\" --export: the title display on the 8th screen, not displayed if empty or equal to \"-\"\nscreenTitle9 = \"-\" --export: the title display on the 9th screen, not displayed if empty or equal to \"-\"\n\ncontainerProficiencyLvl = 5 --export: Talent level for Container Proficiency\ncontainerOptimizationLvl = 5 --export: Talent level for Container Optimization\ngroupByItemName = true --export: if enabled, this will group all entries with the same item name\n\nQuantityRoundedDecimals = 2 --export: maximum of decimals displayed for the quantity value\nPercentRoundedDecimals = 2 --export: maximum of decimals displayed for the percent fill value\nfontSize = 15 --export: the size of the text for all the screen\nmaxAmountOfElementsLoadedByTick = 5000 --export: the maximum number of element loaded by tick of the coroutine on script startup\nmaxAmountOfElementsRefreshedByTick = 200 --export: the maximum number of element refreshed by tick of the coroutine when refreshing values\n--vertical mode code based on a suggestion by Merl\nverticalMode = false --export: rotate the screen 90deg (bottom on right)\n--[[\n -- BETA OPTIONS\n]]\nARViewBeta = false --export: BETA - Enable the AR view of Storage\nARViewBetaDisplayIcon = true --export: BETA - Display the item icon in AR\nARViewBetaDisplayItemName = true --export: BETA - display the item name in AR View\n\n--[[\n\tINIT\n]]\n\nlocal version = '4.2.0'\n\nsystem.print(\"----------------------------------\")\nsystem.print(\"DU-Storage-Monitoring version \" .. version)\nsystem.print(\"----------------------------------\")\n\noptions = {}\noptions.containerMonitoringPrefix_screen1 = PrefixScreen1\noptions.containerMonitoringPrefix_screen2 = PrefixScreen2\noptions.containerMonitoringPrefix_screen3 = PrefixScreen3\noptions.containerMonitoringPrefix_screen4 = PrefixScreen4\noptions.containerMonitoringPrefix_screen5 = PrefixScreen5\noptions.containerMonitoringPrefix_screen6 = PrefixScreen6\noptions.containerMonitoringPrefix_screen7 = PrefixScreen7\noptions.containerMonitoringPrefix_screen8 = PrefixScreen8\noptions.containerMonitoringPrefix_screen9 = PrefixScreen9\noptions.screenTitle1 = screenTitle1\noptions.screenTitle2 = screenTitle2\noptions.screenTitle3 = screenTitle3\noptions.screenTitle4 = screenTitle4\noptions.screenTitle5 = screenTitle5\noptions.screenTitle6 = screenTitle6\noptions.screenTitle7 = screenTitle7\noptions.screenTitle8 = screenTitle8\noptions.screenTitle9 = screenTitle9\noptions.container_proficiency_lvl = containerProficiencyLvl\noptions.container_optimization_lvl = containerOptimizationLvl\noptions.groupByItemName = groupByItemName\noptions.QuantityRoundedDecimals = QuantityRoundedDecimals\noptions.PercentRoundedDecimals = PercentRoundedDecimals\noptions.fontSize = fontSize\noptions.maxAmountOfElementsLoadedByTick = maxAmountOfElementsLoadedByTick\noptions.maxAmountOfElementsRefreshedByTick = maxAmountOfElementsRefreshedByTick\noptions.verticalMode = verticalMode\n\nlocal renderScript = [[\nlocal json = require('dkjson')\nlocal data = json.decode(getInput()) or {}\nlocal vmode = ]] .. tostring(verticalMode) .. [[\n\nlocal rx,ry\nif vmode then\n\try,rx = getResolution()\nelse\n\trx,ry = getResolution()\nend\n\nlocal back=createLayer()\nlocal front=createLayer()\n\nfont_size = data[1][2]\n\nlocal mini=loadFont('Play',12)\nlocal small=loadFont('Play',14)\nlocal smallBold=loadFont('Play-Bold',18)\nlocal itemName=loadFont('Play-Bold',font_size)\nlocal medV=loadFont('Play-Bold', 25)\nlocal bigV=loadFont('Play-Bold', 30)\nlocal big=loadFont('Play',38)\n\nsetBackgroundColor( 15/255,24/255,29/255)\n\nsetDefaultStrokeColor( back,Shape_Line,0,0,0,0.5)\nsetDefaultShadow( back,Shape_Line,6,0,0,0,0.5)\n\nsetDefaultFillColor( front,Shape_BoxRounded,249/255,212/255,123/255,1)\nsetDefaultFillColor( front,Shape_Text,0,0,0,1)\nsetDefaultFillColor( front,Shape_Box,0.075,0.125,0.156,1)\nsetDefaultFillColor( front,Shape_Text,0.710,0.878,0.941,1)\n\nfunction 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\n\nfunction round(a,b)if b then return utils.round(a/b)*b end;return a>=0 and math.floor(a+0.5)or math.ceil(a-0.5)end\n\nfunction 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\n\nfunction renderHeader(title, subtitle)\n local h_factor = 12\n local h = 35\n if subtitle ~= nil and subtitle ~= \"\" and subtitle ~= \"-\" then\n h = 50\n end\n addLine( back,0,h+12,rx,h+12)\n addBox(front,0,12,rx,h)\n if subtitle ~= nil and subtitle ~= \"\" and subtitle ~= \"-\" then\n addText(front,big,subtitle,44,50)\n addText(front,smallBold,title,rx-275,40)\n else\n addText(front,smallBold,title,44,35)\n end\nend\n\nlocal storageBar = createLayer()\nsetDefaultFillColor(storageBar,Shape_Text,110/255,166/255,181/255,1)\nsetDefaultFillColor(storageBar,Shape_Box,0.075,0.125,0.156,1)\nsetDefaultFillColor(storageBar,Shape_Line,1,1,1,1)\n\nlocal storageDark = createLayer()\nsetDefaultFillColor(storageDark,Shape_Text,63/255,92/255,102/255,1)\nsetDefaultFillColor(storageDark,Shape_Box,13/255,24/255,28/255,1)\n\nlocal colorLayer = createLayer()\n\nif vmode then\n local from_top = 10\n setLayerTranslation(back, ry-from_top,0)\n setLayerRotation(back, math.rad(90))\n setLayerTranslation(front, ry-from_top,0)\n setLayerRotation(front, math.rad(90))\n setLayerTranslation(storageBar, ry-from_top,0)\n setLayerRotation(storageBar, math.rad(90))\n setLayerTranslation(storageDark, ry-from_top,0)\n setLayerRotation(storageDark, math.rad(90))\n setLayerTranslation(colorLayer, ry-from_top,0)\n setLayerRotation(colorLayer, math.rad(90))\nend\nfunction renderResistanceBar(title, quantity, max, percent, x, y, w, h, withTitle)\n local r,g,b = getRGBGradient(percent/100,177/255,42/255,42/255,249/255,212/255,123/255,34/255,177/255,76/255)\n\n local quantity_x_pos = font_size * 6.7\n local percent_x_pos = font_size * 2\n\n addBox(storageBar,x,y,w,h)\n\n if withTitle then\n addText(storageBar, small, \"ITEMS\", x, y-5)\n setNextTextAlign(storageDark, AlignH_Center, AlignV_Bottom)\n addText(storageDark, small, \"MAX VOLUME\", x+(w*0.5), y-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)\n addText(storageBar, small, \"QUANTITY\", x+(w*0.75), y-3)\n addText(storageBar, small, \"STORAGE\", x+w-60, y-5)\n end\n\n local pos_y = y+(h/2)-2\n\n setNextTextAlign(storageBar, AlignH_Left, AlignV_Middle)\n addText(storageBar, itemName, title, x+10, pos_y)\n\n setNextFillColor(colorLayer, r, g, b, 1)\n addBox(colorLayer,x,y+h-3,w*(percent)/100,3)\n\n setNextTextAlign(storageDark, AlignH_Center, AlignV_Middle)\n addText(storageDark, itemName, format_number(max) .. ' L', x+(w*0.5), pos_y)\n\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)\n addText(storageBar, itemName, format_number(quantity), x+(w*0.75), pos_y)\n\n setNextFillColor(colorLayer, r, g, b, 1)\n setNextTextAlign(colorLayer, AlignH_Right, AlignV_Middle)\n addText(colorLayer, itemName, format_number(percent) ..\"%\", x+w-10, pos_y)\nend\n\nlocal screen_title = data[1][1]\nrenderHeader('STORAGE MONITORING v]] .. version .. [[', screen_title)\n\nstart_h = 75\nif screen_title ~= nil and screen_title ~= \"\" then\n start_h = 100\nend\n\n\nlocal h = font_size + font_size / 2\nfor i,container in ipairs(data[2]) do\n renderResistanceBar(container[1], container[2], container[3], container[4], 44, start_h, rx-88, h, i==1)\n start_h = start_h+h+5\nend\nrequestAnimationFrame(100)\n]]\n\n--[[\n\tsplit a string on a delimiter By jericho\n]]\nfunction strSplit(a,b)result={}for c in(a..b):gmatch(\"(.-)\"..b)do table.insert(result,c)end;return result end\n\n--[[\n return RGB colors calculated from a gradient between two colors\n]]\nfunction 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\n\n--[[\n\tformatting numbers by adding a space between thousands by Jericho\n]]\nfunction 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\n\ncore = nil\ndatabank = nil\nscreens = {}\nfor slot_name, slot in pairs(unit) do\n if\n type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getClass\n then\n if slot.getClass():lower():find(\"coreunit\") then\n core = slot\n end\n if slot.getClass():lower() == 'screenunit' then\n slot.slotname = slot_name\n table.insert(screens,slot)\n slot.setRenderScript(renderScript)\n end\n if slot.getClass():lower() == 'databankunit' then\n databank = slot\n end\n end\nend\nif #screens == 0 then\n system.print(\"No Screen Detected\")\nelse\n --sorting screens by slotname to be sure the display is not changing\n table.sort(screens, function(a,b) return a.slotname < b.slotname end)\n local plural = \"\"\n if #screens > 1 then plural = \"s\" end\n system.print(#screens .. \" screen\" .. plural .. \" Connected\")\nend\nif core == nil then\n system.print(\"No Core Detected\")\nelse\n system.print(\"Core Connected\")\nend\nif databank == nil then\n system.print(\"No Databank Detected\")\nelse\n system.print(\"Databank Connected\")\n if (databank.hasKey(\"options\")) and (useDatabankValues == true) then\n local db_options = json.decode(databank.getStringValue(\"options\"))\n for key, value in pairs(options) do\n if db_options[key] then options[key] = db_options[key] end\n end\n system.print(\"Options Loaded From Databank\")\n else\n system.print(\"Options Loaded From LUA Parameters\")\n end\nend\nprefixes = {\n options.containerMonitoringPrefix_screen1,\n options.containerMonitoringPrefix_screen2,\n options.containerMonitoringPrefix_screen3,\n options.containerMonitoringPrefix_screen4,\n options.containerMonitoringPrefix_screen5,\n options.containerMonitoringPrefix_screen6,\n options.containerMonitoringPrefix_screen7,\n options.containerMonitoringPrefix_screen8,\n options.containerMonitoringPrefix_screen9\n}\ntitles = {\n options.screenTitle1,\n options.screenTitle2,\n options.screenTitle3,\n options.screenTitle4,\n options.screenTitle5,\n options.screenTitle6,\n options.screenTitle7,\n options.screenTitle8,\n options.screenTitle9\n}\nelementsIdList = {}\nif core ~= nil then\n elementsIdList = core.getElementIdList()\nend\nstorageIdList= {}\ninitIndex = 0\ninitFinished = false\n\nconstructPos = construct.getWorldPosition()\nconstructRight = construct.getWorldRight()\nconstructForward = construct.getWorldForward()\nconstructUp = construct.getWorldUp()\n\n--[[\n Convert a table in local coordinates to a table in world coordinates by Jericho inspired by Koruzarius\n Source : https://github.com/Jericho1060/DualUniverse/blob/master/Vectors/localToWorldPos.lua\n]]--\nfunction 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\n\n\n--Nested Coroutines by Jericho\ncoroutinesTable = {}\n--all functions here will become a coroutine\nMyCoroutines = {\n function()\n if not initFinished then\n system.print(\"Loading contructs elements (\" .. #elementsIdList .. \" elements detected)\")\n for i = 1, #elementsIdList, 1 do\n initIndex = i\n local id = elementsIdList[i]\n local elementType = core.getElementDisplayNameById(id):lower()\n if elementType:lower():find(\"container\") then\n table.insert(storageIdList, id)\n end\n if (i%options.maxAmountOfElementsLoadedByTick) == 0 then\n system.print(i .. ' elements scanned on ' .. #elementsIdList .. ' with ' .. #storageIdList .. \" identified\")\n coroutine.yield(coroutinesTable[1])\n end\n end\n if initIndex == #elementsIdList then\n system.print(#elementsIdList .. \" scanned with \" .. #storageIdList .. \" storage elements identified\")\n initFinished = true\n end\n end\n end,\n function()\n local html = ''\n local storage_elements = {}\n for elemindex,id in ipairs(storageIdList) do\n local elementType = core.getElementDisplayNameById(id)\n if elementType:lower():find(\"container\") then\n local elementName = core.getElementNameById(id)\n if elementName:lower():find(prefixes[1]:lower())\n or elementName:lower():find(prefixes[2]:lower())\n or elementName:lower():find(prefixes[3]:lower())\n or elementName:lower():find(prefixes[4]:lower())\n or elementName:lower():find(prefixes[5]:lower())\n or elementName:lower():find(prefixes[6]:lower())\n or elementName:lower():find(prefixes[7]:lower())\n or elementName:lower():find(prefixes[8]:lower())\n or elementName:lower():find(prefixes[9]:lower())\n then\n local container = {}\n local splitted = strSplit(elementName, '_')\n local name = splitted[2]\n local ingredient = system.getItem(name)\n local container_size = \"XS\"\n local container_amount = 1\n local container_empty_mass = 0\n local container_volume = 0\n local contentQuantity = 0\n local percent_fill = 0\n if not elementType:lower():find(\"hub\") then\n local containerMaxHP = core.getElementMaxHitPointsById(id)\n if containerMaxHP > 68000 then\n container_size = \"XXL\"\n container_empty_mass = 88410\n container_volume = 512000 * (options.container_proficiency_lvl * 0.1) + 512000\n elseif containerMaxHP > 33000 then\n container_size = \"XL\"\n container_empty_mass = 44210\n container_volume = 256000 * (options.container_proficiency_lvl * 0.1) + 256000\n elseif containerMaxHP > 17000 then\n container_size = \"L\"\n container_empty_mass = 14842.7\n container_volume = 128000 * (options.container_proficiency_lvl * 0.1) + 128000\n elseif containerMaxHP > 7900 then\n container_size = \"M\"\n container_empty_mass = 7421.35\n container_volume = 64000 * (options.container_proficiency_lvl * 0.1) + 64000\n elseif containerMaxHP > 900 then\n container_size = \"S\"\n container_empty_mass = 1281.31\n container_volume = 8000 * (options.container_proficiency_lvl * 0.1) + 8000\n else\n container_size = \"XS\"\n container_empty_mass = 229.09\n container_volume = 1000 * (options.container_proficiency_lvl * 0.1) + 1000\n end\n else\n if splitted[3] then\n container_size = splitted[3]\n end\n if splitted[4] then\n container_amount = splitted[4]\n end\n local volume = 0\n container_volume_list = {xxl=512000, xl=256000, l=128000, m=64000, s=8000, xs=1000}\n container_size = container_size:lower()\n if container_volume_list[container_size] then\n volume = container_volume_list[container_size]\n end\n container_volume = (volume * options.container_proficiency_lvl * 0.1 + volume) * tonumber(container_amount)\n container_empty_mass = 55.8\n end\n local totalMass = core.getElementMassById(id)\n local contentMassKg = totalMass - container_empty_mass\n container.id = id\n container.itemid = ingredient.id\n container.realName = elementName\n local elementPos = core.getElementPositionById(id)\n local screenpos = library.getPointOnScreen(ConvertLocalToWorld(elementPos, constructPos, constructRight, constructForward, constructUp))\n container.prefix = splitted[1] .. \"_\"\n container.name = name\n container.ingredient = ingredient\n container.quantity = contentMassKg / (ingredient.unitMass - (ingredient.unitMass * (options.container_optimization_lvl * 0.05)))\n container.volume = container_volume\n container.percent = utils.round((ingredient.unitVolume * container.quantity) * 100 / container_volume)\n if ingredient.name == \"InvalidItem\" then\n container.percent = 0\n container.quantity = 0\n end\n if container.percent > 100 then container.percent = 100 end\n local r,g,b = getRGBGradient(container.percent/100,177,42,42,249,212,123,34,177,76)\n local max_distance = 250\n local img_width = 3\n elemhtml = [[\n
\n ]]\n if ARViewBetaDisplayIcon then\n elemhtml = elemhtml .. [[
]]\n end\n if ARViewBetaDisplayItemName then\n elemhtml = elemhtml .. container.ingredient.locDisplayNameWithSize .. [[
]]\n end\n elemhtml = elemhtml .. container.percent .. [[%\n
\n ]]\n html = html .. elemhtml\n table.insert(storage_elements, container)\n end\n end\n if (elemindex%options.maxAmountOfElementsRefreshedByTick) == 0 then\n coroutine.yield(coroutinesTable[2])\n end\n end\n\n -- group by name and screen\n local groupped = {}\n if groupByItemName then\n for _,v in pairs(storage_elements) do\n local prefix = v.prefix:lower()\n if groupped[prefix .. v.itemid] then\n groupped[prefix .. v.itemid].quantity = groupped[prefix .. v.itemid].quantity + v.quantity\n groupped[prefix .. v.itemid].volume = groupped[prefix .. v.itemid].volume + v.volume\n groupped[prefix .. v.itemid].percent = (v.ingredient.unitVolume * groupped[prefix .. v.itemid].quantity) * 100 / groupped[prefix .. v.itemid].volume\n else\n groupped[prefix .. v.itemid] = v\n end\n end\n else\n groupped = storage_elements\n end\n\n -- sorting by tier\n local tiers = {}\n tiers[1] = {} --tier 0 (thx to Belorion#3127 for pointing Oxygen and Hydrogen are Tier 0 and not 1)\n tiers[2] = {} --tier 1\n tiers[3] = {} --tier 2\n tiers[4] = {} --tier 3\n tiers[5] = {} --tier 4\n tiers[6] = {} --tier 5\n for _,v in pairs(groupped) do\n table.insert(tiers[v.ingredient.tier+1],v)\n end\n\n -- sorting by name\n for k,v in pairs(tiers) do\n table.sort(tiers[k], function(a,b) return a.ingredient.name:lower() < b.ingredient.name:lower() end)\n end\n\n if #screens > 0 then\n for index, screen in pairs(screens) do\n screen_data = {}\n local prefix = prefixes[index]\n local title = titles[index]\n\n for tier_k,tier in pairs(tiers) do\n for _,container in pairs(tier) do\n if container.prefix:lower():find(prefix:lower()) then\n local item_name = container.ingredient.locDisplayNameWithSize\n if container.ingredient.name == 'InvalidItem' then\n item_name = 'Invalid Item Id'\n end\n local storage_data = {\n item_name,\n utils.round(container.quantity * (10 ^ options.QuantityRoundedDecimals)) / (10 ^ options.QuantityRoundedDecimals),\n utils.round(container.volume),\n utils.round(container.percent * (10 ^ options.PercentRoundedDecimals)) / (10 ^ options.PercentRoundedDecimals)\n }\n table.insert(screen_data, storage_data)\n end\n end\n end\n local data_to_send = {\n {\n titles[index],\n options.fontSize\n },\n screen_data\n }\n screen.setScriptInput(json.encode(data_to_send))\n end\n end\n if ARViewBeta then\n system.setScreen(html)\n end\n end\n}\n\nfunction initCoroutines()\n for _,f in pairs(MyCoroutines) do\n local co = coroutine.create(f)\n table.insert(coroutinesTable, co)\n end\nend\n\ninitCoroutines()\n\nrunCoroutines = function()\n for i,co in ipairs(coroutinesTable) do\n if coroutine.status(co) == \"dead\" then\n coroutinesTable[i] = coroutine.create(MyCoroutines[i])\n end\n if coroutine.status(co) == \"suspended\" then\n assert(coroutine.resume(co))\n end\n end\nend\n\nMainCoroutine = coroutine.create(runCoroutines)\n\nsystem.showScreen(true)","filter":{"args":[],"signature":"onStart()","slotKey":"-1"},"key":"1"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-5":{"name":"library","type":{"events":[],"methods":[]}},"-4":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"player","type":{"events":[],"methods":[]}},"-2":{"name":"construct","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}} +{"events":[],"handlers":[{"code":"if coroutine.status(MainCoroutine) == \"dead\" then\n MainCoroutine = coroutine.create(runCoroutines)\nend\nif coroutine.status(MainCoroutine) == \"suspended\" then\n assert(coroutine.resume(MainCoroutine))\nend","filter":{"args":[],"signature":"onUpdate()","slotKey":"-4"},"key":"2"},{"code":"if databank ~= nil then\n databank.setStringValue(\"options\", json.encode(options))\nend","filter":{"args":[],"signature":"onStop()","slotKey":"-1"},"key":"0"},{"code":"--[[\n\tLUA PARAMETERS\n]]\nuseDatabankValues = false --export: if checked and if values were saved in databank, parmaters will be loaded from the databank, if not, following ones will be used\n\nPrefixScreen1 = \"s1_\" --export: the prefix used to enable container monitoring and display on the 1st screen\nPrefixScreen2 = \"s2_\" --export: the prefix used to enable container monitoring and display on the 2nd screen\nPrefixScreen3 = \"s3_\" --export: the prefix used to enable container monitoring and display on the 3rd screen\nPrefixScreen4 = \"s4_\" --export: the prefix used to enable container monitoring and display on the 4th screen\nPrefixScreen5 = \"s5_\" --export: the prefix used to enable container monitoring and display on the 5th screen\nPrefixScreen6 = \"s6_\" --export: the prefix used to enable container monitoring and display on the 6th screen\nPrefixScreen7 = \"s7_\" --export: the prefix used to enable container monitoring and display on the 7th screen\nPrefixScreen8 = \"s8_\" --export: the prefix used to enable container monitoring and display on the 8th screen\nPrefixScreen9 = \"s9_\" --export: the prefix used to enable container monitoring and display on the 9th screen\n\nscreenTitle1 = \"-\" --export: the title display on the 1st screen, not displayed if empty or equal to \"-\"\nscreenTitle2 = \"-\" --export: the title display on the 2nd screen, not displayed if empty or equal to \"-\"\nscreenTitle3 = \"-\" --export: the title display on the 3rd screen, not displayed if empty or equal to \"-\"\nscreenTitle4 = \"-\" --export: the title display on the 4th screen, not displayed if empty or equal to \"-\"\nscreenTitle5 = \"-\" --export: the title display on the 5th screen, not displayed if empty or equal to \"-\"\nscreenTitle6 = \"-\" --export: the title display on the 6th screen, not displayed if empty or equal to \"-\"\nscreenTitle7 = \"-\" --export: the title display on the 7th screen, not displayed if empty or equal to \"-\"\nscreenTitle8 = \"-\" --export: the title display on the 8th screen, not displayed if empty or equal to \"-\"\nscreenTitle9 = \"-\" --export: the title display on the 9th screen, not displayed if empty or equal to \"-\"\n\ncontainerProficiencyLvl = 5 --export: Talent level for Container Proficiency\ncontainerOptimizationLvl = 5 --export: Talent level for Container Optimization\ngroupByItemName = true --export: if enabled, this will group all entries with the same item name\n\nQuantityRoundedDecimals = 2 --export: maximum of decimals displayed for the quantity value\nPercentRoundedDecimals = 2 --export: maximum of decimals displayed for the percent fill value\nfontSize = 15 --export: the size of the text for all the screen\nmaxAmountOfElementsLoadedByTick = 5000 --export: the maximum number of element loaded by tick of the coroutine on script startup\nmaxAmountOfElementsRefreshedByTick = 200 --export: the maximum number of element refreshed by tick of the coroutine when refreshing values\n--vertical mode code based on a suggestion by Merl\nverticalMode = false --export: rotate the screen 90deg (bottom on right)\nverticalModeBottomSide = \"right\" --export: when vertical mode is enabled, on which side the bottom of the screen is positioned (\"left\" or \"right\")\n--defaultSorting = \"none\" --export: the default sorting of items on the screen: \"none\": like in the container, \"items-asc\": ascending sorting on the name, \"items-desc\": descending sorting on the name, \"quantity-asc\": ascending on the quantity, \"quantity-desc\": descending on the quantity\n\n--[[\n\tINIT\n]]\n\nlocal version = '4.3.0'\n\nsystem.print(\"----------------------------------\")\nsystem.print(\"DU-Storage-Monitoring version \" .. version)\nsystem.print(\"----------------------------------\")\n\noptions = {}\noptions.containerMonitoringPrefix_screen1 = PrefixScreen1\noptions.containerMonitoringPrefix_screen2 = PrefixScreen2\noptions.containerMonitoringPrefix_screen3 = PrefixScreen3\noptions.containerMonitoringPrefix_screen4 = PrefixScreen4\noptions.containerMonitoringPrefix_screen5 = PrefixScreen5\noptions.containerMonitoringPrefix_screen6 = PrefixScreen6\noptions.containerMonitoringPrefix_screen7 = PrefixScreen7\noptions.containerMonitoringPrefix_screen8 = PrefixScreen8\noptions.containerMonitoringPrefix_screen9 = PrefixScreen9\noptions.screenTitle1 = screenTitle1\noptions.screenTitle2 = screenTitle2\noptions.screenTitle3 = screenTitle3\noptions.screenTitle4 = screenTitle4\noptions.screenTitle5 = screenTitle5\noptions.screenTitle6 = screenTitle6\noptions.screenTitle7 = screenTitle7\noptions.screenTitle8 = screenTitle8\noptions.screenTitle9 = screenTitle9\noptions.container_proficiency_lvl = containerProficiencyLvl\noptions.container_optimization_lvl = containerOptimizationLvl\noptions.groupByItemName = groupByItemName\noptions.QuantityRoundedDecimals = QuantityRoundedDecimals\noptions.PercentRoundedDecimals = PercentRoundedDecimals\noptions.fontSize = fontSize\noptions.maxAmountOfElementsLoadedByTick = maxAmountOfElementsLoadedByTick\noptions.maxAmountOfElementsRefreshedByTick = maxAmountOfElementsRefreshedByTick\noptions.verticalMode = verticalMode\noptions.verticalModeBottomSide = verticalModeBottomSide\noptions.defaultSorting = defaultSorting\n\n\nlocal sorting=0\nif defaultSorting==\"items-asc\" then sorting = 1\nelseif defaultSorting==\"items-desc\" then sorting = 2\nelseif defaultSorting==\"quantity-asc\" then sorting = 3\nelseif defaultSorting==\"quantity-desc\" then sorting = 4\nend\n\nlocal renderScript = [[\nlocal json = require('dkjson')\nlocal data = json.decode(getInput()) or {}\nlocal vmode = ]] .. tostring(verticalMode) .. [[\n\nlocal vmode_side = \"]] .. verticalModeBottomSide .. [[\"\n\nif items == nil or data[7] then items = {} end\nif page == nil or data[7] then page = 1 end\nif screenTitle == nil or data[7] then page = data[6] or \"\" end\nif sorting == nil or data[7] then sorting = ]] .. sorting .. [[ end\n\nlocal images = {}\n\nif data ~= {} then\n items[#items+1] = {\n data[1],\n data[2],\n data[3],\n data[4],\n data[5],\n data[8]\n }\n setOutput(#items)\n data = nil\nend\n\nlocal rx,ry = getResolution()\nlocal cx, cy = getCursor()\nif vmode then\n ry,rx = getResolution()\n cy, cx = getCursor()\n cx = rx - cx\n if vmode_side == \"right\" then\n cy = ry - cy\n cx = rx - cx\n end\nend\n\nlocal back=createLayer()\nlocal front=createLayer()\n\nfont_size = ]] .. fontSize .. [[\n\nlocal mini=loadFont('Play',12)\nlocal small=loadFont('Play',14)\nlocal smallBold=loadFont('Play-Bold',18)\nlocal itemName=loadFont('Play-Bold',font_size)\nlocal medV=loadFont('Play-Bold', 25)\nlocal bigV=loadFont('Play-Bold', 30)\nlocal big=loadFont('Play',38)\n\nsetBackgroundColor( 15/255,24/255,29/255)\n\nsetDefaultStrokeColor( back,Shape_Line,0,0,0,0.5)\nsetDefaultShadow( back,Shape_Line,6,0,0,0,0.5)\n\nsetDefaultFillColor( front,Shape_BoxRounded,249/255,212/255,123/255,1)\nsetDefaultFillColor( front,Shape_Text,0,0,0,1)\nsetDefaultFillColor( front,Shape_Box,0.075,0.125,0.156,1)\nsetDefaultFillColor( front,Shape_Text,0.710,0.878,0.941,1)\n\nfunction 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\n\nfunction round(a,b)if b then return utils.round(a/b)*b end;return a>=0 and math.floor(a+0.5)or math.ceil(a-0.5)end\n\nfunction 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\n\nfunction renderHeader(title, subtitle)\n local h_factor = 12\n local h = 35\n if subtitle ~= nil and subtitle ~= \"\" and subtitle ~= \"-\" then\n h = 50\n end\n addLine( back,0,h+12,rx,h+12)\n addBox(front,0,12,rx,h)\n if subtitle ~= nil and subtitle ~= \"\" and subtitle ~= \"-\" then\n addText(front,big,subtitle,44,50)\n addText(front,smallBold,title,rx-275,40)\n else\n addText(front,smallBold,title,44,35)\n end\nend\n\nlocal storageBar = createLayer()\nsetDefaultFillColor(storageBar,Shape_Text,110/255,166/255,181/255,1)\nsetDefaultFillColor(storageBar,Shape_Box,0.075,0.125,0.156,1)\nsetDefaultFillColor(storageBar,Shape_Line,1,1,1,1)\n\nlocal storageDark = createLayer()\nsetDefaultFillColor(storageDark,Shape_Text,63/255,92/255,102/255,1)\nsetDefaultFillColor(storageDark,Shape_Box,13/255,24/255,28/255,1)\n\nlocal buttonHover = createLayer()\nsetDefaultFillColor(buttonHover,Shape_Box,249/255,212/255,123/255,1)\nsetDefaultFillColor(buttonHover,Shape_Text,0,0,0,1)\n\nlocal colorLayer = createLayer()\nlocal imagesLayer = createLayer()\n\nif vmode then\n local r = 90\n local tx = ry\n local ty = 0\n if vmode_side == \"left\" then\n r = r + 180\n tx = 0\n ty = rx\n end\n setLayerTranslation(back, tx,ty)\n setLayerRotation(back, math.rad(r))\n setLayerTranslation(front, tx, ty)\n setLayerRotation(front, math.rad(r))\n setLayerTranslation(storageBar, tx, ty)\n setLayerRotation(storageBar, math.rad(r))\n setLayerTranslation(colorLayer, tx, ty)\n setLayerRotation(colorLayer, math.rad(r))\n setLayerTranslation(imagesLayer, tx, ty)\n setLayerRotation(imagesLayer, math.rad(r))\n setLayerTranslation(buttonHover, tx, ty)\n setLayerRotation(buttonHover, math.rad(r))\n setLayerTranslation(storageDark, tx, ty)\n setLayerRotation(storageDark, math.rad(r))\nend\nfunction renderResistanceBar(title, quantity, max, percent, item_id, x, y, w, h, withTitle, withIcon)\n local colorPercent = percent\n if percent > 100 then colorPercent = 100 end\n local r,g,b = getRGBGradient(colorPercent/100,177/255,42/255,42/255,249/255,212/255,123/255,34/255,177/255,76/255)\n\n local quantity_x_pos = font_size * 6.7\n local percent_x_pos = font_size * 2\n\n addBox(storageBar,x,y,w,h)\n\n if withTitle then\n addText(storageBar, small, \"ITEMS\", x, y-5)\n setNextTextAlign(storageDark, AlignH_Center, AlignV_Bottom)\n addText(storageDark, small, \"MAX VOLUME\", x+(w*0.6), y-3)\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom)\n addText(storageBar, small, \"QUANTITY\", x+(w*0.78), y-3)\n setNextTextAlign(storageBar, AlignH_Right, AlignV_Bottom)\n addText(storageBar, small, \"STORAGE\", rx-x, y-5)\n end\n\n local pos_y = y+(h/2)-2\n\n if item_id and tonumber(item_id) > 0 and images[item_id] and withIcon then\n addImage(imagesLayer, images[item_id], x+10, y+font_size*.1, font_size*1.3, font_size*1.2)\n end\n\n setNextTextAlign(storageBar, AlignH_Left, AlignV_Middle)\n addText(storageBar, itemName, title, x+20+font_size, pos_y)\n\n setNextFillColor(colorLayer, r, g, b, 1)\n addBox(colorLayer,x,y+h-3,w*(colorPercent)/100,3)\n\n setNextTextAlign(storageDark, AlignH_Center, AlignV_Middle)\n addText(storageDark, itemName, format_number(max) .. ' L', x+(w*0.6), pos_y)\n\n setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle)\n addText(storageBar, itemName, format_number(quantity), x+(w*0.78), pos_y)\n\n setNextFillColor(colorLayer, r, g, b, 1)\n setNextTextAlign(colorLayer, AlignH_Right, AlignV_Middle)\n addText(colorLayer, itemName, format_number(percent) ..\"%\", rx-x-5, pos_y)\nend\n\nrenderHeader('STORAGE MONITORING v]] .. version .. [[', screenTitle)\n\nstart_h = 75\nif screenTitle ~= nil and screenTitle ~= \"\" then\n start_h = 100\nend\n\n\nlocal h = font_size + font_size / 2\n\nlocal loadedImages = 0\nif data ~= {} then\n for _,item in ipairs(items) do\n if item[1] and images[item[6] ] == nil and loadedImages <= 15 then\n loadedImages = loadedImages + 1\n images[item[6] ] = loadImage(item[5])\n end\n end\nend\n\nfor i,container in ipairs(items) do\n if container[1] then\n renderResistanceBar(container[1], container[2], container[3], container[4], container[6], 44, start_h, rx-88, h, i==1, i<=16)\n start_h = start_h+h+5\n end\nend\nrequestAnimationFrame(100)\n]]\n\n--[[\n\tsplit a string on a delimiter By jericho\n]]\nfunction strSplit(a,b)result={}for c in(a..b):gmatch(\"(.-)\"..b)do table.insert(result,c)end;return result end\n\n--[[\n return RGB colors calculated from a gradient between two colors\n]]\nfunction 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\n\n--[[\n\tformatting numbers by adding a space between thousands by Jericho\n]]\nfunction 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\n\ncore = nil\ndatabank = nil\nscreens = {}\nfor slot_name, slot in pairs(unit) do\n if\n type(slot) == \"table\"\n and type(slot.export) == \"table\"\n and slot.getClass\n then\n if slot.getClass():lower():find(\"coreunit\") then\n core = slot\n end\n if slot.getClass():lower() == 'screenunit' then\n slot.slotname = slot_name\n table.insert(screens,slot)\n slot.setRenderScript(renderScript)\n end\n if slot.getClass():lower() == 'databankunit' then\n databank = slot\n end\n end\nend\nif #screens == 0 then\n system.print(\"No Screen Detected\")\nelse\n --sorting screens by slotname to be sure the display is not changing\n table.sort(screens, function(a,b) return a.slotname < b.slotname end)\n local plural = \"\"\n if #screens > 1 then plural = \"s\" end\n system.print(#screens .. \" screen\" .. plural .. \" Connected\")\nend\nif core == nil then\n system.print(\"No Core Detected\")\nelse\n system.print(\"Core Connected\")\nend\nif databank == nil then\n system.print(\"No Databank Detected\")\nelse\n system.print(\"Databank Connected\")\n if (databank.hasKey(\"options\")) and (useDatabankValues == true) then\n local db_options = json.decode(databank.getStringValue(\"options\"))\n for key, value in pairs(options) do\n if db_options[key] then options[key] = db_options[key] end\n end\n system.print(\"Options Loaded From Databank\")\n else\n system.print(\"Options Loaded From LUA Parameters\")\n end\nend\nprefixes = {\n options.containerMonitoringPrefix_screen1,\n options.containerMonitoringPrefix_screen2,\n options.containerMonitoringPrefix_screen3,\n options.containerMonitoringPrefix_screen4,\n options.containerMonitoringPrefix_screen5,\n options.containerMonitoringPrefix_screen6,\n options.containerMonitoringPrefix_screen7,\n options.containerMonitoringPrefix_screen8,\n options.containerMonitoringPrefix_screen9\n}\ntitles = {\n options.screenTitle1,\n options.screenTitle2,\n options.screenTitle3,\n options.screenTitle4,\n options.screenTitle5,\n options.screenTitle6,\n options.screenTitle7,\n options.screenTitle8,\n options.screenTitle9\n}\nelementsIdList = {}\nif core ~= nil then\n elementsIdList = core.getElementIdList()\nend\nstorageIdList= {}\ninitIndex = 0\ninitFinished = false\nscreens_displayed = false\n\n--Nested Coroutines by Jericho\ncoroutinesTable = {}\n--all functions here will become a coroutine\nMyCoroutines = {\n function()\n if not initFinished then\n system.print(\"Loading contructs elements (\" .. #elementsIdList .. \" elements detected)\")\n for i = 1, #elementsIdList, 1 do\n initIndex = i\n local id = elementsIdList[i]\n local elementType = core.getElementDisplayNameById(id):lower()\n if elementType:lower():find(\"container\") then\n table.insert(storageIdList, id)\n end\n if (i%options.maxAmountOfElementsLoadedByTick) == 0 then\n system.print(i .. ' elements scanned on ' .. #elementsIdList .. ' with ' .. #storageIdList .. \" identified\")\n coroutine.yield(coroutinesTable[1])\n end\n end\n if initIndex == #elementsIdList then\n system.print(#elementsIdList .. \" scanned with \" .. #storageIdList .. \" storage elements identified\")\n initFinished = true\n end\n end\n end,\n function()\n local html = ''\n local storage_elements = {}\n for elemindex,id in ipairs(storageIdList) do\n local elementType = core.getElementDisplayNameById(id)\n if elementType:lower():find(\"container\") then\n local elementName = core.getElementNameById(id)\n if elementName:lower():find(prefixes[1]:lower())\n or elementName:lower():find(prefixes[2]:lower())\n or elementName:lower():find(prefixes[3]:lower())\n or elementName:lower():find(prefixes[4]:lower())\n or elementName:lower():find(prefixes[5]:lower())\n or elementName:lower():find(prefixes[6]:lower())\n or elementName:lower():find(prefixes[7]:lower())\n or elementName:lower():find(prefixes[8]:lower())\n or elementName:lower():find(prefixes[9]:lower())\n then\n local container = {}\n local splitted = strSplit(elementName, '_')\n local name = splitted[2]\n local ingredient = system.getItem(name)\n local container_size = \"XS\"\n local container_amount = 1\n local container_empty_mass = 0\n local container_volume = 0\n local contentQuantity = 0\n local percent_fill = 0\n if not elementType:lower():find(\"hub\") then\n local containerMaxHP = core.getElementMaxHitPointsById(id)\n if containerMaxHP > 68000 then\n container_size = \"XXL\"\n container_empty_mass = 88410\n container_volume = 512000 * (options.container_proficiency_lvl * 0.1) + 512000\n elseif containerMaxHP > 33000 then\n container_size = \"XL\"\n container_empty_mass = 44210\n container_volume = 256000 * (options.container_proficiency_lvl * 0.1) + 256000\n elseif containerMaxHP > 17000 then\n container_size = \"L\"\n container_empty_mass = 14842.7\n container_volume = 128000 * (options.container_proficiency_lvl * 0.1) + 128000\n elseif containerMaxHP > 7900 then\n container_size = \"M\"\n container_empty_mass = 7421.35\n container_volume = 64000 * (options.container_proficiency_lvl * 0.1) + 64000\n elseif containerMaxHP > 900 then\n container_size = \"S\"\n container_empty_mass = 1281.31\n container_volume = 8000 * (options.container_proficiency_lvl * 0.1) + 8000\n else\n container_size = \"XS\"\n container_empty_mass = 229.09\n container_volume = 1000 * (options.container_proficiency_lvl * 0.1) + 1000\n end\n else\n if splitted[3] then\n container_size = splitted[3]\n end\n if splitted[4] then\n container_amount = splitted[4]\n end\n local volume = 0\n container_volume_list = {xxl=512000, xl=256000, l=128000, m=64000, s=8000, xs=1000}\n container_size = container_size:lower()\n if container_volume_list[container_size] then\n volume = container_volume_list[container_size]\n end\n container_volume = (volume * options.container_proficiency_lvl * 0.1 + volume) * tonumber(container_amount)\n container_empty_mass = 55.8\n end\n local totalMass = core.getElementMassById(id)\n local contentMassKg = totalMass - container_empty_mass\n container.id = id\n container.itemid = ingredient.id\n container.realName = elementName\n container.prefix = splitted[1] .. \"_\"\n container.name = name\n container.ingredient = ingredient\n container.quantity = contentMassKg / (ingredient.unitMass - (ingredient.unitMass * (options.container_optimization_lvl * 0.05)))\n container.volume = container_volume\n container.percent = utils.round((ingredient.unitVolume * container.quantity) * 100 / container_volume)\n if ingredient.name == \"InvalidItem\" then\n container.percent = 0\n container.quantity = 0\n end\n if container.percent > 100 then container.percent = 100 end\n table.insert(storage_elements, container)\n end\n end\n if (elemindex%options.maxAmountOfElementsRefreshedByTick) == 0 then\n coroutine.yield(coroutinesTable[2])\n end\n end\n\n -- group by name and screen\n local groupped = {}\n if groupByItemName then\n for _,v in pairs(storage_elements) do\n local prefix = v.prefix:lower()\n if groupped[prefix .. v.itemid] then\n groupped[prefix .. v.itemid].quantity = groupped[prefix .. v.itemid].quantity + v.quantity\n groupped[prefix .. v.itemid].volume = groupped[prefix .. v.itemid].volume + v.volume\n groupped[prefix .. v.itemid].percent = (v.ingredient.unitVolume * groupped[prefix .. v.itemid].quantity) * 100 / groupped[prefix .. v.itemid].volume\n else\n groupped[prefix .. v.itemid] = v\n end\n end\n else\n groupped = storage_elements\n end\n\n -- sorting by tier\n local tiers = {}\n tiers[1] = {} --tier 0 (thx to Belorion#3127 for pointing Oxygen and Hydrogen are Tier 0 and not 1)\n tiers[2] = {} --tier 1\n tiers[3] = {} --tier 2\n tiers[4] = {} --tier 3\n tiers[5] = {} --tier 4\n tiers[6] = {} --tier 5\n for _,v in pairs(groupped) do\n table.insert(tiers[v.ingredient.tier+1],v)\n end\n\n -- sorting by name\n for k,v in pairs(tiers) do\n table.sort(tiers[k], function(a,b) return a.ingredient.name:lower() < b.ingredient.name:lower() end)\n end\n \n if #screens > 0 and not screens_displayed then\n for index, screen in pairs(screens) do\n local prefix = prefixes[index]\n local title = titles[index]\n local refreshScreen=true\n local i = 1\n for tier_k,tier in pairs(tiers) do\n for _,container in pairs(tier) do\n if container.prefix:lower():find(prefix:lower()) then\n local item_name = container.ingredient.locDisplayNameWithSize\n if container.ingredient.name == 'InvalidItem' then\n item_name = 'Invalid Item Id'\n end\n local storage_data = {\n item_name,\n utils.round(container.quantity * (10 ^ options.QuantityRoundedDecimals)) / (10 ^ options.QuantityRoundedDecimals),\n utils.round(container.volume),\n utils.round(container.percent * (10 ^ options.PercentRoundedDecimals)) / (10 ^ options.PercentRoundedDecimals),\n container.ingredient.iconPath,\n titles[index],\n refreshScreen,\n container.ingredient.id,\n screens_displayed\n }\n screen.setScriptInput(json.encode(storage_data))\n refreshScreen = false\n while tonumber(screen.getScriptOutput()) ~= i do\n coroutine.yield(coroutinesTable[2])\n end\n i = i+1\n end\n end\n end\n screen.setScriptInput(json.encode(nil))\n end\n screens_displayed = true\n end\n unit.exit()\n end\n}\n\nfunction initCoroutines()\n for _,f in pairs(MyCoroutines) do\n local co = coroutine.create(f)\n table.insert(coroutinesTable, co)\n end\nend\n\ninitCoroutines()\n\nrunCoroutines = function()\n for i,co in ipairs(coroutinesTable) do\n if coroutine.status(co) == \"dead\" then\n coroutinesTable[i] = coroutine.create(MyCoroutines[i])\n end\n if coroutine.status(co) == \"suspended\" then\n assert(coroutine.resume(co))\n end\n end\nend\n\nMainCoroutine = coroutine.create(runCoroutines)\n\nsystem.showScreen(true)","filter":{"args":[],"signature":"onStart()","slotKey":"-1"},"key":"1"}],"methods":[],"slots":{"0":{"name":"slot1","type":{"events":[],"methods":[]}},"1":{"name":"slot2","type":{"events":[],"methods":[]}},"2":{"name":"slot3","type":{"events":[],"methods":[]}},"3":{"name":"slot4","type":{"events":[],"methods":[]}},"4":{"name":"slot5","type":{"events":[],"methods":[]}},"5":{"name":"slot6","type":{"events":[],"methods":[]}},"6":{"name":"slot7","type":{"events":[],"methods":[]}},"7":{"name":"slot8","type":{"events":[],"methods":[]}},"8":{"name":"slot9","type":{"events":[],"methods":[]}},"9":{"name":"slot10","type":{"events":[],"methods":[]}},"-5":{"name":"library","type":{"events":[],"methods":[]}},"-4":{"name":"system","type":{"events":[],"methods":[]}},"-3":{"name":"player","type":{"events":[],"methods":[]}},"-2":{"name":"construct","type":{"events":[],"methods":[]}},"-1":{"name":"unit","type":{"events":[],"methods":[]}}}} diff --git a/source/unit/onStart.lua b/source/unit/onStart.lua index 587c7b2..36c9ea2 100644 --- a/source/unit/onStart.lua +++ b/source/unit/onStart.lua @@ -34,18 +34,14 @@ maxAmountOfElementsLoadedByTick = 5000 --export: the maximum number of element l maxAmountOfElementsRefreshedByTick = 200 --export: the maximum number of element refreshed by tick of the coroutine when refreshing values --vertical mode code based on a suggestion by Merl verticalMode = false --export: rotate the screen 90deg (bottom on right) ---[[ - -- BETA OPTIONS -]] -ARViewBeta = false --export: BETA - Enable the AR view of Storage -ARViewBetaDisplayIcon = true --export: BETA - Display the item icon in AR -ARViewBetaDisplayItemName = true --export: BETA - display the item name in AR View +verticalModeBottomSide = "right" --export: when vertical mode is enabled, on which side the bottom of the screen is positioned ("left" or "right") +--defaultSorting = "none" --export: the default sorting of items on the screen: "none": like in the container, "items-asc": ascending sorting on the name, "items-desc": descending sorting on the name, "quantity-asc": ascending on the quantity, "quantity-desc": descending on the quantity --[[ INIT ]] -local version = '4.2.0' +local version = '4.3.0' system.print("----------------------------------") system.print("DU-Storage-Monitoring version " .. version) @@ -79,23 +75,60 @@ options.fontSize = fontSize options.maxAmountOfElementsLoadedByTick = maxAmountOfElementsLoadedByTick options.maxAmountOfElementsRefreshedByTick = maxAmountOfElementsRefreshedByTick options.verticalMode = verticalMode +options.verticalModeBottomSide = verticalModeBottomSide +options.defaultSorting = defaultSorting + + +local sorting=0 +if defaultSorting=="items-asc" then sorting = 1 +elseif defaultSorting=="items-desc" then sorting = 2 +elseif defaultSorting=="quantity-asc" then sorting = 3 +elseif defaultSorting=="quantity-desc" then sorting = 4 +end local renderScript = [[ local json = require('dkjson') local data = json.decode(getInput()) or {} local vmode = ]] .. tostring(verticalMode) .. [[ -local rx,ry +local vmode_side = "]] .. verticalModeBottomSide .. [[" + +if items == nil or data[7] then items = {} end +if page == nil or data[7] then page = 1 end +if screenTitle == nil or data[7] then page = data[6] or "" end +if sorting == nil or data[7] then sorting = ]] .. sorting .. [[ end + +local images = {} + +if data ~= {} then + items[#items+1] = { + data[1], + data[2], + data[3], + data[4], + data[5], + data[8] + } + setOutput(#items) + data = nil +end + +local rx,ry = getResolution() +local cx, cy = getCursor() if vmode then - ry,rx = getResolution() -else - rx,ry = getResolution() + ry,rx = getResolution() + cy, cx = getCursor() + cx = rx - cx + if vmode_side == "right" then + cy = ry - cy + cx = rx - cx + end end local back=createLayer() local front=createLayer() -font_size = data[1][2] +font_size = ]] .. fontSize .. [[ local mini=loadFont('Play',12) local small=loadFont('Play',14) @@ -146,23 +179,41 @@ local storageDark = createLayer() setDefaultFillColor(storageDark,Shape_Text,63/255,92/255,102/255,1) setDefaultFillColor(storageDark,Shape_Box,13/255,24/255,28/255,1) +local buttonHover = createLayer() +setDefaultFillColor(buttonHover,Shape_Box,249/255,212/255,123/255,1) +setDefaultFillColor(buttonHover,Shape_Text,0,0,0,1) + local colorLayer = createLayer() +local imagesLayer = createLayer() if vmode then - local from_top = 10 - setLayerTranslation(back, ry-from_top,0) - setLayerRotation(back, math.rad(90)) - setLayerTranslation(front, ry-from_top,0) - setLayerRotation(front, math.rad(90)) - setLayerTranslation(storageBar, ry-from_top,0) - setLayerRotation(storageBar, math.rad(90)) - setLayerTranslation(storageDark, ry-from_top,0) - setLayerRotation(storageDark, math.rad(90)) - setLayerTranslation(colorLayer, ry-from_top,0) - setLayerRotation(colorLayer, math.rad(90)) + local r = 90 + local tx = ry + local ty = 0 + if vmode_side == "left" then + r = r + 180 + tx = 0 + ty = rx + end + setLayerTranslation(back, tx,ty) + setLayerRotation(back, math.rad(r)) + setLayerTranslation(front, tx, ty) + setLayerRotation(front, math.rad(r)) + setLayerTranslation(storageBar, tx, ty) + setLayerRotation(storageBar, math.rad(r)) + setLayerTranslation(colorLayer, tx, ty) + setLayerRotation(colorLayer, math.rad(r)) + setLayerTranslation(imagesLayer, tx, ty) + setLayerRotation(imagesLayer, math.rad(r)) + setLayerTranslation(buttonHover, tx, ty) + setLayerRotation(buttonHover, math.rad(r)) + setLayerTranslation(storageDark, tx, ty) + setLayerRotation(storageDark, math.rad(r)) end -function renderResistanceBar(title, quantity, max, percent, x, y, w, h, withTitle) - local r,g,b = getRGBGradient(percent/100,177/255,42/255,42/255,249/255,212/255,123/255,34/255,177/255,76/255) +function renderResistanceBar(title, quantity, max, percent, item_id, x, y, w, h, withTitle, withIcon) + local colorPercent = percent + if percent > 100 then colorPercent = 100 end + local r,g,b = getRGBGradient(colorPercent/100,177/255,42/255,42/255,249/255,212/255,123/255,34/255,177/255,76/255) local quantity_x_pos = font_size * 6.7 local percent_x_pos = font_size * 2 @@ -172,44 +223,61 @@ function renderResistanceBar(title, quantity, max, percent, x, y, w, h, withTitl if withTitle then addText(storageBar, small, "ITEMS", x, y-5) setNextTextAlign(storageDark, AlignH_Center, AlignV_Bottom) - addText(storageDark, small, "MAX VOLUME", x+(w*0.5), y-3) + addText(storageDark, small, "MAX VOLUME", x+(w*0.6), y-3) setNextTextAlign(storageBar, AlignH_Center, AlignV_Bottom) - addText(storageBar, small, "QUANTITY", x+(w*0.75), y-3) - addText(storageBar, small, "STORAGE", x+w-60, y-5) + addText(storageBar, small, "QUANTITY", x+(w*0.78), y-3) + setNextTextAlign(storageBar, AlignH_Right, AlignV_Bottom) + addText(storageBar, small, "STORAGE", rx-x, y-5) end local pos_y = y+(h/2)-2 + if item_id and tonumber(item_id) > 0 and images[item_id] and withIcon then + addImage(imagesLayer, images[item_id], x+10, y+font_size*.1, font_size*1.3, font_size*1.2) + end + setNextTextAlign(storageBar, AlignH_Left, AlignV_Middle) - addText(storageBar, itemName, title, x+10, pos_y) + addText(storageBar, itemName, title, x+20+font_size, pos_y) setNextFillColor(colorLayer, r, g, b, 1) - addBox(colorLayer,x,y+h-3,w*(percent)/100,3) + addBox(colorLayer,x,y+h-3,w*(colorPercent)/100,3) setNextTextAlign(storageDark, AlignH_Center, AlignV_Middle) - addText(storageDark, itemName, format_number(max) .. ' L', x+(w*0.5), pos_y) + addText(storageDark, itemName, format_number(max) .. ' L', x+(w*0.6), pos_y) setNextTextAlign(storageBar, AlignH_Center, AlignV_Middle) - addText(storageBar, itemName, format_number(quantity), x+(w*0.75), pos_y) + addText(storageBar, itemName, format_number(quantity), x+(w*0.78), pos_y) setNextFillColor(colorLayer, r, g, b, 1) setNextTextAlign(colorLayer, AlignH_Right, AlignV_Middle) - addText(colorLayer, itemName, format_number(percent) .."%", x+w-10, pos_y) + addText(colorLayer, itemName, format_number(percent) .."%", rx-x-5, pos_y) end -local screen_title = data[1][1] -renderHeader('STORAGE MONITORING v]] .. version .. [[', screen_title) +renderHeader('STORAGE MONITORING v]] .. version .. [[', screenTitle) start_h = 75 -if screen_title ~= nil and screen_title ~= "" then +if screenTitle ~= nil and screenTitle ~= "" then start_h = 100 end local h = font_size + font_size / 2 -for i,container in ipairs(data[2]) do - renderResistanceBar(container[1], container[2], container[3], container[4], 44, start_h, rx-88, h, i==1) - start_h = start_h+h+5 + +local loadedImages = 0 +if data ~= {} then + for _,item in ipairs(items) do + if item[1] and images[item[6] ] == nil and loadedImages <= 15 then + loadedImages = loadedImages + 1 + images[item[6] ] = loadImage(item[5]) + end + end +end + +for i,container in ipairs(items) do + if container[1] then + renderResistanceBar(container[1], container[2], container[3], container[4], container[6], 44, start_h, rx-88, h, i==1, i<=16) + start_h = start_h+h+5 + end end requestAnimationFrame(100) ]] @@ -308,18 +376,7 @@ end storageIdList= {} initIndex = 0 initFinished = false - -constructPos = construct.getWorldPosition() -constructRight = construct.getWorldRight() -constructForward = construct.getWorldForward() -constructUp = construct.getWorldUp() - ---[[ - Convert a table in local coordinates to a table in world coordinates by Jericho inspired by Koruzarius - Source : https://github.com/Jericho1060/DualUniverse/blob/master/Vectors/localToWorldPos.lua -]]-- -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 - +screens_displayed = false --Nested Coroutines by Jericho coroutinesTable = {} @@ -421,8 +478,6 @@ MyCoroutines = { container.id = id container.itemid = ingredient.id container.realName = elementName - local elementPos = core.getElementPositionById(id) - local screenpos = library.getPointOnScreen(ConvertLocalToWorld(elementPos, constructPos, constructRight, constructForward, constructUp)) container.prefix = splitted[1] .. "_" container.name = name container.ingredient = ingredient @@ -434,22 +489,6 @@ MyCoroutines = { container.quantity = 0 end if container.percent > 100 then container.percent = 100 end - local r,g,b = getRGBGradient(container.percent/100,177,42,42,249,212,123,34,177,76) - local max_distance = 250 - local img_width = 3 - elemhtml = [[ -
- ]] - if ARViewBetaDisplayIcon then - elemhtml = elemhtml .. [[
]] - end - if ARViewBetaDisplayItemName then - elemhtml = elemhtml .. container.ingredient.locDisplayNameWithSize .. [[
]] - end - elemhtml = elemhtml .. container.percent .. [[% -
- ]] - html = html .. elemhtml table.insert(storage_elements, container) end end @@ -492,12 +531,12 @@ MyCoroutines = { table.sort(tiers[k], function(a,b) return a.ingredient.name:lower() < b.ingredient.name:lower() end) end - if #screens > 0 then + if #screens > 0 and not screens_displayed then for index, screen in pairs(screens) do - screen_data = {} local prefix = prefixes[index] local title = titles[index] - + local refreshScreen=true + local i = 1 for tier_k,tier in pairs(tiers) do for _,container in pairs(tier) do if container.prefix:lower():find(prefix:lower()) then @@ -509,25 +548,27 @@ MyCoroutines = { item_name, utils.round(container.quantity * (10 ^ options.QuantityRoundedDecimals)) / (10 ^ options.QuantityRoundedDecimals), utils.round(container.volume), - utils.round(container.percent * (10 ^ options.PercentRoundedDecimals)) / (10 ^ options.PercentRoundedDecimals) + utils.round(container.percent * (10 ^ options.PercentRoundedDecimals)) / (10 ^ options.PercentRoundedDecimals), + container.ingredient.iconPath, + titles[index], + refreshScreen, + container.ingredient.id, + screens_displayed } - table.insert(screen_data, storage_data) + screen.setScriptInput(json.encode(storage_data)) + refreshScreen = false + while tonumber(screen.getScriptOutput()) ~= i do + coroutine.yield(coroutinesTable[2]) + end + i = i+1 end end end - local data_to_send = { - { - titles[index], - options.fontSize - }, - screen_data - } - screen.setScriptInput(json.encode(data_to_send)) + screen.setScriptInput(json.encode(nil)) end + screens_displayed = true end - if ARViewBeta then - system.setScreen(html) - end + unit.exit() end }