From 9dcc2446b7b6b1fd150474b622655bc3f7d830e8 Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Sun, 23 Nov 2025 16:16:53 -0500 Subject: [PATCH] new conditional swingtimer and druid set bonus debuff durations and warrior talent debuff durations --- Conditionals.lua | 66 +++++++++++++++++++++++ README.md | 88 +++++++++++++++++++++++++++++- Utility.lua | 137 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 289 insertions(+), 2 deletions(-) diff --git a/Conditionals.lua b/Conditionals.lua index 80b69e1..6adbe9f 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -338,6 +338,38 @@ function CleveRoids.ValidateComboPoints(operator, amount) return false end +-- Validates swing timer percentage for SP_SwingTimer addon integration +-- operator: Comparison operator (>, <, =, >=, <=, ~=) +-- amount: Percentage of swing time (e.g., 20 means 20% of attack speed) +-- returns: True if st_timer [operator] (attackSpeed * percent/100) +function CleveRoids.ValidateSwingTimer(operator, amount) + if not operator or not amount then return false end + + -- Check if SP_SwingTimer is loaded by checking for st_timer global + if st_timer == nil then + -- Only show error once per session + if not CleveRoids._swingTimerErrorShown then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[SuperCleveRoidMacros]|r The [swingtimer] conditional requires the SP_SwingTimer addon. Get it at: https://github.com/jrc13245/SP_SwingTimer", 1, 0.5, 0.5) + CleveRoids._swingTimerErrorShown = true + end + return false + end + + -- Get player's attack speed (main hand) + local attackSpeed = UnitAttackSpeed("player") + if not attackSpeed or attackSpeed <= 0 then return false end + + -- Calculate threshold: percentage of swing time + local threshold = attackSpeed * (amount / 100) + + -- Compare current swing timer against threshold + if CleveRoids.operators[operator] then + return CleveRoids.comparators[operator](st_timer, threshold) + end + + return false +end + function CleveRoids.ValidateLevel(unit, operator, amount) if not unit or not operator or not amount then return false end local level = UnitLevel(unit) @@ -2220,5 +2252,39 @@ CleveRoids.Keywords = { end return not CleveRoids.CheckImmunity(conditionals.target or "target", checkValue) + end, + + -- SP_SwingTimer integration conditionals + -- Checks if swing timer is at a percentage of attack speed + -- Usage: [swingtimer:>20] = st_timer > UnitAttackSpeed("player") * 0.2 + swingtimer = function(conditionals) + return And(conditionals.swingtimer, function(args) + if type(args) ~= "table" then return false end + return CleveRoids.ValidateSwingTimer(args.operator, args.amount) + end) + end, + + -- Alias for swingtimer + stimer = function(conditionals) + return And(conditionals.stimer, function(args) + if type(args) ~= "table" then return false end + return CleveRoids.ValidateSwingTimer(args.operator, args.amount) + end) + end, + + -- Negated swingtimer + noswingtimer = function(conditionals) + return And(conditionals.noswingtimer, function(args) + if type(args) ~= "table" then return false end + return not CleveRoids.ValidateSwingTimer(args.operator, args.amount) + end) + end, + + -- Alias for noswingtimer + nostimer = function(conditionals) + return And(conditionals.nostimer, function(args) + if type(args) ~= "table" then return false end + return not CleveRoids.ValidateSwingTimer(args.operator, args.amount) + end) end } diff --git a/README.md b/README.md index 6ac65c4..5e983bf 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,8 @@ The addon automatically applies talent-based duration modifications to debuffs. **Calculation Order:** `final_duration = (base + combo_points) + talent_modifier` **Supported Talent Modifiers:** +- **Warrior:** + - **Booming Voice** (5 ranks): +12% per rank to Demoralizing Shout duration (60% at rank 5) - **Rogue:** - **Taste for Blood** (3 ranks): +2s per rank to Rupture duration - **Improved Gouge** (3 ranks): +0.5s per rank to Gouge duration @@ -236,10 +238,10 @@ The **Carnage** talent (Feral Combat tree, 2/2 required) causes **Ferocious Bite - Rip/Rake must already be active on the target ### Equipment Modifiers -The addon supports item-based duration modifications. +The addon supports item-based duration modifications. **IF YOUR EQUIPMENT DEBUFF DURATION MODIFIER IS MISSING, PLEASE MAKE A BUG REPORT!!!** -**Calculation Order:** `final_duration = ((base + combo_points) + talent_modifier) × equipment_modifier` +**Calculation Order:** `final_duration = (((base + combo_points) + talent_modifier) × equipment_modifier) + set_bonus` **Supported Equipment Modifiers:** - **Idol of Savagery** (Item ID: 61699, Druid Idol): Reduces the time between periodic ticks and the duration of Rake and Rip by 10% (×0.9 multiplier) @@ -250,6 +252,28 @@ The addon supports item-based duration modifications. 3. Talent modifier: 18s + 0 = 18s (if no talent) 4. Equipment modifier: 18s × 0.9 = **16.2s final** +### Set Bonus Modifiers +The addon supports set bonus duration modifications by counting equipped set pieces. +**IF YOUR SET BONUS DEBUFF DURATION MODIFIER IS MISSING, PLEASE MAKE A BUG REPORT!!!** + +**How It Works:** +The system counts how many pieces from a set are equipped and applies the modifier when the threshold is met. + +**Supported Set Bonus Modifiers:** +- **Druid:** + - **Dreamwalker Regalia (4/9)**: +3s to Moonfire duration, +2s to Insect Swarm duration + - Item IDs: 47372, 47373, 47374, 47375, 47376, 47377, 47378, 47379, 47380 + - **Haruspex's Garb (3/5)**: +5s to Faerie Fire duration (non-feral only) + - Item IDs: 19613, 19955, 19840, 19839, 19838 + +**Example:** Moonfire with Dreamwalker Regalia 4-set: +1. Base duration: 18s +2. Set bonus modifier: 18s + 3s = **21s final** + +**Example:** Insect Swarm with Dreamwalker Regalia 4-set: +1. Base duration: 18s +2. Set bonus modifier: 18s + 2s = **20s final** + ### Immunity Tracking System The addon automatically learns and tracks NPC immunities from combat log messages: @@ -442,6 +466,7 @@ The addon automatically learns and tracks NPC immunities from combat log message | queuedspell | [queuedspell]
[queuedspell:X] | | * | if the player has any or a specific spell queued with nampower. | | onswingpending | [onswingpending] | | * | If the player has a on swing spell pending.| | mhimbue/ohimbue | [mhimbue:Flametongue]
[ohimbue] | | * | If the player has weapon imbue on their mh/oh.| +| swingtimer/stimer | [swingtimer:>20]
[stimer:<=50] | * | * | If the swing timer is at a percentage of attack speed. Requires [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer) addon. See [below](#swing-timer-integration) for details.| ### Unit Based ### The default @unitid is usually @target if you don't specify one @@ -589,6 +614,65 @@ The addon automatically learns and tracks NPC immunities from combat log message * ShaguTweaks +### Swing Timer Integration +The addon integrates with [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer) to provide swing timer conditionals for abilities like Slam that benefit from being cast at specific points in your swing cycle. + +**Requirements:** +- [SP_SwingTimer](https://github.com/jrc13245/SP_SwingTimer) addon must be installed +- If SP_SwingTimer is not loaded, the conditional will return false and display an error message once + +**How It Works:** +The conditional compares the current swing timer (`st_timer`) against a percentage of your attack speed. + +`[swingtimer:>20]` checks if `st_timer > UnitAttackSpeed("player") * 0.2` + +This means "cast if more than 20% of my swing time remains." + +**Syntax:** +- `[swingtimer:>X]` - True if swing timer > X% of attack speed +- `[swingtimer:=X]` - True if swing timer >= X% of attack speed +- `[swingtimer:<=X]` - True if swing timer <= X% of attack speed +- `[stimer:>X]` - Alias for swingtimer + +**Negation:** +- `[noswingtimer:<80]` - True if NOT (swing timer < 80% of attack speed) +- `[nostimer:>20]` - Alias for noswingtimer + +**Example Macros:** + +```lua +-- Cast Slam only if more than 20% of swing time remains +-- Equivalent to: /run if st_timer>UnitAttackSpeed("player")*0.2 then CastSpellByName("Slam") end +#showtooltip Slam +/cast [swingtimer:>20] Slam +``` + +```lua +-- Heroic Strike queue management - only queue if swing timer is low +#showtooltip Heroic Strike +/cast [stimer:<30] Heroic Strike +``` + +```lua +-- Complex rotation with swing timer awareness +#showtooltip +/cast [swingtimer:>25] Slam +/cast Heroic Strike +``` + +**Understanding the Percentage:** +- 100% = Full swing time (just attacked, full cooldown remaining) +- 50% = Half of swing time remaining +- 20% = Only 20% of swing time left before next auto-attack +- 0% = About to swing + +For a warrior with a 3.5 second weapon: +- `[swingtimer:>20]` = True if st_timer > 0.7 seconds +- `[swingtimer:>50]` = True if st_timer > 1.75 seconds + +--- + ### HealComm Support This addon uses [SuperWoW](https://github.com/balakethelock/SuperWoW)'s CastSpellByName which is not compatible with the standard HealComm-1.0 library. MarcelineVQ has an updated version of [LunaUnitFrames](https://github.com/MarcelineVQ/LunaUnitFrames) where they added [SuperWoW](https://github.com/balakethelock/SuperWoW) support to the HealComm-1.0 library. If you use [SuperWoW](https://github.com/balakethelock/SuperWoW) and want proper HealComm support with your macros, you need to do one or both of the following: diff --git a/Utility.lua b/Utility.lua index 6807eb6..bc14225 100644 --- a/Utility.lua +++ b/Utility.lua @@ -1361,6 +1361,11 @@ ev:SetScript("OnEvent", function() if duration and CleveRoids.ApplyEquipmentModifier then duration = CleveRoids.ApplyEquipmentModifier(spellID, duration) end + + -- Apply set bonus modifiers for non-combo spells + if duration and CleveRoids.ApplySetBonusModifier then + duration = CleveRoids.ApplySetBonusModifier(spellID, duration) + end end -- DEBUG: Show what duration we calculated @@ -1693,6 +1698,17 @@ CleveRoids.talentModifiers[9827] = { tab = 2, id = 4, talent = "Brutal Impact", -- Carnage is a refresh mechanic: Ferocious Bite at 5 CP refreshes Rip/Rake to their original duration -- This is handled separately in the Carnage refresh logic (lines 1125-1192) +-- WARRIOR talent modifiers +-- Booming Voice: Increases duration of Battle Shout and Demoralizing Shout by 12% per rank (5 ranks max) +-- Talent Position: Tab 2 (Fury), Talent 1 +-- Talent Spell IDs: 12321 (Rank 1), 12835 (Rank 2), 12836 (Rank 3), 12837 (Rank 4), 12838 (Rank 5) +local boomingVoiceModifier = function(base, rank) return base * (1 + rank * 0.12) end -- 12% per rank, 60% at rank 5 +CleveRoids.talentModifiers[1160] = { tab = 2, id = 1, talent = "Booming Voice", modifier = boomingVoiceModifier } -- Demoralizing Shout Rank 1 +CleveRoids.talentModifiers[6190] = { tab = 2, id = 1, talent = "Booming Voice", modifier = boomingVoiceModifier } -- Demoralizing Shout Rank 2 +CleveRoids.talentModifiers[11554] = { tab = 2, id = 1, talent = "Booming Voice", modifier = boomingVoiceModifier } -- Demoralizing Shout Rank 3 +CleveRoids.talentModifiers[11555] = { tab = 2, id = 1, talent = "Booming Voice", modifier = boomingVoiceModifier } -- Demoralizing Shout Rank 4 +CleveRoids.talentModifiers[11556] = { tab = 2, id = 1, talent = "Booming Voice", modifier = boomingVoiceModifier } -- Demoralizing Shout Rank 5 + -- Function to get talent rank -- Supports both position-based (tab, id) and name-based lookup -- Position-based is preferred as it's more reliable and locale-independent (like Cursive addon) @@ -1993,6 +2009,127 @@ function CleveRoids.RegisterEquipmentModifier(spellID, slotID, modifierFunc) return true end +-- ============================================================================ +-- SET BONUS MODIFIER SYSTEM +-- ============================================================================ + +-- Database of set bonus modifiers for debuff durations +-- Structure: [spellID] = { items = {itemID1, itemID2, ...}, threshold = X, modifier = function(baseDuration) } +CleveRoids.setbonusModifiers = CleveRoids.setbonusModifiers or {} + +-- Function to count how many items from a set are currently equipped +function CleveRoids.CountEquippedSetItems(items) + if not items or type(items) ~= "table" then return 0 end + + local count = 0 + -- Check all equipment slots (1-19) + for slot = 1, 19 do + local itemLink = GetInventoryItemLink("player", slot) + if itemLink then + local _, _, itemID = string.find(itemLink, "item:(%d+)") + if itemID then + itemID = tonumber(itemID) + -- Check if this item is in the set + for _, setItemID in ipairs(items) do + if itemID == setItemID then + count = count + 1 + break + end + end + end + end + end + + return count +end + +-- Apply set bonus modifiers to a debuff duration +-- Parameters: +-- spellID: The spell ID +-- baseDuration: The base duration (after combo points, talents, and equipment) +-- Returns: Modified duration, or baseDuration if no set bonus modifier applies +function CleveRoids.ApplySetBonusModifier(spellID, baseDuration) + if not spellID or not baseDuration then + return baseDuration + end + + local modifier = CleveRoids.setbonusModifiers[spellID] + if not modifier then + return baseDuration + end + + -- Check if player has enough set pieces equipped + local equippedCount = CleveRoids.CountEquippedSetItems(modifier.items) + if equippedCount < modifier.threshold then + return baseDuration + end + + local modifiedDuration = modifier.modifier(baseDuration) + + if modifiedDuration ~= baseDuration and CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cff00ffff[Set Bonus Modifier]|r %s (ID:%d): %.1fs -> %.1fs (%d/%d pieces)", + SpellInfo(spellID) or "Unknown", spellID, baseDuration, modifiedDuration, + equippedCount, modifier.threshold) + ) + end + + return modifiedDuration +end + +-- Helper function to register a set bonus modifier +-- Usage: CleveRoids.RegisterSetBonusModifier(spellID, itemsTable, threshold, modifierFunction) +function CleveRoids.RegisterSetBonusModifier(spellID, items, threshold, modifierFunc) + if not spellID or not items or not threshold or not modifierFunc then + return false + end + + CleveRoids.setbonusModifiers[spellID] = { + items = items, + threshold = threshold, + modifier = modifierFunc + } + + return true +end + +-- DRUID set bonus modifiers +-- Dreamwalker Regalia (4/9): Increases Moonfire duration by 3 seconds and Insect Swarm by 2 seconds +-- Item IDs: 47372, 47373, 47374, 47375, 47376, 47377, 47378, 47379, 47380 +local dreamwalkerItems = { 47372, 47373, 47374, 47375, 47376, 47377, 47378, 47379, 47380 } +local dreamwalkerMoonfireModifier = function(base) return base + 3 end +local dreamwalkerInsectSwarmModifier = function(base) return base + 2 end + +-- Moonfire (all ranks) +CleveRoids.setbonusModifiers[8921] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 1 +CleveRoids.setbonusModifiers[8924] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 2 +CleveRoids.setbonusModifiers[8925] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 3 +CleveRoids.setbonusModifiers[8926] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 4 +CleveRoids.setbonusModifiers[8927] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 5 +CleveRoids.setbonusModifiers[8928] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 6 +CleveRoids.setbonusModifiers[8929] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 7 +CleveRoids.setbonusModifiers[9833] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 8 +CleveRoids.setbonusModifiers[9834] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 9 +CleveRoids.setbonusModifiers[9835] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerMoonfireModifier } -- Moonfire Rank 10 + +-- Insect Swarm (all ranks) +CleveRoids.setbonusModifiers[5570] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerInsectSwarmModifier } -- Insect Swarm Rank 1 +CleveRoids.setbonusModifiers[24974] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerInsectSwarmModifier } -- Insect Swarm Rank 2 +CleveRoids.setbonusModifiers[24975] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerInsectSwarmModifier } -- Insect Swarm Rank 3 +CleveRoids.setbonusModifiers[24976] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerInsectSwarmModifier } -- Insect Swarm Rank 4 +CleveRoids.setbonusModifiers[24977] = { items = dreamwalkerItems, threshold = 4, modifier = dreamwalkerInsectSwarmModifier } -- Insect Swarm Rank 5 + +-- Haruspex's Garb (3/5): Increases Faerie Fire duration by 5 seconds +-- Item IDs: 19613, 19955, 19840, 19839, 19838 +local haruspexItems = { 19613, 19955, 19840, 19839, 19838 } +local haruspexFaerieFireModifier = function(base) return base + 5 end + +-- Faerie Fire (non-feral, all ranks) +CleveRoids.setbonusModifiers[770] = { items = haruspexItems, threshold = 3, modifier = haruspexFaerieFireModifier } -- Faerie Fire Rank 1 +CleveRoids.setbonusModifiers[778] = { items = haruspexItems, threshold = 3, modifier = haruspexFaerieFireModifier } -- Faerie Fire Rank 2 +CleveRoids.setbonusModifiers[9749] = { items = haruspexItems, threshold = 3, modifier = haruspexFaerieFireModifier } -- Faerie Fire Rank 3 +CleveRoids.setbonusModifiers[9907] = { items = haruspexItems, threshold = 3, modifier = haruspexFaerieFireModifier } -- Faerie Fire Rank 4 + -- ============================================================================ -- IMMUNITY TRACKING SYSTEM -- ============================================================================