diff --git a/src/Classes/CalcBreakdownControl.lua b/src/Classes/CalcBreakdownControl.lua index e5d34a6d3b..ccdc2a9459 100644 --- a/src/Classes/CalcBreakdownControl.lua +++ b/src/Classes/CalcBreakdownControl.lua @@ -57,7 +57,11 @@ function CalcBreakdownClass:SetBreakdownData(displayData, pinned) end end if #self.sectionList == 0 then - self.calcsTab:ClearDisplayStat() + if self.clearDisplayFunc then + self.clearDisplayFunc() + else + self.calcsTab:ClearDisplayStat() + end return end @@ -116,7 +120,7 @@ end -- Add sections based on the breakdown data generated by the Calcs module function CalcBreakdownClass:AddBreakdownSection(sectionData) - local actor = self.calcsTab.input.showMinion and self.calcsTab.calcsEnv.minion or self.calcsTab.calcsEnv.player + local actor = self:GetActor() local breakdown local ns, name = sectionData.breakdown:match("^(%a+)%.(%a+)$") if ns then @@ -269,7 +273,7 @@ end -- Add a table section showing a list of modifiers function CalcBreakdownClass:AddModSection(sectionData, modList) - local actor = self.calcsTab.input.showMinion and self.calcsTab.calcsEnv.minion or self.calcsTab.calcsEnv.player + local actor = self:GetActor() local build = self.calcsTab.build -- Build list of modifiers to display @@ -674,12 +678,13 @@ function CalcBreakdownClass:Draw(viewPort) local scrollBar = self.controls.scrollBar local width = self.contentWidth local height = self.contentHeight + local borderThickness = self.borderThickness if self.contentHeight > viewPort.height then -- Content won't fit the screen height, so set the scrollbar width = self.contentWidth + scrollBar.width height = viewPort.height - scrollBar.height = height - 4 - scrollBar:SetContentDimension(self.contentHeight - 4, viewPort.height - 4) + scrollBar.height = height - borderThickness * 2 + scrollBar:SetContentDimension(self.contentHeight - borderThickness * 2, viewPort.height - borderThickness * 2) else scrollBar:SetContentDimension(0, 0) end @@ -696,15 +701,14 @@ function CalcBreakdownClass:Draw(viewPort) -- Draw background SetDrawLayer(nil, 10) SetDrawColor(0, 0, 0, 0.9) - DrawImage(nil, x + 2, y + 2, width - 4, height - 4) + DrawImage(nil, x + borderThickness, y + borderThickness, width - borderThickness * 2, height - borderThickness * 2) -- Draw border (this is put in sub layer 11 so it draws over the contents, in case they don't fit the screen) SetDrawLayer(nil, 11) if self.pinned then - SetDrawColor(0.25, 1, 0.25) + SetDrawColor(unpack(self.pinnedColour)) else - SetDrawColor(0.33, 0.66, 0.33) + SetDrawColor(unpack(self.borderColour)) end - local borderThickness = 2 DrawImage(nil, x, y, width, borderThickness) DrawImage(nil, x, y + height - borderThickness, width, borderThickness) DrawImage(nil, x, y, borderThickness, height) @@ -746,7 +750,11 @@ function CalcBreakdownClass:OnKeyDown(key, doubleClick) if key:match("BUTTON") then if not mOver then -- Mouse click outside the control, hide the breakdown - self.calcsTab:ClearDisplayStat() + if self.clearDisplayFunc then + self.clearDisplayFunc() + else + self.calcsTab:ClearDisplayStat() + end self.shown = false return end diff --git a/src/Classes/CalcBreakdownControl.lua.rej b/src/Classes/CalcBreakdownControl.lua.rej new file mode 100644 index 0000000000..0dd444478b --- /dev/null +++ b/src/Classes/CalcBreakdownControl.lua.rej @@ -0,0 +1,53 @@ +diff a/src/Classes/CalcBreakdownControl.lua b/src/Classes/CalcBreakdownControl.lua (rejected hunks) +@@ -27,8 +27,14 @@ function CalcBreakdownClass:CalcBreakdownControl(calcsTab) + self.rangeGuide:Load("Assets/range_guide.png") + self.uiOverlay = NewImageHandle() + self.uiOverlay:Load("Assets/game_ui_small.png") ++ 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.pinnedColour = { 0.25, 1, 0.25 } ++ self.borderColour = { 0.33, 0.66, 0.33 } + return self + end + +@@ -39,21 +45,34 @@ function CalcBreakdownClass:IsMouseOver() + return self:IsMouseInBounds() or self:GetMouseOverControl() + end + +-function CalcBreakdownClass:SetBreakdownData(displayData, pinned) ++function CalcBreakdownClass:GetActor() ++ local env = self.calcsTab[self.envName or "calcsEnv"] ++ local actor = self.calcsTab.input.showMinion and env.minion or env.player ++ if self.forceActor then ++ actor = env[self.forceActor] ++ end ++ return actor, env ++end ++---@param displayData any ++---@param pinned any ++---@param forceActor "player"|"minion"|nil ++function CalcBreakdownClass:SetBreakdownData(displayData, pinned, forceActor) + self.pinned = pinned + if displayData == self.sourceData then + return + end + self.sourceData = displayData ++ self.forceActor = forceActor + self.shown = false + if not displayData then + return + end + + -- Build list of sections ++ local actor, env = self:GetActor() + self.sectionList = wipeTable(self.sectionList) + for _, sectionData in ipairs(displayData) do +- if self.calcsTab:CheckFlag(sectionData) then ++ if self.calcsTab:CheckFlag(sectionData, actor, env.player) then + if sectionData.breakdown then + self:AddBreakdownSection(sectionData) + elseif sectionData.modName then diff --git a/src/Classes/CalcsTab.lua.rej b/src/Classes/CalcsTab.lua.rej new file mode 100644 index 0000000000..1a0373fc73 --- /dev/null +++ b/src/Classes/CalcsTab.lua.rej @@ -0,0 +1,19 @@ +diff a/src/Classes/CalcsTab.lua b/src/Classes/CalcsTab.lua (rejected hunks) +@@ -393,7 +393,7 @@ function CalcsTabClass:SetDisplayStat(displayData, pin) + self.controls.breakdown:SetBreakdownData(displayData, pin) + end + +-function CalcsTabClass:CheckFlag(obj, actor) ++function CalcsTabClass:CheckFlag(obj, actor, player) + actor = actor or (self.input.showMinion and self.calcsEnv.minion or self.calcsEnv.player) + local skillFlags = actor.mainSkill.skillFlags + if obj.flag and not skillFlags[obj.flag] then +@@ -406,7 +406,7 @@ function CalcsTabClass:CheckFlag(obj, actor) + end + end + end +- if obj.playerFlag and not self.calcsEnv.player.mainSkill.skillFlags[obj.playerFlag] then ++ if obj.playerFlag and not (player or self.calcsEnv.player).mainSkill.skillFlags[obj.playerFlag] then + return + end + if obj.notFlag and skillFlags[obj.notFlag] then diff --git a/src/Classes/TextListControl.lua b/src/Classes/TextListControl.lua index 7302a153b9..ea5029dd66 100644 --- a/src/Classes/TextListControl.lua +++ b/src/Classes/TextListControl.lua @@ -42,11 +42,37 @@ function TextListClass:Draw(viewPort) local lineY = -scrollBar.offset for _, lineInfo in ipairs(self.list) do if lineInfo[colIndex] then - DrawString(lineInfo.x or colInfo.x, lineY, lineInfo.align or colInfo.align, lineInfo.height, lineInfo.font or "VAR", lineInfo[colIndex]) + local textX = lineInfo.x or colInfo.x + local align = lineInfo.align or colInfo.align + DrawString(textX, lineY, align, lineInfo.height, lineInfo.font or "VAR", lineInfo[colIndex]) + if lineInfo.underline and lineInfo.underline[colIndex] then + local width = DrawStringWidth(lineInfo.height, "VAR", StripEscapes(lineInfo[colIndex])) + -- note: not fully handled. this is currently only used for + -- the side bar stats + if align == "RIGHT_X" then + textX = textX - width + end + SetDrawColor(0.5, 0.5, 0.5) + DrawImage(nil, textX, lineY + lineInfo.height, width, 1) + end end lineY = lineY + lineInfo.height end end + -- determine which line the user is hovering over + self.hoveredLine = nil + local cursorX, cursorY = GetCursorPos() + if cursorX >= x + 2 and cursorX < x + width - 18 and cursorY >= y + 2 and cursorY < y + height - 2 then + local rowY = y - scrollBar.offset + 2 + -- suboptimal. should do binary search if this causes performance problems + for _, lineInfo in ipairs(self.list) do + if cursorY >= rowY and cursorY < rowY + lineInfo.height then + self.hoveredLine = { line = lineInfo, x = x, y = rowY, width = width } + break + end + rowY = rowY + lineInfo.height + end + end SetViewport() end @@ -54,6 +80,9 @@ function TextListClass:OnKeyDown(key, doubleClick) if not self:IsShown() or not self:IsEnabled() then return end + if key == "LEFTBUTTON" and self.onClick then + self.onClick(self.hoveredLine) + end local mOverControl = self:GetMouseOverControl() if mOverControl and mOverControl.OnKeyDown then return mOverControl:OnKeyDown(key) diff --git a/src/Data/Global.lua b/src/Data/Global.lua index d877132bd6..7ec1c73e94 100644 --- a/src/Data/Global.lua +++ b/src/Data/Global.lua @@ -94,8 +94,9 @@ function updateColorCode(code, color) end function hexToRGB(hex) + hex = hex:gsub("%^x", "") -- Remove "^x" prefix hex = hex:gsub("0x", "") -- Remove "0x" prefix - hex = hex:gsub("#","") -- Remove '#' if present + hex = hex:gsub("#", "") -- Remove '#' if present if #hex ~= 6 then return nil end diff --git a/src/Modules/Build.lua b/src/Modules/Build.lua index e8b9a48140..6d384d3cd2 100644 --- a/src/Modules/Build.lua +++ b/src/Modules/Build.lua @@ -2255,7 +2255,7 @@ function buildMode:RefreshStatList() t_insert(statBoxList, { height = 14, align = "CENTER_X", x = 140, "^8" .. self.calcsTab.mainEnv.minion.mainSkill.infoMessage2}) end end - self:AddDisplayStatList(self.minionDisplayStats, self.calcsTab.mainEnv.minion) + self:AddDisplayStatList(self.minionDisplayStats, self.calcsTab.mainEnv.minion, "minion") t_insert(statBoxList, { height = 10 }) t_insert(statBoxList, { height = 18, "^7Player:" }) end @@ -2263,7 +2263,7 @@ function buildMode:RefreshStatList() t_insert(statBoxList, { height = 16, "^7Skill disabled:" }) t_insert(statBoxList, { height = 14, align = "CENTER_X", x = 140, self.calcsTab.mainEnv.player.mainSkill.disableReason }) end - self:AddDisplayStatList(self.displayStats, self.calcsTab.mainEnv.player) + self:AddDisplayStatList(self.displayStats, self.calcsTab.mainEnv.player, "player") self:InsertItemWarnings() self:EstimatePlayerProgress() end diff --git a/src/Modules/Build.lua.rej b/src/Modules/Build.lua.rej new file mode 100644 index 0000000000..e76b8c8d12 --- /dev/null +++ b/src/Modules/Build.lua.rej @@ -0,0 +1,91 @@ +diff a/src/Modules/Build.lua b/src/Modules/Build.lua (rejected hunks) +@@ -575,7 +575,7 @@ function buildMode:Init(dbFileName, buildName, buildXML, convertBuild, importLin + function self.controls.statBox.onClick(hoveredLine) + self:SetDisplayStat(hoveredLine, true) + end +-self.controls.warnings = new("Control"):Control({"TOPLEFT",self.controls.statBox,"BOTTOMLEFT",true}, {0, 0, 0, 18}) ++ self.controls.warnings = new("Control"):Control({"TOPLEFT",self.controls.statBox,"BOTTOMLEFT",true}, {0, 0, 0, 18}) + self.controls.warnings.lines = {} + self.controls.warnings.width = function(control) + return control.str and DrawStringWidth(16, "FIXED", control.str) + 8 or 0 +@@ -1365,7 +1365,7 @@ function buildMode:SetDisplayStat(hovered, pin) + return + end + +- self.sidebarBreakdownData = self:GetSidebarBreakdown(key, hovered.line.modNames, hovered.line.ignoredSections) ++ self.sidebarBreakdownData = self:GetSidebarBreakdown(key, hovered.line.modNames, hovered.line.ignoredSections, hovered.line.actorName) + self.sidebarBreakdownData.x = hovered.x - 1 + self.sidebarBreakdownData.y = hovered.y + self.sidebarBreakdownData.width = hovered.width +@@ -1802,8 +1802,12 @@ end + ---@param key string A breakdown key + ---@param modNames string[]? A modName key. Required if the mod name is different from the breakdown key. E.g. breakdown has Time while the mod has Speed + ---@param ignoredSections table? +-function buildMode:GetSidebarBreakdown(key, modNames, ignoredSections) +- local entry = self:BuildBreakdownIndex()[key] ++---@param actorName string? ++function buildMode:GetSidebarBreakdown(key, modNames, ignoredSections, actorName) ++ local env = actorName and self.calcsTab.mainEnv[actorName] ++ local breakdownData = env and env.breakdown and env.breakdown[key] ++ local sourceKey = breakdownData and breakdownData.breakdownSource or key ++ local entry = self:BuildBreakdownIndex()[sourceKey] + if not entry then + local output = { { breakdown = key } } + -- it's possible for calc sections to not have a breakdown, while still +@@ -1816,7 +1820,11 @@ function buildMode:GetSidebarBreakdown(key, modNames, ignoredSections) + local displayData = {} + -- this will include all of the hover information for the selected breakdown + for _, cell in ipairs(entry.colData) do +- t_insert(displayData, copyTable(cell)) ++ local displayCell = copyTable(cell) ++ if displayCell.breakdown == sourceKey then ++ displayCell.breakdown = key ++ end ++ t_insert(displayData, displayCell) + end + -- sometimes the breakdown doesn't include the mod names. for + -- example life has its mod tables separated to the inc and more breakdowns. +@@ -1857,19 +1865,34 @@ function buildMode:GetStatBreakdownKey(statData, actorName) + if not breakdown then + return nil + end +- -- note that this intentionally doesn't check if the breakdown exists so +- -- that we can still display mod lists +- if statData.breakdown then +- return statData.breakdown +- end +- if statData.childStat then ++ local key ++ if statData.breakdown ~= nil then ++ key = statData.breakdown ++ elseif statData.childStat then + local parent = breakdown[statData.stat] + if parent and parent[statData.childStat] then +- return statData.stat .. "." .. statData.childStat ++ key = statData.stat .. "." .. statData.childStat + end ++ elseif breakdown[statData.stat] then ++ key = statData.stat ++ end ++ if key == nil then ++ return nil + end +- if breakdown[statData.stat] then +- return statData.stat ++ -- Modifier-only popups do not require a calculated breakdown. ++ if statData.modNames then ++ return key ++ end ++ local namespace, name = key:match("^(%a+)%.(%a+)$") ++ local breakdownData = namespace and breakdown[namespace] and breakdown[namespace][name] or breakdown[key] ++ if breakdownData and (#breakdownData > 0 ++ or breakdownData.radius ++ or breakdownData.rowList and #breakdownData.rowList > 0 ++ or breakdownData.reservations and #breakdownData.reservations > 0 ++ or breakdownData.damageTypes and #breakdownData.damageTypes > 0 ++ or breakdownData.slots and #breakdownData.slots > 0 ++ or breakdownData.modList and #breakdownData.modList > 0) then ++ return key + end + return nil + end diff --git a/src/Modules/BuildDisplayStats.lua b/src/Modules/BuildDisplayStats.lua index 538efd46ca..7542610d2e 100644 --- a/src/Modules/BuildDisplayStats.lua +++ b/src/Modules/BuildDisplayStats.lua @@ -74,7 +74,7 @@ local displayStats = { { stat = "SealCooldown", label = "Seal Gain Frequency", fmt = ".2fs", lowerIsBetter = true }, { stat = "SealMax", label = "Max Number of Seals", fmt = "d" }, { stat = "TimeMaxSeals", label = "Time to Gain Max Seals", fmt = ".2fs", lowerIsBetter = true }, - { stat = "AreaOfEffectRadiusMetres", label = "AoE Radius", fmt = ".1fm" }, + { stat = "AreaOfEffectRadiusMetres", label = "AoE Radius", fmt = ".1fm", breakdown = "AreaOfEffectRadius", modNames = { "AreaOfEffect" } }, { stat = "BrandAttachmentRangeMetre", label = "Attachment Range", fmt = ".1fm", flag = "brand" }, { stat = "BrandTicks", label = "Activations per Brand", fmt = "d", flag = "brand" }, { stat = "ManaCost", label = "Mana Cost", fmt = "d", color = colorCodes.MANA, pool = "ManaUnreserved", compPercent = true, lowerIsBetter = true, condFunc = function(v,o) return o.ManaHasCost end }, @@ -249,6 +249,7 @@ local minionDisplayStats = { { stat = "EnergyShieldLeechGainRate", label = "ES Leech/On Hit Rate", fmt = ".1f", color = colorCodes.ES, compPercent = true }, } -- Extra stats saved to the xml if not already saved there, mostly for 3rd party tools +---@type string[] local extraSaveStats = { "PowerCharges", "PowerChargesMax", diff --git a/src/Modules/BuildDisplayStats.lua.rej b/src/Modules/BuildDisplayStats.lua.rej new file mode 100644 index 0000000000..cd75c9cd2b --- /dev/null +++ b/src/Modules/BuildDisplayStats.lua.rej @@ -0,0 +1,25 @@ +diff a/src/Modules/BuildDisplayStats.lua b/src/Modules/BuildDisplayStats.lua (rejected hunks) +@@ -54,7 +54,7 @@ local displayStats = { + { stat = "AverageDamage", label = "Average Damage", fmt = ".1f", compactValue = true, compPercent = true, flag = "monsterExplode", condFunc = function(v,o) return o.HitChance ~= 100 end }, + { stat = "AverageBurstDamage", label = "Average Burst Damage", fmt = ".1f", compactValue = true, compPercent = true, condFunc = function(v,o) return o.AverageBurstHits and o.AverageBurstHits > 1 and v > 0 end }, + { stat = "PvpAverageDamage", label = "PvP Average Damage", fmt = ".1f", compactValue = true, compPercent = true, flag = "attackPvP" }, +- { stat = "Speed", label = "Attack Rate", fmt = ".2f", compPercent = true, flag = "attack", condFunc = function(v, o) return v > 0 and (o.TriggerTime or 0) == 0 end, breakdown = "MainHand.Speed", modNames = { "Speed" }, ignoredSections = { "Inc. Cast Speed", "More Cast Speed", } }, ++ { stat = "Speed", label = "Attack Rate", fmt = ".2f", compPercent = true, flag = "attack", condFunc = function(v, o) return v > 0 and (o.TriggerTime or 0) == 0 end, breakdown = "Speed", modNames = { "Speed" }, ignoredSections = { "Inc. Cast Speed", "More Cast Speed", } }, + { stat = "KineticFusilladeMaxEffectiveAPS", label = "Max Eff. KF Attack Rate", fmt = ".2f", compPercent = true, flag = "attack", condFunc = function(v) return v > 0 end }, + { stat = "Speed", label = "Cast Rate", fmt = ".2f", compPercent = true, flag = "spell", condFunc = function(v, o) return v > 0 and (o.TriggerTime or 0) == 0 end, modNames = { "Speed" } }, + { stat = "Speed", label = "Effective Trigger Rate", fmt = ".2f", compPercent = true, notFlag = "skipEffectiveRate", condFunc = function(v,o) return (o.TriggerTime or 0) ~= 0 end }, +@@ -68,11 +68,10 @@ local displayStats = { + { stat = "TrapThrowCount", label = "Avg. Traps per Throw", fmt = ".2f"}, + { stat = "MineThrowCount", label = "Avg. Mines per Throw", fmt = ".2f"}, + { stat = "TotemPlacementTime", label = "Totem Placement Time", fmt = ".2fs", compPercent = true, lowerIsBetter = true, condFunc = function(v, o) return not o.TriggerTime end }, +- { stat = "PreEffectiveCritChance", label = "Crit Chance", fmt = ".2f%%", flag = "hit", breakdown = "CritChance" }, +- { stat = "CritChance", label = "Effective Crit Chance", fmt = ".2f%%", flag = "hit", notFlag = "spell", condFunc = function(v, o) return v ~= o.PreEffectiveCritChance end, breakdown = "MainHand.CritChance" }, +- { stat = "CritChance", label = "Effective Crit Chance", fmt = ".2f%%", flag = "hit", notFlag = "attack", condFunc = function(v, o) return v ~= o.PreEffectiveCritChance end, breakdown = "CritChance" }, ++ { stat = "PreEffectiveCritChance", label = "Crit Chance", fmt = ".2f%%", flag = "hit", breakdown = "PreEffectiveCritChance" }, ++ { stat = "CritChance", label = "Effective Crit Chance", fmt = ".2f%%", flag = "hit", condFunc = function(v, o) return v ~= o.PreEffectiveCritChance end, breakdown = "CritChance" }, + { stat = "CritMultiplier", label = "Crit Multiplier", fmt = "d%%", pc = true, condFunc = function(v, o) return (o.CritChance or 0) > 0 end, breakdown = "CritMultiplier" }, +- { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", flag = "attack", breakdown = "MainHand.AccuracyHitChance" }, ++ { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", flag = "attack", breakdown = "HitChance" }, + { stat = "HitChance", label = "Hit Chance", fmt = ".0f%%", condFunc = function(v,o) return o.enemyHasSpellBlock end }, + { stat = "TotalDPS", label = "Hit DPS", fmt = ".1f", compactValue = true, compPercent = true, flag = "notAverage" }, + { stat = "PvpTotalDPS", label = "PvP Hit DPS", fmt = ".1f", compactValue = true, compPercent = true, flag = "notAveragePvP" }, diff --git a/src/Modules/CalcOffence.lua.rej b/src/Modules/CalcOffence.lua.rej new file mode 100644 index 0000000000..276818e16b --- /dev/null +++ b/src/Modules/CalcOffence.lua.rej @@ -0,0 +1,67 @@ +diff a/src/Modules/CalcOffence.lua b/src/Modules/CalcOffence.lua (rejected hunks) +@@ -2472,6 +2472,20 @@ function calcs.offence(env, actor, activeSkill) + } + end + end ++ if breakdown then ++ if skillFlags.bothWeaponAttack then ++ breakdown.HitChance = { ++ "Both weapons:", ++ s_format("%.2f%% ^8(main hand)", output.MainHand.HitChance), ++ s_format("%.2f%% ^8(off hand)", output.OffHand.HitChance), ++ s_format("= %.2f%% ^8(average)", output.HitChance), ++ } ++ else ++ local handBreakdown = skillFlags.weapon1Attack and breakdown.MainHand or breakdown.OffHand ++ breakdown.HitChance = handBreakdown.HitChance or handBreakdown.AccuracyHitChance ++ breakdown.Speed = breakdown.Speed or handBreakdown.Speed ++ end ++ end + if skillData.hitTimeOverride and not skillData.triggeredOnDeath then + output.HitTime = skillData.hitTimeOverride + output.HitSpeed = 1 / output.HitTime +@@ -3077,6 +3091,7 @@ function calcs.offence(env, actor, activeSkill) + local overCap = preCapCritChance - 100 + t_insert(breakdown.CritChance, s_format("Crit is overcapped by %.2f%% (%d%% increased Critical Strike Chance)", overCap, overCap / more / (baseCrit + base) * 100)) + end ++ breakdown.PreEffectiveCritChance = copyTable(breakdown.CritChance) + if env.mode_effective then + if critRolls ~= 0 then + if skillModList:Flag(skillCfg, "Unexciting") then +@@ -3900,6 +3915,36 @@ function calcs.offence(env, actor, activeSkill) + combineStat("ManaOnHitRate", "DPS") + combineStat("ManaOnKill", "DPS") + combineStat("impaleStoredHitAvg", "DPS") ++ if breakdown then ++ if skillFlags.bothWeaponAttack then ++ for _, critStat in ipairs({ "PreEffectiveCritChance", "CritChance" }) do ++ local combinedBreakdown = { "Both weapons:" } ++ for _, pass in ipairs(passList) do ++ t_insert(combinedBreakdown, pass.label .. ":") ++ if pass.breakdown[critStat] then ++ for _, line in ipairs(pass.breakdown[critStat]) do ++ t_insert(combinedBreakdown, line) ++ end ++ else ++ t_insert(combinedBreakdown, s_format("%.2f%%", pass.output[critStat])) ++ end ++ end ++ t_insert(combinedBreakdown, s_format("= %.2f%% ^8(average)", output[critStat])) ++ breakdown[critStat] = combinedBreakdown ++ end ++ else ++ local handBreakdown = skillFlags.weapon1Attack and breakdown.MainHand or breakdown.OffHand ++ breakdown.PreEffectiveCritChance = handBreakdown.PreEffectiveCritChance ++ breakdown.CritChance = handBreakdown.CritChance ++ end ++ local breakdownSource = skillFlags.weapon1Attack and "MainHand.CritChance" or "OffHand.CritChance" ++ if breakdown.PreEffectiveCritChance then ++ breakdown.PreEffectiveCritChance.breakdownSource = breakdownSource ++ end ++ if breakdown.CritChance then ++ breakdown.CritChance.breakdownSource = breakdownSource ++ end ++ end + if skillFlags.bothWeaponAttack then + if breakdown then + breakdown.AverageDamage = { } diff --git a/src/Modules/CalcPerform.lua.rej b/src/Modules/CalcPerform.lua.rej new file mode 100644 index 0000000000..78cf7f5f5d --- /dev/null +++ b/src/Modules/CalcPerform.lua.rej @@ -0,0 +1,10 @@ +diff a/src/Modules/CalcPerform.lua b/src/Modules/CalcPerform.lua (rejected hunks) +@@ -1478,7 +1478,7 @@ function calcs.perform(env, skipEHP) + output.LootRarityMagicEnemies = (lootRarityMagicEnemies > 0) and lootRarityMagicEnemies + modDB:Sum("INC", nil, "LootRarity") or 0 + + local breakdown = nil +- if env.mode == "CALCS" then ++ if env.buildBreakdown then + -- Initialise breakdown module + breakdown = LoadModule(calcs.breakdownModule)(modDB, output, env.player) + env.player.breakdown = breakdown diff --git a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua index d885ad0c89..490e287f0f 100644 --- a/src/Modules/CalcSections.lua +++ b/src/Modules/CalcSections.lua @@ -646,8 +646,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", flag = "notAverage", { format = "{1:output:ImpaleDPS}", { breakdown = "ImpaleDPS" }, }, }, + { label = "Impale Damage", flag = "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/CalcSections.lua.rej b/src/Modules/CalcSections.lua.rej new file mode 100644 index 0000000000..defd589be3 --- /dev/null +++ b/src/Modules/CalcSections.lua.rej @@ -0,0 +1,56 @@ +diff a/src/Modules/CalcSections.lua b/src/Modules/CalcSections.lua (rejected hunks) +@@ -53,7 +53,53 @@ local rageCost = { + "RageCost", "Cost", "RageCostNoMult", "RageCostEfficiency", "CostEfficiency" + } + +--- format {width, id, group, color, subsection:{default hidden, label, data:{}}} ++---@class CalcSectionEntry A single description which is in the shown breakdown popup ++---@field breakdown? string Key indicating which output breakdown should be displayed ++---@field modName? string|string[] ++---@field modType? "BASE"|"INC"|"MORE"|"OVERRIDE" ++---@field cfg? string Config source ++---@field enemy? boolean Whether the enemy modDB should be used ++---@field actor? string Which actor should be used ++---@field modSource? string Restrict mods to e.g. items ++---@field label? string Heading shown above the resulting table ++---@field control? table Injected by calcs for e.g. the skill details ++---@field controlName? string ++ ++---@class CalcSectionColumn : CalcSectionEntry One column cell: a format and descriptions for the popup ++---@field [integer] CalcSectionEntry ++---@field format? string Value format string (e.g. "{0:output:Life}") or literal header text ++ ++---@class CalcSectionRow One row of a subsection ++---@field [integer] CalcSectionColumn Column cells ++---@field label? string Row label ++---@field haveOutput? string Only show the row when this output value is non-zero ++---@field flag? string Only show when the skill has this flag ++---@field flagList? string[] Only show when the skill has all of these flags ++---@field playerFlag? string Only show when the player skill has this flag ++---@field notFlag? string Hide when the skill has this flag ++---@field notFlagList? string[] Hide when the skill has any of these flags ++ ++---@class CalcSectionData A subsection's rows plus subsection configuration values ++---@field [integer] CalcSectionRow The rows ++---@field extra? string Summary text shown beside the header (e.g. "{0:output:Life}") ++---@field colWidth? integer Fixed width for value columns in this subsection ++---@field flag? string Applied to the whole subsection (via subSection[1].data.flag) ++---@field notFlag? string ++ ++---@class CalcSectionSubsection ++---@field defaultCollapsed boolean ++---@field label string ++---@field data CalcSectionData ++ ++---@class CalcSection ++---@field [1] integer Width in columns ++---@field [2] string Section id ++---@field [3] integer Group ++---@field [4] string Colour escape code ++---@field [5] CalcSectionSubsection[] Subsections ++---@field [6] fun()? Optional update function ++ ++---@type CalcSection[] + return { + { 3, "HitDamage", 1, colorCodes.OFFENCE, {{ defaultCollapsed = false, label = "Skill Hit Damage", data = { + extra = "{output:DisplayDamage}", diff --git a/src/Modules/CalcSetup.lua.rej b/src/Modules/CalcSetup.lua.rej new file mode 100644 index 0000000000..9cc49e8763 --- /dev/null +++ b/src/Modules/CalcSetup.lua.rej @@ -0,0 +1,9 @@ +diff a/src/Modules/CalcSetup.lua b/src/Modules/CalcSetup.lua (rejected hunks) +@@ -397,6 +397,7 @@ function calcs.initEnv(build, mode, override, specEnv) + env.configPlaceholder = build.configTab.placeholder + env.calcsInput = build.calcsTab.input + env.mode = mode ++ env.buildBreakdown = mode == "MAIN" or mode == "CALCS" + env.spec = override.spec or build.spec + env.override = override + env.classId = env.spec.curClassId diff --git a/src/Modules/Calcs.lua b/src/Modules/Calcs.lua index f8cf2e8278..2288c5dc90 100644 --- a/src/Modules/Calcs.lua +++ b/src/Modules/Calcs.lua @@ -481,6 +481,7 @@ end -- Process active skill function calcs.buildActiveSkill(env, mode, skill, targetUUID, limitedProcessingFlags) local fullEnv, _, _, _ = calcs.initEnv(env.build, mode, env.override) + fullEnv.buildBreakdown = false -- env.limitedSkills contains a map of uuids that should be limited in calculation -- this is in order to prevent infinite recursion loops