From bdb6ce4e0d5edc63e6c741d3574394cea4852c8b Mon Sep 17 00:00:00 2001 From: Borna Ivankovic Date: Mon, 25 Dec 2023 22:46:39 +0100 Subject: [PATCH 01/16] Watcher's eye trade search --- src/Classes/TradeQueryGenerator.lua | 54 +- src/Data/QueryMods.lua | 1322 ++++++++++++++++++++++++++- 2 files changed, 1357 insertions(+), 19 deletions(-) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 8acb5fb978..044947d216 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -79,6 +79,7 @@ local tradeStatCategoryIndices = { ["Exarch"] = 3, ["Synthesis"] = 3, ["PassiveNode"] = 2, + ["WatchersEye"] = 2, } local influenceSuffixes = { "_shaper", "_elder", "_adjudicator", "_basilisk", "_crusader", "_eyrie"} @@ -401,6 +402,7 @@ function TradeQueryGeneratorClass:InitMods() ["Exarch"] = { }, ["Synthesis"] = { }, ["PassiveNode"] = { }, + ["WatchersEye"] = { }, } -- originates from: https://www.pathofexile.com/api/trade/data/stats @@ -459,6 +461,17 @@ function TradeQueryGeneratorClass:InitMods() end self:GenerateModData(clusterNotableMods, tradeQueryStatsParsed) + -- Watcher's Eye + local watchersEyeMods = {} + for _,v in pairs(data.uniqueMods["Watcher's Eye"]) do + if v.Id:find("SublimeVision") or v.Id:find("SummonArbalist") then + goto continue + end + watchersEyeMods[v.Id] = v.mod + watchersEyeMods[v.Id].type = "WatchersEye" + ::continue:: + end + self:GenerateModData(watchersEyeMods,tradeQueryStatsParsed,{ ["BaseJewel"] = true, ["AnyJewel"] = true },{["AnyJewel"]="AnyJewel"}) -- Base item implicit mods. A lot of this code is duplicated from generateModData(), but with important small logical flow changes to handle the format differences for baseName, entry in pairs(data.itemBases) do if entry.implicit ~= nil then @@ -757,12 +770,35 @@ function TradeQueryGeneratorClass:StartQuery(slot, options) itemCategoryQueryStr = "jewel.abyss" itemCategory = "AbyssJewel" elseif slot.slotName:find("Jewel") ~= nil then - itemCategoryQueryStr = "jewel" - itemCategory = options.jewelType .. "Jewel" - if itemCategory == "AbyssJewel" then - itemCategoryQueryStr = "jewel.abyss" - elseif itemCategory == "BaseJewel" then - itemCategoryQueryStr = "jewel.base" + if options.jewelType == "Watcher's Eye" then + special={ + queryExtra = { + name = "Watcher's Eye" + }, + queryFilters = { + type_filters = { + filters = { + category = { + option = "jewel" + }, + rarity = { + option = "unique" + } + } + } + }, + watchersEye = true + } + itemCategory = "AnyJewel" + itemCategoryQueryStr = "jewel" + else + itemCategoryQueryStr = "jewel" + itemCategory = options.jewelType .. "Jewel" + if itemCategory == "AbyssJewel" then + itemCategoryQueryStr = "jewel.abyss" + elseif itemCategory == "BaseJewel" then + itemCategoryQueryStr = "jewel.base" + end end elseif slot.slotName:find("Flask") ~= nil then itemCategoryQueryStr = "flask" @@ -820,6 +856,10 @@ function TradeQueryGeneratorClass:ExecuteQuery() self:GeneratePassiveNodeWeights(self.modData.PassiveNode) return end + if self.calcContext.special.watchersEye then + self:GenerateModWeights(self.modData.WatchersEye) + return + end self:GenerateModWeights(self.modData["Explicit"]) self:GenerateModWeights(self.modData["Implicit"]) if self.calcContext.options.includeCorrupted then @@ -1017,7 +1057,7 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb end if isJewelSlot then - controls.jewelType = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, 0, 5, 100, 18, { "Any", "Base", "Abyss" }, function(index, value) end) + controls.jewelType = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, 0, 5, 110, 18, { "Any", "Base", "Abyss", "Watcher's Eye" }, function(index, value) end) controls.jewelType.selIndex = self.lastJewelType or 1 controls.jewelTypeLabel = new("LabelControl", {"RIGHT",controls.jewelType,"LEFT"}, -5, 0, 0, 16, "Jewel Type:") diff --git a/src/Data/QueryMods.lua b/src/Data/QueryMods.lua index 14aa763ca6..474fcffee5 100644 --- a/src/Data/QueryMods.lua +++ b/src/Data/QueryMods.lua @@ -2432,7 +2432,7 @@ return { ["specialCaseData"] = { }, ["tradeMod"] = { - ["id"] = "implicit.stat_2524254339", + ["id"] = "implicit.stat_1229298404", ["text"] = "Culling Strike", ["type"] = "implicit", }, @@ -42620,7 +42620,7 @@ return { ["specialCaseData"] = { }, ["tradeMod"] = { - ["id"] = "explicit.stat_2653955271", + ["id"] = "explicit.stat_1123291426", ["text"] = "Damage Penetrates #% Fire Resistance", ["type"] = "explicit", }, @@ -42654,7 +42654,7 @@ return { ["specialCaseData"] = { }, ["tradeMod"] = { - ["id"] = "explicit.stat_2653955271", + ["id"] = "explicit.stat_1123291426", ["text"] = "Damage Penetrates #% Fire Resistance", ["type"] = "explicit", }, @@ -58054,6 +58054,15 @@ return { ["type"] = "implicit", }, }, + ["implicit.stat_1229298404"] = { + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "implicit.stat_1229298404", + ["text"] = "Culling Strike", + ["type"] = "implicit", + }, + }, ["implicit.stat_1263158408"] = { ["1HAxe"] = { ["max"] = 1, @@ -60010,15 +60019,6 @@ return { ["type"] = "implicit", }, }, - ["implicit.stat_2524254339"] = { - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "implicit.stat_2524254339", - ["text"] = "Culling Strike", - ["type"] = "implicit", - }, - }, ["implicit.stat_2530372417"] = { ["1HAxe"] = { ["max"] = 6, @@ -76369,4 +76369,1302 @@ return { }, }, }, + ["WatchersEye"] = { + ["1577_ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRate"] = { + ["AnyJewel"] = { + ["max"] = 30, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2731416566", + ["text"] = "#% increased Maximum total Energy Shield Recovery per second from Leech while affected by Zealotry", + ["type"] = "explicit", + }, + }, + ["1578_ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRateOld"] = { + ["AnyJewel"] = { + ["max"] = 30, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2731416566", + ["text"] = "#% increased Maximum total Energy Shield Recovery per second from Leech while affected by Zealotry", + ["type"] = "explicit", + }, + }, + ["4296_HatredAdditionalCriticalStrikeChance"] = { + ["AnyJewel"] = { + ["max"] = 1.8, + ["min"] = 1.2, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2753985507", + ["text"] = "+#% to Critical Strike Chance while affected by Hatred", + ["type"] = "explicit", + }, + }, + ["4322_DeterminationPhysicalDamageReduction"] = { + ["AnyJewel"] = { + ["max"] = 8, + ["min"] = 5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1873457881", + ["text"] = "#% additional Physical Damage Reduction while affected by Determination", + ["type"] = "explicit", + }, + }, + ["4452_DeterminationAdditionalArmour"] = { + ["AnyJewel"] = { + ["max"] = 1000, + ["min"] = 600, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3742808908", + ["text"] = "+# to Armour while affected by Determination", + ["type"] = "explicit", + }, + }, + ["4540_PrecisionIncreasedAttackDamage"] = { + ["AnyJewel"] = { + ["max"] = 60, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2048747572", + ["text"] = "#% increased Attack Damage while affected by Precision", + ["type"] = "explicit", + }, + }, + ["4576_PrecisionIncreasedAttackSpeed"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3375743050", + ["text"] = "#% increased Attack Speed while affected by Precision", + ["type"] = "explicit", + }, + }, + ["4688_HatredPhysicalConvertedToCold"] = { + ["AnyJewel"] = { + ["max"] = 40, + ["min"] = 25, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_664849247", + ["text"] = "#% of Physical Damage Converted to Cold Damage while affected by Hatred", + ["type"] = "explicit", + }, + }, + ["4689_AngerPhysicalConvertedToFire"] = { + ["AnyJewel"] = { + ["max"] = 40, + ["min"] = 25, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3624529132", + ["text"] = "#% of Physical Damage Converted to Fire Damage while affected by Anger", + ["type"] = "explicit", + }, + }, + ["4690_WrathPhysicalConvertedToLightning"] = { + ["AnyJewel"] = { + ["max"] = 40, + ["min"] = 25, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2106756686", + ["text"] = "#% of Physical Damage Converted to Lightning Damage while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["4858_GraceBlindEnemiesWhenHit"] = { + ["AnyJewel"] = { + ["max"] = 50, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2548097895", + ["text"] = "#% chance to Blind Enemies which Hit you while affected by Grace", + ["type"] = "explicit", + }, + }, + ["4866_DeterminationAdditionalBlock"] = { + ["AnyJewel"] = { + ["max"] = 8, + ["min"] = 5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3692646597", + ["text"] = "+#% Chance to Block Attack Damage while affected by Determination", + ["type"] = "explicit", + }, + }, + ["4923_PrecisionCannotBeBlinded"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1653848515", + ["text"] = "Cannot be Blinded while affected by Precision", + ["type"] = "explicit", + }, + }, + ["4992_ZealotryCastSpeed"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2444534954", + ["text"] = "#% increased Cast Speed while affected by Zealotry", + ["type"] = "explicit", + }, + }, + ["5128_DisciplineAdditionalSpellBlock"] = { + ["AnyJewel"] = { + ["max"] = 8, + ["min"] = 5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1313498929", + ["text"] = "+#% Chance to Block Spell Damage while affected by Discipline", + ["type"] = "explicit", + }, + }, + ["5149_GraceAdditionalChanceToEvade"] = { + ["AnyJewel"] = { + ["max"] = 8, + ["min"] = 5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_969576725", + ["text"] = "+#% chance to Evade Attack Hits while affected by Grace", + ["type"] = "explicit", + }, + }, + ["5212_PurityOfElementsChaosResistance"] = { + ["AnyJewel"] = { + ["max"] = 50, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1138813382", + ["text"] = "+#% to Chaos Resistance while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["5279_HatredIncreasedColdDamage"] = { + ["AnyJewel"] = { + ["max"] = 60, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1413864591", + ["text"] = "#% increased Cold Damage while affected by Hatred", + ["type"] = "explicit", + }, + }, + ["5312_ZealotryConsecratedGroundEnemyDamageTaken"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2434030180", + ["text"] = "Consecrated Ground you create while affected by Zealotry causes enemies to take #% increased Damage", + ["type"] = "explicit", + }, + }, + ["5315_ZealotryConsecratedGroundEffectLingersForMsAfterLeavingTheArea"] = { + ["AnyJewel"] = { + ["max"] = 2, + ["min"] = 2, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2163419452", + ["text"] = "Effects of Consecrated Ground you create while affected by Zealotry Linger for # seconds", + ["type"] = "explicit", + }, + }, + ["5376_ZealotryCriticalStrikeChanceAgainstEnemiesOnConsecratedGround"] = { + ["AnyJewel"] = { + ["max"] = 120, + ["min"] = 100, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_214835567", + ["text"] = "#% increased Critical Strike Chance against Enemies on Consecrated Ground while affected by Zealotry", + ["type"] = "explicit", + }, + }, + ["5396_WrathIncreasedCriticalStrikeChance"] = { + ["AnyJewel"] = { + ["max"] = 100, + ["min"] = 70, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3357049845", + ["text"] = "#% increased Critical Strike Chance while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["5422_AngerIncreasedCriticalStrikeMultiplier"] = { + ["AnyJewel"] = { + ["max"] = 50, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3627458291", + ["text"] = "+#% to Critical Strike Multiplier while affected by Anger", + ["type"] = "explicit", + }, + }, + ["5423_PrecisionIncreasedCriticalStrikeMultiplier"] = { + ["AnyJewel"] = { + ["max"] = 30, + ["min"] = 20, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1817023621", + ["text"] = "+#% to Critical Strike Multiplier while affected by Precision", + ["type"] = "explicit", + }, + }, + ["5436_ZealotryCriticalStrikesPenetratesElementalResistances"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2091518682", + ["text"] = "Critical Strikes Penetrate #% of Enemy Elemental Resistances while affected by Zealotry", + ["type"] = "explicit", + }, + }, + ["5538_ClarityDamageTakenFromManaBeforeLife"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 6, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2383304564", + ["text"] = "#% of Damage taken from Mana before Life while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["5549_ClarityDamageTakenGainedAsMana"] = { + ["AnyJewel"] = { + ["max"] = 20, + ["min"] = 15, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_380220671", + ["text"] = "#% of Damage taken while affected by Clarity Recouped as Mana", + ["type"] = "explicit", + }, + }, + ["5588_HasteDebuffsExpireFaster"] = { + ["AnyJewel"] = { + ["max"] = 20, + ["min"] = 15, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_207635700", + ["text"] = "Debuffs on you expire #% faster while affected by Haste", + ["type"] = "explicit", + }, + }, + ["5689_MalevolenceDamageOverTimeMultiplier"] = { + ["AnyJewel"] = { + ["max"] = 22, + ["min"] = 18, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2736708072", + ["text"] = "+#% to Damage over Time Multiplier while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["5759_PurityOfElementsReducedReflectedElementalDamage"] = { + ["AnyJewel"] = { + ["max"] = 50, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_65331133", + ["text"] = "#% reduced Reflected Elemental Damage taken while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["5838_DisciplineFasterStartOfRecharge"] = { + ["AnyJewel"] = { + ["max"] = 40, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1016185292", + ["text"] = "#% faster start of Energy Shield Recharge while affected by Discipline", + ["type"] = "explicit", + }, + }, + ["5841_DisciplineEnergyShieldPerHit"] = { + ["AnyJewel"] = { + ["max"] = 30, + ["min"] = 20, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3765507527", + ["text"] = "Gain # Energy Shield per Enemy Hit while affected by Discipline", + ["type"] = "explicit", + }, + }, + ["5845_WrathLightningDamageESLeech"] = { + ["AnyJewel"] = { + ["max"] = 1.5, + ["min"] = 1, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_121436064", + ["text"] = "#% of Lightning Damage is Leeched as Energy Shield while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["5858_DisciplineEnergyShieldRecoveryRate"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_80470845", + ["text"] = "#% increased Energy Shield Recovery Rate while affected by Discipline", + ["type"] = "explicit", + }, + }, + ["5864_DisciplineEnergyShieldRegen"] = { + ["AnyJewel"] = { + ["max"] = 2.5, + ["min"] = 1.5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_991194404", + ["text"] = "Regenerate #% of Energy Shield per Second while affected by Discipline", + ["type"] = "explicit", + }, + }, + ["5930_DeterminationReducedExtraDamageFromCrits"] = { + ["AnyJewel"] = { + ["max"] = 60, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_68410701", + ["text"] = "You take #% reduced Extra Damage from Critical Strikes while affected by Determination", + ["type"] = "explicit", + }, + }, + ["5959_AngerIncreasedFireDamage"] = { + ["AnyJewel"] = { + ["max"] = 60, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3337107517", + ["text"] = "#% increased Fire Damage while affected by Anger", + ["type"] = "explicit", + }, + }, + ["6024_VitalityLifeRecoveryFromFlasks"] = { + ["AnyJewel"] = { + ["max"] = 70, + ["min"] = 50, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_362838683", + ["text"] = "#% increased Life Recovery from Flasks while affected by Vitality", + ["type"] = "explicit", + }, + }, + ["6100_ZealotryGainArcaneSurgeFor4SecondsWhenYouCreateConsecratedGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1919069577", + ["text"] = "Gain Arcane Surge for 4 seconds when you create Consecrated Ground while affected by Zealotry", + ["type"] = "explicit", + }, + }, + ["6158_HasteGainOnslaughtOnKill"] = { + ["AnyJewel"] = { + ["max"] = 4, + ["min"] = 4, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1424006185", + ["text"] = "You gain Onslaught for # seconds on Kill while affected by Haste", + ["type"] = "explicit", + }, + }, + ["6169_HasteGainPhasing"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1346311588", + ["text"] = "You have Phasing while affected by Haste", + ["type"] = "explicit", + }, + }, + ["6534_PurityOfIceImmuneToFreeze"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2720072724", + ["text"] = "Immune to Freeze while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["6537_PurityOfFireImmuneToIgnite"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_371612541", + ["text"] = "Immune to Ignite while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["6541_PurityOfLightningImmuneToShock"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_281949611", + ["text"] = "Immune to Shock while affected by Purity of Lightning", + ["type"] = "explicit", + }, + }, + ["6628_MalevolenceLifeAndEnergyShieldRecoveryRate"] = { + ["AnyJewel"] = { + ["max"] = 12, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3643449791", + ["text"] = "#% increased Recovery rate of Life and Energy Shield while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["6637_VitalityLifeGainPerHit"] = { + ["AnyJewel"] = { + ["max"] = 30, + ["min"] = 20, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4259701244", + ["text"] = "Gain # Life per Enemy Hit while affected by Vitality", + ["type"] = "explicit", + }, + }, + ["6643_VitalityDamageLifeLeech"] = { + ["AnyJewel"] = { + ["max"] = 1.2, + ["min"] = 0.8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3656959867", + ["text"] = "#% of Damage leeched as Life while affected by Vitality", + ["type"] = "explicit", + }, + }, + ["6650_AngerFireDamageLifeLeech"] = { + ["AnyJewel"] = { + ["max"] = 1.5, + ["min"] = 1, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2312747856", + ["text"] = "#% of Fire Damage Leeched as Life while affected by Anger", + ["type"] = "explicit", + }, + }, + ["6673_VitalityLifeRecoveryRate"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2690790844", + ["text"] = "#% increased Life Recovery Rate while affected by Vitality", + ["type"] = "explicit", + }, + }, + ["6683_VitalityFlatLifeRegen"] = { + ["AnyJewel"] = { + ["max"] = 140, + ["min"] = 100, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3489570622", + ["text"] = "Regenerate # Life per Second while affected by Vitality", + ["type"] = "explicit", + }, + }, + ["6690_VitalityPercentLifeRegen"] = { + ["AnyJewel"] = { + ["max"] = 1.5, + ["min"] = 1, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1165583295", + ["text"] = "Regenerate #% of Life per second while affected by Vitality", + ["type"] = "explicit", + }, + }, + ["6728_WrathIncreasedLightningDamage"] = { + ["AnyJewel"] = { + ["max"] = 60, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_418293304", + ["text"] = "#% increased Lightning Damage while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["7388_WrathLightningDamageManaLeech"] = { + ["AnyJewel"] = { + ["max"] = 1.5, + ["min"] = 1, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2889601846", + ["text"] = "#% of Lightning Damage is Leeched as Mana while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["7397_ClarityManaRecoveryRate"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_556659145", + ["text"] = "#% increased Mana Recovery Rate while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["8232_ClarityManaAddedAsEnergyShield"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 6, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2831391506", + ["text"] = "Gain #% of Maximum Mana as Extra Maximum Energy Shield while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["8282_HatredAddedColdDamage"] = { + ["AnyJewel"] = { + ["max"] = 87, + ["min"] = 73, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2643562209", + ["text"] = "Adds # to # Cold Damage while affected by Hatred", + ["type"] = "explicit", + }, + }, + ["8416_HasteCooldownRecoveryForMovementSkills"] = { + ["AnyJewel"] = { + ["max"] = 50, + ["min"] = 30, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3332055899", + ["text"] = "#% increased Cooldown Recovery Rate of Movement Skills used while affected by Haste", + ["type"] = "explicit", + }, + }, + ["8436_GraceIncreasedMovementSpeed"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3329402420", + ["text"] = "#% increased Movement Speed while affected by Grace", + ["type"] = "explicit", + }, + }, + ["8597_AngerPhysicalAddedAsFire"] = { + ["AnyJewel"] = { + ["max"] = 25, + ["min"] = 15, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4245204226", + ["text"] = "Gain #% of Physical Damage as Extra Fire Damage while affected by Anger", + ["type"] = "explicit", + }, + }, + ["8600_WrathPhysicalAddedAsLightning"] = { + ["AnyJewel"] = { + ["max"] = 25, + ["min"] = 15, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2255914633", + ["text"] = "Gain #% of Physical Damage as Extra Lightning Damage while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["8622_PurityOfElementsTakePhysicalAsCold"] = { + ["AnyJewel"] = { + ["max"] = 12, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1710207583", + ["text"] = "#% of Physical Damage from Hits taken as Cold Damage while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["8623_PurityOfIceTakePhysicalAsIce"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 6, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1779027621", + ["text"] = "#% of Physical Damage from Hits taken as Cold Damage while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["8624_PurityOfElementsTakePhysicalAsFire"] = { + ["AnyJewel"] = { + ["max"] = 12, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1722775216", + ["text"] = "#% of Physical Damage from Hits taken as Fire Damage while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["8625_PurityOfFireTakePhysicalAsFire"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 6, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1798459983", + ["text"] = "#% of Physical Damage from Hits taken as Fire Damage while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["8626_PurityOfElementsTakePhysicalAsLightning"] = { + ["AnyJewel"] = { + ["max"] = 12, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_873224517", + ["text"] = "#% of Physical Damage from Hits taken as Lightning Damage while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["8627_PurityOfLightningTakePhysicalAsLightning"] = { + ["AnyJewel"] = { + ["max"] = 10, + ["min"] = 6, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_254131992", + ["text"] = "#% of Physical Damage from Hits taken as Lightning Damage while affected by Purity of Lightning", + ["type"] = "explicit", + }, + }, + ["8675_PrideChanceForDoubleDamage"] = { + ["AnyJewel"] = { + ["max"] = 12, + ["min"] = 8, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3371719014", + ["text"] = "#% chance to deal Double Damage while using Pride", + ["type"] = "explicit", + }, + }, + ["8676_PrideChanceToImpale"] = { + ["AnyJewel"] = { + ["max"] = 25, + ["min"] = 25, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4173751044", + ["text"] = "#% chance to Impale Enemies on Hit with Attacks while using Pride", + ["type"] = "explicit", + }, + }, + ["8677_PrideIntimidateOnHit"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3772848194", + ["text"] = "Your Hits Intimidate Enemies for 4 seconds while you are using Pride", + ["type"] = "explicit", + }, + }, + ["8681_PrideIncreasedPhysicalDamage"] = { + ["AnyJewel"] = { + ["max"] = 60, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_576528026", + ["text"] = "#% increased Physical Damage while using Pride", + ["type"] = "explicit", + }, + }, + ["8683_PrideImpaleAdditionalHits"] = { + ["AnyJewel"] = { + ["max"] = 2, + ["min"] = 2, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1011863394", + ["text"] = "Impales you inflict last # additional Hits while using Pride", + ["type"] = "explicit", + }, + }, + ["8778_PrecisionFlaskChargeOnCrit"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3772841281", + ["text"] = "Gain a Flask Charge when you deal a Critical Strike while affected by Precision", + ["type"] = "explicit", + }, + }, + ["8788_ClarityRecoverManaOnSkillUse"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1699077932", + ["text"] = "#% chance to Recover 10% of Mana when you use a Skill while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["8814_HatredColdPenetration"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1222888897", + ["text"] = "Damage Penetrates #% Cold Resistance while affected by Hatred", + ["type"] = "explicit", + }, + }, + ["8816_AngerFirePenetration"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3111519953", + ["text"] = "Damage Penetrates #% Fire Resistance while affected by Anger", + ["type"] = "explicit", + }, + }, + ["8817_WrathLightningPenetration"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1077131949", + ["text"] = "Damage Penetrates #% Lightning Resistance while affected by Wrath", + ["type"] = "explicit", + }, + }, + ["8824_DeterminationReducedReflectedPhysicalDamage"] = { + ["AnyJewel"] = { + ["max"] = 50, + ["min"] = 40, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2457540491", + ["text"] = "#% reduced Reflected Physical Damage taken while affected by Determination", + ["type"] = "explicit", + }, + }, + ["8971_ClarityReducedManaCost"] = { + ["AnyJewel"] = { + ["max"] = 5, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2445618239", + ["text"] = "+# to Total Mana Cost of Skills while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["8976_ClarityReducedManaCostNonChannelled"] = { + ["AnyJewel"] = { + ["max"] = 5, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1853636813", + ["text"] = "Non-Channelling Skills have +# to Total Mana Cost while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["9070_GraceChanceToDodge"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 12, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4071658793", + ["text"] = "+#% chance to Suppress Spell Damage while affected by Grace", + ["type"] = "explicit", + }, + }, + ["9071_HasteChanceToDodgeSpells"] = { + ["AnyJewel"] = { + ["max"] = 8, + ["min"] = 5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2170859717", + ["text"] = "+#% chance to Suppress Spell Damage while affected by Haste", + ["type"] = "explicit", + }, + }, + ["9316_MalevolenceUnaffectedByBleeding"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4104891138", + ["text"] = "Unaffected by Bleeding while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["9320_PurityOfFireUnaffectedByBurningGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3308185931", + ["text"] = "Unaffected by Burning Ground while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["9325_PurityOfIceUnaffectedByChilledGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2647344903", + ["text"] = "Unaffected by Chilled Ground while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["9326_PurityOfLightningUnaffectedByConductivity"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1567542124", + ["text"] = "Unaffected by Conductivity while affected by Purity of Lightning", + ["type"] = "explicit", + }, + }, + ["9331_PurityOfElementsUnaffectedByElementalWeakness"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3223142064", + ["text"] = "Unaffected by Elemental Weakness while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["9332_GraceUnaffectedByEnfeeble"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2365917222", + ["text"] = "Unaffected by Enfeeble while affected by Grace", + ["type"] = "explicit", + }, + }, + ["9333_PurityOfFireUnaffectedByFlammability"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1173690938", + ["text"] = "Unaffected by Flammability while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["9335_PurityOfIceUnaffectedByFrostbite"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4012281889", + ["text"] = "Unaffected by Frostbite while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["9339_MalevolenceUnaffectedByPoison"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_34059570", + ["text"] = "Unaffected by Poison while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["9345_PurityOfLightningUnaffectedByShockedGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2567659895", + ["text"] = "Unaffected by Shocked Ground while affected by Purity of Lightning", + ["type"] = "explicit", + }, + }, + ["9347_HasteUnaffectedByTemporalChains"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2806391472", + ["text"] = "Unaffected by Temporal Chains while affected by Haste", + ["type"] = "explicit", + }, + }, + ["9348_DeterminationUnaffectedByVulnerability"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3207781478", + ["text"] = "Unaffected by Vulnerability while affected by Determination", + ["type"] = "explicit", + }, + }, + ["9497_MalevolenceYourAilmentsDealDamageFaster"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3468843137", + ["text"] = "Damaging Ailments you inflict deal Damage #% faster while affected by Malevolence", + ["type"] = "explicit", + }, + }, + }, } \ No newline at end of file From 05c6930a12240df5a20518cb86a8c83f7930ccbe Mon Sep 17 00:00:00 2001 From: Borna Ivankovic Date: Tue, 30 Jan 2024 18:14:33 +0100 Subject: [PATCH 02/16] Improvements per comments - move find button to separate slot instead of using exisitng jewel sockets - use exisitng Watcher's Eye slot(if available) for usage in weight calculations with test item - added option to include rest of applicable mods from Watcher's Eye mod pool that have weight 0 in final query --- src/Classes/TradeQuery.lua | 27 +++++++- src/Classes/TradeQueryGenerator.lua | 99 +++++++++++++++++++---------- src/Launch.lua | 7 ++ src/Modules/ModParser.lua | 22 +++++++ 4 files changed, 121 insertions(+), 34 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 77a9b6a7b6..4a75a5d4ae 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -425,8 +425,14 @@ Highest Weight - Displays the order retrieved from trade]] local row_count = #slotTables + 1 self.slotTables[row_count] = { slotName = "Megalomaniac", unique = true, alreadyCorrupted = true } self:PriceItemRowDisplay(row_count, top_pane_alignment_ref, row_vertical_padding, row_height) + top_pane_alignment_ref[2] = self.controls["name"..row_count] row_count = row_count + 1 - + -- Watcher's Eye + if self:findValidSlotForWatchersEye() then + self.slotTables[row_count] = { slotName = "Watcher's Eye", unique = true } + self:PriceItemRowDisplay(row_count, top_pane_alignment_ref, row_vertical_padding, row_height) + row_count = row_count + 1 + end local effective_row_count = row_count + 2 + 2 -- Two top menu rows, two bottom rows local pane_height = (row_height + row_vertical_padding) * effective_row_count + 2 * pane_margins_vertical + row_height / 2 local pane_width = 850 @@ -784,13 +790,30 @@ function TradeQueryClass:addChaosEquivalentPriceToItems(items) return outputItems end +-- return valid slot for Watcher's Eye +function TradeQueryClass:findValidSlotForWatchersEye() + local tmpWE=nil + for _,v in ipairs(data.uniques.generated) do + if v:find("Watcher's Eye") then + tmpWE= new("Item",v) + break + end + end + for _,v in pairs(self.itemsTab.sockets) do + if not v.inactive then + if self.itemsTab:IsItemValidForSlot(tmpWE,v.slotName,self.itemsTab.activeItemSet) then + return self.itemsTab.sockets[v.nodeId] + end + end + end +end -- Method to generate pane elements for each item slot function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, row_vertical_padding, row_height) local controls = self.controls local slotTbl = self.slotTables[row_idx] local activeSlotRef = slotTbl.nodeId and self.itemsTab.activeItemSet[slotTbl.nodeId] or self.itemsTab.activeItemSet[slotTbl.slotName] - local activeSlot = slotTbl.nodeId and self.itemsTab.sockets[slotTbl.nodeId] or slotTbl.slotName and self.itemsTab.slots[slotTbl.slotName] + local activeSlot = slotTbl.nodeId and self.itemsTab.sockets[slotTbl.nodeId] or slotTbl.slotName and self.itemsTab.slots[slotTbl.slotName] or slotTbl.slotName == "Watcher's Eye" and self:findValidSlotForWatchersEye() local nameColor = slotTbl.unique and colorCodes.UNIQUE or "^7" controls["name"..row_idx] = new("LabelControl", top_pane_alignment_ref, 0, row_height + row_vertical_padding, 100, row_height - 4, nameColor..slotTbl.slotName) controls["bestButton"..row_idx] = new("ButtonControl", { "LEFT", controls["name"..row_idx], "LEFT"}, 100 + 8, 0, 80, row_height, "Find best", function() diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 044947d216..1025f86023 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -686,6 +686,28 @@ function TradeQueryGeneratorClass:StartQuery(slot, options) calcNodesInsteadOfMods = true, } end + if options.special.itemName == "Watcher's Eye" then + special={ + queryExtra = { + name = "Watcher's Eye" + }, + queryFilters = { + type_filters = { + filters = { + category = { + option = "jewel" + }, + rarity = { + option = "unique" + } + } + } + }, + watchersEye = true + } + itemCategory = "AnyJewel" + itemCategoryQueryStr = "jewel" + end elseif slot.slotName == "Weapon 2" or slot.slotName == "Weapon 1" then if existingItem then if existingItem.type == "Shield" then @@ -770,35 +792,12 @@ function TradeQueryGeneratorClass:StartQuery(slot, options) itemCategoryQueryStr = "jewel.abyss" itemCategory = "AbyssJewel" elseif slot.slotName:find("Jewel") ~= nil then - if options.jewelType == "Watcher's Eye" then - special={ - queryExtra = { - name = "Watcher's Eye" - }, - queryFilters = { - type_filters = { - filters = { - category = { - option = "jewel" - }, - rarity = { - option = "unique" - } - } - } - }, - watchersEye = true - } - itemCategory = "AnyJewel" - itemCategoryQueryStr = "jewel" - else - itemCategoryQueryStr = "jewel" - itemCategory = options.jewelType .. "Jewel" - if itemCategory == "AbyssJewel" then - itemCategoryQueryStr = "jewel.abyss" - elseif itemCategory == "BaseJewel" then - itemCategoryQueryStr = "jewel.base" - end + itemCategoryQueryStr = "jewel" + itemCategory = options.jewelType .. "Jewel" + if itemCategory == "AbyssJewel" then + itemCategoryQueryStr = "jewel.abyss" + elseif itemCategory == "BaseJewel" then + itemCategoryQueryStr = "jewel.base" end elseif slot.slotName:find("Flask") ~= nil then itemCategoryQueryStr = "flask" @@ -877,6 +876,28 @@ function TradeQueryGeneratorClass:ExecuteQuery() end end +function TradeQueryGeneratorClass:addMoreWEMods() + local function getTableOfTradeModIds(tbl) + local tmpTable={} + for _,val in ipairs(tbl) do + table.insert(tmpTable,val.tradeModId) + end + return tmpTable + end + for _,skillGroup in ipairs(self.itemsTab.build.skillsTab.socketGroupList) do + for _,gem in ipairs(skillGroup.gemList) do + if gem.gemData.tags.aura and gem.enabled and gem.enableGlobal2 then + local tmpAura=gem.nameSpec:gsub("Vaal ",""):gsub("Impurity","Purity"):gsub("of","Of"):gsub(" ","") + for id,mod in pairs(self.modData.WatchersEye) do + if id:find(tmpAura) and not isValueInTable(getTableOfTradeModIds(self.modWeights),mod.tradeMod.id) then + table.insert(self.modWeights,{invert=false,meanStatDiff=0,weight=0,tradeModId=mod.tradeMod.id}) + end + end + end + end + end +end + function TradeQueryGeneratorClass:FinishQuery() -- Calc original item Stats without anoint or enchant, and use that diff as a basis for default min sum. local originalItem = self.calcContext.slot and self.itemsTab.items[self.calcContext.slot.selItemId] @@ -900,6 +921,10 @@ function TradeQueryGeneratorClass:FinishQuery() local originalOutput = originalItem and self.calcContext.calcFunc({ repSlotName = self.calcContext.slot.slotName, repItem = self.calcContext.testItem }, { nodeAlloc = true }) or self.calcContext.baseOutput local currentStatDiff = TradeQueryGeneratorClass.WeightedRatioOutputs(self.calcContext.baseOutput, originalOutput, self.calcContext.options.statWeights) * 1000 - (self.calcContext.baseStatValue or 0) + if self.calcContext.options.includeAllWEMods then + self:addMoreWEMods() + end + -- Sort by mean Stat diff rather than weight to more accurately prioritize stats that can contribute more table.sort(self.modWeights, function(a, b) return a.meanStatDiff > b.meanStatDiff @@ -1056,14 +1081,14 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb popupHeight = popupHeight + 23 end - if isJewelSlot then - controls.jewelType = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, 0, 5, 110, 18, { "Any", "Base", "Abyss", "Watcher's Eye" }, function(index, value) end) + if isJewelSlot and context.slotTbl.slotName ~= "Watcher's Eye" then + controls.jewelType = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, 0, 5, 100, 18, { "Any", "Base", "Abyss" }, function(index, value) end) controls.jewelType.selIndex = self.lastJewelType or 1 controls.jewelTypeLabel = new("LabelControl", {"RIGHT",controls.jewelType,"LEFT"}, -5, 0, 0, 16, "Jewel Type:") lastItemAnchor = controls.jewelType popupHeight = popupHeight + 23 - elseif slot and not isAbyssalJewelSlot then + elseif slot and not isAbyssalJewelSlot and context.slotTbl.slotName ~= "Watcher's Eye" then controls.influence1 = new("DropDownControl", {"TOPLEFT",lastItemAnchor,"BOTTOMLEFT"}, 0, 5, 100, 18, influenceDropdownNames, function(index, value) end) controls.influence1.selIndex = self.lastInfluence1 or 1 controls.influence1Label = new("LabelControl", {"RIGHT",controls.influence1,"LEFT"}, -5, 0, 0, 16, "Influence 1:") @@ -1136,6 +1161,13 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb end popupHeight = popupHeight + 4 + if context.slotTbl.slotName == "Watcher's Eye" then + controls.includeAllWEMods = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, 0, 5, 18, "Include all Watcher's Eye mods:", function(state) end) + controls.includeAllWEMods.tooltipText = "Include all Watcher's Eye mods in the generated query for which weights couldn't be calculated" + lastItemAnchor = controls.includeAllWEMods + popupHeight = popupHeight + 23 + end + controls.generateQuery = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, -45, -10, 80, 20, "Execute", function() main:ClosePopup() @@ -1175,6 +1207,9 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb options.maxPrice = tonumber(controls.maxPrice.buf) options.maxPriceType = currencyTable[controls.maxPriceType.selIndex].id end + if controls.includeAllWEMods then + options.includeAllWEMods = controls.includeAllWEMods.state + end options.statWeights = statWeights self:StartQuery(slot, options) diff --git a/src/Launch.lua b/src/Launch.lua index 0775ec4dc2..b8ff97ca20 100644 --- a/src/Launch.lua +++ b/src/Launch.lua @@ -16,6 +16,13 @@ launch = { } SetMainObject(launch) function launch:OnInit() + -- This is the path to emmy_core.dll. The ?.dll at the end is intentional. + package.cpath = package.cpath .. ";C:/Users/borna/.vscode/extensions/tangzx.emmylua-0.5.19/debugger/emmy/windows/x86/?.dll" + local dbg = require("emmy_core") + -- This port must match the Visual Studio Code configuration. Default is 9966. + dbg.tcpListen("localhost", 9966) + -- Uncomment the next line if you want Path of Building to block until the debugger is attached + -- dbg.waitIDE() self.devMode = false self.installedMode = false self.versionNumber = "?" diff --git a/src/Modules/ModParser.lua b/src/Modules/ModParser.lua index 692a44a1f8..ea7ef9432f 100644 --- a/src/Modules/ModParser.lua +++ b/src/Modules/ModParser.lua @@ -4840,6 +4840,28 @@ local specialModList = { ["nearby allies have (%d+)%% chance to block attack damage per (%d+) strength you have"] = function(block, _, str) return { mod("ExtraAura", "LIST", { onlyAllies = true, mod = mod("BlockChance", "BASE", block) }, { type = "PerStat", stat = "Str", div = tonumber(str) }), } end, + -- Watcher's Eye special cases + ["immune to ignite while affected by purity of fire"] = { flag("IgniteImmune", { type = "Condition", var = "AffectedByPurityofFire" }) }, + ["immune to freeze while affected by purity of ice"] = { flag("FreezeImmune", { type = "Condition", var = "AffectedByPurityofIce" }) }, + ["immune to shock while affected by purity of lightning"] = { flag("ShockImmune", { type = "Condition", var = "AffectedByPurityofLightning" }) }, + ["unaffected by vulnerability while affected by determination"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", var = "Vulnerability" }, { type = "Condition", var = "AffectedByDetermination" }) }, + ["unaffected by enfeeble while affected by grace"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", var = "Enfeeble" }, { type = "Condition", var = "AffectedByGrace" }) }, + ["unaffected by temporal chains while affected by haste"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", skillName = "Temporal Chains" }, { type = "Condition", var = "AffectedByHaste" }) }, + ["unaffected by bleeding while affected by malevolence"] = { mod("SelfBleedEffect", "MORE", -100, { type = "Condition", var = "AffectedByMalevolence" }) }, + ["unaffected by poison while affected by malevolence"] = { mod("SelfPoisonEffect", "MORE", -100, { type = "Condition", var = "AffectedByMalevolence" }) }, + ["unaffected by elemental weakness while affected by purity of elements"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", var = "Elemental Weakness" }, { type = "Condition", var = "AffectedByPurityofElements" }) }, + ["unaffected by flammability while affected by purity of fire"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", var = "Flammability" }, { type = "Condition", var = "AffectedByPurityofFire" }) }, + ["unaffected by frostbite while affected by purity of ice"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", var = "Frostbite" }, { type = "Condition", var = "AffectedByPurityofIce" }) }, + ["unaffected by conductivity while affected by purity of lightning"] = { mod("CurseEffectOnSelf", "MORE", -100, { type = "SkillName", var = "Conductivity" }, { type = "Condition", var = "AffectedByPurityofLightning" }) }, + ["cannot be blinded while affected by precision"] = { flag("Condition:CannotBeBlinded",{type = "Condition", var = "AffectedByPrecision"}) }, + ["your hits intimidate enemies for 4 seconds while you are using pride"] = { mod("EnemyModifier", "LIST", { mod = flag("Condition:Intimidated") }, { type = "Condition", var = "AffectedByPride" }) }, + ["(%d+)%% chance to blind enemies which hit you while affected by grace"] = function(num) return { mod("EnemyModifier","LIST",{mod=flag("Condition:Blinded")},{type="Condition",var="AffectedByGrace"}) } end, + ["debuffs on you expire (%d+)%% faster while affected by haste"] = function(num) return { mod("SelfDebuffExpirationRate", "BASE", num, {type = "Condition", var = "AffectedByHaste"}) } end, + ["(%d+)%% chance to recover 10%% of mana when you use a skill while affected by clarity"] = { mod("","", 0,{type="Condition",var="AffectedByClarity"})}, + ["effects of consecrated ground you create while affected by zealotry linger for 2 seconds"] = { mod("","", 0,{type="Condition",var="AffectedByZealotry"})}, + ["unaffected by shocked ground while affected by purity of lightning"] = { mod("","", 0,{type="Condition",var="AffectedByPurityofLightning"}) }, + ["unaffected by chilled ground while affected by purity of ice"] = { mod("","", 0,{type="Condition",var="AffectedByPurityofIce"}) }, + ["unaffected by burning ground while affected by purity of fire"] = { mod("","", 0,{type="Condition",var="AffectedByPurityofFire"}) }, } for _, name in pairs(data.keystones) do specialModList[name:lower()] = { mod("Keystone", "LIST", name) } From 0dd17a22d4a7fafe8d7df2bc4af5c24479a2f1ae Mon Sep 17 00:00:00 2001 From: Borna Ivankovic Date: Tue, 30 Jan 2024 20:05:54 +0100 Subject: [PATCH 03/16] fix merge mistake --- src/Classes/TradeQuery.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 9e99baba83..56a65bcfeb 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -482,11 +482,6 @@ Highest Weight - Displays the order retrieved from trade]] return scrollBarShown end - - local effective_row_count = row_count + 2 + 2 -- Two top menu rows, two bottom rows - local pane_height = (row_height + row_vertical_padding) * effective_row_count + 2 * pane_margins_vertical + row_height / 2 - local pane_width = 850 - self.controls.fullPrice = new("LabelControl", {"BOTTOM", nil, "BOTTOM"}, 0, -row_height - pane_margins_vertical - row_vertical_padding, pane_width - 2 * pane_margins_horizontal, row_height, "") GlobalCache.useFullDPS = GlobalCache.numActiveSkillInFullDPS > 0 self.controls.close = new("ButtonControl", {"BOTTOM", nil, "BOTTOM"}, 0, -pane_margins_vertical, 90, row_height, "Done", function() From ef32c2c476f72dd68b644080651e240eb5cde4c6 Mon Sep 17 00:00:00 2001 From: Borna Ivankovic Date: Tue, 30 Jan 2024 20:14:29 +0100 Subject: [PATCH 04/16] remove debugger setup -.- --- src/Launch.lua | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/Launch.lua b/src/Launch.lua index b8ff97ca20..0775ec4dc2 100644 --- a/src/Launch.lua +++ b/src/Launch.lua @@ -16,13 +16,6 @@ launch = { } SetMainObject(launch) function launch:OnInit() - -- This is the path to emmy_core.dll. The ?.dll at the end is intentional. - package.cpath = package.cpath .. ";C:/Users/borna/.vscode/extensions/tangzx.emmylua-0.5.19/debugger/emmy/windows/x86/?.dll" - local dbg = require("emmy_core") - -- This port must match the Visual Studio Code configuration. Default is 9966. - dbg.tcpListen("localhost", 9966) - -- Uncomment the next line if you want Path of Building to block until the debugger is attached - -- dbg.waitIDE() self.devMode = false self.installedMode = false self.versionNumber = "?" From c2ce9d366b683bd45c7e43e5322b3455dcce35be Mon Sep 17 00:00:00 2001 From: Borna Ivankovic Date: Fri, 2 Feb 2024 20:37:05 +0100 Subject: [PATCH 05/16] Fix getting unweighted mods --- src/Classes/TradeQueryGenerator.lua | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 98e08b87d6..df76c6ff4c 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -887,14 +887,22 @@ function TradeQueryGeneratorClass:addMoreWEMods() end for _,skillGroup in ipairs(self.itemsTab.build.skillsTab.socketGroupList) do for _,gem in ipairs(skillGroup.gemList) do - if gem.gemData.tags.aura and gem.enabled and gem.enableGlobal2 then - local tmpAura=gem.nameSpec:gsub("Vaal ",""):gsub("Impurity","Purity"):gsub("of","Of"):gsub(" ","") - for id,mod in pairs(self.modData.WatchersEye) do - if id:find(tmpAura) and not isValueInTable(getTableOfTradeModIds(self.modWeights),mod.tradeMod.id) then - table.insert(self.modWeights,{invert=false,meanStatDiff=0,weight=0,tradeModId=mod.tradeMod.id}) - end + local tmpAura="" + if not gem.enabled then + goto continue + elseif gem.nameSpec:find("Vaal") and gem.enableGlobal2 then + tmpAura=gem.nameSpec:gsub("Vaal ",""):gsub("Impurity","Purity"):gsub("of","Of"):gsub(" ","") + elseif gem.gemData and gem.gemData.tags.aura or gem.fromItem then + tmpAura=gem.nameSpec:gsub("of","Of"):gsub(" ","") + else + goto continue + end + for id,mod in pairs(self.modData.WatchersEye) do + if id:find(tmpAura) and not isValueInTable(getTableOfTradeModIds(self.modWeights),mod.tradeMod.id) then + table.insert(self.modWeights,{invert=false,meanStatDiff=0,weight=0,tradeModId=mod.tradeMod.id}) end end + ::continue:: end end end From bbb26263d0dd709a985cc18064fc3ab99ca53936 Mon Sep 17 00:00:00 2001 From: Borna Ivankovic Date: Fri, 2 Feb 2024 21:09:43 +0100 Subject: [PATCH 06/16] Actually include corrupted mods --- src/Classes/TradeQueryGenerator.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index df76c6ff4c..e9c6d3e349 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -858,6 +858,9 @@ function TradeQueryGeneratorClass:ExecuteQuery() end if self.calcContext.special.watchersEye then self:GenerateModWeights(self.modData.WatchersEye) + if self.calcContext.options.includeCorrupted then + self:GenerateModWeights(self.modData["Corrupted"]) + end return end self:GenerateModWeights(self.modData["Explicit"]) From abfef518439a833d445ebd82e643b2ed6451aea6 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 16 Mar 2026 11:20:44 +0200 Subject: [PATCH 07/16] fix checkboxcontrol for watchers eye --- src/Classes/TradeQueryGenerator.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index ee691f00a6..23990ea195 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1290,7 +1290,7 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb popupHeight = popupHeight + 4 if context.slotTbl.slotName == "Watcher's Eye" then - controls.includeAllWEMods = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, 0, 5, 18, "Include all Watcher's Eye mods:", function(state) end) + controls.includeAllWEMods = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Include all Watcher's Eye mods:", function(state) end) controls.includeAllWEMods.tooltipText = "Include all Watcher's Eye mods in the generated query for which weights couldn't be calculated" lastItemAnchor = controls.includeAllWEMods popupHeight = popupHeight + 23 From 6ede9c8bc3b5b981d3e86cc7523f1533260cba6f Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:31:20 +0200 Subject: [PATCH 08/16] show tooltips for watcher's eye search. either against all jewels or against an equipped eye --- src/Classes/TradeQuery.lua | 30 +- src/Classes/TradeQueryGenerator.lua | 2 +- src/Data/QueryMods.lua | 686 +++++++++++++++------------- 3 files changed, 382 insertions(+), 336 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 606083ca2c..2cbf699f93 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -888,10 +888,8 @@ function TradeQueryClass:findValidSlotForWatchersEye() end end for _,v in pairs(self.itemsTab.sockets) do - if not v.inactive then - if self.itemsTab:IsItemValidForSlot(tmpWE,v.slotName,self.itemsTab.activeItemSet) then - return self.itemsTab.sockets[v.nodeId] - end + if not v.inactive and self.itemsTab:IsItemValidForSlot(tmpWE,v.slotName,self.itemsTab.activeItemSet) then + return self.itemsTab.sockets[v.nodeId] end end end @@ -1042,7 +1040,16 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro local result = self.resultTbl[row_idx][pb_index] local item = new("Item", result.item_string) tooltip:Clear() - self.itemsTab:AddItemTooltip(tooltip, item, slotTbl) + if slotTbl.slotName == "Watcher's Eye" then + local firstValidSlot = self:findValidSlotForWatchersEye() + local currentItem = firstValidSlot.selItemId ~= 0 and self.itemsTab.items[firstValidSlot.selItemId] + local eyeEquipped = currentItem and currentItem.name:find("Watcher's Eye") + -- for watcher's eye we can compare to an already existing one, or + -- default to comparing with all active sockets + self.itemsTab:AddItemTooltip(tooltip, item, eyeEquipped and firstValidSlot or nil) + else + self.itemsTab:AddItemTooltip(tooltip, item, slotTbl) + end addMegalomaniacCompareToTooltipIfApplicable(tooltip, pb_index) tooltip:AddSeparator(10) tooltip:AddLine(16, string.format("^7Price: %s %s", result.amount, result.currency)) @@ -1070,7 +1077,18 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro -- item.baseName is nil and throws error in the following AddItemTooltip func -- if the item is unidentified local item = new("Item", item_string) - self.itemsTab:AddItemTooltip(tooltip, item, slotTbl, true) + -- for watcher's eye we can compare to an already existing one, or + -- default to comparing with all active sockets + if slotTbl.slotName == "Watcher's Eye" then + local firstValidSlot = self:findValidSlotForWatchersEye() + local currentItem = firstValidSlot.selItemId ~= 0 and self.itemsTab.items[firstValidSlot.selItemId] + local eyeEquipped = currentItem and currentItem.name:find("Watcher's Eye") + -- for watcher's eye we can compare to an already existing one, or + -- default to comparing with all active sockets + self.itemsTab:AddItemTooltip(tooltip, item, eyeEquipped and firstValidSlot or nil, true) + else + self.itemsTab:AddItemTooltip(tooltip, item, slotTbl, true) + end addMegalomaniacCompareToTooltipIfApplicable(tooltip, selected_result_index) end end diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 23990ea195..a9b58614c5 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1291,7 +1291,7 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb if context.slotTbl.slotName == "Watcher's Eye" then controls.includeAllWEMods = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Include all Watcher's Eye mods:", function(state) end) - controls.includeAllWEMods.tooltipText = "Include all Watcher's Eye mods in the generated query for which weights couldn't be calculated" + controls.includeAllWEMods.tooltipText = "Include mods that could not have a weight calculated for them at weight 0." lastItemAnchor = controls.includeAllWEMods popupHeight = popupHeight + 23 end diff --git a/src/Data/QueryMods.lua b/src/Data/QueryMods.lua index 1f787f0ffe..1ec8a7e19c 100644 --- a/src/Data/QueryMods.lua +++ b/src/Data/QueryMods.lua @@ -72578,7 +72578,247 @@ return { }, }, ["WatchersEye"] = { - ["1577_ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRate"] = { + ["10058_ClarityReducedManaCost"] = { + ["AnyJewel"] = { + ["max"] = 5, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2445618239", + ["text"] = "+# to Total Mana Cost of Skills while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["10063_ClarityReducedManaCostNonChannelled"] = { + ["AnyJewel"] = { + ["max"] = 5, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1853636813", + ["text"] = "Non-Channelling Skills have +# to Total Mana Cost while affected by Clarity", + ["type"] = "explicit", + }, + }, + ["10173_GraceChanceToDodge"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 12, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4071658793", + ["text"] = "+#% chance to Suppress Spell Damage while affected by Grace", + ["type"] = "explicit", + }, + }, + ["10174_HasteChanceToDodgeSpells"] = { + ["AnyJewel"] = { + ["max"] = 8, + ["min"] = 5, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2170859717", + ["text"] = "+#% chance to Suppress Spell Damage while affected by Haste", + ["type"] = "explicit", + }, + }, + ["10452_MalevolenceUnaffectedByBleeding"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4104891138", + ["text"] = "Unaffected by Bleeding while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["10456_PurityOfFireUnaffectedByBurningGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3308185931", + ["text"] = "Unaffected by Burning Ground while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["10461_PurityOfIceUnaffectedByChilledGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2647344903", + ["text"] = "Unaffected by Chilled Ground while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["10462_PurityOfLightningUnaffectedByConductivity"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1567542124", + ["text"] = "Unaffected by Conductivity while affected by Purity of Lightning", + ["type"] = "explicit", + }, + }, + ["10467_PurityOfElementsUnaffectedByElementalWeakness"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3223142064", + ["text"] = "Unaffected by Elemental Weakness while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["10468_GraceUnaffectedByEnfeeble"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2365917222", + ["text"] = "Unaffected by Enfeeble while affected by Grace", + ["type"] = "explicit", + }, + }, + ["10469_PurityOfFireUnaffectedByFlammability"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1173690938", + ["text"] = "Unaffected by Flammability while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["10471_PurityOfIceUnaffectedByFrostbite"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_4012281889", + ["text"] = "Unaffected by Frostbite while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["10475_MalevolenceUnaffectedByPoison"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_34059570", + ["text"] = "Unaffected by Poison while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["10481_PurityOfLightningUnaffectedByShockedGround"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2567659895", + ["text"] = "Unaffected by Shocked Ground while affected by Purity of Lightning", + ["type"] = "explicit", + }, + }, + ["10483_HasteUnaffectedByTemporalChains"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2806391472", + ["text"] = "Unaffected by Temporal Chains while affected by Haste", + ["type"] = "explicit", + }, + }, + ["10484_DeterminationUnaffectedByVulnerability"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3207781478", + ["text"] = "Unaffected by Vulnerability while affected by Determination", + ["type"] = "explicit", + }, + }, + ["10665_MalevolenceYourAilmentsDealDamageFaster"] = { + ["AnyJewel"] = { + ["max"] = 15, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3468843137", + ["text"] = "Damaging Ailments you inflict deal Damage #% faster while affected by Malevolence", + ["type"] = "explicit", + }, + }, + ["12_DeterminationReducedReflectedPhysicalDamage"] = { + ["AnyJewel"] = { + ["max"] = 75, + ["min"] = 50, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2255585376", + ["text"] = "#% of Physical Damage from your Hits cannot be Reflected while affected by Determination", + ["type"] = "explicit", + }, + }, + ["1740_ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRate"] = { ["AnyJewel"] = { ["max"] = 30, ["min"] = 30, @@ -72592,7 +72832,7 @@ return { ["type"] = "explicit", }, }, - ["1578_ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRateOld"] = { + ["1741_ZealotryMaximumEnergyShieldPerSecondToMaximumEnergyShieldLeechRateOld"] = { ["AnyJewel"] = { ["max"] = 30, ["min"] = 30, @@ -72606,7 +72846,7 @@ return { ["type"] = "explicit", }, }, - ["4296_HatredAdditionalCriticalStrikeChance"] = { + ["4557_HatredAdditionalCriticalStrikeChance"] = { ["AnyJewel"] = { ["max"] = 1.8, ["min"] = 1.2, @@ -72620,7 +72860,21 @@ return { ["type"] = "explicit", }, }, - ["4322_DeterminationPhysicalDamageReduction"] = { + ["4566_PurityOfElementsMaximumElementalResistances"] = { + ["AnyJewel"] = { + ["max"] = 1, + ["min"] = 1, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_3234824465", + ["text"] = "+#% to all maximum Elemental Resistances while affected by Purity of Elements", + ["type"] = "explicit", + }, + }, + ["4584_DeterminationPhysicalDamageReduction"] = { ["AnyJewel"] = { ["max"] = 8, ["min"] = 5, @@ -72634,7 +72888,7 @@ return { ["type"] = "explicit", }, }, - ["4452_DeterminationAdditionalArmour"] = { + ["4770_DeterminationAdditionalArmour"] = { ["AnyJewel"] = { ["max"] = 1000, ["min"] = 600, @@ -72648,7 +72902,7 @@ return { ["type"] = "explicit", }, }, - ["4540_PrecisionIncreasedAttackDamage"] = { + ["4867_PrecisionIncreasedAttackDamage"] = { ["AnyJewel"] = { ["max"] = 60, ["min"] = 40, @@ -72662,7 +72916,7 @@ return { ["type"] = "explicit", }, }, - ["4576_PrecisionIncreasedAttackSpeed"] = { + ["4909_PrecisionIncreasedAttackSpeed"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -72676,7 +72930,7 @@ return { ["type"] = "explicit", }, }, - ["4688_HatredPhysicalConvertedToCold"] = { + ["5048_HatredPhysicalConvertedToCold"] = { ["AnyJewel"] = { ["max"] = 40, ["min"] = 25, @@ -72690,7 +72944,7 @@ return { ["type"] = "explicit", }, }, - ["4689_AngerPhysicalConvertedToFire"] = { + ["5049_AngerPhysicalConvertedToFire"] = { ["AnyJewel"] = { ["max"] = 40, ["min"] = 25, @@ -72704,7 +72958,7 @@ return { ["type"] = "explicit", }, }, - ["4690_WrathPhysicalConvertedToLightning"] = { + ["5050_WrathPhysicalConvertedToLightning"] = { ["AnyJewel"] = { ["max"] = 40, ["min"] = 25, @@ -72718,7 +72972,7 @@ return { ["type"] = "explicit", }, }, - ["4858_GraceBlindEnemiesWhenHit"] = { + ["5226_GraceBlindEnemiesWhenHit"] = { ["AnyJewel"] = { ["max"] = 50, ["min"] = 30, @@ -72732,7 +72986,7 @@ return { ["type"] = "explicit", }, }, - ["4866_DeterminationAdditionalBlock"] = { + ["5236_DeterminationAdditionalBlock"] = { ["AnyJewel"] = { ["max"] = 8, ["min"] = 5, @@ -72746,7 +73000,7 @@ return { ["type"] = "explicit", }, }, - ["4923_PrecisionCannotBeBlinded"] = { + ["5399_PrecisionCannotBeBlinded"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -72759,7 +73013,7 @@ return { ["type"] = "explicit", }, }, - ["4992_ZealotryCastSpeed"] = { + ["5476_ZealotryCastSpeed"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -72773,7 +73027,7 @@ return { ["type"] = "explicit", }, }, - ["5128_DisciplineAdditionalSpellBlock"] = { + ["5657_DisciplineAdditionalSpellBlock"] = { ["AnyJewel"] = { ["max"] = 8, ["min"] = 5, @@ -72787,7 +73041,7 @@ return { ["type"] = "explicit", }, }, - ["5149_GraceAdditionalChanceToEvade"] = { + ["5680_GraceAdditionalChanceToEvade"] = { ["AnyJewel"] = { ["max"] = 8, ["min"] = 5, @@ -72801,7 +73055,7 @@ return { ["type"] = "explicit", }, }, - ["5212_PurityOfElementsChaosResistance"] = { + ["5749_PurityOfElementsChaosResistance"] = { ["AnyJewel"] = { ["max"] = 50, ["min"] = 30, @@ -72815,7 +73069,21 @@ return { ["type"] = "explicit", }, }, - ["5279_HatredIncreasedColdDamage"] = { + ["5806_PurityOfFireColdAndLightningTakenAsFire"] = { + ["AnyJewel"] = { + ["max"] = 20, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_1723738042", + ["text"] = "#% of Cold and Lightning Damage taken as Fire Damage while affected by Purity of Fire", + ["type"] = "explicit", + }, + }, + ["5819_HatredIncreasedColdDamage"] = { ["AnyJewel"] = { ["max"] = 60, ["min"] = 40, @@ -72829,7 +73097,7 @@ return { ["type"] = "explicit", }, }, - ["5312_ZealotryConsecratedGroundEnemyDamageTaken"] = { + ["5854_ZealotryConsecratedGroundEnemyDamageTaken"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 8, @@ -72843,7 +73111,7 @@ return { ["type"] = "explicit", }, }, - ["5315_ZealotryConsecratedGroundEffectLingersForMsAfterLeavingTheArea"] = { + ["5857_ZealotryConsecratedGroundEffectLingersForMsAfterLeavingTheArea"] = { ["AnyJewel"] = { ["max"] = 2, ["min"] = 2, @@ -72857,7 +73125,7 @@ return { ["type"] = "explicit", }, }, - ["5376_ZealotryCriticalStrikeChanceAgainstEnemiesOnConsecratedGround"] = { + ["5926_ZealotryCriticalStrikeChanceAgainstEnemiesOnConsecratedGround"] = { ["AnyJewel"] = { ["max"] = 120, ["min"] = 100, @@ -72871,7 +73139,7 @@ return { ["type"] = "explicit", }, }, - ["5396_WrathIncreasedCriticalStrikeChance"] = { + ["5946_WrathIncreasedCriticalStrikeChance"] = { ["AnyJewel"] = { ["max"] = 100, ["min"] = 70, @@ -72885,7 +73153,7 @@ return { ["type"] = "explicit", }, }, - ["5422_AngerIncreasedCriticalStrikeMultiplier"] = { + ["5973_AngerIncreasedCriticalStrikeMultiplier"] = { ["AnyJewel"] = { ["max"] = 50, ["min"] = 30, @@ -72899,7 +73167,7 @@ return { ["type"] = "explicit", }, }, - ["5423_PrecisionIncreasedCriticalStrikeMultiplier"] = { + ["5974_PrecisionIncreasedCriticalStrikeMultiplier"] = { ["AnyJewel"] = { ["max"] = 30, ["min"] = 20, @@ -72913,7 +73181,7 @@ return { ["type"] = "explicit", }, }, - ["5436_ZealotryCriticalStrikesPenetratesElementalResistances"] = { + ["5988_ZealotryCriticalStrikesPenetratesElementalResistances"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 8, @@ -72927,7 +73195,7 @@ return { ["type"] = "explicit", }, }, - ["5538_ClarityDamageTakenFromManaBeforeLife"] = { + ["6093_ClarityDamageTakenFromManaBeforeLife"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 6, @@ -72941,7 +73209,7 @@ return { ["type"] = "explicit", }, }, - ["5549_ClarityDamageTakenGainedAsMana"] = { + ["6111_ClarityDamageTakenGainedAsMana"] = { ["AnyJewel"] = { ["max"] = 20, ["min"] = 15, @@ -72955,7 +73223,7 @@ return { ["type"] = "explicit", }, }, - ["5588_HasteDebuffsExpireFaster"] = { + ["6155_HasteDebuffsExpireFaster"] = { ["AnyJewel"] = { ["max"] = 20, ["min"] = 15, @@ -72969,7 +73237,7 @@ return { ["type"] = "explicit", }, }, - ["5689_MalevolenceDamageOverTimeMultiplier"] = { + ["6264_MalevolenceDamageOverTimeMultiplier"] = { ["AnyJewel"] = { ["max"] = 22, ["min"] = 18, @@ -72983,21 +73251,7 @@ return { ["type"] = "explicit", }, }, - ["5759_PurityOfElementsReducedReflectedElementalDamage"] = { - ["AnyJewel"] = { - ["max"] = 50, - ["min"] = 40, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_65331133", - ["text"] = "#% reduced Reflected Elemental Damage taken while affected by Purity of Elements", - ["type"] = "explicit", - }, - }, - ["5838_DisciplineFasterStartOfRecharge"] = { + ["6434_DisciplineFasterStartOfRecharge"] = { ["AnyJewel"] = { ["max"] = 40, ["min"] = 30, @@ -73011,7 +73265,7 @@ return { ["type"] = "explicit", }, }, - ["5841_DisciplineEnergyShieldPerHit"] = { + ["6437_DisciplineEnergyShieldPerHit"] = { ["AnyJewel"] = { ["max"] = 30, ["min"] = 20, @@ -73025,7 +73279,7 @@ return { ["type"] = "explicit", }, }, - ["5845_WrathLightningDamageESLeech"] = { + ["6442_WrathLightningDamageESLeech"] = { ["AnyJewel"] = { ["max"] = 1.5, ["min"] = 1, @@ -73039,7 +73293,7 @@ return { ["type"] = "explicit", }, }, - ["5858_DisciplineEnergyShieldRecoveryRate"] = { + ["6457_DisciplineEnergyShieldRecoveryRate"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73053,7 +73307,7 @@ return { ["type"] = "explicit", }, }, - ["5864_DisciplineEnergyShieldRegen"] = { + ["6464_DisciplineEnergyShieldRegen"] = { ["AnyJewel"] = { ["max"] = 2.5, ["min"] = 1.5, @@ -73067,7 +73321,7 @@ return { ["type"] = "explicit", }, }, - ["5930_DeterminationReducedExtraDamageFromCrits"] = { + ["6541_DeterminationReducedExtraDamageFromCrits"] = { ["AnyJewel"] = { ["max"] = 60, ["min"] = 40, @@ -73081,7 +73335,21 @@ return { ["type"] = "explicit", }, }, - ["5959_AngerIncreasedFireDamage"] = { + ["6557_PurityOfIceFireAndLightningTakenAsCold"] = { + ["AnyJewel"] = { + ["max"] = 20, + ["min"] = 10, + }, + ["sign"] = "", + ["specialCaseData"] = { + }, + ["tradeMod"] = { + ["id"] = "explicit.stat_2189467271", + ["text"] = "#% of Fire and Lightning Damage taken as Cold Damage while affected by Purity of Ice", + ["type"] = "explicit", + }, + }, + ["6573_AngerIncreasedFireDamage"] = { ["AnyJewel"] = { ["max"] = 60, ["min"] = 40, @@ -73095,7 +73363,7 @@ return { ["type"] = "explicit", }, }, - ["6024_VitalityLifeRecoveryFromFlasks"] = { + ["6645_VitalityLifeRecoveryFromFlasks"] = { ["AnyJewel"] = { ["max"] = 70, ["min"] = 50, @@ -73109,7 +73377,7 @@ return { ["type"] = "explicit", }, }, - ["6100_ZealotryGainArcaneSurgeFor4SecondsWhenYouCreateConsecratedGround"] = { + ["6728_ZealotryGainArcaneSurgeFor4SecondsWhenYouCreateConsecratedGround"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73122,7 +73390,7 @@ return { ["type"] = "explicit", }, }, - ["6158_HasteGainOnslaughtOnKill"] = { + ["6792_HasteGainOnslaughtOnKill"] = { ["AnyJewel"] = { ["max"] = 4, ["min"] = 4, @@ -73136,7 +73404,7 @@ return { ["type"] = "explicit", }, }, - ["6169_HasteGainPhasing"] = { + ["6803_HasteGainPhasing"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73149,7 +73417,7 @@ return { ["type"] = "explicit", }, }, - ["6534_PurityOfIceImmuneToFreeze"] = { + ["7235_PurityOfIceImmuneToFreeze"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73162,7 +73430,7 @@ return { ["type"] = "explicit", }, }, - ["6537_PurityOfFireImmuneToIgnite"] = { + ["7238_PurityOfFireImmuneToIgnite"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73175,7 +73443,7 @@ return { ["type"] = "explicit", }, }, - ["6541_PurityOfLightningImmuneToShock"] = { + ["7243_PurityOfLightningImmuneToShock"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73188,7 +73456,7 @@ return { ["type"] = "explicit", }, }, - ["6628_MalevolenceLifeAndEnergyShieldRecoveryRate"] = { + ["7349_MalevolenceLifeAndEnergyShieldRecoveryRate"] = { ["AnyJewel"] = { ["max"] = 12, ["min"] = 8, @@ -73202,7 +73470,7 @@ return { ["type"] = "explicit", }, }, - ["6637_VitalityLifeGainPerHit"] = { + ["7360_VitalityLifeGainPerHit"] = { ["AnyJewel"] = { ["max"] = 30, ["min"] = 20, @@ -73216,7 +73484,7 @@ return { ["type"] = "explicit", }, }, - ["6643_VitalityDamageLifeLeech"] = { + ["7366_VitalityDamageLifeLeech"] = { ["AnyJewel"] = { ["max"] = 1.2, ["min"] = 0.8, @@ -73230,7 +73498,7 @@ return { ["type"] = "explicit", }, }, - ["6650_AngerFireDamageLifeLeech"] = { + ["7373_AngerFireDamageLifeLeech"] = { ["AnyJewel"] = { ["max"] = 1.5, ["min"] = 1, @@ -73244,7 +73512,7 @@ return { ["type"] = "explicit", }, }, - ["6673_VitalityLifeRecoveryRate"] = { + ["7399_VitalityLifeRecoveryRate"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73258,7 +73526,7 @@ return { ["type"] = "explicit", }, }, - ["6683_VitalityFlatLifeRegen"] = { + ["7409_VitalityFlatLifeRegen"] = { ["AnyJewel"] = { ["max"] = 140, ["min"] = 100, @@ -73272,7 +73540,7 @@ return { ["type"] = "explicit", }, }, - ["6690_VitalityPercentLifeRegen"] = { + ["7416_VitalityPercentLifeRegen"] = { ["AnyJewel"] = { ["max"] = 1.5, ["min"] = 1, @@ -73286,7 +73554,7 @@ return { ["type"] = "explicit", }, }, - ["6728_WrathIncreasedLightningDamage"] = { + ["7454_WrathIncreasedLightningDamage"] = { ["AnyJewel"] = { ["max"] = 60, ["min"] = 40, @@ -73300,7 +73568,7 @@ return { ["type"] = "explicit", }, }, - ["7388_WrathLightningDamageManaLeech"] = { + ["8188_WrathLightningDamageManaLeech"] = { ["AnyJewel"] = { ["max"] = 1.5, ["min"] = 1, @@ -73314,7 +73582,7 @@ return { ["type"] = "explicit", }, }, - ["7397_ClarityManaRecoveryRate"] = { + ["8197_ClarityManaRecoveryRate"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73328,7 +73596,7 @@ return { ["type"] = "explicit", }, }, - ["8232_ClarityManaAddedAsEnergyShield"] = { + ["9174_ClarityManaAddedAsEnergyShield"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 6, @@ -73342,7 +73610,7 @@ return { ["type"] = "explicit", }, }, - ["8282_HatredAddedColdDamage"] = { + ["9237_HatredAddedColdDamage"] = { ["AnyJewel"] = { ["max"] = 87, ["min"] = 73, @@ -73356,7 +73624,7 @@ return { ["type"] = "explicit", }, }, - ["8416_HasteCooldownRecoveryForMovementSkills"] = { + ["9405_HasteCooldownRecoveryForMovementSkills"] = { ["AnyJewel"] = { ["max"] = 50, ["min"] = 30, @@ -73370,7 +73638,7 @@ return { ["type"] = "explicit", }, }, - ["8436_GraceIncreasedMovementSpeed"] = { + ["9428_GraceIncreasedMovementSpeed"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73384,7 +73652,7 @@ return { ["type"] = "explicit", }, }, - ["8597_AngerPhysicalAddedAsFire"] = { + ["9631_AngerPhysicalAddedAsFire"] = { ["AnyJewel"] = { ["max"] = 25, ["min"] = 15, @@ -73398,7 +73666,7 @@ return { ["type"] = "explicit", }, }, - ["8600_WrathPhysicalAddedAsLightning"] = { + ["9634_WrathPhysicalAddedAsLightning"] = { ["AnyJewel"] = { ["max"] = 25, ["min"] = 15, @@ -73412,7 +73680,7 @@ return { ["type"] = "explicit", }, }, - ["8622_PurityOfElementsTakePhysicalAsCold"] = { + ["9656_PurityOfElementsTakePhysicalAsCold"] = { ["AnyJewel"] = { ["max"] = 12, ["min"] = 8, @@ -73426,7 +73694,7 @@ return { ["type"] = "explicit", }, }, - ["8623_PurityOfIceTakePhysicalAsIce"] = { + ["9657_PurityOfIceTakePhysicalAsIce"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 6, @@ -73440,7 +73708,7 @@ return { ["type"] = "explicit", }, }, - ["8624_PurityOfElementsTakePhysicalAsFire"] = { + ["9658_PurityOfElementsTakePhysicalAsFire"] = { ["AnyJewel"] = { ["max"] = 12, ["min"] = 8, @@ -73454,7 +73722,7 @@ return { ["type"] = "explicit", }, }, - ["8625_PurityOfFireTakePhysicalAsFire"] = { + ["9659_PurityOfFireTakePhysicalAsFire"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 6, @@ -73468,7 +73736,7 @@ return { ["type"] = "explicit", }, }, - ["8626_PurityOfElementsTakePhysicalAsLightning"] = { + ["9660_PurityOfElementsTakePhysicalAsLightning"] = { ["AnyJewel"] = { ["max"] = 12, ["min"] = 8, @@ -73482,7 +73750,7 @@ return { ["type"] = "explicit", }, }, - ["8627_PurityOfLightningTakePhysicalAsLightning"] = { + ["9661_PurityOfLightningTakePhysicalAsLightning"] = { ["AnyJewel"] = { ["max"] = 10, ["min"] = 6, @@ -73496,7 +73764,7 @@ return { ["type"] = "explicit", }, }, - ["8675_PrideChanceForDoubleDamage"] = { + ["9710_PrideChanceForDoubleDamage"] = { ["AnyJewel"] = { ["max"] = 12, ["min"] = 8, @@ -73510,7 +73778,7 @@ return { ["type"] = "explicit", }, }, - ["8676_PrideChanceToImpale"] = { + ["9711_PrideChanceToImpale"] = { ["AnyJewel"] = { ["max"] = 25, ["min"] = 25, @@ -73524,7 +73792,7 @@ return { ["type"] = "explicit", }, }, - ["8677_PrideIntimidateOnHit"] = { + ["9712_PrideIntimidateOnHit"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73537,7 +73805,7 @@ return { ["type"] = "explicit", }, }, - ["8681_PrideIncreasedPhysicalDamage"] = { + ["9716_PrideIncreasedPhysicalDamage"] = { ["AnyJewel"] = { ["max"] = 60, ["min"] = 40, @@ -73551,7 +73819,7 @@ return { ["type"] = "explicit", }, }, - ["8683_PrideImpaleAdditionalHits"] = { + ["9718_PrideImpaleAdditionalHits"] = { ["AnyJewel"] = { ["max"] = 2, ["min"] = 2, @@ -73565,7 +73833,7 @@ return { ["type"] = "explicit", }, }, - ["8778_PrecisionFlaskChargeOnCrit"] = { + ["9834_PrecisionFlaskChargeOnCrit"] = { ["AnyJewel"] = { ["max"] = 1, ["min"] = 1, @@ -73578,7 +73846,7 @@ return { ["type"] = "explicit", }, }, - ["8788_ClarityRecoverManaOnSkillUse"] = { + ["9845_ClarityRecoverManaOnSkillUse"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73592,7 +73860,7 @@ return { ["type"] = "explicit", }, }, - ["8814_HatredColdPenetration"] = { + ["9875_HatredColdPenetration"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73606,7 +73874,7 @@ return { ["type"] = "explicit", }, }, - ["8816_AngerFirePenetration"] = { + ["9877_AngerFirePenetration"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73620,7 +73888,7 @@ return { ["type"] = "explicit", }, }, - ["8817_WrathLightningPenetration"] = { + ["9878_WrathLightningPenetration"] = { ["AnyJewel"] = { ["max"] = 15, ["min"] = 10, @@ -73634,245 +73902,5 @@ return { ["type"] = "explicit", }, }, - ["8824_DeterminationReducedReflectedPhysicalDamage"] = { - ["AnyJewel"] = { - ["max"] = 50, - ["min"] = 40, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2457540491", - ["text"] = "#% reduced Reflected Physical Damage taken while affected by Determination", - ["type"] = "explicit", - }, - }, - ["8971_ClarityReducedManaCost"] = { - ["AnyJewel"] = { - ["max"] = 5, - ["min"] = 10, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2445618239", - ["text"] = "+# to Total Mana Cost of Skills while affected by Clarity", - ["type"] = "explicit", - }, - }, - ["8976_ClarityReducedManaCostNonChannelled"] = { - ["AnyJewel"] = { - ["max"] = 5, - ["min"] = 10, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_1853636813", - ["text"] = "Non-Channelling Skills have +# to Total Mana Cost while affected by Clarity", - ["type"] = "explicit", - }, - }, - ["9070_GraceChanceToDodge"] = { - ["AnyJewel"] = { - ["max"] = 15, - ["min"] = 12, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_4071658793", - ["text"] = "+#% chance to Suppress Spell Damage while affected by Grace", - ["type"] = "explicit", - }, - }, - ["9071_HasteChanceToDodgeSpells"] = { - ["AnyJewel"] = { - ["max"] = 8, - ["min"] = 5, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2170859717", - ["text"] = "+#% chance to Suppress Spell Damage while affected by Haste", - ["type"] = "explicit", - }, - }, - ["9316_MalevolenceUnaffectedByBleeding"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_4104891138", - ["text"] = "Unaffected by Bleeding while affected by Malevolence", - ["type"] = "explicit", - }, - }, - ["9320_PurityOfFireUnaffectedByBurningGround"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_3308185931", - ["text"] = "Unaffected by Burning Ground while affected by Purity of Fire", - ["type"] = "explicit", - }, - }, - ["9325_PurityOfIceUnaffectedByChilledGround"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2647344903", - ["text"] = "Unaffected by Chilled Ground while affected by Purity of Ice", - ["type"] = "explicit", - }, - }, - ["9326_PurityOfLightningUnaffectedByConductivity"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_1567542124", - ["text"] = "Unaffected by Conductivity while affected by Purity of Lightning", - ["type"] = "explicit", - }, - }, - ["9331_PurityOfElementsUnaffectedByElementalWeakness"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_3223142064", - ["text"] = "Unaffected by Elemental Weakness while affected by Purity of Elements", - ["type"] = "explicit", - }, - }, - ["9332_GraceUnaffectedByEnfeeble"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2365917222", - ["text"] = "Unaffected by Enfeeble while affected by Grace", - ["type"] = "explicit", - }, - }, - ["9333_PurityOfFireUnaffectedByFlammability"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_1173690938", - ["text"] = "Unaffected by Flammability while affected by Purity of Fire", - ["type"] = "explicit", - }, - }, - ["9335_PurityOfIceUnaffectedByFrostbite"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_4012281889", - ["text"] = "Unaffected by Frostbite while affected by Purity of Ice", - ["type"] = "explicit", - }, - }, - ["9339_MalevolenceUnaffectedByPoison"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_34059570", - ["text"] = "Unaffected by Poison while affected by Malevolence", - ["type"] = "explicit", - }, - }, - ["9345_PurityOfLightningUnaffectedByShockedGround"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2567659895", - ["text"] = "Unaffected by Shocked Ground while affected by Purity of Lightning", - ["type"] = "explicit", - }, - }, - ["9347_HasteUnaffectedByTemporalChains"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_2806391472", - ["text"] = "Unaffected by Temporal Chains while affected by Haste", - ["type"] = "explicit", - }, - }, - ["9348_DeterminationUnaffectedByVulnerability"] = { - ["AnyJewel"] = { - ["max"] = 1, - ["min"] = 1, - }, - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_3207781478", - ["text"] = "Unaffected by Vulnerability while affected by Determination", - ["type"] = "explicit", - }, - }, - ["9497_MalevolenceYourAilmentsDealDamageFaster"] = { - ["AnyJewel"] = { - ["max"] = 15, - ["min"] = 10, - }, - ["sign"] = "", - ["specialCaseData"] = { - }, - ["tradeMod"] = { - ["id"] = "explicit.stat_3468843137", - ["text"] = "Damaging Ailments you inflict deal Damage #% faster while affected by Malevolence", - ["type"] = "explicit", - }, - }, }, } \ No newline at end of file From 61a5ee2442290415232397c970cd37f06bf9e7bf Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 16 Mar 2026 16:52:36 +0200 Subject: [PATCH 09/16] remove mirrored button from eye and megalomaniac search because it is useless --- src/Classes/TradeQueryGenerator.lua | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index a9b58614c5..f171a104f2 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1192,9 +1192,12 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb options.special = { itemName = context.slotTbl.slotName } end - controls.includeMirrored = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Mirrored items:", function(state) end) - controls.includeMirrored.state = (self.lastIncludeMirrored == nil or self.lastIncludeMirrored == true) - updateLastAnchor(controls.includeMirrored) + -- these unique items cannot be mirrored + if not context.slotTbl.unique then + controls.includeMirrored = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Mirrored items:", function(state) end) + controls.includeMirrored.state = (self.lastIncludeMirrored == nil or self.lastIncludeMirrored == true) + updateLastAnchor(controls.includeMirrored) + end if not isJewelSlot and not isAbyssalJewelSlot and includeScourge then controls.includeScourge = new("CheckBoxControl", {"TOPRIGHT",lastItemAnchor,"BOTTOMRIGHT"}, {0, 5, 18}, "Scourge Mods:", function(state) end) From ebfe54167951ac9be81411e32c5f3e37ce257e17 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 23 Mar 2026 15:40:11 +0200 Subject: [PATCH 10/16] fix valid slot for watcher's eye --- src/Classes/TradeQuery.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 2cbf699f93..157a4f92b9 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -879,7 +879,13 @@ function TradeQueryClass:addChaosEquivalentPriceToItems(items) end -- return valid slot for Watcher's Eye +-- Tries to first return an existing watcher's eye slot if possible function TradeQueryClass:findValidSlotForWatchersEye() + for _, socket in pairs(self.itemsTab.sockets) do + if not socket.inactive and self.itemsTab.items[socket.selItemId].name:find("Watcher's Eye") then + return socket + end + end local tmpWE=nil for _,v in ipairs(data.uniques.generated) do if v:find("Watcher's Eye") then From 22ea4ce1b1ae42875f5bb1f0859e58b1509980e1 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:45:08 +0200 Subject: [PATCH 11/16] add "include in person" setting --- src/Classes/TradeQuery.lua | 7 ++++++- src/Classes/TradeQueryGenerator.lua | 6 +++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 157a4f92b9..858a0700f7 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -277,6 +277,11 @@ You can click this button to enter your POESESSID. - You can only generate weighted searches for public leagues. (Generated searches can be modified on trade site to work on other leagues and realms)]] + -- Buyout selection + self.controls.includeInPerson = new("CheckBoxControl", { "TOPRIGHT", self.controls.poesessidButton, "BOTTOMRIGHT" }, + { 0, row_vertical_padding, row_height }, "Include in person:", function(state) end, + "This includes in person offers in the search results.", false) + -- Fetches Box self.maxFetchPerSearchDefault = 2 self.controls.fetchCountEdit = new("EditControl", {"TOPRIGHT", nil, "TOPRIGHT"}, {-12, 19, 154, row_height}, "", "Fetch Pages", "%D", 3, function(buf) @@ -449,7 +454,7 @@ Highest Weight - Displays the order retrieved from trade]] t_insert(slotTables, { slotName = self.itemsTab.sockets[nodeId].label, nodeId = nodeId }) end - self.controls.sectionAnchor = new("LabelControl", {"LEFT", self.controls.poesessidButton, "LEFT"}, {0, 0, 0, 0}, "") + self.controls.sectionAnchor = new("LabelControl", {"LEFT", self.controls.poesessidButton, "LEFT"}, {0, row_vertical_padding + row_height, 0, 0}, "") top_pane_alignment_ref = {"TOPLEFT", self.controls.sectionAnchor, "TOPLEFT"} local scrollBarShown = #slotTables > 21 -- clipping starts beyond this -- dynamically hide rows that are above or below the scrollBar diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index f171a104f2..e7b5f326b0 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1016,7 +1016,7 @@ function TradeQueryGeneratorClass:FinishQuery() } } }, - status = { option = "available" }, + status = { option = self.includeInPerson and "available" or "securable" }, stats = { { type = "weight", @@ -1302,6 +1302,10 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb controls.generateQuery = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, {-45, -10, 80, 20}, "Execute", function() main:ClosePopup() + if context.controls.includeInPerson then + self.includeInPerson = context.controls.includeInPerson.state + end + if controls.includeMirrored then self.lastIncludeMirrored, options.includeMirrored = controls.includeMirrored.state, controls.includeMirrored.state end From e42a34acbf4f9fa9b1cdecb5b9c4c7469c6b1538 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:48:23 +0200 Subject: [PATCH 12/16] change in person selection to be a dropdown of the ones that are on the trade site --- src/Classes/TradeQuery.lua | 19 +++++++++++++++---- src/Classes/TradeQueryGenerator.lua | 17 ++++++++++++----- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 858a0700f7..cfd55afca3 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -278,11 +278,22 @@ You can click this button to enter your POESESSID. on trade site to work on other leagues and realms)]] -- Buyout selection - self.controls.includeInPerson = new("CheckBoxControl", { "TOPRIGHT", self.controls.poesessidButton, "BOTTOMRIGHT" }, - { 0, row_vertical_padding, row_height }, "Include in person:", function(state) end, - "This includes in person offers in the search results.", false) + self.tradeTypes = { + "Instant buyout", + "Instant buyout and in person", + "In person (online in league)", + "In person (online)", + "Any", + } + + self.controls.tradeTypeSelection = new("DropDownControl", { "TOPLEFT", self.controls.poesessidButton, "BOTTOMLEFT" }, + { 0, row_vertical_padding, 188, row_height }, self.tradeTypes, function(index, value) + self.tradeTypeIndex = index + end) + -- remember previous choice + self.controls.tradeTypeSelection:SetSel(self.tradeTypeIndex or 1) --- Fetches Box + -- Fetches Box self.maxFetchPerSearchDefault = 2 self.controls.fetchCountEdit = new("EditControl", {"TOPRIGHT", nil, "TOPRIGHT"}, {-12, 19, 154, row_height}, "", "Fetch Pages", "%D", 3, function(buf) self.maxFetchPages = m_min(m_max(tonumber(buf) or self.maxFetchPerSearchDefault, 1), 10) diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index e7b5f326b0..14c05c97dd 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1003,7 +1003,16 @@ function TradeQueryGeneratorClass:FinishQuery() -- This Stat diff value will generally be higher than the weighted sum of the same item, because the stats are all applied at once and can thus multiply off each other. -- So apply a modifier to get a reasonable min and hopefully approximate that the query will start out with small upgrades. local minWeight = megalomaniacSpecialMinWeight or currentStatDiff * 0.5 - + + -- what the trade site API uses for the above + self.tradeTypes = { + "securable", + "available", + "onlineleague", + "online", + "any", + } + local selectedTradeType = self.tradeTypes[self.tradeTypeIndex] -- Generate trade query str and open in browser local filters = 0 local queryTable = { @@ -1016,7 +1025,7 @@ function TradeQueryGeneratorClass:FinishQuery() } } }, - status = { option = self.includeInPerson and "available" or "securable" }, + status = { option = selectedTradeType }, stats = { { type = "weight", @@ -1302,9 +1311,7 @@ function TradeQueryGeneratorClass:RequestQuery(slot, context, statWeights, callb controls.generateQuery = new("ButtonControl", { "BOTTOM", nil, "BOTTOM" }, {-45, -10, 80, 20}, "Execute", function() main:ClosePopup() - if context.controls.includeInPerson then - self.includeInPerson = context.controls.includeInPerson.state - end + self.tradeTypeIndex = context.controls.tradeTypeSelection.selIndex if controls.includeMirrored then self.lastIncludeMirrored, options.includeMirrored = controls.includeMirrored.state, controls.includeMirrored.state From d7f0fc9a4371f0fd1dc309e758ece846d33ccba0 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Tue, 17 Mar 2026 03:10:40 +0200 Subject: [PATCH 13/16] add button to find exact search result for trade tool --- src/Classes/TradeQuery.lua | 62 +++++++++++++++++++++-------- src/Classes/TradeQueryGenerator.lua | 5 ++- src/Classes/TradeQueryRequests.lua | 5 +++ 3 files changed, 54 insertions(+), 18 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index cfd55afca3..78584f580e 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -283,7 +283,6 @@ on trade site to work on other leagues and realms)]] "Instant buyout and in person", "In person (online in league)", "In person (online)", - "Any", } self.controls.tradeTypeSelection = new("DropDownControl", { "TOPLEFT", self.controls.poesessidButton, "BOTTOMLEFT" }, @@ -942,6 +941,7 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro return end context.controls["priceButton"..context.row_idx].label = "Searching..." + self.lastQuery = query self.tradeQueryRequests:SearchWithQueryWeightAdjusted(self.pbRealm, self.pbLeague, query, function(items, errMsg) if errMsg then @@ -1105,8 +1105,6 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro local firstValidSlot = self:findValidSlotForWatchersEye() local currentItem = firstValidSlot.selItemId ~= 0 and self.itemsTab.items[firstValidSlot.selItemId] local eyeEquipped = currentItem and currentItem.name:find("Watcher's Eye") - -- for watcher's eye we can compare to an already existing one, or - -- default to comparing with all active sockets self.itemsTab:AddItemTooltip(tooltip, item, eyeEquipped and firstValidSlot or nil, true) else self.itemsTab:AddItemTooltip(tooltip, item, slotTbl, true) @@ -1118,23 +1116,53 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro return self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]].item_string ~= nil end -- Whisper so we can copy to clipboard - controls["whisperButton"..row_idx] = new("ButtonControl", { "TOPLEFT", controls["importButton"..row_idx], "TOPRIGHT"}, {8, 0, 185, row_height}, function() - return self.totalPrice[row_idx] and "Whisper for " .. self.totalPrice[row_idx].amount .. " " .. self.totalPrice[row_idx].currency or "Whisper" - end, function() - Copy(self.resultTbl[row_idx][self.itemIndexTbl[row_idx]].whisper) - end) - controls["whisperButton"..row_idx].enabled = function() - return self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]].whisper ~= nil - end - controls["whisperButton"..row_idx].tooltipFunc = function(tooltip) + controls["whisperButton" .. row_idx] = new("ButtonControl", + { "TOPLEFT", controls["importButton" .. row_idx], "TOPRIGHT" }, { 8, 0, 185, row_height }, function() + local itemResult = self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]] + + if not itemResult then return "" end + + local price = self.totalPrice[row_idx] and + self.totalPrice[row_idx].amount .. " " .. self.totalPrice[row_idx].currency + + if itemResult.whisper then + return price and "Whisper for " .. price or "Whisper" + else + return price and "Search for selected item" + end + + end, function() + local itemResult = self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]] + if itemResult.whisper then + Copy(itemResult.whisper) + else + local exactQuery = dkjson.decode(self.lastQuery) + -- use trade sum to get the specific item. we could also add the + -- trader name as it is contained in the fetch responses, but + -- this alone doesn't seem to really result in multiple results. + -- trade weight min is exclusive while max is inclusive (makes no sense to me) + exactQuery.query.stats[1].value = { min = tonumber(itemResult.weight) - 0.1, max = tonumber(itemResult.weight) } + + local exactQueryStr = dkjson.encode(exactQuery) + + self.tradeQueryRequests:SearchWithQuery(self.pbRealm, self.pbLeague, exactQueryStr, function(_, _) + end, {callbackQueryId = function(queryId) + local url = self.hostName.."trade/search/"..self.pbLeague.."/"..queryId + Copy(url) + OpenURL(url) + end}) + end + end) + + controls["whisperButton" .. row_idx].tooltipFunc = function(tooltip) tooltip:Clear() - if self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]].item_string then - tooltip.center = true - tooltip:AddLine(16, "Copies the item purchase whisper to the clipboard") - end + tooltip.center = true + local itemResult = self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]] + local text = itemResult.whisper and "Copies the item purchase whisper to the clipboard" or + "Opens the search page to show the item" + tooltip:AddLine(16, text) end end - -- Method to update the Total Price string sum of all items function TradeQueryClass:GetTotalPriceString() local text = "" diff --git a/src/Classes/TradeQueryGenerator.lua b/src/Classes/TradeQueryGenerator.lua index 14c05c97dd..3959d63cfc 100644 --- a/src/Classes/TradeQueryGenerator.lua +++ b/src/Classes/TradeQueryGenerator.lua @@ -1010,7 +1010,6 @@ function TradeQueryGeneratorClass:FinishQuery() "available", "onlineleague", "online", - "any", } local selectedTradeType = self.tradeTypes[self.tradeTypeIndex] -- Generate trade query str and open in browser @@ -1113,6 +1112,10 @@ function TradeQueryGeneratorClass:FinishQuery() } end + if options.account then + queryTable.query.filters.trade_filters.filters.account = {input = options.account} + end + if options.maxLevel and options.maxLevel > 0 then queryTable.query.filters.req_filters = { disabled = false, diff --git a/src/Classes/TradeQueryRequests.lua b/src/Classes/TradeQueryRequests.lua index e656fb5657..0df90bae2a 100644 --- a/src/Classes/TradeQueryRequests.lua +++ b/src/Classes/TradeQueryRequests.lua @@ -285,6 +285,11 @@ function TradeQueryRequestsClass:FetchResultBlock(url, callback) table.insert(items, { amount = trade_entry.listing.price.amount, currency = trade_entry.listing.price.currency, + -- note: using these to travel to the hideout or for a + -- direct whisper is not allowed, even if they are provided + -- right here + -- hideout_token = trade_entry.listing.hideout_token, + -- whisper_token = trade_entry.listing.whisper_token, item_string = common.base64.decode(trade_entry.item.extended.text), whisper = trade_entry.listing.whisper, weight = trade_entry.item.pseudoMods and trade_entry.item.pseudoMods[1]:match("Sum: (.+)") or "0", From 31612cc3f0c0087d02e52c3d26cce303ebef0889 Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 23 Mar 2026 16:37:17 +0200 Subject: [PATCH 14/16] trade query result filtering via trader name --- src/Classes/TradeQuery.lua | 17 ++++++++++------- src/Classes/TradeQueryRequests.lua | 1 + 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index 78584f580e..d323753e88 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -1117,7 +1117,7 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro end -- Whisper so we can copy to clipboard controls["whisperButton" .. row_idx] = new("ButtonControl", - { "TOPLEFT", controls["importButton" .. row_idx], "TOPRIGHT" }, { 8, 0, 185, row_height }, function() + { "TOPLEFT", controls["importButton" .. row_idx], "TOPRIGHT" }, { 8, 0, 170, row_height }, function() local itemResult = self.itemIndexTbl[row_idx] and self.resultTbl[row_idx][self.itemIndexTbl[row_idx]] if not itemResult then return "" end @@ -1128,7 +1128,7 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro if itemResult.whisper then return price and "Whisper for " .. price or "Whisper" else - return price and "Search for selected item" + return price and "Search for " .. price or "Search" end end, function() @@ -1137,11 +1137,14 @@ function TradeQueryClass:PriceItemRowDisplay(row_idx, top_pane_alignment_ref, ro Copy(itemResult.whisper) else local exactQuery = dkjson.decode(self.lastQuery) - -- use trade sum to get the specific item. we could also add the - -- trader name as it is contained in the fetch responses, but - -- this alone doesn't seem to really result in multiple results. - -- trade weight min is exclusive while max is inclusive (makes no sense to me) - exactQuery.query.stats[1].value = { min = tonumber(itemResult.weight) - 0.1, max = tonumber(itemResult.weight) } + -- use trade sum to get the specific item. both min and max + -- weight fields seem to be inconsistent. making the minimum + -- e.g. exactly 172.3 as on the result item does not always work + exactQuery.query.stats[1].value = { min = floor(itemResult.weight, 1) - 0.1, max = round(itemResult.weight, 1) + 0.1 } + -- also apply trader name. this should make false positives + -- extremely unlikely. this doesn't seem to take up a filter + -- slot + exactQuery.query.filters.trade_filters = { filters = { account = itemResult.trader } } local exactQueryStr = dkjson.encode(exactQuery) diff --git a/src/Classes/TradeQueryRequests.lua b/src/Classes/TradeQueryRequests.lua index 0df90bae2a..180050d106 100644 --- a/src/Classes/TradeQueryRequests.lua +++ b/src/Classes/TradeQueryRequests.lua @@ -292,6 +292,7 @@ function TradeQueryRequestsClass:FetchResultBlock(url, callback) -- whisper_token = trade_entry.listing.whisper_token, item_string = common.base64.decode(trade_entry.item.extended.text), whisper = trade_entry.listing.whisper, + trader = trade_entry.listing.account.name, weight = trade_entry.item.pseudoMods and trade_entry.item.pseudoMods[1]:match("Sum: (.+)") or "0", id = trade_entry.id }) From 6b347718c09b81efa8f65618f1785befee46246a Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Mon, 23 Mar 2026 20:10:53 +0200 Subject: [PATCH 15/16] fix trader tool item slot anchor --- src/Classes/TradeQuery.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Classes/TradeQuery.lua b/src/Classes/TradeQuery.lua index d323753e88..7ae9c339c7 100644 --- a/src/Classes/TradeQuery.lua +++ b/src/Classes/TradeQuery.lua @@ -464,7 +464,7 @@ Highest Weight - Displays the order retrieved from trade]] t_insert(slotTables, { slotName = self.itemsTab.sockets[nodeId].label, nodeId = nodeId }) end - self.controls.sectionAnchor = new("LabelControl", {"LEFT", self.controls.poesessidButton, "LEFT"}, {0, row_vertical_padding + row_height, 0, 0}, "") + self.controls.sectionAnchor = new("LabelControl", {"LEFT", self.controls.tradeTypeSelection, "LEFT"}, {0, row_vertical_padding + row_height, 0, 0}, "") top_pane_alignment_ref = {"TOPLEFT", self.controls.sectionAnchor, "TOPLEFT"} local scrollBarShown = #slotTables > 21 -- clipping starts beyond this -- dynamically hide rows that are above or below the scrollBar From 07e417af681a4ba8ae6390fd7c61580ff2cc462c Mon Sep 17 00:00:00 2001 From: vaisest <4550061+vaisest@users.noreply.github.com> Date: Tue, 24 Mar 2026 23:20:18 +0200 Subject: [PATCH 16/16] improvements to timeless jewel tool: trade type and realm selections --- src/Classes/TreeTab.lua | 289 +++++++++++++++++++++++++--------------- 1 file changed, 181 insertions(+), 108 deletions(-) diff --git a/src/Classes/TreeTab.lua b/src/Classes/TreeTab.lua index f596fb4daf..6e94836c3a 100644 --- a/src/Classes/TreeTab.lua +++ b/src/Classes/TreeTab.lua @@ -181,7 +181,8 @@ local TreeTabClass = newClass("TreeTab", "ControlHost", function(self, build) end, nil, nil, true) self.controls.treeSearch.tooltipText = "Uses Lua pattern matching for complex searches.\nPrefix your search with \"oil:\" to search by anoint recipe.\nTo search for multiple terms: (increased.fire.damage|increased.area.of.effect|etc)" - self.tradeLeaguesList = { } + -- table holding all realm/league pairs. (allLeagues[realm] = [league.id,...]) + self.tradeLeaguesList = {} -- Find Timeless Jewel Button self.controls.findTimelessJewel = new("ButtonControl", { "LEFT", self.controls.treeSearch, "RIGHT" }, { 8, 0, 150, 20 }, "Find Timeless Jewel", function() self:FindTimelessJewel() @@ -1471,8 +1472,12 @@ function TreeTabClass:FindTimelessJewel() end) controls.devotionSelect2.selIndex = timelessData.devotionVariant2 - controls.jewelSelectLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 25, 0, 16}, "^7Jewel Type:") - controls.jewelSelect = new("DropDownControl", {"LEFT", controls.jewelSelectLabel, "RIGHT"}, {10, 0, 200, 18}, jewelTypes, function(index, value) + local rowSpacing = 6 + local rowHeight = 17 + local labelHeight = 16 + local labelSpacing = 4 + + controls.jewelSelect = new("DropDownControl", {"TOPLEFT", nil, "TOPLEFT"}, {380, 25, 200, rowHeight}, jewelTypes, function(index, value) timelessData.jewelType = value controls.devotionSelectLabel.shown = value.id == 4 -- Militant Faith controls.protectAllocatedLabel.shown = (value.id == 4 and controls.socketFilter.state) @@ -1485,13 +1490,15 @@ function TreeTabClass:FindTimelessJewel() updateSearchList("", true) end) controls.jewelSelect.selIndex = timelessData.jewelType.id + controls.jewelSelectLabel = new("LabelControl", {"RIGHT", controls.jewelSelect, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Jewel Type:") + - controls.conquerorSelectLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 50, 0, 16}, "^7Conqueror:") - controls.conquerorSelect = new("DropDownControl", {"LEFT", controls.conquerorSelectLabel, "RIGHT"}, {10, 0, 200, 18}, conquerorTypes[timelessData.jewelType.id], function(index, value) + controls.conquerorSelect = new("DropDownControl", {"TOPLEFT", controls.jewelSelect, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, conquerorTypes[timelessData.jewelType.id], function(index, value) timelessData.conquerorType = value self.build.modFlag = true end) controls.conquerorSelect.selIndex = timelessData.conquerorType.id + controls.conquerorSelectLabel = new("LabelControl", {"RIGHT", controls.conquerorSelect, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Conqueror:") local allocatedNodes = { } local protectedNodes = { } @@ -1515,8 +1522,8 @@ function TreeTabClass:FindTimelessJewel() self.allocatedNodesInRadiusCount = #nodeNames end - controls.socketSelectLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 75, 0, 16}, "^7Jewel Socket:") - controls.socketSelect = new("TimelessJewelSocketControl", {"LEFT", controls.socketSelectLabel, "RIGHT"}, {10, 0, 200, 18}, jewelSockets, function(index, value) + + controls.socketSelect = new("TimelessJewelSocketControl", {"TOPLEFT", controls.conquerorSelect, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, jewelSockets, function(index, value) timelessData.jewelSocket = value setAllocatedNodes() -- reset list when changing sockets self.build.modFlag = true @@ -1528,6 +1535,7 @@ function TreeTabClass:FindTimelessJewel() break end end + controls.socketSelectLabel = new("LabelControl", {"RIGHT", controls.socketSelect, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Jewel Socket:") local function clearProtected() -- clear all controls, nodes related to Militant Faith filtering protectedNodesCount = 0 @@ -1538,8 +1546,8 @@ function TreeTabClass:FindTimelessJewel() end end end - controls.socketFilterLabel = new("LabelControl", { "TOPRIGHT", nil, "TOPLEFT" }, { 405, 100, 0, 16 }, "^7Filter Nodes:") - controls.socketFilter = new("CheckBoxControl", { "LEFT", controls.socketFilterLabel, "RIGHT" }, { 10, 0, 18 }, nil, function(value) + + controls.socketFilter = new("CheckBoxControl", {"TOPLEFT", controls.socketSelect, "BOTTOMLEFT"}, {0, rowSpacing, rowHeight}, nil, function(value) timelessData.socketFilter = value self.build.modFlag = true controls.socketFilterAdditionalDistanceLabel.shown = value @@ -1553,6 +1561,7 @@ function TreeTabClass:FindTimelessJewel() clearProtected() end end) + controls.socketFilterLabel = new("LabelControl", {"RIGHT", controls.socketFilter, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Filter Nodes:") controls.socketFilter.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() tooltip:AddLine(16, "^7Enable this option to exclude nodes that you do not have allocated on your active passive skill tree.") @@ -1617,11 +1626,11 @@ function TreeTabClass:FindTimelessJewel() local scrollWheelSpeedTbl2 = { ["SHIFT"] = 0.2, ["CTRL"] = 0.002, ["DEFAULT"] = 0.02 } local nodeSliderStatLabel = "None" - controls.nodeSliderLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 125, 0, 16}, "^7Primary Node Weight:") - controls.nodeSlider = new("SliderControl", {"LEFT", controls.nodeSliderLabel, "RIGHT"}, {10, 0, 200, 16}, function(value) + controls.nodeSlider = new("SliderControl", {"TOPLEFT", controls.socketFilter, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, function(value) controls.nodeSliderValue.label = s_format("^7%.3f", value * 10) parseSearchList(1, controls.searchListFallback and controls.searchListFallback.shown or false) end, scrollWheelSpeedTbl) + controls.nodeSliderLabel = new("LabelControl", {"RIGHT", controls.nodeSlider, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Primary Node Weight:") controls.nodeSlider.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() if not controls.nodeSlider.dragging then @@ -1646,11 +1655,11 @@ function TreeTabClass:FindTimelessJewel() controls.nodeSlider:SetVal(0.1) local nodeSlider2StatLabel = "None" - controls.nodeSlider2Label = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 150, 0, 16}, "^7Secondary Node Weight:") - controls.nodeSlider2 = new("SliderControl", {"LEFT", controls.nodeSlider2Label, "RIGHT"}, {10, 0, 200, 16}, function(value) + controls.nodeSlider2 = new("SliderControl", {"TOPLEFT", controls.nodeSlider, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, function(value) controls.nodeSlider2Value.label = s_format("^7%.3f", value * 10) parseSearchList(1, controls.searchListFallback and controls.searchListFallback.shown or false) end, scrollWheelSpeedTbl) + controls.nodeSlider2Label = new("LabelControl", {"RIGHT", controls.nodeSlider2, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Secondary Node Weight:") controls.nodeSlider2.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() if not controls.nodeSlider2.dragging then @@ -1674,8 +1683,7 @@ function TreeTabClass:FindTimelessJewel() end controls.nodeSlider2:SetVal(0.1) - controls.nodeSlider3Label = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 175, 0, 16}, "^7Minimum Node Weight:") - controls.nodeSlider3 = new("SliderControl", {"LEFT", controls.nodeSlider3Label, "RIGHT"}, {10, 0, 200, 16}, function(value) + controls.nodeSlider3 = new("SliderControl", {"TOPLEFT", controls.nodeSlider2, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, function(value) if value == 1 then controls.nodeSlider3Value.label = "^7Required" else @@ -1683,6 +1691,7 @@ function TreeTabClass:FindTimelessJewel() end parseSearchList(1, controls.searchListFallback and controls.searchListFallback.shown or false) end, scrollWheelSpeedTbl2) + controls.nodeSlider3Label = new("LabelControl", {"RIGHT", controls.nodeSlider3, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Minimum Node Weight:") controls.nodeSlider3.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() if not controls.nodeSlider3.dragging then @@ -1728,8 +1737,7 @@ function TreeTabClass:FindTimelessJewel() end buildMods() - controls.nodeSelectLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 200, 0, 16}, "^7Search for Node:") - controls.nodeSelect = new("DropDownControl", {"LEFT", controls.nodeSelectLabel, "RIGHT"}, {10, 0, 200, 18}, modData, function(index, value) + controls.nodeSelect = new("DropDownControl", {"TOPLEFT", controls.nodeSlider3, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, modData, function(index, value) nodeSliderStatLabel = "None" nodeSlider2StatLabel = "None" if value.id then @@ -1788,6 +1796,7 @@ function TreeTabClass:FindTimelessJewel() self.build.modFlag = true end end) + controls.nodeSelectLabel = new("LabelControl", {"RIGHT", controls.nodeSelect, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Search for Node:") controls.nodeSelect.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() if mode ~= "OUT" and value.descriptions then @@ -1962,7 +1971,6 @@ function TreeTabClass:FindTimelessJewel() updateSearchList(newList, true) end - controls.fallbackWeightsLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 225, 0, 16}, "^7Fallback Weight Mode:") local fallbackWeightsList = { } for id, stat in pairs(data.powerStatList) do if not stat.ignoreForItems and stat.label ~= "Name" then @@ -1973,9 +1981,10 @@ function TreeTabClass:FindTimelessJewel() }) end end - controls.fallbackWeightsList = new("DropDownControl", {"LEFT", controls.fallbackWeightsLabel, "RIGHT"}, {10, 0, 200, 18}, fallbackWeightsList, function(index) + controls.fallbackWeightsList = new("DropDownControl", {"TOPLEFT", controls.nodeSelect, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, fallbackWeightsList, function(index) timelessData.fallbackWeightMode.idx = index end) + controls.fallbackWeightsLabel = new("LabelControl", {"RIGHT", controls.fallbackWeightsList, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Fallback Weight Mode:") controls.fallbackWeightsList.selIndex = timelessData.fallbackWeightMode.idx or 1 controls.fallbackWeightsButton = new("ButtonControl", {"LEFT", controls.fallbackWeightsList, "RIGHT"}, {5, 0, 66, 18}, "Generate", function() setupFallbackWeights() @@ -1985,20 +1994,48 @@ function TreeTabClass:FindTimelessJewel() tooltip:Clear() tooltip:AddLine(16, "^7Click this button to generate new fallback node weights, replacing your old ones.") end - controls.totalMinimumWeightLabel = new("LabelControl", {"TOPRIGHT", nil, "TOPLEFT"}, {405, 250, 0, 16}, "^7Total Minimum Weight:") - controls.totalMinimumWeight = new("EditControl", {"LEFT", controls.totalMinimumWeightLabel, "RIGHT"}, {10, 0, 60, 18}, "", nil, "%D", nil, function(val) + controls.totalMinimumWeight = new("EditControl", {"TOPLEFT", controls.fallbackWeightsList, "BOTTOMLEFT"}, {0, rowSpacing, 200, rowHeight}, "", nil, "%D", nil, function(val) local num = tonumber(val) timelessData.totalMinimumWeight = num or nil self.build.modFlag = true end) + controls.totalMinimumWeightLabel = new("LabelControl", {"RIGHT", controls.totalMinimumWeight, "LEFT"}, {-labelSpacing, 0, 0, labelHeight}, "^7Total Minimum Weight:") controls.totalMinimumWeight.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() tooltip:AddLine(16, "^7Optional: Only show results where total weight meets or exceeds this value.") end + local listWidth = 440 + local listHeight = 200 + local buttonHeight = 20 + local edgePadding = 12 + local listYOffset = -(buttonHeight + edgePadding * 2) + controls.searchList = new("EditControl", { "BOTTOMLEFT", nil, "BOTTOMLEFT" }, + { edgePadding, listYOffset, listWidth, listHeight }, timelessData.searchList, nil, + "^%C\t\n", nil, function(value) + timelessData.searchList = value + parseSearchList(0, false) + self.build.modFlag = true + end, 16, true) + controls.searchList.shown = true + controls.searchList.enabled = true + controls.searchList:SetText(timelessData.searchList and timelessData.searchList or "") - controls.searchListButton = new("ButtonControl", {"TOPLEFT", nil, "TOPLEFT"}, {12, 250, 106, 20}, "^7Desired Nodes", function() - if controls.searchListFallback.shown then + controls.searchListFallback = new("EditControl", { "BOTTOMLEFT", nil, "BOTTOMLEFT" }, + { edgePadding, listYOffset, listWidth, listHeight }, + timelessData.searchListFallback, nil, "^%C\t\n", nil, function(value) + timelessData.searchListFallback = value + parseSearchList(0, true) + self.build.modFlag = true + end, 16, true) + controls.searchListFallback.shown = false + controls.searchListFallback.enabled = false + controls.searchListFallback:SetText(timelessData.searchListFallback and timelessData.searchListFallback or "") + + controls.searchListButton = new("ButtonControl", + { "BOTTOMLEFT", nil, "BOTTOMLEFT" }, + { edgePadding, listYOffset - listHeight - rowSpacing, 106, buttonHeight }, "^7Desired Nodes", function() + if controls.searchListFallback.shown then controls.searchListFallback.shown = false controls.searchListFallback.enabled = false controls.searchList.shown = true @@ -2007,11 +2044,14 @@ function TreeTabClass:FindTimelessJewel() end) controls.searchListButton.tooltipFunc = function(tooltip, mode, index, value) tooltip:Clear() - tooltip:AddLine(16, "^7This contains a list of your desired nodes along with their primary, secondary, and minimum weights.") - tooltip:AddLine(16, "^7This list can be updated manually or by selecting the node you want to update via the search dropdown list and then moving the node weight sliders.") + tooltip:AddLine(16, + "^7This contains a list of your desired nodes along with their primary, secondary, and minimum weights.") + tooltip:AddLine(16, + "^7This list can be updated manually or by selecting the node you want to update via the search dropdown list and then moving the node weight sliders.") end controls.searchListButton.locked = function() return controls.searchList.shown end - controls.searchListFallbackButton = new("ButtonControl", {"LEFT", controls.searchListButton, "RIGHT"}, {5, 0, 110, 20}, "^7Fallback Nodes", function() + + controls.searchListFallbackButton = new("ButtonControl", {"LEFT", controls.searchListButton, "RIGHT"}, {5, 0, 110, buttonHeight}, "^7Fallback Nodes", function() controls.searchList.shown = false controls.searchList.enabled = false controls.searchListFallback.shown = true @@ -2027,68 +2067,12 @@ function TreeTabClass:FindTimelessJewel() tooltip:AddLine(16, "^7Any manual changes made to your fallback nodes are lost when you click the generate button, as it completely replaces them.") end controls.searchListFallbackButton.locked = function() return controls.searchListFallback.shown end - controls.searchList = new("EditControl", {"TOPLEFT", nil, "TOPLEFT"}, {12, 275, 438, 200}, timelessData.searchList, nil, "^%C\t\n", nil, function(value) - timelessData.searchList = value - parseSearchList(0, false) - self.build.modFlag = true - end, 16, true) - controls.searchList.shown = true - controls.searchList.enabled = true - controls.searchList:SetText(timelessData.searchList and timelessData.searchList or "") - controls.searchListFallback = new("EditControl", {"TOPLEFT", nil, "TOPLEFT"}, {12, 275, 438, 200}, timelessData.searchListFallback, nil, "^%C\t\n", nil, function(value) - timelessData.searchListFallback = value - parseSearchList(0, true) - self.build.modFlag = true - end, 16, true) - controls.searchListFallback.shown = false - controls.searchListFallback.enabled = false - controls.searchListFallback:SetText(timelessData.searchListFallback and timelessData.searchListFallback or "") - controls.searchResultsLabel = new("LabelControl", { "TOPLEFT", nil, "TOPRIGHT" }, { -390, 250, 0, 16 }, "^7Results:") - controls.searchResults = new("TimelessJewelListControl", { "TOPLEFT", nil, "TOPRIGHT" }, { -450, 275, 438, 200 }, self.build) - controls.searchTradeLeagueSelect = new("DropDownControl", { "BOTTOMRIGHT", controls.searchResults, "TOPRIGHT" }, { -175, -5, 140, 20 }, nil, function(_, value) - self.timelessJewelLeagueSelect = value - end) + controls.searchResults = new("TimelessJewelListControl", { "BOTTOMLEFT", nil, "BOTTOMLEFT" }, + { edgePadding*2 + listWidth, -(buttonHeight + edgePadding * 2), listWidth, listHeight }, self.build) self.tradeQueryRequests = new("TradeQueryRequests") - controls.msg = new("LabelControl", nil, { -280, 5, 0, 16 }, "") - if #self.tradeLeaguesList > 0 then - controls.searchTradeLeagueSelect:SetList(self.tradeLeaguesList) - -- restore the last league selected - for i, league in ipairs(self.tradeLeaguesList) do - if league == self.timelessJewelLeagueSelect then - controls.searchTradeLeagueSelect:SetSel(i) - break - end - end - else - self.tradeQueryRequests:FetchLeagues("pc", function(leagues, errMsg) - if errMsg then - controls.msg.label = "^1Error fetching league list, default league will be used\n"..errMsg.."^7" - return - end - local tempLeagueTable = { } - for _, league in ipairs(leagues) do - if league ~= "Standard" and league ~= "Ruthless" and league ~= "Hardcore" and league ~= "Hardcore Ruthless" then - if not (league:find("Hardcore") or league:find("Ruthless")) then - -- set the dynamic, base league name to index 1 to sync league shown in dropdown on load with default/old behavior of copy trade url - t_insert(tempLeagueTable, league) - for _, val in ipairs(self.tradeLeaguesList) do - t_insert(tempLeagueTable, val) - end - self.tradeLeaguesList = copyTable(tempLeagueTable) - else - t_insert(self.tradeLeaguesList, league) - end - end - end - t_insert(self.tradeLeaguesList, "Standard") - t_insert(self.tradeLeaguesList, "Hardcore") - t_insert(self.tradeLeaguesList, "Ruthless") - t_insert(self.tradeLeaguesList, "Hardcore Ruthless") - controls.searchTradeLeagueSelect:SetList(self.tradeLeaguesList) - end) - end - controls.searchTradeButton = new("ButtonControl", { "BOTTOMRIGHT", controls.searchResults, "TOPRIGHT" }, { 0, -5, 170, 20 }, "Copy Trade URL", function() + controls.msg = new("LabelControl", nil, { -280, 5, 0, 20 }, "") + controls.searchTradeButton = new("ButtonControl", { "BOTTOMRIGHT", controls.searchResults, "TOPRIGHT" }, { 0, -rowSpacing, 170, buttonHeight }, "Copy Trade URL", function() local seedTrades = {} local startRow = controls.searchResults.selIndex or 1 local endRow = startRow + m_floor(10 / ((timelessData.sharedResults.conqueror.id == 1) and 3 or 1)) @@ -2134,10 +2118,17 @@ function TreeTabClass:FindTimelessJewel() end end + local tradeTypes = { + "securable", + "available", + "onlineleague", + "online", + "any" + } local search = { query = { status = { - option = "available" + option = tradeTypes[self.tradeTypeIndex] }, stats = { { @@ -2171,10 +2162,12 @@ function TreeTabClass:FindTimelessJewel() end -- if the league was not selected via dropdown, then default to the first league in the dropdown or "" if the leagues could not be read - self.timelessJewelLeagueSelect = self.timelessJewelLeagueSelect or (self.tradeLeaguesList and #self.tradeLeaguesList > 0 and self.tradeLeaguesList[1]) or "" + local selectedRealm = controls.realmSelection:GetSelValue():lower() - Copy("https://www.pathofexile.com/trade/search/"..(self.timelessJewelLeagueSelect).."/?q=" .. (s_gsub(dkjson.encode(search), "[^a-zA-Z0-9]", function(a) - return s_format("%%%02X", s_byte(a)) + local realmPath = selectedRealm == "pc" and "" or (selectedRealm .. "/") + Copy("https://www.pathofexile.com/trade/search/" .. realmPath .. + (controls.searchTradeLeagueSelect:GetSelValue()) .. "/?q=" .. (s_gsub(dkjson.encode(search), "[^a-zA-Z0-9]", function(a) + return s_format("%%%02X", s_byte(a)) end))) controls.searchTradeButton.label = "Copy Next Trade URL" @@ -2189,12 +2182,87 @@ function TreeTabClass:FindTimelessJewel() tooltip:AddLine(16, "^7After selecting a row You can also shift+click on another row to select a range of rows to search.") end - local width = 80 - local divider = 10 - local buttons = 3 - local totalWidth = m_floor(width * buttons + divider * (buttons - 1)) - local buttonX = -totalWidth / 2 + width / 2 - + controls.searchTradeLeagueSelect = new("DropDownControl", { "RIGHT", controls.searchTradeButton, "LEFT" }, + { -labelSpacing, 0, 140, buttonHeight }, nil, function(idx, val) + self.timelessJewelLeagueSelect = val + end) + controls.searchTradeLeagueLabel = new("LabelControl", { "TOPRIGHT", controls.searchTradeLeagueSelect, "TOPLEFT" }, + { -labelSpacing, 0, 0, labelHeight }, "^7League:") + -- Realm selection + self.realmList = { + "PC", "Sony", "Xbox" + } + controls.realmSelection = new("DropDownControl", { "BOTTOMLEFT", controls.searchTradeLeagueSelect, "TOPLEFT" }, + { 0, -rowSpacing, 80, buttonHeight }, self.realmList, nil) + local function updateLeagues() + local currentRealmId = controls.realmSelection:GetSelValue():lower() + if self.tradeLeaguesList[currentRealmId] == nil then self.tradeLeaguesList[currentRealmId] = {} end + local leagueList = self.tradeLeaguesList[currentRealmId] + if leagueList and #leagueList > 0 then + controls.searchTradeLeagueSelect:SetList(leagueList) + -- restore the last league selected + for i, league in ipairs(leagueList) do + if league == self.timelessJewelLeagueSelect then + controls.searchTradeLeagueSelect:SetSel(i) + break + end + end + else + self.tradeQueryRequests:FetchLeagues(currentRealmId, function(leagues, errMsg) + if errMsg then + controls.msg.label = "^1Error fetching league list, default league will be used\n" .. errMsg .. "^7" + return + end + local tempLeagueTable = {} + for _, league in ipairs(leagues) do + if league ~= "Standard" and league ~= "Ruthless" and league ~= "Hardcore" and league ~= "Hardcore Ruthless" then + if not (league:find("Hardcore") or league:find("Ruthless")) then + -- set the dynamic, base league name to index 1 to sync league shown in dropdown on load with default/old behavior of copy trade url + t_insert(tempLeagueTable, league) + for _, val in ipairs(leagueList) do + t_insert(tempLeagueTable, val) + end + leagueList = copyTable(tempLeagueTable) + else + t_insert(leagueList, league) + end + end + end + t_insert(leagueList, "Standard") + t_insert(leagueList, "Hardcore") + t_insert(leagueList, "Ruthless") + t_insert(leagueList, "Hardcore Ruthless") + controls.searchTradeLeagueSelect:SetList(leagueList) + end) + end + end + controls.realmSelection.selFunc = function(idx, _) + self.selectedRealmIndex = idx + updateLeagues() + end + -- remember previous choice + controls.realmSelection:SetSel(self.selectedRealmIndex or 1) + -- manually call the function because when initialising, because the + -- function doesnt get called when the selection index doesnt change + controls.realmSelection.selFunc(controls.realmSelection.selIndex) + controls.realmLabel = new("LabelControl", { "TOPRIGHT", controls.realmSelection, "TOPLEFT" }, + { -labelSpacing, 0, 0, labelHeight }, "^7Realm:") + + -- Buyout selection + local tradeTypes = { + "Instant buyout", + "Instant buyout and in person", + "In person (online in league)", + "In person (online)", + "Any (includes offline)" + } + controls.tradeTypeSelection = new("DropDownControl", { "LEFT", controls.realmSelection, "RIGHT" }, + { labelSpacing, 0, 205, buttonHeight }, tradeTypes, function(index, value) + self.tradeTypeIndex = index + end) + -- remember previous choice + self.tradeTypeIndex = self.tradeTypeIndex or 1 + controls.tradeTypeSelection:SetSel(self.tradeTypeIndex) -- Helper function to search a single socket local function searchSingleSocket(socketId, socketInfo) if not treeData.nodes[socketId] or not treeData.nodes[socketId].isJewelSocket then @@ -2510,7 +2578,21 @@ function TreeTabClass:FindTimelessJewel() return results end - controls.searchButton = new("ButtonControl", nil, {buttonX, 485, width, 20}, "Search", function() + local panelWidth = edgePadding * 3 + listWidth * 2 + local buttonDivider = 10 + local buttonWidth = 80 + -- reset button anchored to middle of panel and other buttons anchored to it + controls.resetButton = new("ButtonControl", {"BOTTOMLEFT", nil, "BOTTOMLEFT"}, {panelWidth / 2 - buttonWidth/2, -edgePadding, buttonWidth, buttonHeight}, "Reset", function() + updateSearchList("", true) + updateSearchList("", false) + wipeTable(timelessData.searchResults) + controls.searchTradeButton.enabled = false + clearProtected() + end) + controls.closeButton = new("ButtonControl", {"LEFT", controls.resetButton, "RIGHT"}, {buttonDivider, 0, buttonWidth, buttonHeight}, "Cancel", function() + main:ClosePopup() + end) + controls.searchButton = new("ButtonControl", {"RIGHT", controls.resetButton, "LEFT"}, {-buttonDivider, 0, buttonWidth, buttonHeight}, "Search", function() if timelessData.jewelSocket.id == -1 then wipeTable(timelessData.searchResults) wipeTable(timelessData.sharedResults) @@ -2576,16 +2658,7 @@ function TreeTabClass:FindTimelessJewel() end end end) - controls.resetButton = new("ButtonControl", nil, {buttonX + (width + divider), 485, width, 20}, "Reset", function() - updateSearchList("", true) - updateSearchList("", false) - wipeTable(timelessData.searchResults) - controls.searchTradeButton.enabled = false - clearProtected() - end) - controls.closeButton = new("ButtonControl", nil, {buttonX + (width + divider) * 2, 485, width, 20}, "Cancel", function() - main:ClosePopup() - end) - main:OpenPopup(910, 517, "Find a Timeless Jewel", controls) + local panelHeight = 565 + main:OpenPopup(panelWidth, panelHeight, "Find a Timeless Jewel", controls) end