diff --git a/ComboPointTracker.lua b/ComboPointTracker.lua index edece79..eb5c87f 100644 --- a/ComboPointTracker.lua +++ b/ComboPointTracker.lua @@ -165,14 +165,28 @@ function CleveRoids.CalculateComboScaledDurationByID(spellID, comboPoints) if comboPoints < 1 then comboPoints = 1 end -- Minimum 1 combo point if comboPoints > 5 then comboPoints = 5 end -- Maximum 5 combo points + local baseDuration + -- Check for learned duration first local learned = CleveRoids.GetLearnedComboDuration(spellID, comboPoints) if learned then - return learned + baseDuration = learned + else + -- Fall back to formula + baseDuration = data.base + (comboPoints - 1) * data.increment end - -- Fall back to formula - return data.base + (comboPoints - 1) * data.increment + -- Apply talent modifiers (e.g., Taste for Blood) - additive + if CleveRoids.ApplyTalentModifier then + baseDuration = CleveRoids.ApplyTalentModifier(spellID, baseDuration) + end + + -- Apply equipment modifiers (e.g., Black Morass Idol) - multiplicative + if CleveRoids.ApplyEquipmentModifier then + baseDuration = CleveRoids.ApplyEquipmentModifier(spellID, baseDuration) + end + + return baseDuration end -- Function to track combo points when casting (by spell name) diff --git a/Core.lua b/Core.lua index 7f0b11f..309b2bf 100644 --- a/Core.lua +++ b/Core.lua @@ -3216,6 +3216,11 @@ SlashCmdList["CLEVEROID"] = function(msg) DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combotrack - Show combo point tracking info') DEFAULT_CHAT_FRAME:AddMessage('/cleveroid comboclear - Clear combo tracking data') DEFAULT_CHAT_FRAME:AddMessage('/cleveroid combolearn - Show learned combo durations (per CP)') + DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Talent Modifiers:|r") + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid talents - Show current talent ranks') + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testtalent - Test talent modifier for a spell') + DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Equipment Modifiers:|r") + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testequip - Test equipment modifier for a spell') return end @@ -3398,6 +3403,119 @@ SlashCmdList["CLEVEROID"] = function(msg) return end + -- talents (show current talent ranks) + if cmd == "talents" or cmd == "talent" then + CleveRoids.Print("=== Current Talents ===") + + -- Ensure talents are indexed + if not CleveRoids.Talents or table.getn(CleveRoids.Talents) == 0 then + if CleveRoids.IndexTalents then + CleveRoids.IndexTalents() + end + end + + local count = 0 + for name, rank in pairs(CleveRoids.Talents) do + if type(name) == "string" and tonumber(rank) and tonumber(rank) > 0 then + CleveRoids.Print(name .. ": Rank " .. rank) + count = count + 1 + end + end + + if count == 0 then + CleveRoids.Print("No talents learned yet!") + else + CleveRoids.Print("Total: " .. count .. " talents") + end + return + end + + -- testtalent (test talent modifier for a spell) + if cmd == "testtalent" or cmd == "talenttest" then + local spellID = tonumber(val) + if not spellID then + CleveRoids.Print("Usage: /cleveroid testtalent ") + CleveRoids.Print("Example: /cleveroid testtalent 1943 (Rupture Rank 1)") + return + end + + local spellName = SpellInfo(spellID) or ("Spell " .. spellID) + local modifier = CleveRoids.talentModifiers and CleveRoids.talentModifiers[spellID] + + if not modifier then + CleveRoids.Print(spellName .. " (ID:" .. spellID .. ") has no talent modifier configured") + return + end + + local talentRank = CleveRoids.GetTalentRank and CleveRoids.GetTalentRank(modifier.talent) or 0 + + CleveRoids.Print("=== Talent Modifier Test ===") + CleveRoids.Print("Spell: " .. spellName .. " (ID:" .. spellID .. ")") + CleveRoids.Print("Talent: " .. modifier.talent) + CleveRoids.Print("Your Rank: " .. talentRank .. "/3") + + if talentRank == 0 then + CleveRoids.Print("|cffff0000You don't have this talent!|r") + else + -- Test with a base duration (use 10s as example) + local baseDur = 10 + local modDur = modifier.modifier(baseDur, talentRank) + CleveRoids.Print("Example: 10s base -> " .. modDur .. "s modified (+" .. (modDur - baseDur) .. "s)") + end + return + end + + -- testequip (test equipment modifier for a spell) + if cmd == "testequip" or cmd == "equiptest" then + local spellID = tonumber(val) + if not spellID then + CleveRoids.Print("Usage: /cleveroid testequip ") + CleveRoids.Print("Example: /cleveroid testequip 1079 (Rip Rank 1)") + return + end + + local spellName = SpellInfo(spellID) or ("Spell " .. spellID) + local modifier = CleveRoids.equipmentModifiers and CleveRoids.equipmentModifiers[spellID] + + if not modifier then + CleveRoids.Print(spellName .. " (ID:" .. spellID .. ") has no equipment modifier configured") + return + end + + local itemID = CleveRoids.GetEquippedItemID and CleveRoids.GetEquippedItemID(modifier.slot) + local itemName = "None" + if itemID then + local itemLink = GetInventoryItemLink("player", modifier.slot) + if itemLink then + itemName = string.match(itemLink, "%[(.-)%]") or ("Item " .. itemID) + end + end + + CleveRoids.Print("=== Equipment Modifier Test ===") + CleveRoids.Print("Spell: " .. spellName .. " (ID:" .. spellID .. ")") + CleveRoids.Print("Slot: " .. modifier.slot .. " (Ranged/Relic)") + CleveRoids.Print("Equipped: " .. itemName .. (itemID and (" [" .. itemID .. "]") or "")) + + if not itemID then + CleveRoids.Print("|cffff0000No item equipped in this slot!|r") + else + -- Test with a base duration (use 10s as example) + local baseDur = 10 + local modDur = modifier.modifier(baseDur, itemID) + if modDur ~= baseDur then + CleveRoids.Print("Example: 10s base -> " .. modDur .. "s modified") + if modDur < baseDur then + CleveRoids.Print("|cffff0000Duration reduced by " .. (baseDur - modDur) .. "s|r") + else + CleveRoids.Print("|cff00ff00Duration increased by " .. (modDur - baseDur) .. "s|r") + end + else + CleveRoids.Print("|cffffaa00This item has no effect on this spell|r") + end + end + return + end + -- Unknown command fallback CleveRoids.Print("Usage:") DEFAULT_CHAT_FRAME:AddMessage("/cleveroid - Show current settings") diff --git a/Utility.lua b/Utility.lua index d20e964..5c21fc1 100644 --- a/Utility.lua +++ b/Utility.lua @@ -464,14 +464,14 @@ lib.personalDebuffs = lib.personalDebuffs or { [18657] = 30, -- Hibernate (Rank 2) [18658] = 40, -- Hibernate (Rank 3) - [5570] = 18, -- Insect Swarm (Rank 1) - 12s with talent + [5570] = 18, -- Insect Swarm (Rank 1) [24974] = 18, -- Insect Swarm (Rank 2) [24975] = 18, -- Insect Swarm (Rank 3) [24976] = 18, -- Insect Swarm (Rank 4) [24977] = 18, -- Insect Swarm (Rank 5) - [8921] = 9, -- Moonfire (Rank 1) - 12s with talent - [8924] = 18, -- Moonfire (Rank 2) - 12s with talent + [8921] = 9, -- Moonfire (Rank 1) + [8924] = 18, -- Moonfire (Rank 2) [8925] = 18, -- Moonfire (Rank 3) [8926] = 18, -- Moonfire (Rank 4) [8927] = 18, -- Moonfire (Rank 5) @@ -501,9 +501,9 @@ lib.personalDebuffs = lib.personalDebuffs or { [9827] = 18, -- Pounce Bleed (Rank 3) -- WARLOCK - [172] = 12, -- Corruption (Rank 1) - 18s with talent - [6222] = 15, -- Corruption (Rank 2) - 18s with talent - [6223] = 18, -- Corruption (Rank 3) - 18s with talent + [172] = 12, -- Corruption (Rank 1) + [6222] = 15, -- Corruption (Rank 2) + [6223] = 18, -- Corruption (Rank 3) [7648] = 18, -- Corruption (Rank 4) [11671] = 18, -- Corruption (Rank 5) [11672] = 18, -- Corruption (Rank 6) @@ -614,14 +614,14 @@ lib.personalDebuffs = lib.personalDebuffs or { [19284] = 120, -- Hex of Weakness (Rank 5) [19285] = 120, -- Hex of Weakness (Rank 6) - [589] = 24, -- Shadow Word: Pain (Rank 1) - 18s base, 24s with talent - [594] = 24, -- Shadow Word: Pain (Rank 2) - [970] = 24, -- Shadow Word: Pain (Rank 3) - [992] = 24, -- Shadow Word: Pain (Rank 4) - [2767] = 24, -- Shadow Word: Pain (Rank 5) - [10892] = 24, -- Shadow Word: Pain (Rank 6) - [10893] = 24, -- Shadow Word: Pain (Rank 7) - [10894] = 24, -- Shadow Word: Pain (Rank 8) + [589] = 18, -- Shadow Word: Pain (Rank 1) + [594] = 18, -- Shadow Word: Pain (Rank 2) + [970] = 18, -- Shadow Word: Pain (Rank 3) + [992] = 18, -- Shadow Word: Pain (Rank 4) + [2767] = 18, -- Shadow Word: Pain (Rank 5) + [10892] = 18, -- Shadow Word: Pain (Rank 6) + [10893] = 18, -- Shadow Word: Pain (Rank 7) + [10894] = 18, -- Shadow Word: Pain (Rank 8) [15286] = 60, -- Vampiric Embrace @@ -1132,6 +1132,18 @@ ev:SetScript("OnEvent", function() -- If not a combo scaling spell, use normal duration lookup if not duration then duration = lib:GetDuration(spellID, casterGUID) + + -- Apply talent modifiers for non-combo spells + -- (combo spells already have talent modifiers applied in CalculateComboScaledDurationByID) + if duration and CleveRoids.ApplyTalentModifier then + duration = CleveRoids.ApplyTalentModifier(spellID, duration) + end + + -- Apply equipment modifiers for non-combo spells + -- (combo spells already have equipment modifiers applied in CalculateComboScaledDurationByID) + if duration and CleveRoids.ApplyEquipmentModifier then + duration = CleveRoids.ApplyEquipmentModifier(spellID, duration) + end end -- DEBUG: Show what duration we calculated @@ -1370,6 +1382,197 @@ evCleanup:SetScript("OnEvent", function() end end) +-- ============================================================================ +-- TALENT MODIFIER SYSTEM +-- ============================================================================ + +-- Database of talent modifiers for debuff durations +-- Structure: [spellID] = { talent = "Talent Name", modifier = function(baseDuration, talentRank) } +CleveRoids.talentModifiers = CleveRoids.talentModifiers or {} + +-- ROGUE talent modifiers +-- Taste for Blood: Increases Rupture duration by 2 seconds per rank (3 ranks max) +-- Talent Spell IDs: 14174 (Rank 1), 14175 (Rank 2), 14176 (Rank 3) +CleveRoids.talentModifiers[1943] = { talent = "Taste for Blood", modifier = function(base, rank) return base + (rank * 2) end } -- Rupture Rank 1 +CleveRoids.talentModifiers[8639] = { talent = "Taste for Blood", modifier = function(base, rank) return base + (rank * 2) end } -- Rupture Rank 2 +CleveRoids.talentModifiers[8640] = { talent = "Taste for Blood", modifier = function(base, rank) return base + (rank * 2) end } -- Rupture Rank 3 +CleveRoids.talentModifiers[11273] = { talent = "Taste for Blood", modifier = function(base, rank) return base + (rank * 2) end } -- Rupture Rank 4 +CleveRoids.talentModifiers[11274] = { talent = "Taste for Blood", modifier = function(base, rank) return base + (rank * 2) end } -- Rupture Rank 5 +CleveRoids.talentModifiers[11275] = { talent = "Taste for Blood", modifier = function(base, rank) return base + (rank * 2) end } -- Rupture Rank 6 + +-- Improved Gouge: Increases Gouge duration by 0.5 seconds per rank (3 ranks max) +-- Talent Spell IDs: 13741 (Rank 1), 13793 (Rank 2), 13792 (Rank 3) +CleveRoids.talentModifiers[1776] = { talent = "Improved Gouge", modifier = function(base, rank) return base + (rank * 0.5) end } -- Gouge Rank 1 +CleveRoids.talentModifiers[1777] = { talent = "Improved Gouge", modifier = function(base, rank) return base + (rank * 0.5) end } -- Gouge Rank 2 +CleveRoids.talentModifiers[8629] = { talent = "Improved Gouge", modifier = function(base, rank) return base + (rank * 0.5) end } -- Gouge Rank 3 +CleveRoids.talentModifiers[11285] = { talent = "Improved Gouge", modifier = function(base, rank) return base + (rank * 0.5) end } -- Gouge Rank 4 +CleveRoids.talentModifiers[11286] = { talent = "Improved Gouge", modifier = function(base, rank) return base + (rank * 0.5) end } -- Gouge Rank 5 + +-- PRIEST talent modifiers +-- Improved Shadow Word: Pain: Increases SW:P duration by 3 seconds per rank (2 ranks max) +-- Talent Spell IDs: 15275 (Rank 1), 15317 (Rank 2) +CleveRoids.talentModifiers[589] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 1 +CleveRoids.talentModifiers[594] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 2 +CleveRoids.talentModifiers[970] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 3 +CleveRoids.talentModifiers[992] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 4 +CleveRoids.talentModifiers[2767] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 5 +CleveRoids.talentModifiers[10892] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 6 +CleveRoids.talentModifiers[10893] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 7 +CleveRoids.talentModifiers[10894] = { talent = "Improved Shadow Word: Pain", modifier = function(base, rank) return base + (rank * 3) end } -- SW:P Rank 8 + +-- Function to get talent rank for a talent by name +-- Returns 0 if talent not found or not learned +function CleveRoids.GetTalentRank(talentName) + if not talentName then return 0 end + + -- Ensure talents are indexed + if not CleveRoids.Talents or table.getn(CleveRoids.Talents) == 0 then + if CleveRoids.IndexTalents then + CleveRoids.IndexTalents() + end + end + + local rank = CleveRoids.Talents[talentName] + return (rank and tonumber(rank)) or 0 +end + +-- Apply talent modifiers to a debuff duration +-- Parameters: +-- spellID: The spell ID +-- baseDuration: The base duration (after combo points if applicable) +-- Returns: Modified duration, or baseDuration if no talent modifier applies +function CleveRoids.ApplyTalentModifier(spellID, baseDuration) + if not spellID or not baseDuration then + return baseDuration + end + + local modifier = CleveRoids.talentModifiers[spellID] + if not modifier then + return baseDuration + end + + local talentRank = CleveRoids.GetTalentRank(modifier.talent) + if talentRank == 0 then + return baseDuration + end + + local modifiedDuration = modifier.modifier(baseDuration, talentRank) + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff00ff[Talent Modifier]|r %s (ID:%d): %ds -> %ds (talent: %s rank %d)", + SpellInfo(spellID) or "Unknown", spellID, baseDuration, modifiedDuration, + modifier.talent, talentRank) + ) + end + + return modifiedDuration +end + +-- Helper function to register a talent modifier +-- Usage: CleveRoids.RegisterTalentModifier(spellID, talentName, modifierFunction) +function CleveRoids.RegisterTalentModifier(spellID, talentName, modifierFunc) + if not spellID or not talentName or not modifierFunc then + return false + end + + CleveRoids.talentModifiers[spellID] = { + talent = talentName, + modifier = modifierFunc + } + + return true +end + +-- ============================================================================ +-- EQUIPMENT MODIFIER SYSTEM +-- ============================================================================ + +-- Database of equipment modifiers for debuff durations +-- Structure: [spellID] = { slot = invSlotID, items = { [itemID] = modifierFunction } } +CleveRoids.equipmentModifiers = CleveRoids.equipmentModifiers or {} + +-- DRUID equipment modifiers +-- Black Morass Idol: Reduces Rip duration by 10% (multiplicative) +-- Item ID: 61699, Slot: 18 (Ranged/Relic) +local ripIdolModifier = function(duration, itemID) + if itemID == 61699 then + -- Black Morass: 10% reduction (multiply by 0.9) + return duration * 0.9 + end + return duration +end + +CleveRoids.equipmentModifiers[1079] = { slot = 18, modifier = ripIdolModifier } -- Rip Rank 1 +CleveRoids.equipmentModifiers[9492] = { slot = 18, modifier = ripIdolModifier } -- Rip Rank 2 +CleveRoids.equipmentModifiers[9493] = { slot = 18, modifier = ripIdolModifier } -- Rip Rank 3 +CleveRoids.equipmentModifiers[9752] = { slot = 18, modifier = ripIdolModifier } -- Rip Rank 4 +CleveRoids.equipmentModifiers[9894] = { slot = 18, modifier = ripIdolModifier } -- Rip Rank 5 +CleveRoids.equipmentModifiers[9896] = { slot = 18, modifier = ripIdolModifier } -- Rip Rank 6 + +-- Function to get equipped item ID in a specific slot +function CleveRoids.GetEquippedItemID(slotID) + local itemLink = GetInventoryItemLink("player", slotID) + if not itemLink then return nil end + + local _, _, itemID = string.find(itemLink, "item:(%d+)") + return tonumber(itemID) +end + +-- Apply equipment modifiers to a debuff duration +-- Parameters: +-- spellID: The spell ID +-- baseDuration: The base duration (after combo points and talents) +-- Returns: Modified duration, or baseDuration if no equipment modifier applies +function CleveRoids.ApplyEquipmentModifier(spellID, baseDuration) + if not spellID or not baseDuration then + return baseDuration + end + + local modifier = CleveRoids.equipmentModifiers[spellID] + if not modifier then + return baseDuration + end + + local itemID = CleveRoids.GetEquippedItemID(modifier.slot) + if not itemID then + return baseDuration + end + + local modifiedDuration = modifier.modifier(baseDuration, itemID) + + if modifiedDuration ~= baseDuration and CleveRoids.debug then + local itemName = "Unknown" + local itemLink = GetInventoryItemLink("player", modifier.slot) + if itemLink then + itemName = string.match(itemLink, "%[(.-)%]") or "Unknown" + end + + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff00ff[Equipment Modifier]|r %s (ID:%d): %ds -> %ds (item: %s [%d])", + SpellInfo(spellID) or "Unknown", spellID, baseDuration, modifiedDuration, + itemName, itemID) + ) + end + + return modifiedDuration +end + +-- Helper function to register an equipment modifier +-- Usage: CleveRoids.RegisterEquipmentModifier(spellID, slotID, modifierFunction) +function CleveRoids.RegisterEquipmentModifier(spellID, slotID, modifierFunc) + if not spellID or not slotID or not modifierFunc then + return false + end + + CleveRoids.equipmentModifiers[spellID] = { + slot = slotID, + modifier = modifierFunc + } + + return true +end + -- ============================================================================ -- IMMUNITY TRACKING SYSTEM -- ============================================================================