From 2f89a6f47df2edd05d8a34ee31bdef5316df4c12 Mon Sep 17 00:00:00 2001 From: Theodore Di Pietro Date: Mon, 7 Sep 2026 15:39:40 +0000 Subject: [PATCH] Enable minimal static analysis --- .emmyrc.json | 41 +++++++++++++ .gitignore | 1 - spec/GenerateBuilds.lua | 5 ++ spec/System/TestBuilds_spec.lua | 2 + spec/System/TestImport_spec.lua | 10 +++- spec/TestBuilds/3.13/Dual Savior.lua | 2 +- src/Classes/CalcBreakdownControl.lua | 48 +++++++++------ src/Classes/Control.lua | 1 - src/Classes/ControlHost.lua | 2 +- src/Classes/ImportTab.lua | 3 +- src/Classes/ItemDBControl.lua | 3 + src/Classes/ItemsTab.lua | 1 + src/Classes/ModDB.lua | 2 +- src/Classes/ModList.lua | 2 +- src/Classes/ModStore.lua | 31 +++++++++- src/Classes/NotableDBControl.lua | 4 +- src/Classes/PartyTab.lua | 8 +-- src/Classes/PassiveSpec.lua | 2 + src/Classes/PassiveTreeView.lua | 4 ++ src/Classes/PathControl.lua | 2 +- src/Classes/SharedItemListControl.lua | 2 +- src/Classes/TooltipHost.lua | 4 ++ src/Classes/TradeQuery.lua | 2 +- src/Classes/TradeQueryGenerator.lua | 2 +- src/HeadlessWrapper.lua | 1 + src/Launch.lua | 51 +++++++++++++++- src/Modules/CalcActiveSkill.lua | 2 +- src/Modules/CalcBase.lua | 2 +- src/Modules/CalcBreakdown.lua | 4 +- src/Modules/CalcDefence.lua | 2 +- src/Modules/CalcOffence.lua | 8 +-- src/Modules/CalcPerform.lua | 10 +--- src/Modules/CalcTools.lua | 2 +- src/Modules/Data.lua | 9 +-- src/Modules/DataLegionLookUpTableHelper.lua | 4 +- src/Modules/Main.lua | 13 +++- src/UpdateCheck.lua | 42 +++++++++---- types/busted.lua | 53 +++++++++++++++++ types/lcurl/safe.lua | 66 +++++++++++++++++++++ types/lfs.lua | 13 ++++ types/lua-utf8.lua | 42 +++++++++++++ types/lzip.lua | 31 ++++++++++ types/sha1.lua | 18 ++++++ 43 files changed, 478 insertions(+), 79 deletions(-) create mode 100644 .emmyrc.json create mode 100644 types/busted.lua create mode 100644 types/lcurl/safe.lua create mode 100644 types/lfs.lua create mode 100644 types/lua-utf8.lua create mode 100644 types/lzip.lua create mode 100644 types/sha1.lua diff --git a/.emmyrc.json b/.emmyrc.json new file mode 100644 index 00000000000..9fac097fc81 --- /dev/null +++ b/.emmyrc.json @@ -0,0 +1,41 @@ +{ + "runtime": { + "version": "LuaJIT", + "requirePattern": [ + "?.lua", + "?/init.lua" + ], + "requireLikeFunction": [ + "LoadModule", + "PLoadModule" + ], + "nonstandardSymbol": [ + "+=" + ] + }, + "diagnostics": { + "severity": { + "global-in-non-module": "hint", + "inject-field": "hint", + "unnecessary-assert": "hint", + "unnecessary-if": "hint" + } + }, + "workspace": { + "workspaceRoots": [ + "./src", + "./spec" + ], + "library": [ + "./runtime/lua", + "./types" + ], + "ignoreGlobs": [ + "**/TreeData/**", + "**/Data/ModCache.lua", + "**/Data/Skills/**", + "**/Data/StatDescriptions/stat_descriptions.lua", + "**/Export/**" + ] + } +} diff --git a/.gitignore b/.gitignore index 77eb432dbfd..a08845c3e74 100644 --- a/.gitignore +++ b/.gitignore @@ -43,5 +43,4 @@ runtime/SimpleGraphic/SimpleGraphic.log src/poe_api_response.json runtime/SimpleGraphic/Screenshots -.emmyrc.json .luarc.json diff --git a/spec/GenerateBuilds.lua b/spec/GenerateBuilds.lua index c82d7848ca0..280587472d6 100644 --- a/spec/GenerateBuilds.lua +++ b/spec/GenerateBuilds.lua @@ -1,3 +1,5 @@ +local lfs = require("lfs") + local function fetchBuilds(path, buildList) buildList = buildList or {} for file in lfs.dir(path) do @@ -45,6 +47,9 @@ local buildList = fetchBuilds("../spec/TestBuilds") for filename, testBuild in pairs(buildList) do loadBuildFromXML(testBuild) local fileHnd, errMsg = io.open(filename:gsub("^(.+)%..+$", "%1.lua"), "w+") + if not fileHnd then + error(errMsg) + end fileHnd:write("return {\n xml = [[") fileHnd:write(testBuild) fileHnd:write("]],\n ") diff --git a/spec/System/TestBuilds_spec.lua b/spec/System/TestBuilds_spec.lua index 826bea70e8a..be6140d51db 100644 --- a/spec/System/TestBuilds_spec.lua +++ b/spec/System/TestBuilds_spec.lua @@ -1,3 +1,5 @@ +local lfs = require("lfs") + local function fetchBuilds(path, buildList) buildList = buildList or {} for file in lfs.dir(path) do diff --git a/spec/System/TestImport_spec.lua b/spec/System/TestImport_spec.lua index 185e4a482ef..2983f4a7e2b 100644 --- a/spec/System/TestImport_spec.lua +++ b/spec/System/TestImport_spec.lua @@ -2,9 +2,12 @@ describe("TestImport", function() local dkjson = require "dkjson" local sampleJson, err = io.open("../spec/System/SampleCharacter.json", "r") - if err then - ConPrintf("Failed to read sample character response: %s", err) + if not sampleJson then + local errMsg = err or "unknown error" + ConPrintf("Failed to read sample character response: %s", errMsg) + error(errMsg) end + ---@cast sampleJson -? local sampleData = dkjson.decode(sampleJson:read("*a")).character sampleJson:close() @@ -131,6 +134,9 @@ describe("TestImport", function() local itemId = build.itemsTab.slots.Gloves.selItemId local item = build.itemsTab.items[itemId] + if not item then + error("Failed to import test item") + end local explicitMods = { } for _, modLine in ipairs(item.explicitModLines) do explicitMods[modLine.line] = modLine diff --git a/spec/TestBuilds/3.13/Dual Savior.lua b/spec/TestBuilds/3.13/Dual Savior.lua index db83e887942..e85b0adcea7 100644 --- a/spec/TestBuilds/3.13/Dual Savior.lua +++ b/spec/TestBuilds/3.13/Dual Savior.lua @@ -1061,7 +1061,7 @@ Triggers Level 20 Reflection when Equipped ["ManaLeechDuration"] = 0, ["EnergyShieldRecoveryRateMod"] = 1, ["ChaosResistOver75"] = 0, -["EnergyShieldRegenPercent"] = nan, +["EnergyShieldRegenPercent"] = 0 / 0, ["CurrentScorch"] = 0, ["SelfChillDuration"] = 100, ["BlockChance"] = 15, diff --git a/src/Classes/CalcBreakdownControl.lua b/src/Classes/CalcBreakdownControl.lua index 6db2299accb..f801705f145 100644 --- a/src/Classes/CalcBreakdownControl.lua +++ b/src/Classes/CalcBreakdownControl.lua @@ -31,9 +31,7 @@ function CalcBreakdownClass:CalcBreakdownControl(calcsTab) self.borderThickness = 2 self.controls.scrollBar = new("ScrollBarControl"):ScrollBarControl({ "RIGHT", self, "RIGHT" }, { -2, 0, 18, 0 }, 80, "VERTICAL", true) - self.controls.scrollBar.x = function() - return -self.borderThickness - end + self.controls.scrollBar.x = -self.borderThickness self.pinnedColour = { 0.25, 1, 0.25 } self.borderColour = { 0.33, 0.66, 0.33 } return self @@ -435,13 +433,22 @@ function CalcBreakdownClass:AddModSection(sectionData, modList) -- Modifier is from a passive node, add node name, and add node ID (used to show node location) local nodeId = row.mod.source:match("Tree:(%d+)") local tattooNodeId = row.mod.source:match("Tree:(%w+)") + local tree = build.spec.tree if nodeId then local nodeIdNumber = tonumber(nodeId) - local node = build.spec.nodes[nodeIdNumber] or build.spec.tree.nodes[nodeIdNumber] or build.latestTree.nodes[nodeIdNumber] - row.sourceName = node.dn - row.sourceNameNode = node - elseif tattooNodeId then - row.sourceName = build.spec.tree.tattoo.idMap[tattooNodeId] + ---@type table + local specNodes = build.spec.nodes + ---@type table? + local treeNodes = tree and tree.nodes + ---@type table? + local latestTreeNodes = build.latestTree and build.latestTree.nodes + local node = specNodes[nodeIdNumber] or (treeNodes and treeNodes[nodeIdNumber]) or (latestTreeNodes and latestTreeNodes[nodeIdNumber]) + if node then + row.sourceName = node.dn + row.sourceNameNode = node + end + elseif tattooNodeId and tree and tree.tattoo then + row.sourceName = tree.tattoo.idMap[tattooNodeId] end elseif sourceType == "Skill" then -- Extract skill name @@ -629,16 +636,22 @@ function CalcBreakdownClass:DrawBreakdownTable(viewPort, x, y, section) SetDrawColor(1, 1, 1) DrawImage(nil, viewerX, viewerY, 304, 304) local viewer = self.nodeViewer + ---@cast viewer PassiveTreeView viewer.zoom = 5 - local scale = self.calcsTab.build.spec.tree.size / 1500 - viewer.zoomX = -ttNode.x / scale - viewer.zoomY = -ttNode.y / scale - SetViewport(viewerX + 2, viewerY + 2, 300, 300) - viewer:Draw(self.calcsTab.build, { x = 0, y = 0, width = 300, height = 300 }, { }) - SetDrawLayer(nil, 30) - SetDrawColor(1, 0, 0) - DrawImage(viewer.highlightRing, 135, 135, 30, 30) - SetViewport() + local tree = self.calcsTab.build.spec.tree + if tree then + local scale = tree.size / 1500 + ---@type number + viewer.zoomX = -ttNode.x / scale + ---@type number + viewer.zoomY = -ttNode.y / scale + SetViewport(viewerX + 2, viewerY + 2, 300, 300) + viewer:Draw(self.calcsTab.build, { x = 0, y = 0, width = 300, height = 300 }, { }) + SetDrawLayer(nil, 30) + SetDrawColor(1, 0, 0) + DrawImage(viewer.highlightRing, 135, 135, 30, 30) + SetViewport() + end end SetDrawLayer(nil, 10) end @@ -697,6 +710,7 @@ function CalcBreakdownClass:Draw(viewPort) -- Content won't fit the screen height, so set the scrollbar width = self.contentWidth + scrollBar.width height = viewPort.height + ---@cast scrollBar any scrollBar.height = height - borderThickness * 2 scrollBar:SetContentDimension(self.contentHeight - borderThickness * 2, viewPort.height - borderThickness * 2) else diff --git a/src/Classes/Control.lua b/src/Classes/Control.lua index 22383af172c..7fd90dacb22 100644 --- a/src/Classes/Control.lua +++ b/src/Classes/Control.lua @@ -65,7 +65,6 @@ function ControlClass:Control(anchor, rect) return self end ----@generic T ---@alias Prop (fun(self: self): T) | T ---@param name string diff --git a/src/Classes/ControlHost.lua b/src/Classes/ControlHost.lua index 83563f758b4..b606c1cbaab 100644 --- a/src/Classes/ControlHost.lua +++ b/src/Classes/ControlHost.lua @@ -81,7 +81,7 @@ function ControlHostClass:ProcessControlsInput(inputEvents, viewPort) inputEvents[id] = nil end - local mOverControl = self:GetMouseOverControl(viewPort) + local mOverControl = self:GetMouseOverControl() -- Avoid calculating isMouseInRegion as much as possible as it's expensive if mOverControl and (not selControl or mOverControl.OnHoverKeyUp) then diff --git a/src/Classes/ImportTab.lua b/src/Classes/ImportTab.lua index a44f91bc0aa..14a4bddcc55 100644 --- a/src/Classes/ImportTab.lua +++ b/src/Classes/ImportTab.lua @@ -885,7 +885,7 @@ function ImportTabClass:DownloadItems(realm) end) end function ImportTabClass:DownloadSiteCharacterList(realm) - function FindMatchingStandardLeague(league) + local function FindMatchingStandardLeague(league) -- Find a Standard league name for a given league name -- Reference https://api.pathofexile.com/league?realm=pc if string.find(league, "Hardcore") then @@ -970,7 +970,6 @@ function ImportTabClass:DownloadSiteCharacterList(realm) self.lastAccountHash = common.sha1(accountName) main.lastAccountName = accountName main.gameAccounts[accountName] = main.gameAccounts[accountName] or {} - main.gameAccounts[accountName].sessionID = sessionID local leagueList = {} for i, char in ipairs(charList) do if not isValueInArray(leagueList, char.league) then diff --git a/src/Classes/ItemDBControl.lua b/src/Classes/ItemDBControl.lua index bc5c227bf3d..0188cd0d305 100644 --- a/src/Classes/ItemDBControl.lua +++ b/src/Classes/ItemDBControl.lua @@ -18,6 +18,9 @@ local ItemDBClass = newClass("ItemDBControl", "ListControl") ---@field byTitle? table ---@field loading boolean? +---@class UniqueItemDBData: ItemDBData +---@field byTitle table + ---@param anchor Anchor? ---@param rect Rect? ---@param itemsTab ItemsTab diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 0b3852aba58..f6506327b9e 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -87,6 +87,7 @@ end ---@class ItemsTab: UndoHandler, ControlHost, Control ---@field displayItem Item? +---@field items table local ItemsTabClass = newClass("ItemsTab", "UndoHandler", "ControlHost", "Control") ---@param build Build diff --git a/src/Classes/ModDB.lua b/src/Classes/ModDB.lua index ffaf1e5ea59..e05ea70ecce 100644 --- a/src/Classes/ModDB.lua +++ b/src/Classes/ModDB.lua @@ -259,7 +259,7 @@ function ModDBClass:ListInternal(context, result, cfg, flags, keywordFlags, sour if mod.type == "LIST" and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or mod.source:match("[^:]+") == source) then local value if mod[1] then - local value = context:EvalMod(mod, cfg) or nullValue + local value = context:EvalMod(mod, cfg) if value then t_insert(result, value) end diff --git a/src/Classes/ModList.lua b/src/Classes/ModList.lua index ec686674d77..b856e3287f4 100644 --- a/src/Classes/ModList.lua +++ b/src/Classes/ModList.lua @@ -204,7 +204,7 @@ function ModListClass:ListInternal(context, result, cfg, flags, keywordFlags, so if mod.name == modName and mod.type == "LIST" and band(flags, mod.flags) == mod.flags and MatchKeywordFlags(keywordFlags, mod.keywordFlags) and (not source or mod.source:match("[^:]+") == source) then local value if mod[1] then - local value = context:EvalMod(mod, cfg) or nullValue + local value = context:EvalMod(mod, cfg) if value then t_insert(result, value) end diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index 0dd30e41d66..3d17760e888 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -27,12 +27,37 @@ local conditionName = setmetatable({ }, { __index = function(t, var) return t[var] end }) --- TODO: very incomplete ---@class ModCfg ----@field flags number? bit mask ----@field keywordFlags number? +---@field flags integer? bit mask +---@field keywordFlags integer? ---@field skillName string? +---@field summonSkillName string? +---@field skillGem any? +---@field skillGrantedEffect any? +---@field skillPart integer? +---@field skillTypes table? +---@field skillCond table? +---@field skillDist number? +---@field slotName string? +---@field socketColor any? +---@field socketNum integer? ---@field source string? +---@field actor string? +---@field skillStats table? +---@field baseFlags table? +---@field dexterityGems integer? +---@field intelligenceGems integer? +---@field strengthGems integer? +---@field item Item? + +---@class SkillCfg: ModCfg +---@field flags integer +---@field keywordFlags integer +---@field skillName string +---@field skillGrantedEffect any +---@field skillPart integer +---@field skillTypes table +---@field skillCond table ---@class ModStore local ModStoreClass = newClass("ModStore") diff --git a/src/Classes/NotableDBControl.lua b/src/Classes/NotableDBControl.lua index 8253f4a471f..65a53648f2a 100644 --- a/src/Classes/NotableDBControl.lua +++ b/src/Classes/NotableDBControl.lua @@ -18,7 +18,7 @@ local function IsAnointableNode(node) return node.recipe and #node.recipe >= 1 end ----@class NotableDBControl : ListControl +---@class NotableDBControl : ListControl local NotableDBClass = newClass("NotableDBControl", "ListControl") ---@param itemsTab ItemsTab @@ -290,5 +290,5 @@ end ---@param index number ---@param node table function NotableDBClass:OnSelCopy(index, node) - Copy(item.dn) + Copy(node.dn) end \ No newline at end of file diff --git a/src/Classes/PartyTab.lua b/src/Classes/PartyTab.lua index da9af814916..8ccb770180e 100644 --- a/src/Classes/PartyTab.lua +++ b/src/Classes/PartyTab.lua @@ -195,10 +195,10 @@ function PartyTabClass:PartyTab(build) -- Parse the XML local dbXML, errMsg = common.xml.ParseXML(self.importCodeXML) if not dbXML then - launch:ShowErrMsg("^1Error loading '%s': %s", fileName, errMsg) + launch:ShowErrMsg("^1Error loading import code: %s", errMsg) return elseif dbXML[1].elem ~= "PathOfBuilding" then - launch:ShowErrMsg("^1Error parsing '%s': 'PathOfBuilding' root element missing", fileName) + launch:ShowErrMsg("^1Error parsing import code: 'PathOfBuilding' root element missing") return end @@ -916,8 +916,8 @@ function PartyTabClass:ParseBuffs(list, buf, buffType, label) end end if list["AuraDebuff"] and list["AuraDebuff"]["Vaal"] then - if not list["Aura"] or not list["Aura"]["Vaal"] or not list["Aura"]["Vaal"][aura] then - for aura, auraMod in pairs(list["AuraDebuff"]["Vaal"]) do + for aura, auraMod in pairs(list["AuraDebuff"]["Vaal"]) do + if not list["Aura"] or not list["Aura"]["Vaal"] or not list["Aura"]["Vaal"][aura] then t_insert(labelList, aura..": "..auraMod.effectMult.."%\n") end end diff --git a/src/Classes/PassiveSpec.lua b/src/Classes/PassiveSpec.lua index 3e645124071..b80b72c397c 100644 --- a/src/Classes/PassiveSpec.lua +++ b/src/Classes/PassiveSpec.lua @@ -19,6 +19,7 @@ local bor = bit.bor ---@class PassiveSpec: UndoHandler ---@field nodes table ---@field allocNodes table +---@field jewel_data table local PassiveSpecClass = newClass("PassiveSpec", "UndoHandler") ---@param build Build @@ -82,6 +83,7 @@ function PassiveSpecClass:Init(treeVersion, convert) -- Table of jewels equipped in this tree -- Keys are node IDs, values are items self.jewels = { } + self.jewel_data = { } -- Tree graphs dynamically generated from cluster jewels -- Keys are subgraph IDs, values are graphs diff --git a/src/Classes/PassiveTreeView.lua b/src/Classes/PassiveTreeView.lua index c7f8976b473..fb7b4b1e398 100644 --- a/src/Classes/PassiveTreeView.lua +++ b/src/Classes/PassiveTreeView.lua @@ -26,6 +26,8 @@ local function isAbyssConquered(node) end ---@class PassiveTreeView +---@field zoomX number +---@field zoomY number local PassiveTreeViewClass = newClass("PassiveTreeView") function PassiveTreeViewClass:PassiveTreeView() @@ -72,7 +74,9 @@ function PassiveTreeViewClass:PassiveTreeView() self.zoomLevel = 3 self.zoom = 1.2 ^ self.zoomLevel + ---@type number self.zoomX = 0 + ---@type number self.zoomY = 0 self.searchStr = "" diff --git a/src/Classes/PathControl.lua b/src/Classes/PathControl.lua index af32ccbbb5d..8eeed1d8540 100644 --- a/src/Classes/PathControl.lua +++ b/src/Classes/PathControl.lua @@ -6,7 +6,7 @@ local ipairs = ipairs local t_insert = table.insert ----@class PathControl +---@class PathControl: Control, ControlHost, UndoHandler local PathClass = newClass("PathControl", "Control", "ControlHost", "UndoHandler") function PathClass:PathControl(anchor, rect, basePath, subPath, onChange) diff --git a/src/Classes/SharedItemListControl.lua b/src/Classes/SharedItemListControl.lua index cbbb32728f4..22f0441c407 100644 --- a/src/Classes/SharedItemListControl.lua +++ b/src/Classes/SharedItemListControl.lua @@ -7,7 +7,7 @@ local pairs = pairs local t_insert = table.insert local t_remove = table.remove ----@class SharedItemListControl: ListControl +---@class SharedItemListControl: ListControl local SharedItemListClass = newClass("SharedItemListControl", "ListControl") ---@param anchor Anchor? diff --git a/src/Classes/TooltipHost.lua b/src/Classes/TooltipHost.lua index 42a9c3e0e90..ae40afb6e52 100644 --- a/src/Classes/TooltipHost.lua +++ b/src/Classes/TooltipHost.lua @@ -4,6 +4,10 @@ -- Tooltip host -- ---@class TooltipHost +---@field tooltip Tooltip +---@field tooltipText string? +---@field tooltipFunc? fun(tooltip: Tooltip, ...: any) +---@field Object Control local TooltipHostClass = newClass("TooltipHost") function TooltipHostClass:TooltipHost(tooltipText) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 66ad1fc1ff9..20047b804d0 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -734,7 +734,7 @@ function TradeQueryClass:SetStatWeights(previousSelectionList) local sliderOffsetX = round(184 * (1 - controls.Slider.val)) local tooltipWidth, tooltipHeight = self:GetSize() if main.screenW >= 1338 - sliderOffsetX then - return controls[stat.label.."Slider"].tooltip.realDraw(self, x - 8 - sliderOffsetX, y - 4 - tooltipHeight, width, height, viewPort) + return controls.Slider.tooltip.realDraw(self, x - 8 - sliderOffsetX, y - 4 - tooltipHeight, width, height, viewPort) end return controls.Slider.tooltip.realDraw(self, x, y, width, height, viewPort) end diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 826cb34a086..be42a07bc8d 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -330,7 +330,7 @@ function TradeQueryGeneratorClass:ProcessMod(modId, mod, tradeQueryStatsParsed, end if #tokens ~= 0 and #tokens ~= 2 and #tokens ~= 4 then - logToFile("Unexpected # of tokens found for mod: %s", mod[i]) + logToFile("Unexpected # of tokens found for mod: %s", modLine) goto nextModLine end diff --git a/src/HeadlessWrapper.lua b/src/HeadlessWrapper.lua index 6ab11221cfa..c49b76ac265 100644 --- a/src/HeadlessWrapper.lua +++ b/src/HeadlessWrapper.lua @@ -50,6 +50,7 @@ if __mainObject__.promptMsg then end -- The build module; once a build is loaded, you can find all the good stuff in here +---@type Build build = __mainObject__.main.modes["BUILD"] -- Here's some helpful helper functions to help you get started diff --git a/src/Launch.lua b/src/Launch.lua index e0f67db802d..85bc4bd5e4b 100644 --- a/src/Launch.lua +++ b/src/Launch.lua @@ -12,7 +12,51 @@ SetWindowTitle(APP_NAME) ConExecute("set vid_mode 8") ConExecute("set vid_resizable 3") +---@class DownloadResponse +---@field header string +---@field body string + +---@class DownloadParams +---@field header? string +---@field body? string + ---@diagnostic disable-next-line: lowercase-global +---@class Launch +---@field ApplyUpdate fun(...: any) +---@field CanExit fun(...: any): boolean +---@field CheckForUpdate fun(...: any) +---@field DownloadPage fun(self: Launch, url: string, callback: fun(response: DownloadResponse, errMsg?: string), params?: DownloadParams) +---@field DrawPopup fun(...: any) +---@field OnChar fun(...: any) +---@field OnExit fun(...: any) +---@field OnFrame fun(...: any) +---@field OnInit fun(...: any) +---@field OnKeyDown fun(...: any) +---@field OnKeyUp fun(...: any) +---@field OnSubCall fun(...: any) +---@field OnSubError fun(...: any) +---@field OnSubFinished fun(...: any) +---@field RegisterSubScript fun(...: any) +---@field RunPromptFunc fun(...: any) +---@field ShowErrMsg fun(...: any) +---@field ShowPrompt fun(...: any) +---@field StartEmmyDebugger fun(...: any) +---@field [string] any +---@field connectionProtocol? number +---@field devMode boolean +---@field installedMode boolean +---@field main Main? +---@field noSSL? boolean +---@field proxyURL? string +---@field subScripts table +---@field updateAvailable? boolean +---@field updateCheckBackground? boolean +---@field updateCheckRunning? boolean +---@field updateErrMsg? string +---@field updateProgress? string +---@field versionBranch string +---@field versionNumber string +---@field versionPlatform string launch = { } SetMainObject(launch) jit.opt.start('maxtrace=4000','maxmcode=8192') @@ -69,7 +113,10 @@ function launch:OnInit() RenderInit("DPI_AWARE") ConPrintf("Loading main script...") local errMsg - errMsg, self.main = PLoadModule("Modules/Main") + ---@type Main? + local loadedMain + errMsg, loadedMain = PLoadModule("Modules/Main") + self.main = loadedMain if errMsg then self:ShowErrMsg("Error loading main script: %s", errMsg) elseif not self.main then @@ -254,7 +301,7 @@ end ---Download the given page in the background, and calls the provided callback function when done: ---@param url string ----@param callback fun(response:table, errMsg:string) @ response = { header, body } +---@param callback fun(response: table, errMsg?: string) @ response = { header, body } ---@param params? table @ params = { header, body } function launch:DownloadPage(url, callback, params) params = params or {} diff --git a/src/Modules/CalcActiveSkill.lua b/src/Modules/CalcActiveSkill.lua index de16231644e..d156c2c7092 100644 --- a/src/Modules/CalcActiveSkill.lua +++ b/src/Modules/CalcActiveSkill.lua @@ -460,7 +460,7 @@ function calcs.buildActiveSkillModList(env, activeSkill) end -- Build config structure for modifier searches - ---@class ModCfg + ---@type SkillCfg activeSkill.skillCfg = { flags = bor(skillModFlags, activeSkill.weapon1Flags or activeSkill.weapon2Flags or 0), keywordFlags = skillKeywordFlags, diff --git a/src/Modules/CalcBase.lua b/src/Modules/CalcBase.lua index 391aeda567a..c4daa4fc435 100644 --- a/src/Modules/CalcBase.lua +++ b/src/Modules/CalcBase.lua @@ -90,4 +90,4 @@ return calcs ---@class ActiveSkill ---@field skillModList ModList ----@field skillCfg ModCfg +---@field skillCfg SkillCfg diff --git a/src/Modules/CalcBreakdown.lua b/src/Modules/CalcBreakdown.lua index 000f494fd17..24ed561341d 100644 --- a/src/Modules/CalcBreakdown.lua +++ b/src/Modules/CalcBreakdown.lua @@ -209,7 +209,7 @@ return function(modDB, output, actor) t_insert(out, "Total leeched per instance:") t_insert(out, s_format("%d ^8(size of leech destination pool)", pool)) t_insert(out, s_format("x %.2f ^8(base leech rate is %d%% per second)", data.misc.LeechRateBase, 100 * data.misc.LeechRateBase)) - local rateMod = calcLib.mod(modDB, skillCfg, rate) + local rateMod = calcLib.mod(modDB, actor.mainSkill.skillCfg, rate) if rateMod ~= 1 then t_insert(out, s_format("x %.2f ^8(leech rate modifier)", rateMod)) end @@ -229,7 +229,7 @@ return function(modDB, output, actor) t_insert(out, "Rate per instance:") t_insert(out, s_format("%d ^8(size of leech destination pool)", pool)) t_insert(out, s_format("x %.2f ^8(base leech rate is %d%% per second)", data.misc.LeechRateBase, 100 * data.misc.LeechRateBase)) - local rateMod = calcLib.mod(modDB, skillCfg, rate) + local rateMod = calcLib.mod(modDB, actor.mainSkill.skillCfg, rate) if rateMod ~= 1 then t_insert(out, s_format("x %.2f ^8(leech rate modifier)", rateMod)) end diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index bc768a077ac..c85d7d12013 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -1603,7 +1603,7 @@ function calcs.defence(env, actor) -- Foulborn Ancestral Vision modDB:NewMod("ArmourDefense", "MAX", tonumber(spellSuppressionAppliesToChanceToDefendWithArmourPercentArmour) - 100, "Chance to Defend from Spell Suppression: Max Calc", { type = "Condition", var = "ArmourMax" }) modDB:NewMod("ArmourDefense", "MAX", math.min((spellSuppressionAppliesToChanceToDefendWithArmourPercent * spellSuppressionChance) / 100, 1.0) * (tonumber(spellSuppressionAppliesToChanceToDefendWithArmourPercentArmour) - 100), "Chance to Defend from Spell Suppression: Average Calc", { type = "Condition", var = "ArmourAvg" }) - modDB:NewMod("ArmourDefense", "MAX", math.min(math.floor((spellSuppressionAppliesToChanceToDefendWithArmourPercent * spellSuppressionChance) / 100), 1.0) * (tonumber(spellSuppressionAppliesToChanceToDefendWithArmourPercentArmour) - 100), modSource or "Chance to Defend from Spell Suppression: Min Calc", { type = "Condition", var = "ArmourMax", neg = true }, { type = "Condition", var = "ArmourAvg", neg = true }) + modDB:NewMod("ArmourDefense", "MAX", math.min(math.floor((spellSuppressionAppliesToChanceToDefendWithArmourPercent * spellSuppressionChance) / 100), 1.0) * (tonumber(spellSuppressionAppliesToChanceToDefendWithArmourPercentArmour) - 100), "Chance to Defend from Spell Suppression: Min Calc", { type = "Condition", var = "ArmourMax", neg = true }, { type = "Condition", var = "ArmourAvg", neg = true }) end output.ArmourDefense = (modDB:Max(nil, "ArmourDefense") or 0) / 100 output.RawArmourDefense = output.ArmourDefense > 0 and ((1 + output.ArmourDefense) * 100) or nil diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 8458c4e468f..3ed6b0c7746 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -2503,8 +2503,8 @@ function calcs.offence(env, actor, activeSkill) end output.SustainableTrauma = skillModList:Flag(nil, "HasTrauma") and skillModList:Sum("BASE", skillCfg, "Multiplier:SustainableTraumaStacks") --Mantra of Flames buff count - modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + skillModList:Sum("BASE", cfg, "Multiplier:TraumaStacks") - modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + skillModList:Sum("BASE", cfg, "Multiplier:VoltaxicWaitingStages") + modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + skillModList:Sum("BASE", skillCfg, "Multiplier:TraumaStacks") + modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + skillModList:Sum("BASE", skillCfg, "Multiplier:VoltaxicWaitingStages") if isAttack then -- Combine hit chance and attack speed combineStat("AccuracyHitChance", "AVERAGE") @@ -3524,7 +3524,7 @@ function calcs.offence(env, actor, activeSkill) resist = resist > 0 and resist * (1 - (skillModList:Sum("BASE", nil, "PartialIgnoreEnemyPhysicalDamageReduction") / 100 + ChanceToIgnoreEnemyPhysicalDamageReduction / 100)) or resist end else - resist = calcResistForType(damageType, dotCfg) + resist = calcResistForType(damageType, cfg) if ((skillModList:Flag(cfg, "ChaosDamageUsesLowestResistance") or skillModList:Flag(cfg, "ChaosDamageUsesHighestResistance")) and damageType == "Chaos") or (skillModList:Flag(cfg, "ElementalDamageUsesLowestResistance") and isElemental[damageType]) then -- Default to using the current damage type @@ -3535,7 +3535,7 @@ function calcs.offence(env, actor, activeSkill) -- Find the lowest resist of all the elements and use that if it's lower for _, eleDamageType in ipairs(dmgTypeList) do if isElemental[eleDamageType] and useThisResist(eleDamageType) > 0 and damageType ~= eleDamageType then - local currentElementResist = calcResistForType(eleDamageType, dotCfg) + local currentElementResist = calcResistForType(eleDamageType, cfg) -- If it's explicitly lower, then use the resist and update which element we're using to account for penetration if skillModList:Flag(cfg, "ChaosDamageUsesHighestResistance") then if resist < currentElementResist then diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index c7f75eda6c4..a80deef6ab4 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -26,7 +26,7 @@ local bnot = bit.bnot --- specified by @uuid or if not found in cache computes teh cache. --- @param env table --- @param activeSkill table active skill to be used as main when calculating output values ---- @param ... table keys to values to be returned (Note: EmmyLua does not natively support documenting variadic parameters) +--- @param ... string keys to values to be returned --- @return table unpacked table containing the desired values local function getCachedOutputValue(env, activeSkill, ...) local uuid = cacheSkillUUID(activeSkill, env) @@ -912,6 +912,7 @@ local function doActorMisc(env, actor) condList["LeechingEnergyShield"] = true end if modDB:Flag(nil, "Condition:CanGainRage") or modDB:Sum("BASE", nil, "RageRegen") > 0 then + local skillCfg = actor.mainSkill and actor.mainSkill.skillCfg local maxStacks = m_floor(modDB:Sum("BASE", skillCfg, "MaximumRage") * modDB:More(skillCfg, "MaximumRage")) local minStacks = m_min(modDB:Sum("BASE", nil, "MinimumRage"), maxStacks) local rageConfig = modDB:Sum("BASE", nil, "Multiplier:RageStack") @@ -3459,13 +3460,6 @@ function calcs.perform(env, skipEHP) end end - -- Check for modifiers to apply to actors affected by player auras or curses - for _, value in ipairs(modDB:List(nil, "AffectedByAuraMod")) do - for actor in pairs(affectedByAura) do - actor.modDB:AddMod(value.mod) - end - end - -- Merge keystones again to catch any that were added by buffs modLib.mergeKeystones(env, env.modDB) diff --git a/src/Modules/CalcTools.lua b/src/Modules/CalcTools.lua index aafc74e523e..1b161ac7d25 100644 --- a/src/Modules/CalcTools.lua +++ b/src/Modules/CalcTools.lua @@ -253,7 +253,7 @@ end --- Correct the tags on conversion with multipliers so they carry over correctly --- @param mod table --- @param multiplier number ---- @param minionMods boolean @convert ActorConditions pointing at parent to normal Conditions +--- @param minionMods? boolean @convert ActorConditions pointing at parent to normal Conditions --- @return table @converted multipliers function calcLib.getConvertedModTags(mod, multiplier, minionMods) local modifiers = { } diff --git a/src/Modules/Data.lua b/src/Modules/Data.lua index 26afb5f4dfb..a699f35d0d5 100644 --- a/src/Modules/Data.lua +++ b/src/Modules/Data.lua @@ -109,16 +109,17 @@ end data = { } -- Misc data tables +---@type MiscDataExport local miscData = LoadModule("Data/Misc") for k, v in pairs(miscData) do data[k] = v end ----@alias TransformFunc fun(in: number|string): (number|string)? +---@alias TransformFunc fun(value: number|string): number|string|nil ---@class PowerStat ---@field stat? string stat ID ---@field label string A short description of the stat ----@field transform TransformFunc?: number|string A function to e.g. invert the value, if the stat represents something where lower is better +---@field transform? TransformFunc A function to e.g. invert the value, if the stat represents something where lower is better ---@field combinedOffDef? boolean ---@field ignoreForNodes? boolean ---@field ignoreForItems? boolean @@ -1128,7 +1129,7 @@ for gemId, gem in pairs(data.gems) do if gem.vaalGem and data.skills[gem.secondaryGrantedEffectId..alt] then data.gemGrantedEffectIdForVaalGemId[gem.secondaryGrantedEffectId..alt] = gemId..alt data.gemVaalGemIdForBaseGemId[gemId..alt] = data.gemVaalGemIdForBaseGemId[gemId]..alt - local newGem = { name, gameId, variantId, grantedEffectId, secondaryGrantedEffectId, vaalGem, tags = {}, tagString, reqStr, reqDex, reqInt, naturalMaxLevel } + local newGem = { } -- Hybrid gems (e.g. Vaal gems) use the display name of the active skill e.g. Vaal Summon Skeletons of Sorcery newGem.name = "Vaal " .. data.skills[gem.secondaryGrantedEffectId..alt].baseTypeName newGem.gameId = gem.gameId @@ -1184,7 +1185,7 @@ end ---@field tags table # e.g. { armour = true, helmet = true, str_armour = true } ---@field influenceTags? table # influence -> mod tag, e.g. { shaper = "helmet_shaper" } ---@field implicit? string # implicit mod line(s), newline-separated ----@field implicitModTypes ModTypeList[] # per-implicit list of mod-type tags +---@field implicitModTypes? ModTypeList[] # per-implicit list of mod-type tags ---@field implicitIds? string[] # per-implicit GGG mod id ---@field enchant? string # enchant mod line(s) ---@field enchantModTypes? ModTypeList[] diff --git a/src/Modules/DataLegionLookUpTableHelper.lua b/src/Modules/DataLegionLookUpTableHelper.lua index a936f6e0f01..2d035ae4450 100644 --- a/src/Modules/DataLegionLookUpTableHelper.lua +++ b/src/Modules/DataLegionLookUpTableHelper.lua @@ -120,7 +120,7 @@ end local function repairLUTs() ConPrintf("Error NodeIndexMapping file empty") local nodeIDList = { } - GetScriptPath() + local scriptPath = GetScriptPath() for _, jewelType in ipairs({2, 3, 4, 5, 6}) do loadTimelessJewel(jewelType, 1) local jewelTypeName = data.timelessJewelTypes[jewelType]:gsub("%s+", "") @@ -141,7 +141,7 @@ local function repairLUTs() --- Code for compressing existing data if it changed local compressedFileData = Deflate(jewelData) - local file = assert(io.open(scriptPath .. "Data/TimelessJewelData/" .. jewelTypeName .. ".zip", "wb+")) + local file = assert(io.open(scriptPath .. "/Data/TimelessJewelData/" .. jewelTypeName .. ".zip", "wb+")) file:write(compressedFileData) file:close() if jewelType == 1 then diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index 294fb64c24b..af49012dc75 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -54,12 +54,21 @@ local tempTable2 = { } ---@diagnostic disable-next-line: lowercase-global ---@class Main : ControlHost ---@field allowTreeDownload? boolean +---@field errorReadingSettings? boolean +---@field newModeChangeToTree? boolean +---@field rareDB ItemDBData +---@field saveNewModCache? boolean +---@field showDragText? boolean +---@field tree table +---@field uniqueDB UniqueItemDBData +---@field updateAvailableShown? boolean main = new("ControlHost"):ControlHost() function main:Init() self:DetectUnicodeSupport() self.modes = { } self.modes["LIST"] = LoadModule("Modules/BuildList") + ---@type Build self.modes["BUILD"] = LoadModule("Modules/Build") self.popups = { } @@ -147,7 +156,7 @@ function main:Init() self.inputEvents = { } self.tooltipLines = { } - ---@type table + ---@type table self.tree = { } self:LoadTree(latestTreeVersion) @@ -155,7 +164,7 @@ function main:Init() self:ChangeUserPath(self.userPath, ignoreBuild) end - ---@type ItemDBData + ---@type UniqueItemDBData self.uniqueDB = { list = { }, byTitle = { }, loading = true } ---@type ItemDBData self.rareDB = { list = {}, loading = true } diff --git a/src/UpdateCheck.lua b/src/UpdateCheck.lua index 969357151d0..b8909ef654e 100644 --- a/src/UpdateCheck.lua +++ b/src/UpdateCheck.lua @@ -7,10 +7,14 @@ local connectionProtocol, proxyURL, noSSL = ... local xml = require("xml") -local sha1 = require("sha1") +local sha1 = require("sha1").sha1 local curl = require("lcurl.safe") local lzip = require("lzip") +---@type fun(format: string, current: integer, total: integer)? +---@diagnostic disable-next-line: undefined-global dynamic subscript callback +local updateProgress = UpdateProgress + local globalRetryLimit = 10 local function downloadFileText(source, file) for i = 1, 5 do @@ -69,11 +73,15 @@ local function downloadFile(source, file, outName) easy:setopt(curl.OPT_SSL_VERIFYHOST, 0) ConPrintf("SSL verification disabled") end - local file = io.open(outName, "wb+") - easy:setopt_writefunction(file) + local outputFile, openErr = io.open(outName, "wb+") + if not outputFile then + ConPrintf("Couldn't open '%s' for writing (%s)", outName, openErr) + return nil, openErr + end + easy:setopt_writefunction(outputFile) local _, error = easy:perform() easy:close() - file:close() + outputFile:close() if not error then return true end @@ -219,8 +227,8 @@ downloadFile(localSource, "changelog.txt", scriptPath.."/changelog.txt") local failedFile = false local zipFiles = { } for index, data in ipairs(updateFiles) do - if UpdateProgress then - UpdateProgress("Downloading %d/%d", index, #updateFiles) + if updateProgress then + updateProgress("Downloading %d/%d", index, #updateFiles) end local partSources = remoteSources[data.part] local source = partSources[localPlatform] or partSources["any"] @@ -235,13 +243,19 @@ for index, data in ipairs(updateFiles) do downloadFile(source, "", zipFileName) zipFiles[zipName] = lzip.open(zipFileName) end + ---@type LzipArchive? local zip = zipFiles[zipName] if zip then local zippedFile = zip:OpenFile(data.name) if zippedFile then - local file = io.open(fileName, "wb+") - file:write(zippedFile:Read("*a")) - file:close() + local outputFile, openErr = io.open(fileName, "wb+") + if outputFile then + outputFile:write(zippedFile:Read("*a")) + outputFile:close() + else + ConPrintf("Couldn't extract '%s' from '%s' (couldn't open output: %s)", data.name, zipName, openErr) + failedFile = true + end zippedFile:Close() else ConPrintf("Couldn't extract '%s' from '%s' (extract failed)", data.name, zipName) @@ -326,13 +340,19 @@ table.insert(ops, 'move "'..scriptPath..'/Update/manifest.xml" "'..scriptPath..' if updateMode == "basic" then -- Update script will need to relaunch the normal environment after updating table.insert(opsRuntime, 'start "'..runtimeExecutable..'"') - local opRuntimeFile = io.open(scriptPath.."/Update/opFileRuntime.txt", "w+") + local opRuntimeFile, opRuntimeErr = io.open(scriptPath.."/Update/opFileRuntime.txt", "w+") + if not opRuntimeFile then + return nil, "Couldn't write update operations.\nReason: "..(opRuntimeErr or "Unknown error") + end opRuntimeFile:write(table.concat(opsRuntime, "\n")) opRuntimeFile:close() end -- Write operations file -local opFile = io.open(scriptPath.."/Update/opFile.txt", "w+") +local opFile, opFileErr = io.open(scriptPath.."/Update/opFile.txt", "w+") +if not opFile then + return nil, "Couldn't write update operations.\nReason: "..(opFileErr or "Unknown error") +end opFile:write(table.concat(ops, "\n")) opFile:close() diff --git a/types/busted.lua b/types/busted.lua new file mode 100644 index 00000000000..2bd37b5003c --- /dev/null +++ b/types/busted.lua @@ -0,0 +1,53 @@ +---@meta + +---@alias BustedCallback fun(...: any) + +---@class BustedAssertion +---@operator call(any...): any +---@field are BustedAssertion +---@field are_not BustedAssertion +---@field has BustedAssertion +---@field has_no BustedAssertion +---@field is BustedAssertion +---@field is_not BustedAssertion +---@field was BustedAssertion +---@field was_not BustedAssertion +---@field [string] fun(...: any): BustedAssertion +assert = { } + +---@param name string +---@param callback BustedCallback +function describe(name, callback) end + +---@param name string +---@param callback BustedCallback +function it(name, callback) end + +---@param name string +---@param callback BustedCallback +function expose(name, callback) end + +---@param name string +---@param callback? BustedCallback +function pending(name, callback) end + +---@param callback BustedCallback +function before_each(callback) end + +---@param callback BustedCallback +function after_each(callback) end + +---@param callback BustedCallback +function before_all(callback) end + +---@param callback BustedCallback +function after_all(callback) end + +---@param callback BustedCallback +function setup(callback) end + +---@param callback BustedCallback +function teardown(callback) end + +---@param callback BustedCallback +function finally(callback) end diff --git a/types/lcurl/safe.lua b/types/lcurl/safe.lua new file mode 100644 index 00000000000..d566710761e --- /dev/null +++ b/types/lcurl/safe.lua @@ -0,0 +1,66 @@ +---@meta +---@module "lcurl.safe" + +---@alias CurlWriteCallback fun(data: string): boolean|integer + +---@class CurlError +---@field msg fun(self: CurlError): string + +---@class CurlEasy +local easy = { } + +---@param value string +---@return string +function easy:escape(value) end + +---@param option integer +---@param value any +function easy:setopt(option, value) end + +---@param url string +function easy:setopt_url(url) end + +---@param userAgent string +function easy:setopt_useragent(userAgent) end + +---@param callback CurlWriteCallback|any +function easy:setopt_writefunction(callback) end + +---@param callback CurlWriteCallback +function easy:setopt_headerfunction(callback) end + +---@return boolean? success +---@return CurlError? error +function easy:perform() end + +function easy:close() end + +---@param info integer +---@return any +function easy:getinfo(info) end + +---@return integer? +function easy:getinfo_response_code() end + +---@class Curl +---@field easy fun(): CurlEasy +---@field OPT_ACCEPT_ENCODING integer +---@field OPT_FOLLOWLOCATION integer +---@field OPT_HTTPHEADER integer +---@field OPT_IPRESOLVE integer +---@field OPT_POST integer +---@field OPT_POSTFIELDS integer +---@field OPT_PROXY integer +---@field OPT_SSL_VERIFYHOST integer +---@field OPT_SSL_VERIFYPEER integer +---@field OPT_USERAGENT integer +---@field INFO_REDIRECT_URL integer +---@field INFO_RESPONSE_CODE integer +---@field INFO_SIZE_DOWNLOAD integer +---@type Curl +local curl = { } + +---@return CurlEasy +function curl.easy() end + +return curl diff --git a/types/lfs.lua b/types/lfs.lua new file mode 100644 index 00000000000..b1d86779f79 --- /dev/null +++ b/types/lfs.lua @@ -0,0 +1,13 @@ +---@meta +---@module "lfs" + +---@class LfsAttributes +---@field mode string + +---@class LuaFileSystem +---@field dir fun(path: string): fun(): string? +---@field attributes fun(path: string): LfsAttributes?, string? +---@type LuaFileSystem +local lfs = { } + +return lfs diff --git a/types/lua-utf8.lua b/types/lua-utf8.lua new file mode 100644 index 00000000000..3441c5eb270 --- /dev/null +++ b/types/lua-utf8.lua @@ -0,0 +1,42 @@ +---@meta +---@module "lua-utf8" + +local utf8 = { } + +---@param text string +---@param pattern string +---@param init? integer +---@return string +function utf8.match(text, pattern, init) end + +---@param text string +---@param index? integer +---@param offset? integer +---@return integer? +function utf8.next(text, index, offset) end + +---@param text string +---@return string +function utf8.reverse(text) end + +---@param text string +---@param pattern string +---@param replacement string|fun(...): string +---@return string +function utf8.gsub(text, pattern, replacement) end + +---@param text string +---@param pattern string +---@param init? integer +---@param plain? boolean +---@return integer? start +---@return integer? finish +function utf8.find(text, pattern, init, plain) end + +---@param text string +---@param start? integer +---@param finish? integer +---@return string +function utf8.sub(text, start, finish) end + +return utf8 diff --git a/types/lzip.lua b/types/lzip.lua new file mode 100644 index 00000000000..3b355ac8acf --- /dev/null +++ b/types/lzip.lua @@ -0,0 +1,31 @@ +---@meta +---@module "lzip" + +---@class LzipEntry +local entry = { } + +---@param format string +---@return string +function entry:Read(format) end + +function entry:Close() end + +---@class LzipArchive +local archive = { } + +---@param name string +---@return LzipEntry? +function archive:OpenFile(name) end + +function archive:Close() end + +---@class Lzip +---@field open fun(fileName: string): LzipArchive? +---@type Lzip +local lzip = { } + +---@param fileName string +---@return LzipArchive? +function lzip.open(fileName) end + +return lzip diff --git a/types/sha1.lua b/types/sha1.lua new file mode 100644 index 00000000000..958befbc81e --- /dev/null +++ b/types/sha1.lua @@ -0,0 +1,18 @@ +---@meta +---@module "sha1" + +---@class SHA1 +---@field _DESCRIPTION string +---@field _LICENSE string +---@field _URL string +---@field _VERSION string +---@field version string +---@field sha1 fun(text: string): string +---@field binary fun(text: string): string +---@field hmac fun(key: string, text: string): string +---@field hmac_binary fun(key: string, text: string): string +---@operator call(string): string +---@type SHA1 +local sha1 = { } + +return sha1