-- WarriorFury.lua -- October 2022 -- Updated for PTR Build 46181 -- Last Modified 10/20/2022 18:18 UTC if UnitClassBase( "player" ) ~= "WARRIOR" then return end local addon, ns = ... local Hekili = _G[ addon ] local class = Hekili.Class local state = Hekili.State local FindPlayerAuraByID = ns.FindPlayerAuraByID local IsActiveSpell = ns.IsActiveSpell -- Conduits (Patch 10.0) : In all cases, talents override and disable conduits they share effects with. -- Talents override: -- Piercing Verdict -- Cacophonous Roar -- Inspiring Presence -- Merciless Bonegrinder -- Ashen Juggernaut -- Depths of Insanity -- Conduits that need modeled. -- [X] Indelible Victory -- [X] Stalwart Guardian -- [X] Disturb the Peace local spec = Hekili:NewSpecialization( 72 ) local base_rage_gen, fury_rage_mult = 1.75, 1.00 local offhand_mod = 0.50 spec:RegisterResource( Enum.PowerType.Rage, { mainhand_fury = { swing = "mainhand", last = function () local swing = state.swings.mainhand local t = state.query_time return swing + floor( ( t - swing ) / state.swings.mainhand_speed ) * state.swings.mainhand_speed end, interval = "mainhand_speed", stop = function () return state.time == 0 or state.swings.mainhand == 0 end, value = function () -- annihilator: auto-attacks deal an additional (10% of Attack power) Physical damage and generate 2 Rage. -- swift strikes: annihilator generates 2 additional rage return ( ( ( state.talent.war_machine.enabled and 1.2 or 1 ) * base_rage_gen * fury_rage_mult * state.swings.mainhand_speed ) + ( state.talent.annihilator.enabled and (state.talent.swift_strikes.rank > 0 and 2 + (state.talent.swift_strikes.rank * 1 ) or 2 ) or 0 ) ) end }, offhand_fury = { swing = "offhand", last = function () local swing = state.swings.offhand local t = state.query_time return swing + floor( ( t - swing ) / state.swings.offhand_speed ) * state.swings.offhand_speed end, interval = "offhand_speed", stop = function () return state.time == 0 or state.swings.offhand == 0 end, value = function () -- annihilator: auto-attacks deal an additional (10% of Attack power) Physical damage and generate 2 Rage. -- swift strikes: annihilator generates 2 additional rage return ( ( state.talent.war_machine.enabled and 1.1 or 1 ) * base_rage_gen * fury_rage_mult * state.swings.offhand_speed * offhand_mod ) + ( state.talent.annihilator.enabled and (state.talent.swift_strikes.rank > 0 and 2 + (state.talent.swift_strikes.rank * 1 ) or 2 ) or 0 ) end, }, battle_trance = { aura = "battle_trance", last = function () local app = state.buff.battle_trance.applied local t = state.query_time return app + floor( ( t - app ) / 3 ) * 3 end, interval = 3, value = 5, }, conquerors_banner = { aura = "conquerors_banner", last = function () local app = state.buff.conquerors_banner.applied local t = state.query_time return app + floor( t - app ) end, interval = 1, value = 6, -- Fury 6, Arms 4, Prot 4 }, ravager = { aura = "ravager", last = function () local app = state.buff.ravager.applied local t = state.query_time return app + floor( ( t - app ) / state.haste ) * state.haste end, interval = function () return state.haste end, value = function () return state.talent.storm_of_steel.enabled and 15 or 10 end, }, } ) -- Talents spec:RegisterTalents( { anger_management = { 90415, 152278, 1 }, -- annihilator = { 90419, 383916, 1 }, -- armored_to_the_teeth = { 90258, 384124, 2 }, -- ashen_juggernaut = { 90409, 392536, 1 }, -- avatar = { 90365, 107574, 1 }, -- barbaric_training = { 92222, 390674, 1 }, -- berserker_rage = { 90372, 18499 , 1 }, -- berserker_shout = { 90348, 384100, 1 }, -- berserker_stance = { 90325, 386196, 1 }, -- berserkers_torment = { 90362, 390123, 1 }, -- bitter_immunity = { 90356, 383762, 1 }, -- blood_and_thunder = { 90342, 384277, 1 }, -- bloodborne = { 90401, 385703, 1 }, -- bloodcraze = { 90405, 393950, 1 }, -- bloodthirst = { 90392, 23881 , 1 }, -- bounding_stride = { 90355, 202163, 1 }, -- cacophonous_roar = { 90383, 382954, 1 }, -- cold_steel_hot_blood = { 90402, 383959, 1 }, -- concussive_blows = { 90335, 383115, 1 }, -- crackling_thunder = { 90342, 203201, 1 }, -- critical_thinking = { 90425, 383297, 2 }, -- cruel_strikes = { 90381, 392777, 2 }, -- cruelty = { 90428, 392931, 1 }, -- crushing_force = { 90349, 382764, 2 }, -- dancing_blades = { 90417, 391683, 1 }, -- defensive_stance = { 90330, 386208, 1 }, -- deft_experience = { 90421, 383295, 2 }, -- depths_of_insanity = { 90413, 383922, 1 }, -- double_time = { 90382, 103827, 1 }, -- dual_wield_specialization = { 90373, 382900, 1 }, -- elysian_might = { 90323, 386285, 1 }, -- endurance_training = { 90376, 391997, 1 }, -- enraged_regeneration = { 90395, 184364, 1 }, -- fast_footwork = { 90371, 382260, 1 }, -- focus_in_chaos = { 90403, 383486, 1 }, -- frenzied_flurry = { 90422, 383605, 1 }, -- frenzy = { 90406, 335077, 1 }, -- fresh_meat = { 90399, 215568, 1 }, -- frothing_berserker = { 90350, 215571, 1 }, -- furious_blows = { 90336, 390354, 1 }, -- hack_and_slash = { 90407, 383877, 1 }, -- heroic_leap = { 90346, 6544 , 1 }, -- honed_reflexes = { 90367, 391270, 1 }, -- hurricane = { 90389, 390563, 1 }, -- impending_victory = { 90326, 202168, 1 }, -- improved_bloodthirst = { 90397, 383852, 1 }, -- improved_enrage = { 90398, 383848, 1 }, -- improved_execute = { 90430, 316402, 1 }, -- improved_raging_blow = { 90390, 383854, 1 }, -- improved_whirlwind = { 90427, 12950 , 1 }, -- inspiring_presence = { 90332, 382310, 1 }, -- intervene = { 90329, 3411 , 1 }, -- intimidating_shout = { 90384, 5246 , 1 }, -- invigorating_fury = { 90393, 383468, 1 }, -- leeching_strikes = { 90344, 382258, 1 }, -- massacre = { 90410, 206315, 1 }, -- meat_cleaver = { 90391, 280392, 1 }, -- menace = { 90383, 275338, 1 }, -- odyns_fury = { 90418, 385059, 1 }, -- onslaught = { 90424, 315720, 1 }, -- overwhelming_rage = { 90378, 382767, 2 }, -- pain_and_gain = { 90353, 382549, 1 }, -- piercing_howl = { 90348, 12323 , 1 }, -- piercing_verdict = { 90379, 382948, 1 }, -- raging_armaments = { 90426, 388049, 1 }, -- raging_blow = { 90396, 85288 , 1 }, -- rallying_cry = { 90331, 97462 , 1 }, -- rampage = { 90408, 184367, 1 }, -- ravager = { 90388, 228920, 1 }, -- reckless_abandon = { 90415, 202751, 1 }, -- recklessness = { 90412, 1719 , 1 }, -- reinforced_plates = { 90368, 382939, 1 }, -- rumbling_earth = { 90374, 275339, 1 }, -- second_wind = { 90332, 29838 , 1 }, -- seismic_reverberation = { 90340, 382956, 1 }, -- shattering_throw = { 90351, 64382 , 1 }, -- shockwave = { 90375, 46968 , 1 }, -- sidearm = { 90335, 384404, 1 }, -- singleminded_fury = { 90400, 81099 , 1 }, -- slaughtering_strikes = { 90411, 388004, 1 }, -- sonic_boom = { 90321, 390725, 1 }, -- spear_of_bastion = { 90380, 376079, 1 }, -- spell_reflection = { 90385, 23920 , 1 }, -- storm_bolt = { 90337, 107570, 1 }, -- storm_of_steel = { 90389, 382953, 1 }, -- storm_of_swords = { 90420, 388903, 1 }, -- sudden_death = { 90429, 280721, 1 }, -- swift_strikes = { 90416, 383459, 2 }, -- tenderize = { 90423, 388933, 1 }, -- thunder_clap = { 92223, 6343, 1 }, -- TODO: is 396719 in BETA Build for Arms/Fury thunderous_roar = { 90359, 384318, 1 }, -- thunderous_words = { 90358, 384969, 1 }, -- titanic_rage = { 90417, 394329, 1 }, -- titanic_throw = { 90341, 384090, 1 }, -- titans_torment = { 90362, 390135, 1 }, -- unbridled_ferocity = { 90414, 389603, 1 }, -- uproar = { 90357, 391572, 1 }, -- vicious_contempt = { 90404, 383885, 2 }, -- war_machine = { 90386, 346002, 1 }, -- warpaint = { 90394, 208154, 1 }, -- wild_strikes = { 90360, 382946, 2 }, -- wrath_and_fury = { 90387, 392936, 1 }, -- wrecking_throw = { 90351, 384110, 1 }, -- } ) -- PvP Talents spec:RegisterPvpTalents( { barbarian = 166 , -- 280745 battle_trance = 170 , -- 213857 bloodrage = 172 , -- 329038 death_sentence = 25 , -- 198500 death_wish = 179 , -- 199261 demolition = 5373, -- 329033 disarm = 3533, -- 236077 enduring_rage = 177 , -- 198877 master_and_commander = 3528, -- 235941 rebound = 5548, -- 213915 slaughterhouse = 3735, -- 352998 warbringer = 5431, -- 356353 } ) -- Auras spec:RegisterAuras( { annihilator = { id = 383915 }, ashen_juggernaut = { id = 392537, duration = 12, max_stack = 5 }, avatar = { id = 107574, duration = 20, max_stack = 1 }, battle_shout = { id = 6673, duration = 3600, max_stack = 1 }, battle_trance = { --PvP Talent id = 213858, duration = 18, max_stack = 1 }, berserker_rage = { id = 18499, duration = 6, max_stack = 1 }, berserker_shout = { id = 384100, duration = 6, max_stack = 1 }, berserker_stance = { id = 386196, duration = 3600, max_stack = 1 }, bloodcraze = { id = 393951, duration = 20, max_stack = 5 }, bloodrage = { id = 329038, duration = 4, tick_time = 1, max_stack = 1 }, bloodthirst = { id = 23881, duration = 20, max_stack = 1 }, charge = { id = 105771, duration = 1, max_stack = 1, }, concussive_blows = { id = 383116, duration = 10, max_stack = 1 }, crushing_impact = { id = 394330, duration = 6, max_stack = 1 }, dancing_blades = { id = 391688, duration = 10, max_stack = 1 }, death_wish = { id = 199261, duration = 15, max_stack = 10 }, defensive_stance = { id = 386208, duration = 3600, max_stack = 1 }, disarm = { id = 236077, duration = 6, max_stack = 1 }, elysian_might = { id = 386286, duration = 8, max_stack = 1 }, enrage = { id = 184362, duration = 4, max_stack = 1, }, enraged_regeneration = { id = 184364, duration = function () return state.talent.invigorating_fury.enabled and 11 or 8 end, max_stack = 1, }, frenzy = { id = 335082, duration = 12, max_stack = 4, }, gushing_wound = { id = 385042, duration = 6, tick_time = 2, max_stack = 1, }, hamstring = { id = 1715, duration = 15, max_stack = 1, }, hurricane = { id = 390581, duration = 6, max_stack = 6 }, indelible_victory = { id = 336642, duration = 8, max_stack = 1 }, intimidating_shout = { id = function () return talent.menace.enabled and 316593 or 5246 end, duration = function () return talent.menace.enabled and 15 or 8 end, max_stack = 1, }, odyns_fury = { id = 385060, duration = 4, tick_time = 1, max_stack = 1 }, piercing_howl = { id = 12323, duration = 8, max_stack = 1, }, quick_thinking = { id = 392778, duration = 10, max_stack = 1 }, raging_blow = { id = 85288, duration = 12, max_stack = 1 }, rallying_cry = { id = 97463, duration = function () return 10 + ( talent.inspiring_presence.enabled and 3 or 0 ) end, max_stack = 1, }, ravager = { id = 228920, duration = 12, tick_time = 2, max_stack = 1 }, recklessness = { id = 1719, duration = function() return state.talent.depths_of_insanity.enabled and 16 or 12 end, max_stack = 1, }, rend = { id = 388539, duration = 15, tick_time = 3, max_stack = 1 }, slaughtering_strikes_annihilator = { id = 393943, duration = 12, max_stack = 5 }, slaughtering_strikes_raging_blow = { id = 393931, duration = 12, max_stack = 5 }, spear_of_bastion = { id = 376080, duration = 4, tick_time = 1, max_stack = 1 }, spell_reflection = { id = 23920, duration = function () return legendary.misshapen_mirror.enabled and 8 or 5 end, max_stack = 1, }, sudden_death = { id = 280776, duration = 10, max_stack = 1 }, taunt = { id = 355, duration = 3, max_stack = 1, }, thunder_clap = { id = 6343, duration = 10, max_stack = 1 }, thunderous_roar = { id = 384318, duration = 8, tick_time = 2, max_stack = 1 }, victorious = { id = 32216, duration = 20, max_stack = 1, }, war_machine = { id = 262232, duration = 8, max_stack = 1 }, whirlwind = { id = 85739, duration = 20, max_stack = function () if talent.meat_cleaver.enabled then return 4 elseif talent.improved_whirlwind.enabled then return 2 else return 0 end end, copy = "meat_cleaver" }, } ) -- Tier 28 spec:RegisterSetBonuses( "tier28_2pc", 364554, "tier28_4pc", 363738 ) -- 2-Set - Frenzied Destruction - Raging Blow deals 15% increased damage and gains an additional charge. -- 4-Set - Frenzied Destruction - Raging Blow has a 20% chance to grant Recklessness for 4 sec. -- Now appropriately grants Crushing Blow and Bloodbath when Reckless Abandon is talented, and no longer grants 50 Rage when Recklessness triggers while Reckless Abandon is talented. spec:RegisterSetBonuses( "tier29_2pc", 393708, "tier29_4pc", 393709 ) -- 2-Set - Execute’s chance to critically strike increased by 10%. -- 4-Set - Sudden Death’s chance to reset the cooldown of Execute and make it usable on any target, regardless of health, is greatly increased. spec:RegisterGear( 'tier20', 147187, 147188, 147189, 147190, 147191, 147192 ) spec:RegisterAura( "raging_thirst", { id = 242300, duration = 8 } ) -- fury 2pc. spec:RegisterAura( "bloody_rage", { id = 242952, duration = 10, max_stack = 10 } ) -- fury 4pc. spec:RegisterGear( 'tier21', 152178, 152179, 152180, 152181, 152182, 152183 ) spec:RegisterAura( "slaughter", { id = 253384, duration = 4 } ) -- fury 2pc dot. spec:RegisterAura( "outrage", { id = 253385, duration = 8 } ) -- fury 4pc. spec:RegisterGear( "ceannar_charger", 137088 ) spec:RegisterGear( "timeless_stratagem", 143728 ) spec:RegisterGear( "kazzalax_fujiedas_fury", 137053 ) spec:RegisterAura( "fujiedas_fury", { id = 207776, duration = 10, max_stack = 4 } ) spec:RegisterGear( "mannoroths_bloodletting_manacles", 137107 ) -- NYI. spec:RegisterGear( "najentuss_vertebrae", 137087 ) spec:RegisterGear( "valarjar_berserkers", 151824 ) spec:RegisterGear( "ayalas_stone_heart", 137052 ) spec:RegisterAura( "stone_heart", { id = 225947, duration = 10 } ) spec:RegisterGear( "the_great_storms_eye", 151823 ) spec:RegisterAura( "tornados_eye", { id = 248142, duration = 6, max_stack = 6 } ) spec:RegisterGear( "archavons_heavy_hand", 137060 ) spec:RegisterGear( "weight_of_the_earth", 137077 ) -- NYI. spec:RegisterGear( "soul_of_the_battlelord", 151650 ) state.IsActiveSpell = IsActiveSpell local whirlwind_consumers = { crushing_blow = 1, bloodbath = 1, bloodthirst = 1, execute = 1, impending_victory = 1, raging_blow = 1, rampage = 1, onslaught = 1, victory_rush = 1 } local whirlwind_stacks = 0 local rageSpent = 0 local gloryRage = 0 local fresh_meat_actual = {} local fresh_meat_virtual = {} local last_rampage_target = nil local TriggerColdSteelHotBlood = setfenv( function() applyDebuff( "target", "gushing_wound" ) gain( 4, "rage" ) end, state ) local TriggerSlaughteringStrikesAnnihilator = setfenv( function() addStack( "slaughtering_strikes_annihilator", nil, 1 ) end, state ) local RemoveFrenzy = setfenv( function() removeBuff( "frenzy" ) end, state ) spec:RegisterCombatLogEvent( function( _, subtype, _, sourceGUID, sourceName, sourceFlags, _, destGUID, destName, destFlags, _, spellID, spellName, school, amount, interrupt, a, b, c, d, critical ) if sourceGUID == state.GUID then if subtype == "SPELL_CAST_SUCCESS" then local ability = class.abilities[ spellID ] if not ability then return end if state.talent.improved_whirlwind.enabled and ability.key == "whirlwind" then whirlwind_stacks = state.talent.meat_cleaver.enabled and 4 or 2 elseif whirlwind_consumers[ ability.key ] and whirlwind_stacks > 0 then whirlwind_stacks = whirlwind_stacks - 1 elseif ability.key == "rampage" and last_rampage_target ~= destGUID and state.talent.frenzy.enabled then RemoveFrenzy() last_rampage_target = destGUID end elseif subtype == "SPELL_DAMAGE" and UnitGUID( "target" ) == destGUID then local ability = class.abilities[ spellID ] if not ability then return end if ability.key == "bloodthirst" or ability.key == "bloodbath" then if critical and state.talent.cold_steel_hot_blood.enabled then -- Critical boolean is the 21st parameter in SPELL_DAMAGE within CLEU (Ref: https://wowpedia.fandom.com/wiki/COMBAT_LOG_EVENT#Payload) TriggerColdSteelHotBlood() -- Bloodthirst/bath critical strike occured. elseif state.talent.fresh_meat.enabled and not fresh_meat_actual[ destGUID ] then fresh_meat_actual[ destGUID ] = true end end elseif subtype == "SWING_DAMAGE" and UnitGUID( "target" ) == destGUID then -- amt is the 12th parameter in SWING_DAMAGE within CLEU (Ref: https://wowpedia.fandom.com/wiki/COMBAT_LOG_EVENT#Payload) local amt = spellID if amt > 0 and state.talent.annihilator.enabled and state.talent.slaughtering_strikes.enabled then TriggerSlaughteringStrikesAnnihilator() end end end end ) local wipe = table.wipe spec:RegisterEvent( "PLAYER_REGEN_ENABLED", function() wipe( fresh_meat_actual ) end ) spec:RegisterHook( "UNIT_ELIMINATED", function( id ) fresh_meat_actual[ id ] = nil end ) local RAGE = Enum.PowerType.Rage local lastRage = -1 spec:RegisterUnitEvent( "UNIT_POWER_FREQUENT", "player", nil, function( event, unit, powerType ) if powerType == "RAGE" then local current = UnitPower( "player", RAGE ) if current < lastRage - 3 then -- Spent Rage, -3 is used as a Hack to avoid Rage decay triggering if state.talent.anger_management.enabled then rageSpent = ( rageSpent + (lastRage - current) ) % 20 end if state.legendary.glory.enabled and FindPlayerAuraByID( 324143 ) then gloryRage = ( gloryRage + lastRage - current ) % 25 end end lastRage = current end end ) spec:RegisterStateExpr( "rage_spent", function () return rageSpent end ) spec:RegisterStateExpr( "glory_rage", function () return gloryRage end ) spec:RegisterHook( "spend", function( amt, resource ) if resource == "rage" then if talent.anger_management.enabled then rage_spent = rage_spent + amt local reduction = floor( rage_spent / 20 ) rage_spent = rage_spent % 20 if reduction > 0 then cooldown.recklessness.expires = cooldown.recklessness.expires - reduction end end if legendary.glory.enabled and buff.conquerors_banner.up then glory_rage = glory_rage + amt local addition = floor( glory_rage / 10 ) * 0.5 glory_rage = glory_rage % 10 if addition > 0 then buff.conquerors_banner.expires = buff.conquerors_banner.expires + addition end end end end ) local WillOfTheBerserker = setfenv( function() applyBuff( "will_of_the_berserker" ) end, state ) local TriggerHurricane = setfenv( function() addStack( "hurricane", nil, 1 ) end, state ) spec:RegisterHook( "reset_precast", function () rage_spent = nil glory_rage = nil if buff.whirlwind.up then if whirlwind_stacks == 0 then removeBuff( "whirlwind" ) elseif whirlwind_stacks < buff.whirlwind.stack then applyBuff( "whirlwind", buff.whirlwind.remains, whirlwind_stacks ) end end if legendary.will_of_the_berserker.enabled and buff.recklessness.up then state:QueueAuraExpiration( "recklessness", WillOfTheBerserker, buff.recklessness.expires ) end wipe( fresh_meat_virtual ) active_dot.hit_by_fresh_meat = 0 for k, v in pairs( fresh_meat_actual ) do fresh_meat_virtual[ k ] = v if k == target.unit then applyDebuff( "target", "hit_by_fresh_meat" ) else active_dot.hit_by_fresh_meat = active_dot.hit_by_fresh_meat + 1 end end if buff.ravager.up and talent.hurricane.enabled then local next_hu = query_time + (1 * state.haste) - ( ( query_time - buff.ravager.applied ) % (1 * state.haste) ) while ( next_hu <= buff.ravager.expires ) do state:QueueAuraEvent( "ravager_hurricane", TriggerHurricane, next_hu, "AURA_PERIODIC" ) next_hu = next_hu + (1 * state.haste) end end end ) spec:RegisterStateExpr( "cycle_for_execute", function () if active_enemies == 1 or target.health_pct < ( talent.massacre.enabled and 35 or 20 ) or not settings.cycle or buff.execute_ineligible.down or buff.sudden_death.up then return false end return Hekili:GetNumTargetsBelowHealthPct( talent.massacre.enabled and 35 or 20, false, max( settings.cycle_min, offset + delay ) ) > 0 end ) spec:RegisterStateExpr( "cycle_for_condemn", function () if active_enemies == 1 or target.health_pct < ( talent.massacre.enabled and 35 or 20 ) or target.health_pct > 80 or not settings.cycle or not action.condemn.known or buff.condemn_ineligible.down or buff.sudden_death.up then return false end return Hekili:GetNumTargetsBelowHealthPct( talent.massacre.enabled and 35 or 20, false, max( settings.cycle_min, offset + delay ) ) > 0 or Hekili:GetNumTargetsAboveHealthPct( 80, false, max( settings.cycle_min, offset + delay ) ) > 0 end ) -- Abilities spec:RegisterAbilities( { avatar = { id = 107574, cast = 0, cooldown = 90, gcd = "off", spend = -10, spendType = "rage", talent = "avatar", startsCombat = false, texture = 613534, toggle = "cooldowns", handler = function () applyBuff( "avatar" ) if talent.berserkers_torment.enabled then applyBuff ( "recklessness", 4) end if talent.titans_torment.enabled then applyBuff ( "odyns_fury" ) active_dot.odyns_fury = max( active_dot.odyns_fury, active_enemies ) if talent.titanic_rage.enabled then applyBuff( "crushing_impact" ) end end end, }, battle_shout = { id = 6673, cast = 0, cooldown = 15, gcd = "spell", startsCombat = false, texture = 132333, essential = true, nobuff = "battle_shout", handler = function () applyBuff( "battle_shout" ) end, }, berserker_rage = { id = 18499, cast = 0, cooldown = 60, gcd = "off", toggle = "cooldowns", talent = "berserker_rage", startsCombat = false, texture = 136009, handler = function () applyBuff( "berserker_rage" ) end, }, berserker_shout = { id = 384100, cast = 0, cooldown = 60, gcd = "off", talent = "berserker_shout", startsCombat = false, texture = 136009, toggle = "cooldowns", handler = function () applyBuff( "berserker_shout" ) end, }, berserker_stance = { id = 386196, cast = 0, cooldown = 3, gcd = "off", talent = "berserker_stance", startsCombat = false, texture = 132275, essential = true, handler = function () applyBuff( "berserker_stance" ) removeBuff( "defensive_stance" ) end, }, bitter_immunity = { id = 383762, cast = 0, cooldown = 180, gcd = "off", talent = "bitter_immunity", startsCombat = false, texture = 136088, toggle = "cooldowns", handler = function () gain( 0.2 * health.max, "health" ) end, }, bloodbath = { id = 335096, known = 23881, cast = 0, cooldown = function () if talent.deft.experience.enabled then return 3 - talent.deft_experience.rank * 0.75 else return 3 end end, hasteCD = true, gcd = "spell", spend = -8, spendType = "rage", cycle = function () return talent.fresh_meat.enabled and "hit_by_fresh_meat" or nil end, startsCombat = true, texture = 136012, bind = "bloodthirst", talent = "reckless_abandon", buff = "recklessness", handler = function () gain( health.max * ( buff.enraged_regeneration.up and 0.23 or 0.03 ) , "health" ) if talent.invigorating_fury.enabled then gain ( health.max * 0.2 , "health" ) end if talent.bloodcraze.enabled then addStack( "bloodcraze", nil, 1 ) end removeStack( "whirlwind" ) if talent.cold_steel_hot_blood.enabled and stat.crit >= 100 then applyDebuff( "target", "gushing_wound" ) gain( 4, "rage" ) end if legendary.cadence_of_fujieda.enabled then if buff.cadence_of_fujieda.stack < 5 then stat.haste = stat.haste + 0.01 end addStack( "cadence_of_fujieda", nil, 1 ) end if talent.fresh_meat.enabled and debuff.hit_by_fresh_meat.down then applyBuff( "enrage" ) applyDebuff( "target", "hit_by_fresh_meat" ) end end, }, bloodrage = { id = 329038, cast = 0, cooldown = 20, gcd = "off", spend = function() return health.max * (0.05) end, spendType = "health", pvptalent = "bloodrage", startsCombat = false, texture = 132277, handler = function () applyBuff ( "bloodrage" ) end, }, bloodthirst = { id = 23881, cast = 0, cooldown = function () if talent.deft_experience.enabled then return 4.5 - talent.deft_experience.rank * 0.75 else return 4.5 end end, hasteCD = true, gcd = "spell", spend = -8, spendType = "rage", cycle = function () return talent.fresh_meat.enabled and "hit_by_fresh_meat" or nil end, talent = "bloodthirst", startsCombat = true, texture = 136012, bind = "bloodbath", readyTime = function() if buff.crushing_impact.up then return buff.crushing_impact.remains end if talent.reckless_abandon.enabled then return buff.recklessness.remains end return 0 end, handler = function () gain( health.max * ( buff.enraged_regeneration.up and 0.23 or 0.03 ) , "health" ) if talent.bloodcraze.enabled then addStack( "bloodcraze", nil, 1 ) end removeStack( "whirlwind" ) if talent.cold_steel_hot_blood.enabled and stat.crit >= 100 then applyDebuff( "target", "gushing_wound" ) gain( 4, "rage" ) end if talent.invigorating_fury.enabled then gain ( health.max * 0.2 , "health" ) end if legendary.cadence_of_fujieda.enabled then if buff.cadence_of_fujieda.stack < 5 then stat.haste = stat.haste + 0.01 end addStack( "cadence_of_fujieda", nil, 1 ) end if talent.fresh_meat.enabled and debuff.hit_by_fresh_meat.down then applyBuff( "enrage" ) applyDebuff( "target", "hit_by_fresh_meat" ) end end, auras = { cadence_of_fujieda = { id = 335558, duration = 12, max_stack = 5, }, hit_by_fresh_meat = { duration = 3600, max_stack = 1, } }, }, charge = { id = 100, cast = 0, charges = function () return talent.double_time.enabled and 2 or nil end, cooldown = function () return talent.double_time.enabled and 17 or 20 end, recharge = function () return talent.double_time.enabled and 17 or 20 end, gcd = "off", spend = -20, spentType = "rage", startsCombat = true, texture = 132337, usable = function () return target.distance > 10 and ( query_time - action.charge.lastCast > gcd.execute ) end, handler = function () applyDebuff( "target", "charge" ) setDistance( 5 ) end, }, crushing_blow = { id = 335097, known = 85288, cast = 0, charges = function () return ( talent.raging_blow.enabled and 1 or 0 ) + ( set_bonus.tier28_2pc > 0 and 1 or 0 ) + ( talent.improved_raging_blow and 1 or 0 ) + ( talent.raging_armaments and 1 or 0 ) end, cooldown = 8, recharge = 8, hasteCD = true, gcd = "spell", spend = function () return -12 - talent.swift_strikes.rank end, spendType = "rage", startsCombat = true, texture = 132215, notalent = "annihilator", bind = "raging_blow", buff = "recklessness", usable = function () return buff.crushing_impact.up or ( talent.reckless_abandon.enabled and buff.recklessness.up ) end, handler = function () removeStack( "whirlwind" ) if talent.reckless_abandon.enabled then spendCharges( "raging_blow", 1 ) end if buff.will_of_the_berserker.up then buff.will_of_the_berserker.expires = query_time + 12 end end, }, death_wish = { id = 199261, cast = 0, cooldown = 5, gcd = "spell", spend = 6777, spendType = "health", pvptalent = "death_wish", startsCombat = false, texture = 136146, handler = function () addStack( "death_wish", nil, 1 ) end, }, defensive_stance = { id = 386208, cast = 0, cooldown = 3, gcd = "off", talent = "defensive_stance", startsCombat = false, texture = 132341, handler = function () applyBuff( "defensive_stance" ) removeBuff( "berserker_stance" ) end, }, disarm = { id = 236077, cast = 0, cooldown = 45, gcd = "spell", pvptalent = "disarm", startsCombat = false, texture = 132343, handler = function () applyDebuff( "target", "disarm") end, }, enraged_regeneration = { id = 184364, cast = 0, cooldown = function () return 120 - ( conduit.stalwart_guardian.enabled and 20 or 0 ) end, gcd = "off", toggle = "defensives", talent = "enraged_regeneration", startsCombat = false, texture = 132345, handler = function () applyBuff( "enraged_regeneration" ) end, }, execute = { id = function () return IsActiveSpell( 280735 ) and 280735 or 5308 end, known = 5308, copy = { 280735, 5308 }, noOverride = 317485, cast = 0, cooldown = function () return ( talent.massacre.enabled and 4.5 or 6 ) end, hasteCD = true, gcd = "spell", spend = function () return ( talent.improved_execute.enabled and -20 or 0 ) end, spendType = "rage", startsCombat = true, texture = 135358, usable = function () if buff.sudden_death.up then return true end if cycle_for_execute then return true end return target.health_pct < (talent.massacre.enabled and 35 or 20), "requires target in execute range" end, cycle = "execute_ineligible", indicator = function () if cycle_for_execute then return "cycle" end end, timeToReady = function() -- Instead of using regular resource requirements, we'll use timeToReady to support the spend system. if talent.improved_execute.enabled then return 0 -- We gain rage when using excute with this talent elseif rage.current >= 20 then return 0 else return rage.time_to_20 end end, handler = function () if not buff.sudden_death.up and not buff.stone_heart.up and not talent.improved_execute.enabled then -- Execute costs rage local cost = min( rage.current, 40 ) spend( cost, "rage", nil, true ) else removeBuff( "sudden_death" ) end removeStack( "whirlwind" ) if talent.ashen_juggernaut.enabled then applyBuff( "ashen_juggernaut" ) end end, auras = { -- Target Swapping execute_ineligible = { duration = 3600, max_stack = 1, generate = function( t, auraType ) if buff.sudden_death.down and buff.stone_heart.down and target.health_pct > ( talent.massacre.enabled and 35 or 20 ) then t.count = 1 t.expires = query_time + 3600 t.applied = query_time t.duration = 3600 t.caster = "player" return end t.count = 0 t.expires = 0 t.applied = 0 t.caster = "nobody" end } } }, hamstring = { id = 1715, cast = 0, cooldown = 0, gcd = "spell", spend = 10, spendType = "rage", startsCombat = true, texture = 132316, handler = function () applyDebuff ( "target", "hamstring" ) end, }, heroic_leap = { id = 6544, cast = 0, cooldown = function () return talent.bounding_stride.enabled and 30 or 45 end, charges = function () return legendary.leaper.enabled and 3 or nil end, recharge = function () return legendary.leaper.enabled and ( talent.bounding_stride.enabled and 30 or 45 ) or nil end, gcd = "off", talent = "heroic_leap", startsCombat = false, texture = 236171, usable = function () return ( query_time - action.heroic_leap.lastCast > gcd.execute ) end, handler = function () setDistance( 15 ) -- probably heroic_leap + charge combo. if talent.bounding_stride.enabled then applyBuff( "bounding_stride" ) end end, copy = 52174 }, heroic_throw = { id = 57755, cast = 0, cooldown = 1, gcd = "spell", startsCombat = true, texture = 132453, handler = function () end, }, impending_victory = { id = 202168, cast = 0, cooldown = 25, gcd = "spell", spend = 10, spendType = "rage", startsCombat = true, texture = 589768, talent = "impending_victory", handler = function () gain( health.max * 0.3, "health" ) removeStack( "whirlwind" ) if conduit.indelible_victory.enabled then applyBuff( "indelible_victory" ) end end, }, intervene = { id = 3411, cast = 0, cooldown = 30, gcd = "off", talent = "intervene", startsCombat = false, texture = 132365, handler = function () end, }, intimidating_shout = { id = function () return talent.menace.enabled and 316593 or 5246 end, copy = { 316593, 5246 }, cast = 0, cooldown = 90, gcd = "spell", talent = "intimidating_shout", startsCombat = true, texture = 132154, toggle = "cooldowns", handler = function () applyDebuff( "target", "intimidating_shout" ) active_dot.intimidating_shout = max( active_dot.intimidating_shout, active_enemies ) end, }, odyns_fury = { id = 385059, cast = 0, cooldown = 45, gcd = "spell", talent = "odyns_fury", startsCombat = false, texture = 1278409, handler = function () applyDebuff( "target", "odyns_fury" ) active_dot.odyns_fury = max( active_dot.odyns_fury, active_enemies ) if talent.dancing_blades.enabled then applyBuff( "dancing_blades" ) end if talent.titanic_rage.enabled then applyBuff( "enrage" ) applyBuff( "crushing_impact" ) end if talent.titans_torment.enabled then applyBuff( "avatar", 4 ) end end, }, onslaught = { id = 315720, cast = 0, cooldown = 18, hasteCD = true, gcd = "spell", spend = -20, spendType = "rage", talent = "onslaught", startsCombat = true, texture = 132364, handler = function () removeStack( "whirlwind" ) applyBuff( "enrage" , talent.tenderize.enabled and 6 or 5 ) -- Tenderize increases enrage by 1 second only when using onslaught, weirdly. end, }, piercing_howl = { id = 12323, cast = 0, cooldown = function () return 30 - ( conduit.disturb_the_peace.enabled and 5 or 0 ) end, gcd = "spell", talent = "piercing_howl", startsCombat = true, texture = 136147, handler = function () applyDebuff( "target", "piercing_howl" ) active_dot.piercing_howl = max( active_dot.piercing_howl, active_enemies ) end, }, pummel = { id = 6552, cast = 0, cooldown = function () return 15 - (talent.concussive_blows.enabled and 1 or 0) - (talent.honed_reflexes.enabled and 1 or 0) end, gcd = "off", startsCombat = true, texture = 132938, toggle = "interrupts", debuff = "casting", readyTime = state.timeToInterrupt, handler = function () interrupt() if talent.concussive_blows.enabled then applyDebuff( "target", "concussive_blows" ) end end, }, raging_blow = { id = 85288, cast = 0, charges = function () return ( talent.raging_blow.enabled and 1 or 0 ) + ( set_bonus.tier28_2pc > 0 and 1 or 0 ) + ( talent.improved_raging_blow and 1 or 0 ) + ( talent.raging_armaments and 1 or 0 ) end, cooldown = 8 * state.haste, recharge = 8 * state.haste, hasteCD = true, gcd = "spell", spend = function () if talent.swift_strikes.rank > 0 then return -12 - talent.swift_strikes.rank * 1 else return -12 end end, spendType = "rage", talent = "raging_blow", notalent = "annihilator", startsCombat = true, texture = 589119, bind = "crushing_blow", readyTime = function () if talent.reckless_abandon.enabled or talent.titanic_rage.enabled then return max( buff.recklessness.remains, buff.crushing_impact.remains ) else return 0 end end, handler = function () removeStack( "whirlwind" ) if talent.reckless_abandon.enabled then spendCharges( "crushing_blow", 1 ) end if buff.will_of_the_berserker.up then buff.will_of_the_berserker.expires = query_time + 12 end if talent.slaughtering_strikes.enabled then addStack ( "slaughtering_strikes_raging_blow" ,nil , 1 ) end end, }, rallying_cry = { id = 97462, cast = 0, cooldown = 180, gcd = "spell", talent = "rallying_cry", startsCombat = false, texture = 132351, toggle = "cooldowns", shared = "player", handler = function () applyBuff( "rallying_cry" ) gain( (talent.inspiring_presence.enabled and 0.25 or 0.15) * health.max, "health" ) end, }, rampage = { id = 184367, cast = 0, cooldown = 0, gcd = "spell", spend = 80, spendType = "rage", startsCombat = true, texture = 132352, talent = "rampage", handler = function () if talent.frenzy.enabled then addStack( "frenzy", nil, 1 ) end applyBuff( "enrage" ) removeStack( "whirlwind" ) end, }, ravager = { id = 228920, cast = 0, charges = function () return (talent.storm_of_steel.enabled and 2 or 1) end, cooldown = 90, recharge = 90, gcd = "spell", talent = "ravager", startsCombat = true, texture = 970854, toggle = "cooldowns", handler = function () applyBuff( "ravager" ) end, }, recklessness = { id = 1719, cast = 0, cooldown = 90, gcd = "off", toggle = "cooldowns", talent = "recklessness", startsCombat = false, texture = 458972, handler = function () applyBuff( "recklessness" ) if talent.reckless_abandon.enabled then gain( 50, "rage" ) end if talent.berserkers_torment.enabled then applyBuff( "avatar", 4 ) end if legendary.will_of_the_berserker.enabled then state:QueueAuraExpiration( "recklessness", WillOfTheBerserker, buff.recklessness.expires ) end end, auras = { will_of_the_berserker = { -- Shadowlands Legendary id = 335597, duration = 12, max_stack = 1 } } }, shattering_throw = { id = 64382, cast = 1.5, cooldown = 180, gcd = "spell", talent = "shattering_throw", startsCombat = true, texture = 311430, range = 30, toggle = "cooldowns", }, shockwave = { id = 46968, cast = 0, cooldown = function () return ( ( talent.rumbling_earth.enabled and active_enemies >= 3 ) and 25 or 40 ) end, gcd = "spell", spend = -10, spendType = "rage", talent = "shockwave", startsCombat = true, texture = 236312, toggle = "interrupts", debuff = function () return settings.shockwave_interrupt and "casting" or nil end, readyTime = function () return settings.shockwave_interrupt and timeToInterrupt() or nil end, usable = function () return not target.is_boss end, handler = function () applyDebuff( "target", "shockwave" ) active_dot.shockwave = max( active_dot.shockwave, active_enemies ) if not target.is_boss then interrupt() end end, }, slam = { id = 1464, cast = 0, cooldown = function () return talent.storm_of_swords.enabled and 12 or 0 end, gcd = "spell", spend = 20, spendType = "rage", startsCombat = true, texture = 132340, handler = function () removeStack( "whirlwind" ) end, }, spear_of_bastion = { id = 376079, cast = 0, cooldown = 90, gcd = "spell", spend = function () return (-25 * ( talent.piercing_verdict.enabled and 2 or 1 ) ) end, spendType = "rage", talent = "spear_of_bastion", startsCombat = false, texture = 3565453, toggle = "cooldowns", handler = function () applyDebuff ("target", "spear_of_bastion" ) end, }, spell_reflection = { id = 23920, cast = 0, cooldown = 25, gcd = "off", toggle = "interrupts", talent = "spell_reflection", startsCombat = false, texture = 132361, debuff = "casting", readyTime = state.timeToInterrupt, handler = function () applyBuff( "spell_reflection" ) end, }, storm_bolt = { id = 107570, cast = 0, cooldown = 30, gcd = "spell", startsCombat = true, texture = 613535, talent = "storm_bolt", handler = function () applyDebuff( "target", "storm_bolt" ) end, }, taunt = { id = 355, cast = 0, cooldown = 8, gcd = "off", startsCombat = true, texture = 136080, handler = function () applyDebuff( "target", "taunt" ) end, }, thunder_clap = { id = 6343, cast = 0, cooldown = 6, gcd = "spell", hasteCD = true, spend = function() return 30 + ( talent.blood_and_thunder.enabled and 10 or 0 ) end, spendType = "rage", talent = "thunder_clap", startsCombat = true, texture = 136105, handler = function () applyDebuff( "target", "thunder_clap" ) active_dot.thunder_clap = max( active_dot.thunder_clap, active_enemies ) end, }, thunderous_roar = { id = 384318, cast = 0, cooldown = function() return 90 - (talent.uproar.enabled and 30 or 0 ) end, gcd = "spell", spend = -10, spendType = "rage", talent = "thunderous_roar", startsCombat = true, texture = 642418, toggle = "cooldowns", handler = function () applyDebuff ("target", "thunderous_roar" ) active_dot.thunderous_roar = max( active_dot.thunderous_roar, active_enemies ) end, }, titanic_throw = { id = 384090, cast = 0, cooldown = 6, gcd = "spell", talent = "titanic_throw", startsCombat = true, texture = 132453, handler = function () end, }, victory_rush = { id = 34428, cast = 0, cooldown = 0, gcd = "spell", startsCombat = true, texture = 132342, notalent = "impending_victory", buff = "victorious", handler = function () removeBuff( "victorious" ) removeStack( "whirlwind" ) gain( 0.2 * health.max, "health" ) if conduit.indelible_victory.enabled then applyBuff( "indelible_victory" ) end end, }, whirlwind = { id = 190411, cast = 0, cooldown = function () return ( talent.storm_of_swords.enabled and 7 * haste or 0 ) end, gcd = "spell", startsCombat = true, -- TODO: Find a way to calculate the extra 1 rage per extra target hit? spend = function() return talent.improved_whirlwind.enabled and ( -3 - min( 5, active_enemies ) ) or 0 end, spendType = "rage", texture = 132369, usable = function () if settings.check_ww_range and target.outside7 then return false, "target is outside of whirlwind range" end return true end, handler = function () if talent.improved_whirlwind.enabled then applyBuff ( "whirlwind", nil, talent.meat_cleaver.enabled and 4 or 2 ) end end, }, wrecking_throw = { id = 384110, cast = 0, cooldown = function () return (pvptalent.demolition.enabled and 45 * 0.5 or 45) end, gcd = "spell", talent = "wrecking_throw", startsCombat = false, texture = 460959, handler = function () end, }, } ) spec:RegisterSetting( "check_ww_range", false, { name = "Check |T132369:0|t Whirlwind Range", desc = "If checked, when your target is outside of |T132369:0|t Whirlwind's range, it will not be recommended.", type = "toggle", width = "full" } ) spec:RegisterSetting( "shockwave_interrupt", true, { name = "Only |T236312:0|t Shockwave as Interrupt (when Talented)", desc = "If checked, |T236312:0|t Shockwave will only be recommended when your target is casting.", type = "toggle", width = "full" } ) spec:RegisterSetting( "heroic_charge", false, { name = "Use Heroic Charge Combo", desc = "If checked, the default priority will check |cFFFFD100settings.heroic_charge|r to determine whether to use Heroic Leap + Charge together.\n\n" .. "This is generally a DPS increase but the erratic movement can be disruptive to smooth gameplay.", type = "toggle", width = "full", } ) spec:RegisterPack( "Fury", 20221028, [[Hekili:fNvFVTnVr8pl(FCtW6CSCIBt7smW2d2gAXd6aM7WgWWJKOLOT4IeLhjvs9dm0N9Dh1BKYIkoPDB)rlIPU349YV7ojFp)V6VoMOO(FzX8fl8MV42zZVDXh88xRoSN6VEpj6bYo4p4Km4))tfId4HhsZjXiVY8cre8GeLAV8JxD1oMkPyZSO8SRKSSIuIILZJeKTk83rx5VEtblv9jU)MHu8T38b)1KcvsUWF9Aw2pbsMfhtRiNkJ8x7VoLjvsTYz8DP0afrSJQGd(I(6q5KnP0y))G)6ibtrfmcQ1TBNjOrpKsLso8Vzf7ldpESmu)ekxa3sGGmcJlldVRmCxuCfbxugIpSmCvzON38YWPLHkskLRML)iv8ucnndmKaTeQvEz4LNY8Tv8o5fWU242kO8F9GLX5nBj4NIqNR)AbjBpgJuGVeDanNt)gnQa8WgEeGKRnjjkNhtZ49i5MXDJ1oR48N4TxY6BeHZzjmiQNl6Ulv35HJax2zlBsZZJvjmHuHgXYZYiqzmvBbY90006Cb5SNa5K(eJhxf2QStbHfhqFuBOXXYzmE1txAzhqcm4ue5fYarorG2Y7()LTKhFGld2IvDGz8ENMXKNnc03qrBOMfffVVSFTn9Zq)CzkPyxIoICRt1hLGxvz1nWmXChMzdb2Nq()WlL)irHmXucEZpRarxMPBNsV8UnevIwdUHqoBz1Ld7zvp2uNAxS5D93LgH4tMwmUlAhn5WTTV0027Cs9S(3zFd7c49O79wWowb2EuERjLT1o9P6dwuH4kO8ujI(cuHno2sks76q0W2(ISmAApjVWD4GLba53DFz48zlRYVYai8m0Zgd9Ki8OkGEdS5Qm7wC3bL7fwObdlYfl1vXdrxnYXn2ihjacglkiLs23cP3EVZRBqyEVDJ3wHJnlMQRXIisf4QbOCqCN4lhfSmkppfRnNrEKac1QJ21MzrpcLjIHq8QMczh5rAAq(wiCtd2IjSbeXgulDczeAAGYSYTRazTDjUbSgQzMHaPcjvGjKNdOLLyq3tNGsza0Rm4FveVdd1DauneaxmQ287xEyb8GzrsLGKgers7NVpc(ZWMNorSozqYO7OBGuHhOIUhA3RXWVq2PJicw0dYNb0QP3edQbKbaGLoxFSoAvTERon9GKr4bzO)RTDhGdgbfnCciOhoaAHBvXuLu2b99myPJyzx4AANQws6QjembyoiMr15)lM7YyCxp9mT8hRCREkeNKGakZBDBnkQIOwDm61XOE2Wn01l47Pv1js09OjJKL4EDG6l6llKbd9rey(9geHeoPTn1Gg2ZmJOXqZzuIkicqYH1fATPHMFeTPX4CO2QyhV(auTnVa0IGQFeGlDvT6vqfiS9Yx6(S7bpAEgmLG)O7IbwNOGt3MdCcii74uLgwOQsIghGiNYz0)DbB)(Xc7wGCBHNKO30SF)F4a4(lrQAw28DGlGi4OE8x)3)9)1V8PV8N)yzyz4xtGyllBFUqvgcwyz4BSUOVbSD0Wey(Vmhhiawxnpd2Kfoa62ZH5yNv(5FMXHh5bY8NY5G21p(n152)dqkQCJF3KHdNFXIVDzl)38k43ZG)3))E(jGF7p(x(5EcYB(pmj9k8PoK01)WK0Rio1xsLF(zsdRhI9fMa(kSm7R2YVxb8UxHamRb8(bKeQDNBZtHTnGIz0JjiqtUNOc48cjkwe)uHKvHNugIqDimQQHoEUowuWTOoogjog6xaW(0pcbYWFleymMlcJTFnHb6BViFllfJSWpYysP2yKf7nIZ7OCaLmceVGXFaBnG5dFsvXKgGnd3xpgna8Ec9aehqzZYbe2dOTfLweJBttzGnkASP)5FtsrjrZK)YBHlvclkXKAc)qNwRVU0VTpLfXuPDY1823O0FxzyUOrnFTseqK7xqHgBE0cdn)eln14gvlsvdPAVH(iErgmrToFpnxbHZpPlmWdU2oEHj(6oRrWQdlWgsAhEttc5S2ou)M7VYSBYBzBVFYlPPu5NhsKD9GWGEnf3xTAu7Vb6Q2kevkotXD3d7tE84jl(TAPjpgl0HmEXyRmUAXYPdVQ4QBwEPPuRwf06eT1QTn379zYq96AihogN8URnPhQ3cGrbYElohX9U3tZKNUv0SoTDplu5dotNb19wMAyw07WyWu7cwMhAVpLLfzUHJBnm116tt7nUQPs1U0Q4YalG0JZPxm4IqhpoP3kqxoSoMmSsUyiN8XJNoG8DlMBj5(fBNoL)uxjpEhp64rRUzoCHSwoXHTmMPm5uBXK8(J13gv77RpXRyVkX54LAhlhvIJfew5nDYqd3F84PReC3IHP1clQ3q(vLLwd9AaMnZ6b3x)knDv(v7dS)6k3Tlk(4XlWJw55nFAT7)KpgYLneD78PtgHiTkS)gj35nBPdtgUX1Fxe3eu)vrCtGXRkTF6aMMcg(a53tgYdDPBL077qmuENZuKHsgw5TCeT19Lg(VTIA(KcoQ96bKHLnvirnFLc3I241pR7gv9vfw5nsK28frFM80(QWho0pcAYZLh9syTUYZnb4hg4Lk0E2ZyiJUDlNv85mdjUjR7fM4Me7Vka(Ia8)pp]] )