diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6dc161a82..ec4304e1f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,9 +6,55 @@ on: branches: [ dev ] workflow_dispatch: jobs: + lint: + runs-on: ubuntu-latest + container: ghcr.io/musholic/lastepochplanner-tests:latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Download LuaLS Addons + run: | + git clone --depth 1 https://github.com/LuaLS/LLS-Addons.git .lls-addons + cd .lls-addons + git submodule update --init --depth 1 addons/busted + git submodule update --init --depth 1 addons/luassert + git submodule update --init --depth 1 addons/luafilesystem + + - name: Patch .luarc.json for CI + clean up + run: | + sed -i 's|${addons}|.lls-addons/addons|g' .luarc.json + # Delete the Lua 5.3 specific file so it doesn't break LuaJIT parsing + rm -f runtime/lua/sha1/lua53_ops.lua + # Delete the ModCache which slows down type checking + rm -f src/Data/ModCache.lua + - name: Type Check Code Base + run: lua-language-server --check . --check_format=json --logpath=. + - name: Annotate GitHub PR from check.json + if: always() + run: | + LOG_FILE="check.json" + if [ -f "$LOG_FILE" ]; then + cat $LOG_FILE | jq -r --arg ws "$GITHUB_WORKSPACE" ' + to_entries[] | + # 1. Strip "file://..." prefix and convert to relative path + (.key | sub("file://"; "") | ltrimstr($ws) | ltrimstr("/")) as $file | + + # 2. Iterate over issues + .value[] | + # 3. Map LSP severity (1:Error, 2:Warning) to GH format + (if .severity == 1 then "error" else "warning" end) as $sev | + + # 4. Convert 0-indexed LSP (line 55, char 0) to 1-indexed GH (line 56, col 1) + # GitHub requires line and col. Optional: endLine, endColumn + "::\($sev) file=\($file),line=\(.range.start.line + 1),col=\(.range.start.character + 1),endLine=\(.range.end.line + 1),endColumn=\(.range.end.character + 1)::\($file): \(.message) (code: \(.code))" + ' + else + echo "::warning::check.json not found, skipping annotations." + fi run_tests: runs-on: ubuntu-latest - container: ghcr.io/pathofbuildingcommunity/pathofbuilding-tests:latest + container: ghcr.io/musholic/lastepochplanner-tests:latest steps: - name: Checkout uses: actions/checkout@v4 @@ -19,7 +65,7 @@ jobs: run: cd src; luacov-coveralls --repo-token=${{ secrets.github_token }} -e TestData -e Data -e runtime check_modcache: runs-on: ubuntu-latest - container: ghcr.io/pathofbuildingcommunity/pathofbuilding-tests:latest + container: ghcr.io/musholic/lastepochplanner-tests:latest steps: - name: Install git dependency run: apk add git diff --git a/.gitignore b/.gitignore index e707d9bac..40796e305 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,7 @@ src/Export/ggpk/*.dll src/TreeData/**/*.scm src/TreeData/**/*.txt src/TreeData/**/*.bat - + # PoB Trader *_currency_values.json diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 000000000..f742cc124 --- /dev/null +++ b/.luarc.json @@ -0,0 +1,37 @@ +{ + "runtime": { + "version": "LuaJIT" + }, + "workspace": { + "library": [ + "runtime/lua", + "${addons}/busted/module/library", + "${addons}/luassert/module/library", + "${addons}/luafilesystem/module/library" + ], + "ignoreDir": ["runtime", "src/Export"] + }, + "diagnostics": { + "globals": ["UpdateProgress"], + "severity": { + "need-check-nil": "Information", + "param-type-mismatch": "Information", + "undefined-field": "Information", + "undefined-doc-class": "Information", + "undefined-doc-name": "Information", + "missing-return": "Information", + "inject-field": "Information", + "assign-type-mismatch": "Information", + "cast-local-type": "Information" + }, + "disable": ["lowercase-global"] + }, + "hint": { + "enable": true + }, + "Lua.format.defaultConfig": { + "indent_style": "tab", + "indent_size": "4" + }, + "workspace.checkThirdParty": false +} diff --git a/Dockerfile b/Dockerfile index d1abe3a90..e7a3ec27c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.18 AS base +FROM alpine:3.22 AS base # Common dependencies RUN apk add --no-cache cmake readline-dev build-base tar @@ -18,13 +18,16 @@ RUN git clone https://github.com/LuaJIT/LuaJIT && cd LuaJIT && git checkout 871d RUN cd LuaJIT && make FROM buildbase AS emmyluadebugger -RUN git clone --depth 1 --branch 1.7.1 https://github.com/EmmyLua/EmmyLuaDebugger +RUN git clone --depth 1 --branch 1.8.7 https://github.com/EmmyLua/EmmyLuaDebugger RUN cd EmmyLuaDebugger && mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Release ../ && make FROM base # Luarocks packages dependencies RUN apk add --no-cache curl unzip openssl +# Install other dependencies useful for the CI +RUN apk add --no-cache libxml2-utils git lua-language-server jq + RUN --mount=type=cache,from=buildBase,source=/opt,target=/opt make -C /opt/lua-5.1.5/ install RUN --mount=type=cache,from=luarocks,source=/opt,target=/opt make -C /opt/luarocks-3.7.0/ install diff --git a/spec/System/TestTradeQueryRequests_spec.lua b/spec/System/TestTradeQueryRequests_spec.lua index 873b5102d..081c1bd5a 100644 --- a/spec/System/TestTradeQueryRequests_spec.lua +++ b/spec/System/TestTradeQueryRequests_spec.lua @@ -70,6 +70,7 @@ describe("TradeQueryRequests", function() it("retries on 429 with exponential backoff", function() local orig_os_time = os.time local mock_time = 1000 + ---@diagnostic disable-next-line: duplicate-set-field os.time = function() return mock_time end local request = { @@ -192,4 +193,4 @@ describe("TradeQueryRequests", function() requests.FetchResultBlock = orig_fetchBlock end) end) -end) \ No newline at end of file +end) diff --git a/src/Classes/CalcsTab.lua b/src/Classes/CalcsTab.lua index 1f896dd7a..71035cb22 100644 --- a/src/Classes/CalcsTab.lua +++ b/src/Classes/CalcsTab.lua @@ -192,7 +192,7 @@ Effective DPS: Curses and enemy properties (such as resistances and status condi self.powerBuilderInitialized = nil end) -function CalcsTabClass:Load(xml, dbFileName) +function CalcsTabClass:Load(xml, fileName) for _, node in ipairs(xml) do if type(node) == "table" then if node.elem == "Input" then @@ -631,7 +631,7 @@ function CalcsTabClass:PowerBuilder() -- Calculate the impact of every cluster notable -- used for the power report screen - for nodeName, node in pairs(self.build.spec.tree.clusterNodeMap) do + for nodeId, node in pairs(self.build.spec.tree.clusterNodeMap) do if not node.power then node.power = {} end diff --git a/src/Classes/ControlHost.lua b/src/Classes/ControlHost.lua index 30f1573cb..d573afbd6 100644 --- a/src/Classes/ControlHost.lua +++ b/src/Classes/ControlHost.lua @@ -60,7 +60,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/EditControl.lua b/src/Classes/EditControl.lua index 2964057ad..495ffc0ae 100644 --- a/src/Classes/EditControl.lua +++ b/src/Classes/EditControl.lua @@ -297,7 +297,7 @@ function EditClass:Draw(viewPort, noTooltip) else SetDrawColor(self.inactiveCol) if self.inactiveText then - local inactiveText = type(inactiveText) == "string" and self.inactiveText or self.inactiveText(self.buf) + local inactiveText = type(self.inactiveText) == "string" and self.inactiveText or self.inactiveText(self.buf) DrawString(-self.controls.scrollBarH.offset, -self.controls.scrollBarV.offset, "LEFT", textHeight, self.font, inactiveText) elseif self.protected then DrawString(-self.controls.scrollBarH.offset, -self.controls.scrollBarV.offset, "LEFT", textHeight, self.font, string.rep(protected_replace, #self.buf)) diff --git a/src/Classes/GemSelectControl.lua b/src/Classes/GemSelectControl.lua index 41eaaf21e..df0263323 100644 --- a/src/Classes/GemSelectControl.lua +++ b/src/Classes/GemSelectControl.lua @@ -309,7 +309,7 @@ function GemSelectClass:UpdateSortCache() sortCache.dps[gemId] = baseDPS -- Ignore gems that don't support the active skill if sortCache.canSupport[gemId] or (gemData.grantedEffect.hasGlobalEffect and not gemData.grantedEffect.support) then - local output = self:CalcOutputWithThisGem(calcFunc, gemData, useFullDPS, fastCalcOptions, calcBase) + local output = self:CalcOutputWithThisGem(calcFunc, gemData, useFullDPS) -- Check for nil because some fields may not be populated, default to 0 sortCache.dps[gemId] = (dpsField == "FullDPS" and output[dpsField] ~= nil and output[dpsField]) or (output.Minion and output.Minion.CombinedDPS) or (output[dpsField] ~= nil and output[dpsField]) or 0 end @@ -452,7 +452,7 @@ function GemSelectClass:Draw(viewPort, noTooltip) if calcFunc then self.tooltip:Clear() local gemData = self.gems[self.list[self.hoverSel]] - local output = self:CalcOutputWithThisGem(calcFunc, gemData, self.skillsTab.sortGemsByDPSField == "FullDPS", nil, calcBase) + local output = self:CalcOutputWithThisGem(calcFunc, gemData, self.skillsTab.sortGemsByDPSField == "FullDPS") local gemInstance = { level = self.skillsTab:ProcessGemLevel(gemData), quality = self.skillsTab.defaultGemQuality or 0, @@ -911,4 +911,4 @@ function GemSelectClass:OnKeyUp(key) end local newSel = self.EditControl:OnKeyUp(key) return newSel == self.EditControl and self or newSel -end \ No newline at end of file +end diff --git a/src/Classes/ImportTab.lua b/src/Classes/ImportTab.lua index b3cb06563..011099e81 100644 --- a/src/Classes/ImportTab.lua +++ b/src/Classes/ImportTab.lua @@ -920,6 +920,7 @@ function ImportTabClass:ImportItemsAndSkills(charData) elseif not orderA and not orderB then return groupOrder[a] < groupOrder[b] else + ---@diagnostic disable-next-line: return-type-mismatch return orderA end end) diff --git a/src/Classes/ItemsTab.lua b/src/Classes/ItemsTab.lua index 5a2b9aa8f..22d6886e5 100644 --- a/src/Classes/ItemsTab.lua +++ b/src/Classes/ItemsTab.lua @@ -2163,7 +2163,7 @@ end ---Gets the name of the anointed node on an item ---@param item table @The item to get the anoint from ----@return string @The name of the anointed node, or nil if there is no anoint +---@return table? @The names of the anointed node, or nil if there is no anoint function ItemsTabClass:getAnoint(item) local result = { } if item then diff --git a/src/Classes/ModDB.lua b/src/Classes/ModDB.lua index 3ff5c16a3..eb0fff171 100644 --- a/src/Classes/ModDB.lua +++ b/src/Classes/ModDB.lua @@ -209,7 +209,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) or nil if value then t_insert(result, value) end @@ -315,4 +315,4 @@ function ModDBClass:Print() for i, name in ipairs(nameList) do ConPrintf("%s = %d", name, self.multipliers[name]) end -end \ No newline at end of file +end diff --git a/src/Classes/ModList.lua b/src/Classes/ModList.lua index 00ae37856..ff8720539 100644 --- a/src/Classes/ModList.lua +++ b/src/Classes/ModList.lua @@ -179,7 +179,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) or nil if value then t_insert(result, value) end diff --git a/src/Classes/ModStore.lua b/src/Classes/ModStore.lua index 6470f1b21..42fee7da4 100644 --- a/src/Classes/ModStore.lua +++ b/src/Classes/ModStore.lua @@ -278,6 +278,7 @@ function ModStoreClass:GetStat(stat, cfg) local totalLife = self.actor.output["Life"] if totalLife == 0 then return 0 else for _, activeSkill in ipairs(self.actor.activeSkillList) do + ---@diagnostic disable-next-line: undefined-global if (activeSkill.skillTypes[SkillType.HasReservation] and not activeSkill.skillFlags.disable and activeSkill.buffList and activeSkill.buffList[1] and cfg and (isNameInBuffList(activeSkill.buffList, cfg.skillName) or isNameInBuffList(activeSkill.buffList, cfg.summonSkillName)) ) then local lifeBase = activeSkill.skillData["LifeReservedBase"] or 0 reservedPercentLife = lifeBase / totalLife * 100 @@ -863,4 +864,4 @@ function ModStoreClass:EvalMod(mod, cfg, globalLimits) end end return value -end \ No newline at end of file +end diff --git a/src/Classes/PartyTab.lua b/src/Classes/PartyTab.lua index 3415db5b6..b02560994 100644 --- a/src/Classes/PartyTab.lua +++ b/src/Classes/PartyTab.lua @@ -191,9 +191,11 @@ local PartyTabClass = newClass("PartyTab", "ControlHost", "Control", function(se -- Parse the XML local dbXML, errMsg = common.xml.ParseXML(self.importCodeXML) if not dbXML then + ---@diagnostic disable-next-line: undefined-global launch:ShowErrMsg("^1Error loading '%s': %s", fileName, errMsg) return elseif dbXML[1].elem ~= "PathOfBuilding2" then + ---@diagnostic disable-next-line: undefined-global launch:ShowErrMsg("^1Error parsing '%s': 'PathOfBuilding2' root element missing", fileName) return end @@ -908,6 +910,7 @@ function PartyTabClass:ParseBuffs(list, buf, buffType, label) end end if list["AuraDebuff"] and list["AuraDebuff"]["Vaal"] then + ---@diagnostic disable-next-line: undefined-global 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 t_insert(labelList, aura..": "..auraMod.effectMult.."%\n") @@ -1032,4 +1035,4 @@ function PartyTabClass:exportBuffs(buffType) self.buffExports[buffType] = { ConvertedToText = true } self.buffExports[buffType].string = buf return buf -end \ No newline at end of file +end diff --git a/src/Classes/PassiveSpec.lua b/src/Classes/PassiveSpec.lua index 294782679..ad78dc970 100644 --- a/src/Classes/PassiveSpec.lua +++ b/src/Classes/PassiveSpec.lua @@ -2255,7 +2255,7 @@ end --- Adds a line to or replaces a node given a line to add/replace with --- @param node table The node to replace/add to --- @param sd string The line being parsed and added ---- @param replacement boolean true to replace the node with the new mod, false to simply add it +--- @param replacement boolean? true to replace the node with the new mod, false to simply add it function PassiveSpecClass:NodeAdditionOrReplacementFromString(node,sd,replacement) local addition = {} addition.sd = {sd} diff --git a/src/Classes/ResizableEditControl.lua b/src/Classes/ResizableEditControl.lua index 66a0402e3..d18b7c117 100644 --- a/src/Classes/ResizableEditControl.lua +++ b/src/Classes/ResizableEditControl.lua @@ -26,7 +26,7 @@ local ResizableEditClass = newClass("ResizableEditControl", "EditControl", funct self.protected = false end) function ResizableEditClass:Draw(viewPort, noTooltip) - self:SetBoundedDrag(self) + self:SetBoundedDrag() self.EditControl:Draw(viewPort, noTooltip) end function ResizableEditClass:SetBoundedDrag() diff --git a/src/Classes/SkillsTab.lua b/src/Classes/SkillsTab.lua index 5d203a27a..ff917f572 100644 --- a/src/Classes/SkillsTab.lua +++ b/src/Classes/SkillsTab.lua @@ -1035,7 +1035,7 @@ function SkillsTabClass:ProcessSocketGroup(socketGroup) gemInstance.color = "^8" gemInstance.nameSpec = gemInstance.nameSpec or "" local prevDefaultLevel = gemInstance.gemData and gemInstance.gemData.naturalMaxLevel or (gemInstance.new and 20) - gemInstance.gemData, gemInstance.grantedEffect = nil + gemInstance.gemData, gemInstance.grantedEffect = nil, nil if gemInstance.gemId then -- Specified by gem ID -- Used for skills granted by skill gems @@ -1074,7 +1074,7 @@ function SkillsTabClass:ProcessSocketGroup(socketGroup) gemInstance.nameSpec = gemInstance.gemData.name end else - gemInstance.errMsg, gemInstance.gemData, gemInstance.skillId = nil + gemInstance.errMsg, gemInstance.gemData, gemInstance.skillId = nil, nil, nil end if gemInstance.gemData and gemInstance.gemData.grantedEffect.unsupported then gemInstance.errMsg = gemInstance.nameSpec .. " is not supported yet" diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 95d62dd7d..b559c45ae 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -59,7 +59,7 @@ local TradeQueryClass = newClass("TradeQuery", function(self, itemsTab) end) ---Fetch currency short-names from Poe API (used for PoeNinja price pairing) ----@param callback fun() +---@param callback fun(data, errMsg) function TradeQueryClass:FetchCurrencyConversionTable(callback) launch:DownloadPage( "https://www.pathofexile.com/api/trade2/data/static", @@ -94,7 +94,8 @@ function TradeQueryClass:PullLeagueList() self.hostName .. "api/leagues?type=main&compact=1", function(response, errMsg) if errMsg then - self:SetNotice(self.controls.pbNotice, "Error: " .. tostring(errMsg)) + self:SetNotice(self.controls.pbNotice, "Error: " .. tostring(errMsg)) + ---@diagnostic disable-next-line: redundant-return-value return "POE ERROR", "Error: "..errMsg else local json_data = dkjson.decode(response.body) @@ -168,7 +169,7 @@ function TradeQueryClass:PullPoENinjaCurrencyConversion(league) self:SetNotice(self.controls.pbNotice, "Failed to Get PoE Ninja response") return end - self:PriceBuilderProcessPoENinjaResponse(json_data, self.controls) + self:PriceBuilderProcessPoENinjaResponse(json_data) local print_str = "" for key, value in pairs(self.pbCurrencyConversion[self.pbLeague]) do print_str = print_str .. '"'..key..'": '..tostring(value)..',' @@ -545,6 +546,7 @@ function TradeQueryClass:SetStatWeights(previousSelectionList) local sliderOffsetX = round(184 * (1 - controls.Slider.val)) local tooltipWidth, tooltipHeight = self:GetSize() if main.screenW >= 1338 - sliderOffsetX then + ---@diagnostic disable-next-line: undefined-global return controls[stat.label.."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) diff --git a/src/Classes/TradeQueryRequests.lua b/src/Classes/TradeQueryRequests.lua index b0cdf8632..541a58de8 100644 --- a/src/Classes/TradeQueryRequests.lua +++ b/src/Classes/TradeQueryRequests.lua @@ -5,10 +5,12 @@ -- local dkjson = require "dkjson" +local m_min = math.min ---@class TradeQueryRequests local TradeQueryRequestsClass = newClass("TradeQueryRequests", function(self, rateLimiter) - self.maxFetchPerSearch = 10 + self.maxFetchPerSearch = 10 + ---@diagnostic disable-next-line: undefined-global self.tradeQuery = tradeQuery self.rateLimiter = rateLimiter or new("TradeQueryRateLimiter") self.requestQueue = { @@ -71,7 +73,7 @@ end ---@param league string ---@param query string ---@param callback fun(items:table, errMsg:string) ----@param params table @ params = { callbackQueryId = fun(queryId:string) } +---@param params { callbackQueryId: fun(queryId:string) }? function TradeQueryRequestsClass:SearchWithQuery(realm, league, query, callback, params) params = params or {} --ConPrintf("Query json: %s", query) @@ -238,7 +240,7 @@ end ---Fetch item details for itemHashes ---@param itemHashes string[] ---@param queryId string ----@param callback fun(items:table, errMsg:string) +---@param callback fun(items:table, errMsg:string?) function TradeQueryRequestsClass:FetchResults(itemHashes, queryId, callback) local quantity_found = math.min(#itemHashes, self.maxFetchPerSearch) local max_block_size = 10 @@ -264,7 +266,7 @@ end ---Fetch details for paginated items ---@param url string ----@param callback fun(items: table, errMsg:string) +---@param callback fun(items: table, errMsg:string?) function TradeQueryRequestsClass:FetchResultBlock(url, callback) table.insert(self.requestQueue["fetch"], { url = url, @@ -526,7 +528,7 @@ end ---@param root string ---@param realm string ---@param league string ----@param queryId string +---@param queryId string? function TradeQueryRequestsClass:buildUrl(root, realm, league, queryId) local result = root if realm and realm ~='pc' then diff --git a/src/Data/ClusterJewels.lua b/src/Data/ClusterJewels.lua index 4f323ef68..9976a06fe 100644 --- a/src/Data/ClusterJewels.lua +++ b/src/Data/ClusterJewels.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: duplicate-index -- This file is automatically generated, do not edit! -- Jewel data (c) Grinding Gear Games @@ -1048,4 +1049,4 @@ return { [0] = 4, }, }, -} \ No newline at end of file +} diff --git a/src/Data/Gems.lua b/src/Data/Gems.lua index 3f8db70f7..c09a5a86e 100644 --- a/src/Data/Gems.lua +++ b/src/Data/Gems.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: duplicate-index -- This file is automatically generated, do not edit! -- Gem data (c) Grinding Gear Games @@ -18841,4 +18842,4 @@ return { Tier = 5, naturalMaxLevel = 1, }, -} \ No newline at end of file +} diff --git a/src/Data/SkillStatMap.lua b/src/Data/SkillStatMap.lua index 99a3e994c..ae973f6e0 100644 --- a/src/Data/SkillStatMap.lua +++ b/src/Data/SkillStatMap.lua @@ -1249,9 +1249,6 @@ return { ["shock_chance_+%"] = { mod("EnemyShockChance", "INC", nil), }, -["active_skill_shock_chance_+%_final"] = { - mod("EnemyShockChance", "MORE", nil), -}, ["always_shock"] = { mod("EnemyShockChance", "BASE", nil), value = 100, @@ -2223,9 +2220,6 @@ return { ["mine_detonation_radius_+%"] = { mod("MineDetonationAreaOfEffect", "INC", nil), }, -["mine_throwing_speed_+%_per_frenzy_charge"] = { - mod("MineLayingSpeed", "INC", nil, 0, 0, { type = "Multiplier", var = "FrenzyCharge" }), -}, ["remote_mined_by_support"] = { flag("ManaCostGainAsReservation"), flag("LifeCostGainAsReservation"), @@ -2863,9 +2857,6 @@ return { ["quality_display_siege_cascade_damage_+%_final_vs_immobilised_enemies_is_gem"] = { -- Display Only }, -["quality_display_active_skill_base_area_of_effect_radius_is_gem"] = { - -- Display Only -}, ["quality_display_lightning_arrow_is_gem"] = { -- Display Only }, @@ -2884,9 +2875,6 @@ return { ["quality_display_active_skill_damage_+%_final_vs_immobilised_enemies_is_gem"] = { -- Display Only }, -["quality_display_base_number_of_projectiles_is_gem"] = { - -- Display Only -}, ["quality_display_sandstorm_swipe_is_gem"] = { -- Display Only }, diff --git a/src/Data/Skills/act_int.lua b/src/Data/Skills/act_int.lua index 7cf580ce2..2eb63c280 100644 --- a/src/Data/Skills/act_int.lua +++ b/src/Data/Skills/act_int.lua @@ -1804,8 +1804,8 @@ skills["BonestormPlayer"] = { [40] = { critChance = 15, levelRequirement = 90, cost = { ManaPerMinute = 21924, }, }, }, preDamageFunc = function(activeSkill, output) - activeSkill.skillData.hitTimeMultiplier = math.ceil(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:BonestormStage") / output.ProjectileCount or 1) - activeSkill.skillData.channelTimeMultiplier = math.ceil(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:BonestormStage") / output.ProjectileCount or 1) + activeSkill.skillData.hitTimeMultiplier = math.ceil(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:BonestormStage") / (output.ProjectileCount or 1)) + activeSkill.skillData.channelTimeMultiplier = math.ceil(activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:BonestormStage") / (output.ProjectileCount or 1)) activeSkill.skillData.dpsMultiplier = activeSkill.skillModList:Sum("BASE", activeSkill.skillCfg, "Multiplier:BonestormStage") end, statSets = { @@ -22496,4 +22496,4 @@ skills["WitheringPresencePlayer"] = { }, }, } -} \ No newline at end of file +} diff --git a/src/Data/Skills/other.lua b/src/Data/Skills/other.lua index 138b5395e..db355966f 100644 --- a/src/Data/Skills/other.lua +++ b/src/Data/Skills/other.lua @@ -9183,7 +9183,7 @@ skills["VoidIllusionSpawnPlayer"] = { if statSet.index ~= 3 then local allExplodeMods = activeSkill.skillModList:Tabulate("LIST", activeSkill.skillCfg, "ExplodeMod") for _, explodeMod in ipairs(allExplodeMods) do - local activeEffectSource = activeSkill.activeEffect.srcInstance.explodeSource.modSource or "Tree:"..activeSkill.activeEffect.srcInstance.explodeSource.id + local activeEffectSource = activeSkill.activeEffect.srcInstance.explodeSource.modSource or ("Tree:"..activeSkill.activeEffect.srcInstance.explodeSource.id) if explodeMod.mod.source == activeEffectSource then local explodeMod = explodeMod.value if explodeMod.type == "RandomElement" then diff --git a/src/HeadlessWrapper.lua b/src/HeadlessWrapper.lua index db8bd5807..d16200f6b 100644 --- a/src/HeadlessWrapper.lua +++ b/src/HeadlessWrapper.lua @@ -77,7 +77,7 @@ function GetAsyncCount() end -- Search Handles -function NewFileSearch() end +function NewFileSearch(path, dirOnly) end -- General Functions function SetWindowTitle(title) end @@ -110,7 +110,7 @@ function GetUserPath() return "" end function MakeDir(path) end -function RemoveDir(path) end +function RemoveDir(path, recursive) end function SetWorkDir(path) end function GetWorkDir() return "" @@ -160,8 +160,9 @@ function SpawnProcess(cmdName, args) end function OpenURL(url) end function SetProfiling(isEnabled) end function Restart() end -function Exit() end +function Exit(errMsg) end function TakeScreenshot() end +function SetForeground() end ---@return string? provider ---@return string? version @@ -193,6 +194,7 @@ runCallback("OnFrame") -- Need at least one frame for everything to initialise if mainObject.promptMsg then -- Something went wrong during startup print(mainObject.promptMsg) + ---@diagnostic disable-next-line: discard-returns io.read("*l") return end diff --git a/src/Launch.lua b/src/Launch.lua index 2d5305cd5..51d1bb87b 100644 --- a/src/Launch.lua +++ b/src/Launch.lua @@ -249,8 +249,8 @@ 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 params? table @ params = { header, body } +---@param callback fun(response:{header:string, body:string}, errMsg:string) +---@param params? { header:string, body:string } function launch:DownloadPage(url, callback, params) params = params or {} local script = [[ diff --git a/src/Modules/BuildDisplayStats.lua b/src/Modules/BuildDisplayStats.lua index 538efd46c..01ca76000 100644 --- a/src/Modules/BuildDisplayStats.lua +++ b/src/Modules/BuildDisplayStats.lua @@ -58,10 +58,10 @@ local displayStats = { { stat = "WithPoisonDPS", label = "Total DPS inc. Poison", fmt = ".1f", compPercent = true, flag = "notAverage", condFunc = function(v,o) return v ~= o.TotalDPS and (o.TotalDot or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.ImpaleDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end }, { stat = "DecayDPS", label = "Decay DPS", fmt = ".1f", compPercent = true }, { stat = "TotalDotDPS", label = "Total DoT DPS", fmt = ".1f", compPercent = true, condFunc = function(v,o) return o.showTotalDotDPS or ( v ~= o.TotalDot and v ~= o.TotalPoisonDPS and v ~= o.CausticGroundDPS and v ~= (o.TotalIgniteDPS or o.IgniteDPS) and v ~= o.BurningGroundDPS and v ~= o.BleedDPS and v~= o.CorruptingBloodDPS and v ~= o.MirageCausticGroundDPS and v ~= o.MirageBurningGroundDPS) end, warnFunc = function(v) return v >= data.misc.DotDpsCap and "DoT DPS exceeds in game limit" end }, - { stat = "ImpaleDPS", label = "Impale Damage", fmt = ".1f", compPercent = true, flag = "impale", flag = "showAverage" }, - { stat = "WithImpaleDPS", label = "Damage inc. Impale", fmt = ".1f", compPercent = true, flag = "impale", flag = "showAverage", condFunc = function(v,o) return v ~= o.TotalDPS and (o.TotalDot or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.PoisonDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end }, - { stat = "ImpaleDPS", label = "Impale DPS", fmt = ".1f", compPercent = true, flag = "impale", flag = "notAverage" }, - { stat = "WithImpaleDPS", label = "Total DPS inc. Impale", fmt = ".1f", compPercent = true, flag = "impale", flag = "notAverage", condFunc = function(v,o) return v ~= o.TotalDPS and (o.TotalDot or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.PoisonDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end }, + { stat = "ImpaleDPS", label = "Impale Damage", fmt = ".1f", compPercent = true, flag = "showAverage" }, + { stat = "WithImpaleDPS", label = "Damage inc. Impale", fmt = ".1f", compPercent = true, flag = "showAverage", condFunc = function(v,o) return v ~= o.TotalDPS and (o.TotalDot or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.PoisonDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end }, + { stat = "ImpaleDPS", label = "Impale DPS", fmt = ".1f", compPercent = true, flag = "notAverage" }, + { stat = "WithImpaleDPS", label = "Total DPS inc. Impale", fmt = ".1f", compPercent = true, flag = "notAverage", condFunc = function(v,o) return v ~= o.TotalDPS and (o.TotalDot or 0) == 0 and (o.IgniteDPS or 0) == 0 and (o.PoisonDPS or 0) == 0 and (o.BleedDPS or 0) == 0 end }, { stat = "MirageDPS", label = "Total Mirage DPS", fmt = ".1f", compPercent = true, flag = "mirageArcher", condFunc = function(v,o) return v > 0 end }, { stat = "MirageDPS", label = "Total Wisp DPS", fmt = ".1f", compPercent = true, flag = "wisp", condFunc = function(v,o) return v > 0 end }, { stat = "CullingDPS", label = "Culling DPS", fmt = ".1f", compPercent = true, condFunc = function(v,o) return (o.CullingDPS or 0) > 0 end }, diff --git a/src/Modules/BuildList.lua b/src/Modules/BuildList.lua index f0c1ba68e..354753a18 100644 --- a/src/Modules/BuildList.lua +++ b/src/Modules/BuildList.lua @@ -143,7 +143,7 @@ function listMode:OnFrame(inputEvents) local build = self.controls.buildList.copyBuild if build.subPath ~= self.subPath then if build.folderName then - main:CopyFolder(build.folderName, main.buildPath..build.subPath, main.buildPath..self.subPath) + main:CopyFolder(build.folderName, main.buildPath..build.subPath) else copyFile(build.fullFileName, self:GetDestName(self.subPath, build.fileName)) end diff --git a/src/Modules/CalcBreakdown.lua b/src/Modules/CalcBreakdown.lua index c2740d5dc..506e38936 100644 --- a/src/Modules/CalcBreakdown.lua +++ b/src/Modules/CalcBreakdown.lua @@ -207,6 +207,7 @@ function breakdown.leech(instant, instantRate, instances, pool, rate, max, dur, 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)) + ---@diagnostic disable-next-line: undefined-global local rateMod = calcLib.mod(modDB, skillCfg, rate) if rateMod ~= 1 then t_insert(out, s_format("x %.2f ^8(leech rate modifier)", rateMod)) @@ -227,6 +228,7 @@ function breakdown.leech(instant, instantRate, instances, pool, rate, max, dur, 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)) + ---@diagnostic disable-next-line: undefined-global local rateMod = calcLib.mod(modDB, skillCfg, rate) if rateMod ~= 1 then t_insert(out, s_format("x %.2f ^8(leech rate modifier)", rateMod)) diff --git a/src/Modules/CalcDefence.lua b/src/Modules/CalcDefence.lua index 77a2a6667..82b4581b5 100644 --- a/src/Modules/CalcDefence.lua +++ b/src/Modules/CalcDefence.lua @@ -347,10 +347,13 @@ function calcs.doActorLifeManaSpiritReservation(actor) end -- Based on code from FR and BS found in act_*.txt ----@param activeSkill/output/breakdown references table passed in from calc offence +---@param activeSkill table references table passed in from calc offence +---@param output table references table passed in from calc offence +---@param breakdown table references table passed in from calc offence ---@param sourceType string type of incoming damage - it will be converted (taken as) from this type if applicable ---@param baseDmg string for which to calculate the damage ----@return table of taken damage parts, and number, sum of damages +---@return table dmgBreakdown table of taken damage parts +---@return number totalDmgTaken sum of damages function calcs.applyDmgTakenConversion(activeSkill, output, breakdown, sourceType, baseDmg) local damageBreakdown = { } local totalDamageTaken = 0 @@ -3520,14 +3523,14 @@ function calcs.buildDefenceEstimations(env, actor) sourcePool = m_max(sourcePool - poolProtected, 0) + m_min(sourcePool, poolProtected) / (wardBypass / 100) output[damageType.."TotalHitPool"] = sourcePool else - output[damageType.."TotalHitPool"] = output[damageType.."TotalHitPool"] + output.Ward or 0 + output[damageType.."TotalHitPool"] = output[damageType.."TotalHitPool"] + (output.Ward or 0) end -- aegis output[damageType.."TotalHitPool"] = output[damageType.."TotalHitPool"] + m_max(m_max(output[damageType.."Aegis"], output["sharedAegis"]), isElemental[damageType] and output[damageType.."AegisDisplay"] or 0) -- guard skill - local GuardAbsorbRate = output["sharedGuardAbsorbRate"] or 0 + output[damageType.."GuardAbsorbRate"] or 0 + local GuardAbsorbRate = (output["sharedGuardAbsorbRate"] or 0) + (output[damageType.."GuardAbsorbRate"] or 0) if GuardAbsorbRate > 0 then - local GuardAbsorb = output["sharedGuardAbsorb"] or 0 + output[damageType.."GuardAbsorb"] or 0 + local GuardAbsorb = (output["sharedGuardAbsorb"] or 0) + (output[damageType.."GuardAbsorb"] or 0) if GuardAbsorbRate >= 100 then output[damageType.."TotalHitPool"] = output[damageType.."TotalHitPool"] + GuardAbsorb else diff --git a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua index 7f237aa2f..a4da408c3 100644 --- a/src/Modules/CalcOffence.lua +++ b/src/Modules/CalcOffence.lua @@ -277,7 +277,7 @@ local function setMoltenStrikeTertiaryRadiusBreakdown(breakdown, deadzoneRadius, breakdownRadius.radius = currentDist end -- Calculate and return reload time in seconds for a specific Crossbow skill ----@param actor table @actor using the skill +---@param weaponData table ---@param boltSkill table @skill that uses the ammo to shoot bolts ---@return number local function calcCrossbowReloadTime(weaponData, boltSkill) @@ -1390,7 +1390,7 @@ function calcs.offence(env, actor, activeSkill) if activeSkill.skillTypes[SkillType.Warcry] then local full_duration = calcSkillDuration(skillModList, skillCfg, activeSkill.skillData, env, enemyDB) local cooldownOverride = skillModList:Override(skillCfg, "CooldownRecovery") - local actual_cooldown = cooldownOverride or (activeSkill.skillData.cooldown or 0 + skillModList:Sum("BASE", skillCfg, "CooldownRecovery")) / calcLib.mod(skillModList, skillCfg, "CooldownRecovery") + local actual_cooldown = cooldownOverride or ((activeSkill.skillData.cooldown or 0) + skillModList:Sum("BASE", skillCfg, "CooldownRecovery")) / calcLib.mod(skillModList, skillCfg, "CooldownRecovery") local uptime = env.modDB:Flag(nil, "Condition:WarcryMaxHit") and 1 or m_min(full_duration / actual_cooldown, 1) local unscaledEffect = calcLib.mod(skillModList, skillCfg, "WarcryEffect", "BuffEffect") output.WarcryEffectMod = unscaledEffect * uptime @@ -1512,7 +1512,7 @@ function calcs.offence(env, actor, activeSkill) if breakdown then breakdown.Cooldown = { - s_format("%.2fs ^8(base)", skillData.cooldown or 0 + addedCooldown), + s_format("%.2fs ^8(base)", (skillData.cooldown or 0) + addedCooldown), s_format("/ %.2f ^8(increased/reduced cooldown recovery)", 1 + skillModList:Sum("INC", skillCfg, "CooldownRecovery") / 100), } @@ -2868,8 +2868,10 @@ function calcs.offence(env, actor, activeSkill) breakdown.SustainableTrauma = storedSustainedTraumaBreakdown end output.SustainableTrauma = skillModList:Flag(nil, "HasTrauma") and skillModList:Sum("BASE", skillCfg, "Multiplier:SustainableTraumaStacks") - --Mantra of Flames buff count + --Mantra of Flames buff count + ---@diagnostic disable-next-line: undefined-global modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + skillModList:Sum("BASE", cfg, "Multiplier:TraumaStacks") + ---@diagnostic disable-next-line: undefined-global modDB.multipliers["BuffOnSelf"] = (modDB.multipliers["BuffOnSelf"] or 0) + skillModList:Sum("BASE", cfg, "Multiplier:VoltaxicWaitingStages") if isAttack then -- Combine hit chance and attack speed @@ -3034,7 +3036,7 @@ function calcs.offence(env, actor, activeSkill) globalOutput.AncestralCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, value.skillData, actor) globalOutput.AncestralExertsCount = env.modDB:Sum("BASE", nil, "NumAncestralExerts") or 0 local baseUptimeRatio = m_min((globalOutput.AncestralExertsCount / globalOutput.Speed) / (globalOutput.AncestralCryCooldown + globalOutput.AncestralCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + local storedUses = (value.skillData.storedUses or 0) + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") globalOutput.AncestralUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.AncestralUpTimeRatio if globalBreakdown then @@ -3057,7 +3059,7 @@ function calcs.offence(env, actor, activeSkill) if activeSkill.skillTypes[SkillType.Melee] then globalOutput.InfernalExertsCount = env.modDB:Sum("BASE", nil, "NumInfernalExerts") or 0 local baseUptimeRatio = m_min((globalOutput.InfernalExertsCount / globalOutput.Speed) / (globalOutput.InfernalCryCooldown + globalOutput.InfernalCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + local storedUses = (value.skillData.storedUses or 0) + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") globalOutput.InfernalUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.InfernalUpTimeRatio if globalBreakdown then @@ -3081,7 +3083,7 @@ function calcs.offence(env, actor, activeSkill) globalOutput.IntimidatingCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, value.skillData, actor) globalOutput.IntimidatingExertsCount = env.modDB:Sum("BASE", nil, "NumIntimidatingExerts") or 0 local baseUptimeRatio = m_min((globalOutput.IntimidatingExertsCount / globalOutput.Speed) / (globalOutput.IntimidatingCryCooldown + globalOutput.IntimidatingCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + local storedUses = (value.skillData.storedUses or 0) + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") globalOutput.IntimidatingUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.IntimidatingUpTimeRatio if globalBreakdown then @@ -3126,7 +3128,7 @@ function calcs.offence(env, actor, activeSkill) globalOutput.RallyingCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, value.skillData, actor) globalOutput.RallyingExertsCount = env.modDB:Sum("BASE", nil, "NumRallyingExerts") or 0 local baseUptimeRatio = m_min((globalOutput.RallyingExertsCount / globalOutput.Speed) / (globalOutput.RallyingCryCooldown + globalOutput.RallyingCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + local storedUses = (value.skillData.storedUses or 0) + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") globalOutput.RallyingUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.RallyingUpTimeRatio if globalBreakdown then @@ -3172,7 +3174,7 @@ function calcs.offence(env, actor, activeSkill) globalOutput.SeismicCryCastTime = calcWarcryCastTime(value.skillModList, value.skillCfg, value.skillData, actor) globalOutput.SeismicExertsCount = env.modDB:Sum("BASE", nil, "NumSeismicExerts") or 0 local baseUptimeRatio = m_min((globalOutput.SeismicExertsCount / globalOutput.Speed) / (globalOutput.SeismicCryCooldown + globalOutput.SeismicCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + local storedUses = (value.skillData.storedUses or 0) + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") globalOutput.SeismicUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.SeismicUpTimeRatio -- account for AoE increase @@ -3202,7 +3204,7 @@ function calcs.offence(env, actor, activeSkill) if activeSkill.skillTypes[SkillType.Melee] then globalOutput.BattleCryExertsCount = env.modDB:Sum("BASE", nil, "NumBattlemageExerts") or 0 local baseUptimeRatio = m_min((globalOutput.BattleCryExertsCount / globalOutput.Speed) / (globalOutput.BattleMageCryCooldown + globalOutput.BattleMageCryCastTime), 1) * 100 - local storedUses = value.skillData.storedUses or 0 + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") + local storedUses = (value.skillData.storedUses or 0) + value.skillModList:Sum("BASE", value.skillCfg, "AdditionalCooldownUses") globalOutput.BattlemageUpTimeRatio = m_min(100, baseUptimeRatio * storedUses) globalOutput.GlobalWarcryUptimeRatio = globalOutput.GlobalWarcryUptimeRatio + globalOutput.BattlemageUpTimeRatio if globalBreakdown then @@ -3494,6 +3496,7 @@ function calcs.offence(env, actor, activeSkill) output.CritBifurcates = bifurcateMultiChance local damageBonus = extraDamage local bifurcatedBonus = bifurcateMultiChance * extraDamage / 100 + ---@diagnostic disable-next-line: undefined-global if breakdown and enemyInc ~= 1 then breakdown.CritBifurcates = { s_format("%.2f%% ^8(effective crit chance)", critChancePercentage), @@ -3657,7 +3660,7 @@ function calcs.offence(env, actor, activeSkill) local noEnergyShieldLeech = skillModList:Flag(cfg, "CannotLeechEnergyShield") or enemyDB:Flag(nil, "CannotLeechEnergyShieldFromSelf") or skillModList:Flag(cfg, "CannotGainEnergyShield") local noManaLeech = skillModList:Flag(cfg, "CannotLeechMana") or enemyDB:Flag(nil, "CannotLeechManaFromSelf") or skillModList:Flag(cfg, "CannotGainMana") for _, damageType in ipairs(dmgTypeList) do - local damageTypeHitMin, damageTypeHitMax, damageTypeHitAvg, damageTypeLuckyChance, damageTypeHitAvgLucky, damageTypeHitAvgNotLucky = 0, 0, 0, 0, 0 + local damageTypeHitMin, damageTypeHitMax, damageTypeHitAvg, damageTypeLuckyChance, damageTypeHitAvgLucky, damageTypeHitAvgNotLucky = 0, 0, 0, 0, 0, 0 if skillFlags.hit and canDeal[damageType] then damageTypeHitMin, damageTypeHitMax = calcDamage(activeSkill, output, cfg, pass == 2 and breakdown and breakdown[damageType], damageType, 0) if pass == 2 and breakdown then @@ -4064,7 +4067,7 @@ function calcs.offence(env, actor, activeSkill) output.AverageDamage = output.AverageHit * output.HitChance / 100 globalOutput.AverageBurstHits = output.AverageBurstHits or 1 local repeatPenalty = skillModList:Flag(nil, "HasSeals") and activeSkill.skillTypes[SkillType.Unleashable] and not skillModList:Flag(nil, "NoRepeatBonuses") and calcLib.mod(skillModList, skillCfg, "SealRepeatPenalty") or 1 - globalOutput.AverageBurstDamage = output.AverageDamage + output.AverageDamage * (globalOutput.AverageBurstHits - 1) * repeatPenalty or 0 + globalOutput.AverageBurstDamage = output.AverageDamage + output.AverageDamage * (globalOutput.AverageBurstHits - 1) * repeatPenalty globalOutput.ShowBurst = globalOutput.AverageBurstHits > 1 output.TotalDPS = output.AverageDamage * (globalOutput.HitSpeed or globalOutput.Speed) * skillData.dpsMultiplier * quantityMultiplier if breakdown then @@ -4519,7 +4522,7 @@ function calcs.offence(env, actor, activeSkill) ---Calculates damage to be used in poise related ailment calculations ---@param ailment string ---@param defaultDamageTypes table - ---@return number, number, number, number min / max / avg hit, min / max/ avg crit damage + ---@return number hitMin, number hitMax, number hitAvg, number critMin, number critMax, number critAvg, number poseDamageAvg local function calcMinMaxPoiseSourceDamage(ailment, defaultDamageTypes) local canCrit = not skillModList:Flag(cfg, "AilmentsAreNeverFromCrit") local hitMin, hitMax, hitAvg = 0, 0, 0 @@ -4561,7 +4564,7 @@ function calcs.offence(env, actor, activeSkill) ---@param sourceCritChance number ---@param sourceHitDmg number ---@param sourceCritDmg number - ---@param hideFromBreakdown boolean + ---@param hideFromBreakdown boolean? ---@return number baseVal local function calcAilmentDamage(ailment, sourceCritChance, sourceHitDmg, sourceCritDmg, hideFromBreakdown) @@ -5424,6 +5427,7 @@ function calcs.offence(env, actor, activeSkill) end local inc = skillModList:Sum("INC", dotCfg, "Damage", "ChaosDamage") local more = skillModList:More(dotCfg, "Damage", "ChaosDamage") + ---@diagnostic disable-next-line: undefined-global local mult = skillModList:Override(dotTypeCfg, "DotMultiplier") or skillModList:Sum("BASE", dotTypeCfg, "DotMultiplier") + skillModList:Sum("BASE", dotTypeCfg, "ChaosDotMultiplier") output.DecayDPS = skillData.decay * (1 + inc/100) * more * (1 + mult/100) * effMult output.DecayDuration = 8 * debuffDurationMult diff --git a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua index cd5a2bc12..4e6eb8384 100644 --- a/src/Modules/CalcPerform.lua +++ b/src/Modules/CalcPerform.lua @@ -23,7 +23,7 @@ local m_huge = math.huge --- @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) ---- @return table unpacked table containing the desired values +--- @return ... unpacked table containing the desired values local function getCachedOutputValue(env, activeSkill, ...) local uuid = cacheSkillUUID(activeSkill, env) if not GlobalCache.cachedData[env.mode][uuid] or env.mode == "CALCULATOR" then @@ -494,6 +494,7 @@ local function doActorMisc(env, actor) end -- Fortify if modDB:Flag(nil, "Fortified") or modDB:Sum("BASE", nil, "Multiplier:Fortification") > 0 then + ---@diagnostic disable-next-line: undefined-global local maxStacks = modDB:Override(nil, "MaximumFortification") or modDB:Sum("BASE", skillCfg, "MaximumFortification") local minStacks = m_min(modDB:Sum("BASE", nil, "MinimumFortification"), maxStacks) local stacks = modDB:Override(nil, "FortificationStacks") or (alliedFortify > 0 and alliedFortify) or (minStacks > 0 and minStacks) or maxStacks @@ -660,6 +661,7 @@ local function doActorMisc(env, actor) condList["LeechingEnergyShield"] = true end if modDB:Flag(nil, "Condition:CanGainRage") or modDB:Sum("BASE", nil, "RageRegen") > 0 then + ---@diagnostic disable-next-line: undefined-global local maxStacks = modDB:Sum("BASE", skillCfg, "MaximumRage") local minStacks = m_min(modDB:Sum("BASE", nil, "MinimumRage"), maxStacks) local rageConfig = modDB:Sum("BASE", nil, "Multiplier:RageStack") @@ -2907,6 +2909,7 @@ function calcs.perform(env, skipEHP) -- Check for modifiers to apply to actors affected by player auras or curses for _, value in ipairs(modDB:List(nil, "AffectedByAuraMod")) do + ---@diagnostic disable-next-line: undefined-global for actor in pairs(affectedByAura) do actor.modDB:AddMod(value.mod) end diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index ab1596c29..8ca5d0ae6 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -510,8 +510,8 @@ return { { label = "Trigger Rate Cap", flag = "triggered", notFlagList = {"focused", "hasOverride"}, { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "CooldownRecovery", modType = "INC", cfg = "skill", }, }, }, { label = "Trigger Rate Cap", flagList = {"triggered", "hasOverride"}, notFlag = "focused", { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "CooldownRecovery", modType = "OVERRIDE", cfg = "skill", }, }, }, { label = "Trigger Rate Cap", flagList = {"triggered", "focused"}, { format = "{2:output:TriggerRateCap}", { breakdown = "TriggerRateCap" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, }, - { label = "Eff. Source Rate", flag = "triggered", notFlag = "focused", notFlag = "globalTrigger", { format = "{2:output:EffectiveSourceRate}", { breakdown = "EffectiveSourceRate" } }, }, - { label = "Skill Trigger Rate", flag = "triggered", notFlag = "focused", { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, }, }, + { label = "Eff. Source Rate", flag = "triggered", notFlagList = {"focused", "globalTrigger"}, { format = "{2:output:EffectiveSourceRate}", { breakdown = "EffectiveSourceRate" } }, }, + { label = "Skill Trigger Rate", flag = "triggered", notFlagList = {"focused", "globalTrigger"}, { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, }, }, { label = "Skill Trigger Rate", flagList = {"triggered", "focused"}, { format = "{2:output:SkillTriggerRate}", { breakdown = "SkillTriggerRate" }, { breakdown = "SimData" }, { modName = "FocusCooldownRecovery", modType = "INC", cfg = "skill", }, }, }, { label = "Cast time", flag = "spell", notFlag = "triggered", { format = "{2:output:Time}s", }, }, { label = "CWDT Threshold", haveOutput = "CWDTThreshold", flag = "triggered", { format = "{2:output:CWDTThreshold}", { breakdown = "CWDTThreshold" }, }, }, @@ -618,8 +618,8 @@ return { { label = "OH DMG Mod.", bgCol = colorCodes.OFFHANDBG, flag = "weapon2Attack", haveOutput = "OffHand.ImpaleModifier", { format = "{3:output:OffHand.ImpaleModifier}", modType = "MORE", { breakdown = "OffHand.ImpaleModifier" }, }, }, - { label = "Impale DPS", flag = "impale", flag = "notAverage", { format = "{1:output:ImpaleDPS}", { breakdown = "ImpaleDPS" }, }, }, - { label = "Impale Damage", flag = "impale", flag = "showAverage", { format = "{1:output:ImpaleDPS}", { breakdown = "ImpaleDPS" }, }, }, + { label = "Impale DPS", flagList = {"impale", "notAverage"}, { format = "{1:output:ImpaleDPS}", { breakdown = "ImpaleDPS" }, }, }, + { label = "Impale Damage", flagList = {"impale", "showAverage"}, { format = "{1:output:ImpaleDPS}", { breakdown = "ImpaleDPS" }, }, }, } } } }, { 1, "SkillTypeStats", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill type-specific Stats", data = { diff --git a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua index 58f354d66..a4096eed3 100644 --- a/src/Modules/CalcSetup.lua +++ b/src/Modules/CalcSetup.lua @@ -175,6 +175,7 @@ function calcs.buildModListForNode(env, node, incSmallPassiveSkill, includeKeyst end if modList:Flag(nil, "PassiveSkillHasOtherEffect") then + ---@diagnostic disable-next-line: undefined-global for i, mod in ipairs(modList:List(skillCfg, "NodeModifier")) do if i == 1 then wipeTable(modList) end modList:AddMod(mod.mod) diff --git a/src/Modules/CalcTools.lua b/src/Modules/CalcTools.lua index 2cfc9b7a8..749c1b46f 100644 --- a/src/Modules/CalcTools.lua +++ b/src/Modules/CalcTools.lua @@ -198,7 +198,7 @@ end --- Correct the tags on conversion with multipliers so they carry over correctly --- @param mod table --- @param multiplier number ---- @param minionMods bool @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 = { } @@ -220,7 +220,7 @@ end --- Get the gameId from the gemName which will be the same as the base gem for transfigured gems --- @param gemName string --- @param dropVaal boolean ---- @return string +--- @return string? function calcLib.getGameIdFromGemName(gemName, dropVaal) if type(gemName) ~= "string" then return @@ -238,9 +238,9 @@ end --- Use getGameIdFromGemName to get gameId from the gemName and passed in type. Return true if they're the same and not nil --- @param gemName string ---- @param type string +--- @param typeName string --- @param dropVaal boolean ---- @return boolean +--- @return boolean? function calcLib.isGemIdSame(gemName, typeName, dropVaal) local gemNameId = calcLib.getGameIdFromGemName(gemName, dropVaal) local typeId = calcLib.getGameIdFromGemName(typeName, dropVaal) diff --git a/src/Modules/Common.lua b/src/Modules/Common.lua index 6e532828a..97a3eaf19 100644 --- a/src/Modules/Common.lua +++ b/src/Modules/Common.lua @@ -45,6 +45,7 @@ end -- Optimize coroutines to run at full framerate local co_create = coroutine.create local active_coroutines = setmetatable({}, { __mode = "k" }) +---@diagnostic disable-next-line: duplicate-set-field function coroutine.create(func) local co = co_create(func) active_coroutines[co] = true @@ -247,7 +248,7 @@ end --- Clean item text by removing or replacing unsupported or redundant characters or sequences ---@param text string ----@return string +---@return string? function sanitiseText(text) if not text then return nil end -- Something something unicode support something grumble @@ -653,7 +654,7 @@ end --- Rounds down a number to the nearest decimal places ---@param val number ----@param dec number +---@param dec number? ---@return number function floor(val, dec) if dec then @@ -758,7 +759,7 @@ end function getFormatNumSep(dec) return function(val) - return formatNumSep(val, dec) + return formatNumSep(val) end end @@ -1049,4 +1050,4 @@ function GetVirtualScreenSize() height = math.floor(height / scale) end return width, height -end \ No newline at end of file +end diff --git a/src/Modules/DataLegionLookUpTableHelper.lua b/src/Modules/DataLegionLookUpTableHelper.lua index cf9cf29a6..f9ac8203c 100644 --- a/src/Modules/DataLegionLookUpTableHelper.lua +++ b/src/Modules/DataLegionLookUpTableHelper.lua @@ -196,6 +196,7 @@ local function repairLUTs() jewelType = jewelType + 1 end --]] + ---@diagnostic disable-next-line: undefined-global local compressedFile = io.open(scriptPath .. "/Data/TimelessJewelData/" .. data.timelessJewelTypes[jewelType], "rb") if compressedFile then ConPrintf("base LUT found: " .. jewelTypeName) @@ -204,6 +205,7 @@ local function repairLUTs() --- Code for compressing existing data if it changed local compressedFileData = Deflate(jewelData) + ---@diagnostic disable-next-line: undefined-global local file = assert(io.open(scriptPath .. "Data/TimelessJewelData/" .. jewelTypeName .. ".zip", "wb+")) file:write(compressedFileData) file:close() @@ -313,4 +315,4 @@ local function readLUT(seed, nodeID, jewelType) return { } end -return readLUT, repairLUTs \ No newline at end of file +return readLUT, repairLUTs diff --git a/src/Modules/ItemTools.lua b/src/Modules/ItemTools.lua index a013bbd7c..442493ca3 100644 --- a/src/Modules/ItemTools.lua +++ b/src/Modules/ItemTools.lua @@ -50,7 +50,8 @@ function itemLib.formatValue(value, baseValueScalar, valueScalar, precision, dis if displayPrecision then value = roundSymmetric(value, displayPrecision) end -- presentation if displayPrecision and not ifRequired then -- whitespace is needed return string.format("%."..displayPrecision.."f", value) - elseif displayPrecision then + elseif displayPrecision then + ---@diagnostic disable-next-line: redundant-parameter return tostring(value, displayPrecision) else return tostring(roundSymmetric(value, precision and m_min(2, m_floor(math.log(precision, 10) + 0.001)) or 2)) -- max decimals ingame is 2 @@ -87,10 +88,10 @@ function itemLib.applyRange(line, range, valueScalar, baseValueScalar) --- Takes a completely strippedLine where all values and ranges are replaced with a # + signs are kept for consistency upon re-substitution. --- This will then substitute back in the values until a line in scalabilityData is found this start with substituting everything and until none. --- This means if there is a more generic mod that might be scalable on both parameters but their is a narrower one that isn't it won't be scaled. - ---@param line the modLine stripped of all values and ranges replaced by # - ---@param values all values present in the modLine - ---@return scalableLine line with only scalableValues replaced with # - ---@return scalableValues values which can be scaled and added into scalableLine in place of a # + ---@param line string the modLine stripped of all values and ranges replaced by # + ---@param values table all values present in the modLine + ---@return string? scalableLine line with only scalableValues replaced with # + ---@return table? scalableValues values which can be scaled and added into scalableLine in place of a # local function findScalableLine(line, values) local function replaceNthInstance(input, pattern, replacement, n) local count = 0 @@ -366,4 +367,4 @@ itemLib.wiki = { return key == itemLib.wiki.key end, triggered = false -} \ No newline at end of file +} diff --git a/src/Modules/Main.lua b/src/Modules/Main.lua index 9905923bb..6a42bdc53 100644 --- a/src/Modules/Main.lua +++ b/src/Modules/Main.lua @@ -1184,6 +1184,7 @@ function main:OpenOptionsPopup() self.notSupportedModTooltips = initialNotSupportedModTooltips self.invertSliderScrollDirection = initialInvertSliderScrollDirection self.disableDevAutoSave = initialDisableDevAutoSave + ---@diagnostic disable-next-line: undefined-global self.showPublicBuilds = initialShowPublicBuilds self.showFlavourText = initialShowFlavourText self.showAnimations = initialShowAnimations diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 3feb3402a..f4311057b 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -883,7 +883,6 @@ local modNameList = { ["to ignore enemy physical damage reduction"] = "ChanceToIgnoreEnemyPhysicalDamageReduction", ["weapon swap speed"] = "WeaponSwapSpeed", ["to chain an additional time from terrain"] = "TerrainChainChance", - ["to apply lightning exposure on hit"] = "LightningExposureChance", ["to pierce an enemy"] = "PierceChance", ["to chain an additional time"] = "ChainChance", ["penalty to accuracy rating at range"] = "AccuracyPenalty", @@ -1873,7 +1872,6 @@ local modTagList = { ["if you[' ]h?a?ve hit a cursed enemy recently"] = { tagList = { { type = "Condition", var = "HitRecently" }, { type = "ActorCondition", actor = "enemy", var = "Cursed" } } }, ["if you[' ]h?a?ve dealt a projectile attack hit in the past eight seconds"] = { tag = { type = "Condition", var = "HitProjectileRecently" } }, ["if you[' ]h?a?ve dealt a melee hit in the past eight seconds"] = { tag = { type = "Condition", var = "HitMeleeRecently" } }, - ["if you[' ]h?a?ve dealt a melee hit in the past eight seconds"] = { tag = { type = "Condition", var = "HitMeleeRecently" } }, ["if you[' ]h?a?ve hit an enemy with a melee attack recently"] = { tag = { type = "Condition", var = "HitMeleeRecently" } }, ["if you[' ]h?a?ve triggered a skill recently"] = { tag = { type = "Condition", var = "TriggeredSkillRecently" } }, ["when you or your totems hit an enemy with a spell"] = { tag = { type = "Condition", varList = { "HitSpellRecently","TotemsHitSpellRecently" } }, }, @@ -1953,7 +1951,6 @@ local modTagList = { ["for each skill you've used recently, up to (%d+)%%"] = function(num) return { tag = { type = "Multiplier", var = "SkillUsedRecently", limit = num, limitTotal = true } } end, ["for each different non%-instant spell you[' ]h?a?ve cast recently"] = { tag = { type = "Multiplier", var = "NonInstantSpellCastRecently" } }, ["if you[' ]h?a?ve used a warcry recently"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } }, - ["when you warcry"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } }, ["if you[' ]h?a?ve warcried recently"] = { tag = { type = "Condition", var = "UsedWarcryRecently" } }, ["for each time you[' ]h?a?ve warcried recently"] = { tag = { type = "Multiplier", var = "WarcryUsedRecently" } }, ["for each time you[' ]h?a?ve been stunned recently"] = { tag = { type = "Multiplier", var = "StunnedRecently" } }, @@ -2160,8 +2157,10 @@ end local function extraSupport(name, level, slot) local skillId = gemIdLookup[name] or gemIdLookup[name:gsub("^increased ","")] or gemIdLookup[name:gsub(" support$","")] + ---@diagnostic disable-next-line: undefined-global if itemSlotName == "main hand" then slot = "Weapon 1" + ---@diagnostic disable-next-line: undefined-global elseif itemSlotName == "off hand" then slot = "Weapon 2" elseif slot then @@ -6903,6 +6902,7 @@ for k, v in pairs(jewelOtherFuncs) do -- Need to not modify any nodes already modified by timeless jewels -- Some functions return a function instead of simply adding mods, so if -- we don't see a node right away, run the outer function first + ---@diagnostic disable-next-line: redundant-parameter local innerFuncOrNil = v(cap1, cap2, cap3, cap4, cap5) -- In all (current) cases, there is only one nested layer, so no need for recursion return function(node, out, other) diff --git a/src/Modules/StatDescriber.lua b/src/Modules/StatDescriber.lua index ddccfa477..b66f6bf95 100644 --- a/src/Modules/StatDescriber.lua +++ b/src/Modules/StatDescriber.lua @@ -180,7 +180,9 @@ local function applySpecial(val, spec) val[spec.v].min = val[spec.v].min * 0.6 val[spec.v].max = val[spec.v].max * 0.6 elseif spec.k == "mod_value_to_item_class" then + ---@diagnostic disable-next-line: undefined-global val[spec.v].min = ItemClasses[val[spec.v].min].Name + ---@diagnostic disable-next-line: undefined-global val[spec.v].max = ItemClasses[val[spec.v].max].Name val[spec.v].fmt = "s" elseif spec.k == "one_hundred_divide_by_value" then