You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2218 lines
99 KiB
2218 lines
99 KiB
-- MageArcane.lua
|
|
-- June 2018
|
|
|
|
local addon, ns = ...
|
|
local Hekili = _G[ addon ]
|
|
|
|
local class = Hekili.Class
|
|
local state = Hekili.State
|
|
|
|
local PTR = ns.PTR
|
|
|
|
local GetItemCooldown = _G.GetItemCooldown
|
|
|
|
|
|
-- Conduits
|
|
-- [x] arcane_prodigy
|
|
-- [-] artifice_of_the_archmage
|
|
-- [-] magis_brand
|
|
-- [x] nether_precision
|
|
|
|
-- Covenant
|
|
-- [-] ire_of_the_ascended
|
|
-- [x] siphoned_malice
|
|
-- [x] gift_of_the_lich
|
|
-- [x] discipline_of_the_grove
|
|
|
|
-- Endurance
|
|
-- [-] cryofreeze
|
|
-- [-] diverted_energy
|
|
-- [x] tempest_barrier
|
|
|
|
-- Finesse
|
|
-- [x] flow_of_time
|
|
-- [x] incantation_of_swiftness
|
|
-- [x] winters_protection
|
|
-- [x] grounding_surge
|
|
|
|
|
|
if UnitClassBase( 'player' ) == 'MAGE' then
|
|
local spec = Hekili:NewSpecialization( 62, true )
|
|
|
|
spec:RegisterResource( Enum.PowerType.ArcaneCharges, {
|
|
arcane_orb = {
|
|
aura = "arcane_orb",
|
|
|
|
last = function ()
|
|
local app = state.buff.arcane_orb.applied
|
|
local t = state.query_time
|
|
|
|
return app + floor( ( t - app ) * 2 ) * 0.5
|
|
end,
|
|
|
|
interval = 0.5,
|
|
value = function () return state.active_enemies end,
|
|
},
|
|
} )
|
|
|
|
spec:RegisterResource( Enum.PowerType.Mana ) --[[, {
|
|
evocation = {
|
|
aura = "evocation",
|
|
|
|
last = function ()
|
|
local app = state.buff.evocation.applied
|
|
local t = state.query_time
|
|
|
|
return app + floor( t - app )
|
|
end,
|
|
|
|
interval = 0.1,
|
|
value = function () return state.mana.regen * 0.1 end,
|
|
}
|
|
} ) ]]
|
|
|
|
-- Talents
|
|
spec:RegisterTalents( {
|
|
amplification = 22458, -- 236628
|
|
rule_of_threes = 22461, -- 264354
|
|
arcane_familiar = 22464, -- 205022
|
|
|
|
master_of_time = 23072, -- 342249
|
|
shimmer = 22443, -- 212653
|
|
slipstream = 16025, -- 236457
|
|
|
|
incanters_flow = 22444, -- 1463
|
|
focus_magic = 22445, -- 321358
|
|
rune_of_power = 22447, -- 116011
|
|
|
|
resonance = 22453, -- 205028
|
|
arcane_echo = 22467, -- 342231
|
|
nether_tempest = 22470, -- 114923
|
|
|
|
chrono_shift = 22907, -- 235711
|
|
ice_ward = 22448, -- 205036
|
|
ring_of_frost = 22471, -- 113724
|
|
|
|
reverberate = 22455, -- 281482
|
|
arcane_orb = 22449, -- 153626
|
|
supernova = 22474, -- 157980
|
|
|
|
overpowered = 21630, -- 155147
|
|
time_anomaly = 21144, -- 210805
|
|
enlightened = 21145, -- 321387
|
|
} )
|
|
|
|
-- PvP Talents
|
|
spec:RegisterPvpTalents( {
|
|
arcane_empowerment = 61, -- 276741
|
|
arcanosphere = 5397, -- 353128
|
|
kleptomania = 3529, -- 198100
|
|
mass_invisibility = 637, -- 198158
|
|
master_of_escape = 635, -- 210476
|
|
netherwind_armor = 3442, -- 198062
|
|
prismatic_cloak = 3531, -- 198064
|
|
temporal_shield = 3517, -- 198111
|
|
torment_the_weak = 62, -- 198151
|
|
} )
|
|
|
|
-- Auras
|
|
spec:RegisterAuras( {
|
|
alter_time = {
|
|
id = 342246,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
},
|
|
arcane_charge = {
|
|
duration = 3600,
|
|
max_stack = 4,
|
|
generate = function ()
|
|
local ac = buff.arcane_charge
|
|
|
|
if arcane_charges.current > 0 then
|
|
ac.count = arcane_charges.current
|
|
ac.applied = query_time
|
|
ac.expires = query_time + 3600
|
|
ac.caster = "player"
|
|
return
|
|
end
|
|
|
|
ac.count = 0
|
|
ac.applied = 0
|
|
ac.expires = 0
|
|
ac.caster = "nobody"
|
|
end,
|
|
},
|
|
arcane_familiar = {
|
|
id = 210126,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
},
|
|
arcane_intellect = {
|
|
id = 1459,
|
|
duration = 3600,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
shared = "player", -- use anyone's buff on the player, not just player's.
|
|
},
|
|
arcane_orb = {
|
|
duration = 2.5,
|
|
max_stack = 1,
|
|
--[[ generate = function ()
|
|
local last = action.arcane_orb.lastCast
|
|
local ao = buff.arcane_orb
|
|
|
|
if query_time - last < 2.5 then
|
|
ao.count = 1
|
|
ao.applied = last
|
|
ao.expires = last + 2.5
|
|
ao.caster = "player"
|
|
return
|
|
end
|
|
|
|
ao.count = 0
|
|
ao.applied = 0
|
|
ao.expires = 0
|
|
ao.caster = "nobody"
|
|
end, ]]
|
|
},
|
|
arcane_power = {
|
|
id = 12042,
|
|
duration = function () return level > 55 and 15 or 10 end,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
blink = {
|
|
id = 1953,
|
|
},
|
|
chilled = {
|
|
id = 205708,
|
|
duration = 8,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
chrono_shift_buff = {
|
|
id = 236298,
|
|
duration = 5,
|
|
max_stack = 1,
|
|
},
|
|
chrono_shift = {
|
|
id = 236299,
|
|
duration = 5,
|
|
max_stack = 1,
|
|
},
|
|
clearcasting = {
|
|
id = function () return pvptalent.arcane_empowerment.enabled and 276743 or 263725 end,
|
|
duration = 15,
|
|
type = "Magic",
|
|
max_stack = function ()
|
|
return 1 + ( level > 31 and 2 or 0 ) + ( pvptalent.arcane_empowerment.enabled and 2 or 0 )
|
|
end,
|
|
copy = { 263725, 276743 }
|
|
},
|
|
enlightened = {
|
|
id = 321390,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
},
|
|
evocation = {
|
|
id = 12051,
|
|
duration = function () return 6 * haste end,
|
|
tick_time = function () return haste end,
|
|
max_stack = 1,
|
|
},
|
|
focus_magic = {
|
|
id = 321358,
|
|
duration = 1800,
|
|
max_stack = 1,
|
|
friendly = true,
|
|
},
|
|
focus_magic_buff = {
|
|
id = 321363,
|
|
duration = 10,
|
|
max_stack = 1,
|
|
},
|
|
frost_nova = {
|
|
id = 122,
|
|
duration = 8,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
greater_invisibility = {
|
|
id = 110960,
|
|
duration = 20,
|
|
max_stack = 1,
|
|
},
|
|
hypothermia = {
|
|
id = 41425,
|
|
duration = 30,
|
|
max_stack = 1,
|
|
},
|
|
ice_block = {
|
|
id = 45438,
|
|
duration = 10,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
incanters_flow = {
|
|
id = 116267,
|
|
duration = 3600,
|
|
max_stack = 5,
|
|
meta = {
|
|
stack = function() return state.incanters_flow_stacks end,
|
|
stacks = function() return state.incanters_flow_stacks end,
|
|
}
|
|
},
|
|
mirror_image = {
|
|
id = 55342,
|
|
duration = 40,
|
|
max_stack = 3,
|
|
generate = function ()
|
|
local mi = buff.mirror_image
|
|
|
|
if action.mirror_image.lastCast > 0 and query_time < action.mirror_image.lastCast + 40 then
|
|
mi.count = 1
|
|
mi.applied = action.mirror_image.lastCast
|
|
mi.expires = mi.applied + 40
|
|
mi.caster = "player"
|
|
return
|
|
end
|
|
|
|
mi.count = 0
|
|
mi.applied = 0
|
|
mi.expires = 0
|
|
mi.caster = "nobody"
|
|
end,
|
|
},
|
|
mirrors_of_torment = {
|
|
id = 314793,
|
|
duration = 20,
|
|
type = "Magic",
|
|
max_stack = 3,
|
|
},
|
|
nether_tempest = {
|
|
id = 114923,
|
|
duration = 12,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
presence_of_mind = {
|
|
id = 205025,
|
|
duration = 3600,
|
|
max_stack = function () return level > 53 and 3 or 2 end,
|
|
},
|
|
prismatic_barrier = {
|
|
id = 235450,
|
|
duration = 60,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
radiant_spark = {
|
|
id = 307443,
|
|
duration = 8,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
radiant_spark_vulnerability = {
|
|
id = 307454,
|
|
duration = 3.707,
|
|
max_stack = 4,
|
|
},
|
|
ring_of_frost = {
|
|
id = 82691,
|
|
duration = 10,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
rule_of_threes = {
|
|
id = 264774,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
},
|
|
rune_of_power = {
|
|
id = 116014,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
},
|
|
shimmer = {
|
|
id = 212653,
|
|
},
|
|
slow = {
|
|
id = 31589,
|
|
duration = 15,
|
|
type = "Magic",
|
|
max_stack = 1,
|
|
},
|
|
slow_fall = {
|
|
id = 130,
|
|
duration = 30,
|
|
max_stack = 1,
|
|
},
|
|
temporal_displacement = {
|
|
id = 80354,
|
|
duration = 600,
|
|
max_stack = 1,
|
|
},
|
|
touch_of_the_magi = {
|
|
id = 210824,
|
|
duration = function () return set_bonus.tier28_4pc > 0 and 12 or 8 end,
|
|
max_stack = 1,
|
|
},
|
|
|
|
|
|
-- Azerite Powers
|
|
brain_storm = {
|
|
id = 273330,
|
|
duration = 30,
|
|
max_stack = 1,
|
|
},
|
|
|
|
equipoise = {
|
|
id = 264352,
|
|
duration = 3600,
|
|
max_stack = 1,
|
|
},
|
|
|
|
|
|
-- Conduits
|
|
nether_precision = {
|
|
id = 336889,
|
|
duration = 10,
|
|
max_stack = 2
|
|
},
|
|
|
|
|
|
-- Legendaries
|
|
grisly_icicle = {
|
|
id = 348007,
|
|
duration = 8,
|
|
max_stack = 1
|
|
}
|
|
} )
|
|
|
|
|
|
-- Variables from APL (11/13/2021)
|
|
-- actions.precombat+=/variable,name=aoe_target_count,op=set,value=3+(1*covenant.kyrian)
|
|
spec:RegisterVariable( "aoe_target_count", function ()
|
|
return 3
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=evo_pct,op=reset,default=15
|
|
spec:RegisterVariable( "evo_pct", function ()
|
|
return 15
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=prepull_evo,op=set,if=(runeforge.siphon_storm&(covenant.venthyr|covenant.necrolord|conduit.arcane_prodigy)),value=1,value_else=0
|
|
spec:RegisterVariable( "prepull_evo", function ()
|
|
if ( equipped.siphon_storm and ( covenant.venthyr or covenant.necrolord or conduit.arcane_prodigy.enabled ) ) then
|
|
return 1
|
|
else
|
|
return 0
|
|
end
|
|
end )
|
|
|
|
|
|
local opener_completed = false
|
|
|
|
spec:RegisterEvent( "PLAYER_REGEN_ENABLED", function ()
|
|
opener_completed = false
|
|
-- Hekili:Print( "Opener reset (out of combat).")
|
|
end )
|
|
|
|
|
|
-- actions.precombat+=/variable,name=have_opened,op=set,if=active_enemies>=variable.aoe_target_count,value=1,value_else=0
|
|
-- actions.calculations=variable,name=have_opened,op=set,value=1,if=variable.have_opened=0&prev_gcd.1.evocation&!(runeforge.siphon_storm|runeforge.temporal_warp)
|
|
-- actions.calculations+=/variable,name=have_opened,op=set,value=1,if=variable.have_opened=0&buff.arcane_power.down&cooldown.arcane_power.remains&(runeforge.siphon_storm|runeforge.temporal_warp)
|
|
-- TODO: This needs to be updated so that have_opened stays at 1 once it has been set to 1.
|
|
spec:RegisterVariable( "have_opened", function ()
|
|
return opener_completed
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=final_burn,op=set,value=0
|
|
-- actions.calculations+=/variable,name=final_burn,op=set,value=1,if=buff.arcane_charge.stack=buff.arcane_charge.max_stack&!buff.rule_of_threes.up&fight_remains<=((mana%action.arcane_blast.cost)*action.arcane_blast.execute_time)
|
|
spec:RegisterVariable( "final_burn", function ()
|
|
if buff.arcane_charge.stack == buff.arcane_charge.max_stack and not buff.rule_of_threes.up and fight_remains <= ( mana.current / action.arcane_blast.cost ) * action.arcane_blast.execute_time then
|
|
return 1
|
|
end
|
|
|
|
return 0
|
|
end )
|
|
|
|
|
|
-- actions.precombat+=/variable,name=harmony_stack_time,op=reset,default=9
|
|
spec:RegisterVariable( "harmony_stack_time", function ()
|
|
return 9
|
|
end )
|
|
|
|
-- + actions.precombat+=/variable,name=always_sync_cooldowns,op=reset,default=1
|
|
spec:RegisterVariable( "always_sync_cooldowns", function ()
|
|
return 1
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=rs_max_delay_for_totm,op=reset,default=5
|
|
spec:RegisterVariable( "rs_max_delay_for_totm", function ()
|
|
return 5
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=rs_max_delay_for_rop,op=reset,default=5
|
|
spec:RegisterVariable( "rs_max_delay_for_rop", function ()
|
|
return 5
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=rs_max_delay_for_ap,op=reset,default=20
|
|
spec:RegisterVariable( "rs_max_delay_for_ap", function ()
|
|
return 20
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=mot_preceed_totm_by,op=reset,default=8
|
|
spec:RegisterVariable( "mot_preceed_totm_by", function ()
|
|
return 8
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=mot_max_delay_for_totm,op=reset,default=10
|
|
spec:RegisterVariable( "mot_max_delay_for_totm", function ()
|
|
return 10
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=mot_max_delay_for_ap,op=reset,default=15
|
|
spec:RegisterVariable( "mot_max_delay_for_ap", function ()
|
|
return 15
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=ap_max_delay_for_totm,default=-1,op=set,if=variable.ap_max_delay_for_totm=-1,value=10+(20*conduit.arcane_prodigy)
|
|
spec:RegisterVariable( "ap_max_delay_for_totm", function ()
|
|
if conduit.arcane_prodigy.enabled then
|
|
return 30
|
|
end
|
|
|
|
return 10
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=ap_max_delay_for_mot,op=reset,default=20
|
|
spec:RegisterVariable( "ap_max_delay_for_mot", function ()
|
|
return 20
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=rop_max_delay_for_totm,op=set,value=20-(5*conduit.arcane_prodigy)
|
|
spec:RegisterVariable( "rop_max_delay_for_totm", function ()
|
|
if conduit.arcane_prodigy.enabled then
|
|
return 15
|
|
end
|
|
|
|
return 20
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=totm_max_delay_for_ap,op=set,value=5+20*(covenant.night_fae|(conduit.arcane_prodigy&active_enemies<variable.aoe_target_count))+15*(covenant.kyrian&runeforge.arcane_harmony&active_enemies>=variable.aoe_target_count)
|
|
spec:RegisterVariable( "totm_max_delay_for_ap", function ()
|
|
local value = 5
|
|
|
|
if ( covenant.night_fae or ( conduit.arcane_prodigy.enabled and active_enemies < variable.aoe_target_count ) ) then
|
|
value = value + 20
|
|
end
|
|
|
|
if ( covenant.kyrian and runeforge.arcane_harmony.enabled and active_enemies >= variable.aoe_target_count ) then
|
|
value = value + 15
|
|
end
|
|
|
|
return value
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=totm_max_delay_for_rop,op=set,value=20-(8*conduit.arcane_prodigy)
|
|
spec:RegisterVariable( "totm_max_delay_for_rop", function ()
|
|
if conduit.arcane_prodigy.enabled then
|
|
return 12
|
|
end
|
|
|
|
return 20
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=barrage_mana_pct,op=set,if=covenant.night_fae,value=60-(mastery_value*100)
|
|
-- actions.precombat+=/variable,name=barrage_mana_pct,op=set,if=covenant.kyrian,value=95-(mastery_value*100)
|
|
-- actions.precombat+=/variable,name=barrage_mana_pct,op=set,if=variable.barrage_mana_pct=0,value=80-(mastery_value*100)
|
|
spec:RegisterVariable( "barrage_mana_pct", function ()
|
|
if covenant.night_fae then return 60 - mastery_value * 100 end
|
|
if covenant.kyrian then return 95 - mastery_value * 100 end
|
|
return 80 - mastery_value * 100
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=ap_minimum_mana_pct,op=reset,default=15
|
|
spec:RegisterVariable( "ap_minimum_mana_pct", function ()
|
|
return 15
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=totm_max_charges,op=reset,default=2
|
|
spec:RegisterVariable( "totm_max_charges", function ()
|
|
return 2
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=aoe_totm_max_charges,op=reset,default=2
|
|
spec:RegisterVariable( "aoe_totm_max_charges", function ()
|
|
return 2
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=fishing_opener,default=-1,op=set,if=variable.fishing_opener=-1,value=1*(equipped.empyreal_ordnance|(talent.rune_of_power&(talent.arcane_echo|!covenant.kyrian)&(!covenant.necrolord|active_enemies=1|runeforge.siphon_storm)&!covenant.venthyr))|(covenant.venthyr&equipped.moonlit_prism)
|
|
spec:RegisterVariable( "fishing_opener", function ()
|
|
if ( equipped.empyreal_ordnance or ( talent.rune_of_power.enabled and ( talent.arcane_echo.enabled or not covenant.kyrian ) and ( not covenant.necrolord or active_enemies == 1 or runeforge.siphon_storm.enabled ) and not covenant.venthyr ) ) or ( covenant.venthyr and equipped.moonlit_prism ) then
|
|
return 1
|
|
end
|
|
|
|
return 0
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=ap_on_use,op=set,value=equipped.macabre_sheet_music|equipped.gladiators_badge|equipped.gladiators_medallion|equipped.darkmoon_deck_putrescence|equipped.inscrutable_quantum_device|equipped.soulletting_ruby|equipped.sunblood_amethyst|equipped.wakeners_frond|equipped.flame_of_battle
|
|
spec:RegisterVariable( "ap_on_use", function ()
|
|
return equipped.macabre_sheet_music or equipped.gladiators_badge or equipped.gladiators_medallion or equipped.darkmoon_deck_putrescence or equipped.inscrutable_quantum_device or equipped.soulletting_ruby or equipped.sunblood_amethyst or equipped.wakeners_frond or equipped.flame_of_battle
|
|
end )
|
|
|
|
-- actions.precombat+=/variable,name=aoe_spark_target_count,op=reset,default=8+(2*runeforge.harmonic_echo)
|
|
-- actions.precombat+=/variable,name=aoe_spark_target_count,op=max,value=variable.aoe_target_count
|
|
spec:RegisterVariable( "aoe_spark_target_count", function ()
|
|
return max( variable.aoe_target_count, 8 + ( runeforge.harmonic_echo.enabled and 2 or 0 ) )
|
|
end )
|
|
|
|
-- # Either a fully stacked harmony or in execute range with Bombardment
|
|
-- actions.calculations+=/variable,name=empowered_barrage,op=set,value=buff.arcane_harmony.stack>=15|(runeforge.arcane_bombardment&target.health.pct<35)
|
|
spec:RegisterVariable( "empowered_barrage", function ()
|
|
return buff.arcane_harmony.stack >= 15 or ( runeforge.arcane_bombardment.enabled and target.health.pct < 35 )
|
|
end )
|
|
|
|
-- ## actions.calculations+=/variable,name=last_ap_use,default=0,op=set,if=buff.arcane_power.up&(variable.last_ap_use=0|time>=variable.last_ap_use+15),value=time
|
|
-- ## Arcane Prodigy gives a variable amount of cdr, but we'll use a flat estimation here. The simc provided remains_expected expression does not work well for prodigy due to the bursty nature of the cdr.
|
|
-- ## actions.calculations+=/variable,name=estimated_ap_cooldown,op=set,value=(cooldown.arcane_power.duration*(1-(0.03*conduit.arcane_prodigy.rank)))-(time-variable.last_ap_use)
|
|
|
|
-- actions.calculations+=/variable,name=time_until_ap,op=set,if=conduit.arcane_prodigy,value=cooldown.arcane_power.remains_expected
|
|
-- actions.calculations+=/variable,name=time_until_ap,op=set,if=!conduit.arcane_prodigy,value=cooldown.arcane_power.remains
|
|
-- # We'll delay AP up to 20sec for TotM
|
|
-- actions.calculations+=/variable,name=time_until_ap,op=max,value=cooldown.touch_of_the_magi.remains,if=(cooldown.touch_of_the_magi.remains-variable.time_until_ap)<20
|
|
-- # Since Ruby is such a powerful trinket for Kyrian, we'll stick to the two minute cycle until we get a high enough rank of prodigy
|
|
-- actions.calculations+=/variable,name=time_until_ap,op=max,value=trinket.soulletting_ruby.cooldown.remains,if=conduit.arcane_prodigy&conduit.arcane_prodigy.rank<5&equipped.soulletting_ruby&covenant.kyrian&runeforge.arcane_harmony
|
|
spec:RegisterVariable( "time_until_ap", function ()
|
|
local value = 0
|
|
|
|
if conduit.arcane_prodigy.enabled then
|
|
value = cooldown.arcane_power.remains_expected
|
|
else
|
|
value = cooldown.arcane_power.remains
|
|
end
|
|
|
|
if ( cooldown.touch_of_the_magi.remains - value ) < 20 then
|
|
value = max( value, cooldown.touch_of_the_magi.remains )
|
|
end
|
|
|
|
if conduit.arcane_prodigy.enabled and conduit.arcane_prodigy.rank < 5 and equipped.soulletting_ruby and covenant.kyrian and runeforge.arcane_harmony.enabled then
|
|
value = max( value, trinket.soulletting_ruby.cooldown.remains )
|
|
end
|
|
|
|
return value
|
|
end )
|
|
|
|
-- # We'll delay TotM up to 20sec for AP
|
|
-- actions.calculations+=/variable,name=holding_totm,op=set,value=cooldown.touch_of_the_magi.ready&variable.time_until_ap<20
|
|
spec:RegisterVariable( "holding_totm", function ()
|
|
return cooldown.touch_of_the_magi.ready and variable.time_until_ap < 20
|
|
end )
|
|
|
|
-- # Radiant Spark does not immediately put up the vulnerability debuff so it can be difficult to discern that we're at the zeroth vulnerability stack
|
|
-- actions.calculations+=/variable,name=just_used_spark,op=set,value=(prev_gcd.1.radiant_spark|prev_gcd.2.radiant_spark|prev_gcd.3.radiant_spark)&action.radiant_spark.time_since<gcd.max*4
|
|
spec:RegisterVariable( "just_used_spark", function ()
|
|
return ( prev_gcd[1].radiant_spark or prev_gcd[2].radiant_spark or prev_gcd[3].radiant_spark ) and action.radiant_spark.time_since < gcd.max * 4
|
|
end )
|
|
|
|
-- ## Original SimC checked debuff.radiant_spark_vulnerability.down, but that doesn't work when the addon applies RSV instantly.
|
|
-- ## actions.calculations+=/variable,name=just_used_spark,op=set,value=(prev_gcd.1.radiant_spark|prev_gcd.2.radiant_spark|prev_gcd.3.radiant_spark)&debuff.radiant_spark_vulnerability.down
|
|
spec:RegisterVariable( "just_used_spark_vulnerability", function ()
|
|
return ( prev_gcd[1].radiant_spark or prev_gcd[2].radiant_spark or prev_gcd[3].radiant_spark ) and debuff.radiant_spark_vulnerability.down
|
|
end )
|
|
|
|
-- actions.calculations+=/variable,name=outside_of_cooldowns,op=set,value=buff.arcane_power.down&buff.rune_of_power.down&debuff.touch_of_the_magi.down&!variable.just_used_spark&debuff.radiant_spark_vulnerability.down
|
|
spec:RegisterVariable( "outside_of_cooldowns", function ()
|
|
return buff.arcane_power.down and buff.rune_of_power.down and debuff.touch_of_the_magi.down and not variable.just_used_spark and debuff.radiant_spark_vulnerability.down
|
|
end )
|
|
|
|
-- actions.calculations+=/variable,name=stack_harmony,op=set,value=runeforge.arcane_infinity&((covenant.kyrian&cooldown.touch_of_the_magi.remains<variable.harmony_stack_time))
|
|
spec:RegisterVariable( "stack_harmony", function ()
|
|
return runeforge.arcane_harmony.enabled and ( covenant.kyrian and cooldown.touch_of_the_magi.remains < variable.harmony_stack_time )
|
|
end )
|
|
|
|
|
|
do
|
|
-- Builds Disciplinary Command; written so that it can be ported to the other two Mage specs.
|
|
|
|
function Hekili:EmbedDisciplinaryCommand( x )
|
|
local file_id = x.id
|
|
|
|
x:RegisterAuras( {
|
|
disciplinary_command = {
|
|
id = 327371,
|
|
duration = 20,
|
|
},
|
|
|
|
disciplinary_command_arcane = {
|
|
duration = 10,
|
|
max_stack = 1,
|
|
},
|
|
|
|
disciplinary_command_frost = {
|
|
duration = 10,
|
|
max_stack = 1,
|
|
},
|
|
|
|
disciplinary_command_fire = {
|
|
duration = 10,
|
|
max_stack = 1,
|
|
}
|
|
} )
|
|
|
|
local __last_arcane, __last_fire, __last_frost, __last_disciplinary_command = 0, 0, 0, 0
|
|
local __last_arcSpell, __last_firSpell, __last_froSpell
|
|
|
|
x:RegisterHook( "reset_precast", function ()
|
|
if not legendary.disciplinary_command.enabled then return end
|
|
|
|
if now - __last_arcane < 10 then applyBuff( "disciplinary_command_arcane", 10 - ( now - __last_arcane ) ) end
|
|
if now - __last_fire < 10 then applyBuff( "disciplinary_command_fire", 10 - ( now - __last_fire ) ) end
|
|
if now - __last_frost < 10 then applyBuff( "disciplinary_command_frost", 10 - ( now - __last_frost ) ) end
|
|
|
|
if now - __last_disciplinary_command < 30 then
|
|
setCooldown( "buff_disciplinary_command", 30 - ( now - __last_disciplinary_command ) )
|
|
end
|
|
|
|
Hekili:Debug( "Disciplinary Command:\n - Arcane: %.2f, %s\n - Fire : %.2f, %s\n - Frost : %.2f, %s\n - ICD : %.2f", buff.disciplinary_command_arcane.remains, __last_arcSpell or "None", buff.disciplinary_command_fire.remains, __last_firSpell or "None", buff.disciplinary_command_frost.remains, __last_froSpell or "None", cooldown.buff_disciplinary_command.remains )
|
|
end )
|
|
|
|
x:RegisterStateFunction( "update_disciplinary_command", function( action )
|
|
local ability = class.abilities[ action ]
|
|
|
|
if not ability then return end
|
|
if ability.item or ability.from == 0 then return end
|
|
|
|
if ability.discipline == "arcane" then applyBuff( "disciplinary_command_arcane" )
|
|
elseif ability.discipline == "fire" then applyBuff( "disciplinary_command_fire" )
|
|
elseif ability.discipline == "frost" then applyBuff( "disciplinary_command_frost" )
|
|
else
|
|
local sAction = x.abilities[ action ]
|
|
local sDiscipline = sAction and sAction.discipline
|
|
|
|
if sDiscipline then
|
|
if sDiscipline == "arcane" then applyBuff( "disciplinary_command_arcane" )
|
|
elseif sDiscipline == "fire" then applyBuff( "disciplinary_command_fire" )
|
|
elseif sDiscipline == "frost" then applyBuff( "disciplinary_command_frost" ) end
|
|
else applyBuff( "disciplinary_command_" .. state.spec.key ) end
|
|
end
|
|
|
|
if buff.disciplinary_command_arcane.up and buff.disciplinary_command_fire.up and buff.disciplinary_command_frost.up then
|
|
applyBuff( "disciplinary_command" )
|
|
setCooldown( "buff_disciplinary_command", 30 )
|
|
removeBuff( "disciplinary_command_arcane" )
|
|
removeBuff( "disciplinary_command_fire" )
|
|
removeBuff( "disciplinary_command_frost" )
|
|
end
|
|
end )
|
|
|
|
x:RegisterHook( "runHandler", function( action )
|
|
if not legendary.disciplinary_command.enabled or cooldown.buff_disciplinary_command.remains > 0 then return end
|
|
update_disciplinary_command( action )
|
|
end )
|
|
|
|
local triggerEvents = {
|
|
SPELL_CAST_SUCCESS = true,
|
|
SPELL_HEAL = true,
|
|
SPELL_SUMMON= true
|
|
}
|
|
|
|
local spellChanges = {
|
|
[108853] = 319836,
|
|
[212653] = 1953,
|
|
[342130] = 116011,
|
|
[337137] = 1,
|
|
}
|
|
|
|
local spellSchools = {
|
|
[4] = "fire",
|
|
[16] = "frost",
|
|
[64] = "arcane"
|
|
}
|
|
|
|
x:RegisterHook( "COMBAT_LOG_EVENT_UNFILTERED", function( _, subtype, _, sourceGUID, _, _, _, _, _, _, _, spellID, spellName, spellSchool )
|
|
if sourceGUID == GUID then
|
|
if triggerEvents[ subtype ] then
|
|
spellID = spellChanges[ spellID ] or spellID
|
|
if not IsSpellKnown( spellID, false ) then return end
|
|
|
|
local school = spellSchools[ spellSchool ]
|
|
if not school then return end
|
|
|
|
if school == "arcane" then __last_arcane = GetTime(); __last_arcSpell = spellName
|
|
elseif school == "fire" then __last_fire = GetTime(); __last_firSpell = spellName
|
|
elseif school == "frost" then __last_frost = GetTime(); __last_froSpell = spellName end
|
|
return
|
|
elseif subtype == "SPELL_AURA_APPLIED" and spellID == class.auras.disciplinary_command.id then
|
|
__last_disciplinary_command = GetTime()
|
|
__last_arcane = 0
|
|
__last_fire = 0
|
|
__last_frost = 0
|
|
end
|
|
end
|
|
end, false )
|
|
|
|
x:RegisterAbility( "buff_disciplinary_command", {
|
|
cooldown_special = function ()
|
|
local remains = ( now + offset ) - __last_disciplinary_command
|
|
|
|
if remains < 30 then
|
|
return __last_disciplinary_command, 30
|
|
end
|
|
|
|
return 0, 0
|
|
end,
|
|
unlisted = true,
|
|
|
|
cast = 0,
|
|
cooldown = 30,
|
|
gcd = "off",
|
|
|
|
handler = function()
|
|
applyBuff( "disciplinary_command" )
|
|
end,
|
|
} )
|
|
end
|
|
|
|
Hekili:EmbedDisciplinaryCommand( spec )
|
|
end
|
|
|
|
|
|
spec:RegisterHook( "spend", function( amt, resource )
|
|
if resource == "arcane_charges" then
|
|
if arcane_charges.current == 0 then
|
|
removeBuff( "arcane_charge" )
|
|
else
|
|
applyBuff( "arcane_charge", nil, arcane_charges.current )
|
|
end
|
|
|
|
elseif resource == "mana" then
|
|
if azerite.equipoise.enabled and mana.percent < 70 then
|
|
removeBuff( "equipoise" )
|
|
end
|
|
end
|
|
end )
|
|
|
|
spec:RegisterHook( "gain", function( amt, resource )
|
|
if resource == "arcane_charges" then
|
|
if arcane_charges.current == 0 then
|
|
removeBuff( "arcane_charge" )
|
|
else
|
|
if talent.rule_of_threes.enabled and arcane_charges.current >= 3 and arcane_charges.current - amt < 3 then
|
|
applyBuff( "rule_of_threes" )
|
|
end
|
|
applyBuff( "arcane_charge", nil, arcane_charges.current )
|
|
end
|
|
end
|
|
end )
|
|
|
|
|
|
spec:RegisterStateTable( "burn_info", setmetatable( {
|
|
__start = 0,
|
|
start = 0,
|
|
__average = 20,
|
|
average = 20,
|
|
n = 1,
|
|
__n = 1,
|
|
}, {
|
|
__index = function( t, k )
|
|
if k == "active" then
|
|
return t.start > 0
|
|
end
|
|
end,
|
|
} ) )
|
|
|
|
|
|
spec:RegisterTotem( "rune_of_power", 609815 )
|
|
|
|
|
|
spec:RegisterStateTable( "incanters_flow", {
|
|
changed = 0,
|
|
count = 0,
|
|
direction = 0,
|
|
|
|
startCount = 0,
|
|
startTime = 0,
|
|
startIndex = 0,
|
|
|
|
values = {
|
|
[0] = { 0, 1 },
|
|
{ 1, 1 },
|
|
{ 2, 1 },
|
|
{ 3, 1 },
|
|
{ 4, 1 },
|
|
{ 5, 0 },
|
|
{ 5, -1 },
|
|
{ 4, -1 },
|
|
{ 3, -1 },
|
|
{ 2, -1 },
|
|
{ 1, 0 }
|
|
},
|
|
|
|
f = CreateFrame( "Frame" ),
|
|
fRegistered = false,
|
|
|
|
reset = setfenv( function ()
|
|
if talent.incanters_flow.enabled then
|
|
if not incanters_flow.fRegistered then
|
|
Hekili:ProfileFrame( "Incanters_Flow_Arcane", incanters_flow.f )
|
|
-- One-time setup.
|
|
incanters_flow.f:RegisterUnitEvent( "UNIT_AURA", "player" )
|
|
incanters_flow.f:SetScript( "OnEvent", function ()
|
|
-- Check to see if IF changed.
|
|
if state.talent.incanters_flow.enabled then
|
|
local flow = state.incanters_flow
|
|
local name, _, count = FindUnitBuffByID( "player", 116267, "PLAYER" )
|
|
local now = GetTime()
|
|
|
|
if name then
|
|
if count ~= flow.count then
|
|
if count == 1 then flow.direction = 0
|
|
elseif count == 5 then flow.direction = 0
|
|
else flow.direction = ( count > flow.count ) and 1 or -1 end
|
|
|
|
flow.changed = GetTime()
|
|
flow.count = count
|
|
end
|
|
else
|
|
flow.count = 0
|
|
flow.changed = GetTime()
|
|
flow.direction = 0
|
|
end
|
|
end
|
|
end )
|
|
|
|
incanters_flow.fRegistered = true
|
|
end
|
|
|
|
if now - incanters_flow.changed >= 1 then
|
|
if incanters_flow.count == 1 and incanters_flow.direction == 0 then
|
|
incanters_flow.direction = 1
|
|
incanters_flow.changed = incanters_flow.changed + 1
|
|
elseif incanters_flow.count == 5 and incanters_flow.direction == 0 then
|
|
incanters_flow.direction = -1
|
|
incanters_flow.changed = incanters_flow.changed + 1
|
|
end
|
|
end
|
|
|
|
if incanters_flow.count == 0 then
|
|
incanters_flow.startCount = 0
|
|
incanters_flow.startTime = incanters_flow.changed + floor( now - incanters_flow.changed )
|
|
incanters_flow.startIndex = 0
|
|
else
|
|
incanters_flow.startCount = incanters_flow.count
|
|
incanters_flow.startTime = incanters_flow.changed + floor( now - incanters_flow.changed )
|
|
incanters_flow.startIndex = 0
|
|
|
|
for i, val in ipairs( incanters_flow.values ) do
|
|
if val[1] == incanters_flow.count and val[2] == incanters_flow.direction then incanters_flow.startIndex = i; break end
|
|
end
|
|
end
|
|
else
|
|
incanters_flow.count = 0
|
|
incanters_flow.changed = 0
|
|
incanters_flow.direction = 0
|
|
end
|
|
end, state ),
|
|
} )
|
|
|
|
spec:RegisterStateExpr( "incanters_flow_stacks", function ()
|
|
if not talent.incanters_flow.enabled then return 0 end
|
|
|
|
local index = incanters_flow.startIndex + floor( query_time - incanters_flow.startTime )
|
|
if index > 10 then index = index % 10 end
|
|
|
|
return incanters_flow.values[ index ][ 1 ]
|
|
end )
|
|
|
|
spec:RegisterStateExpr( "incanters_flow_dir", function()
|
|
if not talent.incanters_flow.enabled then return 0 end
|
|
|
|
local index = incanters_flow.startIndex + floor( query_time - incanters_flow.startTime )
|
|
if index > 10 then index = index % 10 end
|
|
|
|
return incanters_flow.values[ index ][ 2 ]
|
|
end )
|
|
|
|
-- Seemingly, a very silly way to track Incanter's Flow...
|
|
local incanters_flow_time_obj = setmetatable( { __stack = 0 }, {
|
|
__index = function( t, k )
|
|
if not state.talent.incanters_flow.enabled then return 0 end
|
|
|
|
local stack = t.__stack
|
|
local ticks = #state.incanters_flow.values
|
|
|
|
local start = state.incanters_flow.startIndex + floor( state.offset + state.delay )
|
|
|
|
local low_pos, high_pos
|
|
|
|
if k == "up" then low_pos = 5
|
|
elseif k == "down" then high_pos = 6 end
|
|
|
|
local time_since = ( state.query_time - state.incanters_flow.changed ) % 1
|
|
|
|
for i = 0, 10 do
|
|
local index = ( start + i )
|
|
if index > 10 then index = index % 10 end
|
|
|
|
local values = state.incanters_flow.values[ index ]
|
|
|
|
if values[ 1 ] == stack and ( not low_pos or index <= low_pos ) and ( not high_pos or index >= high_pos ) then
|
|
return max( 0, i - time_since )
|
|
end
|
|
end
|
|
|
|
return 0
|
|
end
|
|
} )
|
|
|
|
spec:RegisterStateTable( "incanters_flow_time_to", setmetatable( {}, {
|
|
__index = function( t, k )
|
|
incanters_flow_time_obj.__stack = tonumber( k ) or 0
|
|
return incanters_flow_time_obj
|
|
end
|
|
} ) )
|
|
|
|
|
|
spec:RegisterStateExpr( "fake_mana_gem", function ()
|
|
return false
|
|
end )
|
|
|
|
|
|
spec:RegisterStateFunction( "start_burn_phase", function ()
|
|
burn_info.start = query_time
|
|
end )
|
|
|
|
|
|
spec:RegisterStateFunction( "stop_burn_phase", function ()
|
|
if burn_info.start > 0 then
|
|
burn_info.average = burn_info.average * burn_info.n
|
|
burn_info.average = burn_info.average + ( query_time - burn_info.start )
|
|
burn_info.n = burn_info.n + 1
|
|
|
|
burn_info.average = burn_info.average / burn_info.n
|
|
burn_info.start = 0
|
|
end
|
|
end )
|
|
|
|
|
|
spec:RegisterStateExpr( "burn_phase", function ()
|
|
return burn_info.start > 0
|
|
end )
|
|
|
|
spec:RegisterStateExpr( "average_burn_length", function ()
|
|
return burn_info.average or 15
|
|
end )
|
|
|
|
|
|
local clearcasting_consumed = 0
|
|
|
|
|
|
spec:RegisterHook( "COMBAT_LOG_EVENT_UNFILTERED", function( _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName )
|
|
if sourceGUID == GUID then
|
|
if subtype == "SPELL_CAST_SUCCESS" then
|
|
if spellID == 12042 then
|
|
burn_info.__start = GetTime()
|
|
-- Hekili:Print( "Burn phase started." )
|
|
elseif spellID == 12051 and burn_info.__start > 0 then
|
|
burn_info.__average = burn_info.__average * burn_info.__n
|
|
burn_info.__average = burn_info.__average + ( query_time - burn_info.__start )
|
|
burn_info.__n = burn_info.__n + 1
|
|
|
|
burn_info.__average = burn_info.__average / burn_info.__n
|
|
burn_info.__start = 0
|
|
-- Hekili:Print( "Burn phase ended." )
|
|
|
|
-- Setup for opener_done variable.
|
|
if not ( state.runeforge.siphon_storm.enabled or runeforge.temporal_warp.enabled ) then
|
|
opener_completed = true
|
|
-- Hekili:Print( "Opener completed (evocation)." )
|
|
end
|
|
end
|
|
|
|
elseif subtype == "SPELL_AURA_REMOVED" and ( spellID == 276743 or spellID == 263725 ) then
|
|
-- Clearcasting was consumed.
|
|
clearcasting_consumed = GetTime()
|
|
end
|
|
end
|
|
end, false )
|
|
|
|
|
|
spec:RegisterStateExpr( "tick_reduction", function ()
|
|
return action.shifting_power.cdr / 4
|
|
end )
|
|
|
|
spec:RegisterStateExpr( "full_reduction", function ()
|
|
return action.shifting_power.cdr
|
|
end )
|
|
|
|
|
|
local abs = math.abs
|
|
|
|
local ExpireArcaneLucidity = setfenv( function()
|
|
mana.regen = mana.regen / 1.25
|
|
end, state )
|
|
|
|
spec:RegisterHook( "reset_precast", function ()
|
|
if pet.rune_of_power.up then applyBuff( "rune_of_power", pet.rune_of_power.remains )
|
|
else removeBuff( "rune_of_power" ) end
|
|
|
|
if burn_info.__start > 0 and ( ( state.time == 0 and now - player.casttime > ( gcd.execute * 4 ) ) or ( now - burn_info.__start >= 45 ) ) and ( ( cooldown.evocation.remains == 0 and cooldown.arcane_power.remains < action.evocation.cooldown - 45 ) or ( cooldown.evocation.remains > cooldown.arcane_power.remains + 45 ) ) then
|
|
-- Hekili:Print( "Burn phase ended to avoid Evocation and Arcane Power desynchronization (%.2f seconds).", now - burn_info.__start )
|
|
burn_info.__start = 0
|
|
end
|
|
|
|
if buff.casting.up and buff.casting.v1 == 5143 and abs( action.arcane_missiles.lastCast - clearcasting_consumed ) < 0.15 then
|
|
applyBuff( "clearcasting_channel", buff.casting.remains )
|
|
end
|
|
|
|
burn_info.start = burn_info.__start
|
|
burn_info.average = burn_info.__average
|
|
burn_info.n = burn_info.__n
|
|
|
|
if arcane_charges.current > 0 then applyBuff( "arcane_charge", nil, arcane_charges.current ) end
|
|
|
|
fake_mana_gem = GetItemCount( 36799 ) > 0
|
|
|
|
incanters_flow.reset()
|
|
|
|
-- This will set the opener to be completed, which persists while in combat. For opener_done.
|
|
if not opener_completed and InCombatLockdown() then
|
|
if true_active_enemies > variable.aoe_target_count then
|
|
opener_completed = true
|
|
-- Hekili:Print( "Opener completed (aoe)." )
|
|
elseif buff.arcane_power.down and cooldown.arcane_power.true_remains > 0 and ( runeforge.siphon_storm.enabled or runeforge.temporal_warp.enabled ) then
|
|
opener_completed = true
|
|
-- Hekili:Print( "Opener completed (Arcane Power)." )
|
|
end
|
|
end
|
|
|
|
-- Tier 28
|
|
if buff.arcane_lucidity.up then
|
|
state:QueueAuraExpiration( "arcane_lucidity", ExpireArcaneLucidity, buff.arcane_lucidity.expires )
|
|
end
|
|
end )
|
|
|
|
|
|
spec:RegisterStateFunction( "handle_radiant_spark", function()
|
|
if debuff.radiant_spark_vulnerability.down then applyDebuff( "target", "radiant_spark_vulnerability" )
|
|
else
|
|
debuff.radiant_spark_vulnerability.count = debuff.radiant_spark_vulnerability.count + 1
|
|
|
|
-- Implemented with max of 5 stacks (application of 5th stack makes the debuff expire in 0.1 seconds, to give us time to Arcane Barrage).
|
|
if debuff.radiant_spark_vulnerability.stack == debuff.radiant_spark_vulnerability.max_stack then
|
|
debuff.radiant_spark_vulnerability.expires = query_time + 0.1
|
|
applyBuff( "radiant_spark_consumed", debuff.radiant_spark.remains )
|
|
end
|
|
end
|
|
end )
|
|
|
|
|
|
-- Tier 28
|
|
spec:RegisterGear( "tier28", 188845, 188844, 188843, 188842, 188839 )
|
|
spec:RegisterSetBonuses( "tier28_2pc", 364539, "tier28_4pc", 363682 )
|
|
-- 2-Set - Arcane Lucidity - Increases your Arcane damage dealt to enemies affected by Touch of the Magi by %10%.
|
|
-- 4-Set - Arcane Lucidity - Touch of the Magi's duration is increased by 4 sec and grants 25% mana regeneration for 12 sec.
|
|
spec:RegisterAura( "arcane_lucidity", {
|
|
id = 363685,
|
|
duration = 12,
|
|
max_stack = 1,
|
|
} )
|
|
|
|
|
|
-- Abilities
|
|
spec:RegisterAbilities( {
|
|
alter_time = {
|
|
id = function () return buff.alter_time.down and 342247 or 342245 end,
|
|
cast = 0,
|
|
cooldown = function () return talent.master_of_time.enabled and 30 or 60 end,
|
|
gcd = "spell",
|
|
|
|
spend = 0.01,
|
|
spendType = "mana",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
startsCombat = true,
|
|
texture = 609811,
|
|
|
|
handler = function ()
|
|
if buff.alter_time.down then
|
|
applyBuff( "alter_time" )
|
|
else
|
|
removeBuff( "alter_time" )
|
|
if talent.master_of_time.enabled then setCooldown( "blink", 0 ) end
|
|
end
|
|
end,
|
|
|
|
copy = 342247,
|
|
},
|
|
|
|
|
|
arcane_barrage = {
|
|
id = 44425,
|
|
cast = 0,
|
|
cooldown = 3,
|
|
hasteCD = true,
|
|
gcd = "spell",
|
|
|
|
startsCombat = true,
|
|
texture = 236205,
|
|
|
|
-- velocity = 24, -- ignore this, bc charges are consumed on cast.
|
|
|
|
handler = function ()
|
|
if level > 51 then gain( 0.02 * mana.max * arcane_charges.current, "mana" ) end
|
|
|
|
spend( arcane_charges.current, "arcane_charges" )
|
|
removeBuff( "arcane_harmony" )
|
|
|
|
if talent.chrono_shift.enabled then
|
|
applyBuff( "chrono_shift_buff" )
|
|
applyDebuff( "target", "chrono_shift" )
|
|
end
|
|
|
|
if debuff.radiant_spark.up and buff.radiant_spark_consumed.down then handle_radiant_spark() end
|
|
end,
|
|
},
|
|
|
|
|
|
arcane_blast = {
|
|
id = 30451,
|
|
cast = function ()
|
|
if buff.presence_of_mind.up then return 0 end
|
|
return 2.25 * ( 1 - ( 0.08 * arcane_charges.current ) ) * haste end,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function ()
|
|
if buff.rule_of_threes.up then return 0 end
|
|
local mult = 0.0275 * ( 1 + arcane_charges.current ) * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 )
|
|
-- if azerite.equipoise.enabled and mana.pct < 70 then return ( mana.modmax * mult ) - 190 end
|
|
return mana.modmax * mult, "mana"
|
|
end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135735,
|
|
|
|
handler = function ()
|
|
if buff.presence_of_mind.up then
|
|
removeStack( "presence_of_mind" )
|
|
if buff.presence_of_mind.down then setCooldown( "presence_of_mind", 60 ) end
|
|
end
|
|
removeBuff( "rule_of_threes" )
|
|
removeStack( "nether_precision" )
|
|
gain( 1, "arcane_charges" )
|
|
|
|
if debuff.radiant_spark.up and buff.radiant_spark_consumed.down then handle_radiant_spark() end
|
|
end,
|
|
},
|
|
|
|
|
|
arcane_explosion = {
|
|
id = 1449,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
discipline = "arcane",
|
|
|
|
spend = function ()
|
|
if not pvptalent.arcane_empowerment.enabled and buff.clearcasting.up then return 0 end
|
|
return 0.1 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 )
|
|
end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 136116,
|
|
|
|
usable = function () return not state.spec.arcane or target.distance < 10, "target out of range" end,
|
|
handler = function ()
|
|
if buff.expanded_potential.up then removeBuff( "expanded_potential" )
|
|
else
|
|
removeStack( "clearcasting" )
|
|
if legendary.sinful_delight.enabled then gainChargeTime( "mirrors_of_torment", 4 ) end
|
|
end
|
|
gain( 1, "arcane_charges" )
|
|
end,
|
|
},
|
|
|
|
|
|
summon_arcane_familiar = {
|
|
id = 205022,
|
|
cast = 0,
|
|
cooldown = 10,
|
|
gcd = "spell",
|
|
|
|
startsCombat = false,
|
|
texture = 1041232,
|
|
|
|
nobuff = "arcane_familiar",
|
|
essential = true,
|
|
|
|
handler = function ()
|
|
if buff.arcane_familiar.down then mana.max = mana.max * 1.10 end
|
|
applyBuff( "arcane_familiar" )
|
|
end,
|
|
|
|
copy = "arcane_familiar"
|
|
},
|
|
|
|
|
|
arcane_intellect = {
|
|
id = 1459,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.04 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
nobuff = "arcane_intellect",
|
|
essential = true,
|
|
|
|
startsCombat = false,
|
|
texture = 135932,
|
|
|
|
handler = function ()
|
|
applyBuff( "arcane_intellect" )
|
|
end,
|
|
},
|
|
|
|
|
|
arcane_missiles = {
|
|
id = 5143,
|
|
cast = function () return ( buff.clearcasting.up and 0.8 or 1 ) * 2.5 * haste end,
|
|
channeled = true,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function ()
|
|
if buff.rule_of_threes.up or buff.clearcasting.up then return 0 end
|
|
return 0.15 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 136096,
|
|
|
|
aura = function () return buff.clearcasting_channel.up and "clearcasting_channel" or "casting" end,
|
|
breakchannel = function ()
|
|
removeBuff( "clearcasting_channel" )
|
|
end,
|
|
|
|
tick_time = function ()
|
|
if buff.clearcasting_channel.up then return buff.clearcasting_channel.tick_time end
|
|
return 0.5 * haste
|
|
end,
|
|
|
|
start = function ()
|
|
if buff.clearcasting.up then
|
|
removeStack( "clearcasting" )
|
|
if legendary.sinful_delight.enabled then gainChargeTime( "mirrors_of_torment", 4 ) end
|
|
applyBuff( "clearcasting_channel" )
|
|
elseif buff.rule_of_threes.up then removeBuff( "rule_of_threes" ) end
|
|
|
|
if buff.expanded_potential.up then removeBuff( "expanded_potential" ) end
|
|
|
|
if conduit.arcane_prodigy.enabled and cooldown.arcane_power.remains > 0 then
|
|
reduceCooldown( "arcane_power", conduit.arcane_prodigy.mod * 0.1 )
|
|
end
|
|
end,
|
|
|
|
tick = function ()
|
|
if legendary.arcane_harmony.enabled then addStack( "arcane_harmony", nil, 1 ) end
|
|
if debuff.radiant_spark.up and buff.radiant_spark_consumed.down then handle_radiant_spark() end
|
|
end,
|
|
|
|
auras = {
|
|
arcane_harmony = {
|
|
id = 332777,
|
|
duration = 3600,
|
|
max_stack = 18
|
|
},
|
|
clearcasting_channel = {
|
|
duration = function () return 2.5 * haste end,
|
|
tick_time = function () return ( 2.5 / 6 ) * haste end,
|
|
max_stack = 1,
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
arcane_orb = {
|
|
id = 153626,
|
|
cast = 0,
|
|
cooldown = 20,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.01 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 1033906,
|
|
|
|
talent = "arcane_orb",
|
|
|
|
handler = function ()
|
|
gain( 1, "arcane_charges" )
|
|
applyBuff( "arcane_orb" )
|
|
end,
|
|
},
|
|
|
|
|
|
arcane_power = {
|
|
id = 12042,
|
|
cast = 0,
|
|
cooldown = function () return ( essence.vision_of_perfection.enabled and 0.87 or 1 ) * 120 end,
|
|
gcd = "off",
|
|
|
|
toggle = "cooldowns",
|
|
nobuff = "arcane_power", -- don't overwrite a free proc.
|
|
|
|
startsCombat = true,
|
|
texture = 136048,
|
|
|
|
handler = function ()
|
|
applyBuff( "arcane_power" )
|
|
if talent.rune_of_power.enabled then applyBuff( "rune_of_power" ) end
|
|
start_burn_phase()
|
|
end,
|
|
},
|
|
|
|
|
|
blink = {
|
|
id = function () return talent.shimmer.enabled and 212653 or 1953 end,
|
|
cast = 0,
|
|
charges = function () return talent.shimmer.enabled and 2 or nil end,
|
|
cooldown = function () return ( talent.shimmer.enabled and 20 or 15 ) - conduit.flow_of_time.mod * 0.001 end,
|
|
recharge = function () return ( talent.shimmer.enabled and ( 20 - conduit.flow_of_time.mod * 0.001 ) or nil ) end,
|
|
gcd = "off",
|
|
|
|
spend = function () return 0.02 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = function () return talent.shimmer.enabled and 135739 or 135736 end,
|
|
|
|
handler = function ()
|
|
if conduit.tempest_barrier.enabled then applyBuff( "tempest_barrier" ) end
|
|
end,
|
|
|
|
copy = { 212653, 1953, "shimmer", "blink_any" },
|
|
|
|
auras = {
|
|
tempest_barrier = {
|
|
id = 337299,
|
|
duration = 15,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
conjure_mana_gem = {
|
|
id = 759,
|
|
cast = 3,
|
|
cooldown = 0,
|
|
icd = 10, -- Probably don't want to recast within 10 seconds.
|
|
gcd = "spell",
|
|
|
|
spend = 0.18,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = 134132,
|
|
|
|
usable = function ()
|
|
if fake_mana_gem then return false, "already has a mana_gem" end
|
|
return true
|
|
end,
|
|
|
|
handler = function ()
|
|
fake_mana_gem = true
|
|
end,
|
|
},
|
|
|
|
|
|
mana_gem = {
|
|
name = "|cff00ccff[Mana Gem]|r",
|
|
known = function ()
|
|
return state.fake_mana_gem
|
|
end,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "off",
|
|
|
|
startsCombat = false,
|
|
texture = 134132,
|
|
|
|
item = 36799,
|
|
bagItem = true,
|
|
|
|
usable = function ()
|
|
return fake_mana_gem, "requires mana_gem in bags"
|
|
end,
|
|
|
|
readyTime = function ()
|
|
local start, duration = GetItemCooldown( 36799 )
|
|
return max( 0, start + duration - query_time )
|
|
end,
|
|
|
|
handler = function ()
|
|
gain( 0.25 * health.max, "health" )
|
|
end,
|
|
|
|
copy = "use_mana_gem"
|
|
},
|
|
|
|
|
|
--[[ shimmer = {
|
|
id = 212653,
|
|
cast = 0,
|
|
charges = 2,
|
|
cooldown = 20,
|
|
recharge = 20,
|
|
gcd = "off",
|
|
|
|
spend = function () return 0.02 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = 135739,
|
|
|
|
talent = "shimmer",
|
|
|
|
handler = function ()
|
|
-- applies shimmer (212653)
|
|
end,
|
|
}, ]]
|
|
|
|
|
|
--[[ conjure_refreshment = {
|
|
id = 190336,
|
|
cast = 3,
|
|
cooldown = 15,
|
|
gcd = "spell",
|
|
|
|
spend = 0.03,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 134029,
|
|
|
|
handler = function ()
|
|
end,
|
|
}, ]]
|
|
|
|
|
|
counterspell = {
|
|
id = 2139,
|
|
cast = 0,
|
|
cooldown = function () return 24 - ( conduit.grounding_surge.mod * 0.1 ) end, -- Assume always successful.
|
|
gcd = "off",
|
|
|
|
interrupt = true,
|
|
toggle = "interrupts",
|
|
|
|
spend = function () return 0.02 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135856,
|
|
|
|
debuff = "casting",
|
|
readyTime = state.timeToInterrupt,
|
|
|
|
handler = function ()
|
|
interrupt()
|
|
end,
|
|
},
|
|
|
|
|
|
evocation = {
|
|
id = 12051,
|
|
cast = function () return 6 * haste end,
|
|
charges = 1,
|
|
cooldown = 90,
|
|
recharge = 90,
|
|
gcd = "spell",
|
|
|
|
channeled = true,
|
|
fixedCast = true,
|
|
|
|
-- toggle = "cooldowns",
|
|
|
|
startsCombat = false,
|
|
texture = 136075,
|
|
|
|
aura = "evocation",
|
|
tick_time = function () return haste end,
|
|
|
|
start = function ()
|
|
stop_burn_phase()
|
|
applyBuff( "evocation" )
|
|
if azerite.brain_storm.enabled then
|
|
gain( 2, "arcane_charges" )
|
|
applyBuff( "brain_storm" )
|
|
end
|
|
|
|
if legendary.siphon_storm.enabled then
|
|
applyBuff( "siphon_storm" )
|
|
end
|
|
|
|
mana.regen = mana.regen * 8.5 / haste
|
|
end,
|
|
|
|
tick = function ()
|
|
if legendary.siphon_storm.enabled then
|
|
addStack( "siphon_storm", nil, 1 )
|
|
end
|
|
end,
|
|
|
|
finish = function ()
|
|
mana.regen = mana.regen / 8.5 * haste
|
|
end,
|
|
|
|
breakchannel = function ()
|
|
removeBuff( "evocation" )
|
|
mana.regen = mana.regen / 8.5 * haste
|
|
end,
|
|
|
|
auras = {
|
|
-- Legendary
|
|
siphon_storm = {
|
|
id = 332934,
|
|
duration = 30,
|
|
max_stack = 5
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
fire_blast = {
|
|
id = 319836,
|
|
cast = 0,
|
|
cooldown = 12,
|
|
gcd = "spell",
|
|
|
|
discipline = "fire",
|
|
|
|
spend = 0.01,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135807,
|
|
|
|
handler = function ()
|
|
if legendary.sinful_delight.enabled then gainChargeTime( "mirrors_of_torment", 4 ) end
|
|
if debuff.radiant_spark.up and buff.radiant_spark_consumed.down then handle_radiant_spark() end
|
|
end,
|
|
},
|
|
|
|
|
|
focus_magic = {
|
|
id = 321358,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = 0.02,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135754,
|
|
|
|
talent = "focus_magic",
|
|
|
|
usable = function () return active_dot.focus_magic == 0 and group, "can apply one in a group" end,
|
|
handler = function ()
|
|
applyBuff( "focus_magic" )
|
|
end,
|
|
},
|
|
|
|
|
|
frostbolt = {
|
|
id = 116,
|
|
cast = 1.874,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
discipline = "frost",
|
|
|
|
spend = 0.02,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135846,
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "chilled" )
|
|
if debuff.radiant_spark.up and buff.radiant_spark_consumed.down then handle_radiant_spark() end
|
|
end,
|
|
},
|
|
|
|
|
|
frost_nova = {
|
|
id = 122,
|
|
cast = 0,
|
|
charges = function () return talent.ice_ward.enabled and 2 or nil end,
|
|
cooldown = 30,
|
|
recharge = 30,
|
|
gcd = "spell",
|
|
|
|
discipline = "frost",
|
|
|
|
spend = function () return 0.02 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135848,
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "frost_nova" )
|
|
if legendary.grisly_icicle.enabled then applyDebuff( "target", "grisly_icicle" ) end
|
|
end,
|
|
},
|
|
|
|
|
|
greater_invisibility = {
|
|
id = 110959,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "spell",
|
|
|
|
toggle = "defensives",
|
|
defensive = true,
|
|
|
|
startsCombat = false,
|
|
texture = 575584,
|
|
|
|
handler = function ()
|
|
applyBuff( "greater_invisibility" )
|
|
if conduit.incantation_of_swiftness.enabled then applyBuff( "incantation_of_swiftness" ) end
|
|
end,
|
|
|
|
auras = {
|
|
-- Conduit
|
|
incantation_of_swiftness = {
|
|
id = 337278,
|
|
duration = 6,
|
|
max_stack = 1
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
ice_block = {
|
|
id = 45438,
|
|
cast = 0,
|
|
cooldown = function () return 240 + ( conduit.winters_protection.mod * 0.001 ) end,
|
|
gcd = "spell",
|
|
|
|
toggle = "defensives",
|
|
defensive = true,
|
|
|
|
startsCombat = false,
|
|
texture = 135841,
|
|
|
|
handler = function ()
|
|
applyBuff( "ice_block" )
|
|
applyDebuff( "player", "hypothermia" )
|
|
end,
|
|
},
|
|
|
|
|
|
mirror_image = {
|
|
id = 55342,
|
|
cast = 0,
|
|
cooldown = 120,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.02 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
startsCombat = false,
|
|
texture = 135994,
|
|
|
|
handler = function ()
|
|
applyBuff( "mirror_image", nil, 3 )
|
|
end,
|
|
},
|
|
|
|
|
|
nether_tempest = {
|
|
id = 114923,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.02 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 610471,
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "nether_tempest" )
|
|
end,
|
|
},
|
|
|
|
|
|
polymorph = {
|
|
id = 118,
|
|
cast = 1.7,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.04 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = 136071,
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "polymorph" )
|
|
end,
|
|
},
|
|
|
|
|
|
presence_of_mind = {
|
|
id = 205025,
|
|
cast = 0,
|
|
cooldown = 60,
|
|
gcd = "spell",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
startsCombat = false,
|
|
texture = 136031,
|
|
|
|
nobuff = "presence_of_mind",
|
|
|
|
handler = function ()
|
|
applyBuff( "presence_of_mind", nil, level > 53 and 3 or 2 )
|
|
end,
|
|
},
|
|
|
|
|
|
prismatic_barrier = {
|
|
id = 235450,
|
|
cast = 0,
|
|
cooldown = 25,
|
|
gcd = "spell",
|
|
|
|
defensive = true,
|
|
|
|
spend = function() return 0.03 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = 135991,
|
|
|
|
handler = function ()
|
|
applyBuff( "prismatic_barrier" )
|
|
if legendary.triune_ward.enabled then
|
|
applyBuff( "blazing_barrier" )
|
|
applyBuff( "ice_barrier" )
|
|
end
|
|
end,
|
|
},
|
|
|
|
|
|
remove_curse = {
|
|
id = 475,
|
|
cast = 0,
|
|
cooldown = 8,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.01 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 136082,
|
|
|
|
debuff = "dispellable_curse",
|
|
handler = function ()
|
|
removeDebuff( "player", "dispellable_curse" )
|
|
end,
|
|
},
|
|
|
|
|
|
ring_of_frost = {
|
|
id = 113724,
|
|
cast = 2,
|
|
cooldown = 45,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.08 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 464484,
|
|
|
|
talent = "ring_of_frost",
|
|
|
|
handler = function ()
|
|
end,
|
|
},
|
|
|
|
|
|
rune_of_power = {
|
|
id = 116011,
|
|
cast = 1.5,
|
|
charges = 2,
|
|
cooldown = 40,
|
|
recharge = 40,
|
|
gcd = "spell",
|
|
|
|
startsCombat = false,
|
|
texture = 609815,
|
|
|
|
nobuff = "rune_of_power",
|
|
talent = "rune_of_power",
|
|
|
|
handler = function ()
|
|
applyBuff( "rune_of_power" )
|
|
end,
|
|
},
|
|
|
|
|
|
slow = {
|
|
id = 31589,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.01 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 136091,
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "slow" )
|
|
end,
|
|
},
|
|
|
|
|
|
slow_fall = {
|
|
id = 130,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.01 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = 135992,
|
|
|
|
handler = function ()
|
|
applyBuff( "slow_fall" )
|
|
end,
|
|
},
|
|
|
|
|
|
spellsteal = {
|
|
id = 30449,
|
|
cast = 0,
|
|
cooldown = 0,
|
|
gcd = "spell",
|
|
|
|
spend = function () return 0.21 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 135729,
|
|
|
|
debuff = "stealable_magic",
|
|
handler = function ()
|
|
removeDebuff( "target", "stealable_magic" )
|
|
end,
|
|
},
|
|
|
|
|
|
supernova = {
|
|
id = 157980,
|
|
cast = 0,
|
|
cooldown = 25,
|
|
gcd = "spell",
|
|
|
|
startsCombat = true,
|
|
texture = 1033912,
|
|
|
|
talent = "supernova",
|
|
|
|
handler = function ()
|
|
end,
|
|
},
|
|
|
|
|
|
time_warp = {
|
|
id = 80353,
|
|
cast = 0,
|
|
cooldown = 300,
|
|
gcd = "off",
|
|
|
|
spend = function () return 0.04 * ( buff.arcane_power.up and ( talent.overpowered.enabled and 0.5 or 0.7 ) or 1 ) end,
|
|
spendType = "mana",
|
|
|
|
toggle = "cooldowns",
|
|
|
|
startsCombat = true,
|
|
texture = 458224,
|
|
|
|
handler = function ()
|
|
applyBuff( "time_warp" )
|
|
applyDebuff( "player", "temporal_displacement" )
|
|
end,
|
|
},
|
|
|
|
|
|
touch_of_the_magi = {
|
|
id = 321507,
|
|
cast = 1.5,
|
|
cooldown = 45,
|
|
gcd = "spell",
|
|
|
|
spend = 0.05,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 1033909,
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "touch_of_the_magi" )
|
|
if set_bonus.tier28_4pc > 0 then
|
|
applyBuff( "arcane_lucidity" )
|
|
mana.regen = mana.regen * 1.25
|
|
state:QueueAuraExpiration( "arcane_lucidity", ExpireArcaneLucidity, buff.arcane_lucidity.expires )
|
|
end
|
|
if level > 45 then gain( 4, "arcane_charges" ) end
|
|
end,
|
|
},
|
|
|
|
|
|
-- Mage - Kyrian - 307443 - radiant_spark (Radiant Spark)
|
|
-- TODO: Increase vulnerability stack on direct damage spells.
|
|
radiant_spark = {
|
|
id = 307443,
|
|
cast = 1.5,
|
|
cooldown = 30,
|
|
gcd = "spell",
|
|
|
|
spend = 0.02,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 3565446,
|
|
|
|
toggle = "essences",
|
|
|
|
handler = function ()
|
|
applyBuff( "radiant_spark" )
|
|
applyDebuff( "target", "radiant_spark" )
|
|
-- applyDebuff( "target", "radiant_spark_vulnerability" )
|
|
-- RSV doesn't apply until the next hit.
|
|
end,
|
|
|
|
auras = {
|
|
radiant_spark = {
|
|
id = 307443,
|
|
duration = 10,
|
|
max_stack = 1
|
|
},
|
|
radiant_spark_vulnerability = {
|
|
id = 307454,
|
|
duration = 8,
|
|
max_stack = 5
|
|
},
|
|
radiant_spark_consumed = {
|
|
id = 307747,
|
|
duration = 10,
|
|
max_stack = 1
|
|
},
|
|
}
|
|
},
|
|
|
|
-- Mage - Necrolord - 324220 - deathborne (Deathborne)
|
|
deathborne = {
|
|
id = 324220,
|
|
cast = 1.5,
|
|
cooldown = 180,
|
|
gcd = "spell",
|
|
|
|
spend = 0.05,
|
|
spendType = "mana",
|
|
|
|
startsCombat = false,
|
|
texture = 3578226,
|
|
|
|
toggle = "essences", -- maybe should be cooldowns.
|
|
|
|
handler = function ()
|
|
applyBuff( "deathborne" )
|
|
if soulbind.kevins_oozeling.enabled then applyBuff( "kevins_oozeling" ) end
|
|
end,
|
|
|
|
auras = {
|
|
deathborne = {
|
|
id = 324220,
|
|
duration = function () return 20 + ( conduit.gift_of_the_lich.mod * 0.001 ) end,
|
|
max_stack = 1,
|
|
},
|
|
}
|
|
},
|
|
|
|
-- Mage - Night Fae - 314791 - shifting_power (Shifting Power)
|
|
shifting_power = {
|
|
id = 314791,
|
|
cast = function () return 4 * haste end,
|
|
channeled = true,
|
|
cooldown = 45,
|
|
gcd = "spell",
|
|
|
|
spend = 0.05,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 3636841,
|
|
|
|
toggle = "essences",
|
|
|
|
-- -action.shifting_power.execute_time%action.shifting_power.new_tick_time*(dbc.effect.815503.base_value%1000+conduit.discipline_of_the_grove.time_value)
|
|
cdr = function ()
|
|
return - action.shifting_power.execute_time / action.shifting_power.tick_time * ( -3 + conduit.discipline_of_the_grove.time_value )
|
|
end,
|
|
|
|
full_reduction = function ()
|
|
return - action.shifting_power.execute_time / action.shifting_power.tick_time * ( -3 + conduit.discipline_of_the_grove.time_value )
|
|
end,
|
|
|
|
start = function ()
|
|
applyBuff( "shifting_power" )
|
|
end,
|
|
|
|
tick = function ()
|
|
-- TODO: Identify which abilities have their CDs reduced.
|
|
end,
|
|
|
|
finish = function ()
|
|
removeBuff( "shifting_power" )
|
|
end,
|
|
|
|
auras = {
|
|
shifting_power = {
|
|
id = 314791,
|
|
duration = function () return 4 * haste end,
|
|
tick_time = function () return haste end,
|
|
max_stack = 1,
|
|
},
|
|
heart_of_the_fae = {
|
|
id = 356881,
|
|
duration = 15,
|
|
max_stack = 1,
|
|
}
|
|
}
|
|
},
|
|
|
|
-- Mage - Venthyr - 314793 - mirrors_of_torment (Mirrors of Torment)
|
|
-- TODO: Get spell ID of the snare, root, silence.
|
|
mirrors_of_torment = {
|
|
id = 314793,
|
|
cast = 1.5,
|
|
cooldown = 90,
|
|
gcd = "spell",
|
|
|
|
spend = 0.04,
|
|
spendType = "mana",
|
|
|
|
startsCombat = true,
|
|
texture = 3565720,
|
|
|
|
toggle = "essences",
|
|
|
|
handler = function ()
|
|
applyDebuff( "target", "mirrors_of_torment", nil, 3 )
|
|
end,
|
|
|
|
auras = {
|
|
mirrors_of_torment = {
|
|
id = 314793,
|
|
duration = 20,
|
|
max_stack = 3, -- ???
|
|
},
|
|
-- Conduit
|
|
siphoned_malice = {
|
|
id = 337090,
|
|
duration = 10,
|
|
max_stack = 3
|
|
}
|
|
},
|
|
},
|
|
} )
|
|
|
|
|
|
spec:RegisterSetting( "arcane_info", nil, {
|
|
type = "description",
|
|
name = "The Arcane Mage module treats combat as one of two phases. The 'Burn' phase begins when you have used Arcane Power and begun aggressively burning mana. The 'Conserve' phase starts when you've completed a burn phase and used Evocation to refill your mana bar. This phase is less " ..
|
|
"aggressive with mana expenditure, so that you will be ready when it is time to start another burn phase.",
|
|
width = "full",
|
|
fontSize = "medium",
|
|
order = 1,
|
|
} )
|
|
|
|
--[[ spec:RegisterSetting( "am_spam", 0, {
|
|
type = "toggle",
|
|
name = "Use |T136096:0|t Arcane Missiles Spam",
|
|
icon = 136096,
|
|
width = "full",
|
|
get = function () return Hekili.DB.profile.specs[ 62 ].settings.am_spam == 1 end,
|
|
set = function ( _, val )
|
|
Hekili.DB.profile.specs[ 62 ].settings.am_spam = val and 1 or 0
|
|
end,
|
|
order = 2,
|
|
} ) ]]
|
|
|
|
|
|
--[[ spec:RegisterSetting( "conserve_mana", 75, { -- NYI
|
|
type = "range",
|
|
name = "Minimum Mana (Conserve Phase)",
|
|
desc = "Specify the amount of mana (%) that should be conserved when conserving mana before a burn phase.",
|
|
|
|
min = 25,
|
|
max = 100,
|
|
step = 1,
|
|
|
|
width = "full",
|
|
order = 2,
|
|
}
|
|
} ) ]]
|
|
|
|
|
|
spec:RegisterOptions( {
|
|
enabled = true,
|
|
|
|
aoe = 3,
|
|
|
|
nameplates = true,
|
|
nameplateRange = 8,
|
|
|
|
damage = true,
|
|
damageExpiration = 6,
|
|
|
|
potion = "spectral_intellect",
|
|
|
|
package = "Arcane",
|
|
} )
|
|
|
|
|
|
spec:RegisterPack( "Arcane", 20220911, [[Hekili:S3ZAZTTrs(BXF4SP21lTeLKJZEwUkhV(UDtLhUIYw5Q6QtuGGdfrmiap8qYAlx83(nDppWmdMxaKkX(I)WM1IeCqpD3t)U75QtU6NV6YLjnKR(HzhpB2XF9jNm9KtoE2XND1Ln3VLC1LBtsFFYn0)rrYg6)91vPjf4hFFEzYs4Nxx2wLs)O1nnBR)Rp7z3K1SUDX00YnpRoBtBEstwzrAvYQg4VtF2vxUOnlV5FuC1c7V7tOR5ws6v)WZNrx1SLljShLuNE1LFpfy2Dndm29Txs22q2SGuT76to5P7UgwODF7UV9nRtkUHu)x39T)LDx)pRP)c6UjlnjF31xMT5n7UM(9lZZkUz31LR2DDk9pli0VmROHuv1Ufaz4VQBijlzpZtOVXus(8Ku4lFYuCPzVNDxNGWZ8SIvzfzn3tFDLYpCDs1MYc6NnjRz31fKBbO9osb9pYZUf(X1Mp7rSv)VrFp0N6T3wMsrta8q35)4B3D9V2wt)8fK0KwyVDFz7tQO))LTnmyTIdvfuyVOK(HT14ELJ2U(TFyBEznDBWEpVEjBb3wLvwjbFCPf)I3vEhP6juaLSAfHIba4oTSmFz5DuGArsnbqt0)5B(B)K4HQN2H(RBQYsXxrzkPMbnWlPISSnL(9V5D)t6hKTH(px2s4aaLMapCsffIE97(UDxVQS698v97lVfEL3MuLLSiNu)mkXnLZSvZ(9FxBYUR3uUSnN44Ln9QlZZQBQb24Btkj0))FapsqkGfD5vFZvxMsXie6lHEGabVxU76V6QlzCbxDzvlLQvUA(wa)CvdLvEq)CozVSAb8BpDm)wIGwcRWzgRG4z3uwwKN1mNsHR3OUWBRi3o)M0Ltpz6MSQQYQAy30uwTbzqFmLRD31pIsr)FBZ2ULSCAZAY8vzv1nZRZUjJEK5JFSJtW8BNwr2KKb0JJecq0HekiFUZnD3Qw2MUgHl6YVj5MSU19LxGqiBFoLGNuW)1hiPTnK5me3FMkE4ukqGBi5YYrGiPBmR4z42sGILpjSPEEak5R2D9Z1bMa7XVgF6fTRwnToB76YI51arAA7wfICpciakFLtqbxnosGkbS6gY06gQ4(DxFb)vP)LBs(WC(dyfwKa8RabX4dr5CwsWNSpWPb72yMFHti33AIVx55LtoT37yrEsnIz(6rS(D7rko6KVosAOBy4KJJI8W4rzcBJ6nY4A05zdZ7tjCuaYTWTtcjCeXkNgZRYe86ER92t4BoKCvkOFkJNtrKM(giWBWT0xLvS)p1K02HSmfg)B0jVZmo51Nhbil(p6DY(lv(f6CbAKcNY2gnojbTjzoPGSjJuJh9Pcy)lOO8t2D9FIAbcPz(IYI26PnzKQzVy(STPAcW7D0SNq81uy(QlPMNssQYVFo(3ZZwXPNP5eyjQBOg3mNBrjIQFSoW9YNRIoTa4FDuaoUTDsI5YbD(9QCmCvD89)MmQ5zudQMA)0PXtHiQaQyurm0xmDL8VCUL77Lh54ye7as4o3L9xN4wJGuiZjhFa4wJ5meZ0lpN1PCkZoNz)v)NAqQkqP2Ny9Wqsvf49hyyBCAQ0rgV0pYWVqOzU12SFOFf55g7tFWdLRDvw9A40D5w651kBEl03Aq0V1QSTS)4xwtO0fQWWc0fic1xzWDq6)L6z6LuxOO)Xp)lupdj3qkwsDUHu)uWBr6)lbmihCJH6pi48h1zic)x(ZLnF)ZE97OpuwbLypL70f4Vj4CzDj4zg8V(PY3b((s)xGJQ1RrV2Ar)rFc4sh1VB4TGFed1vRI2NGGo8MP4uQJ3BlRsYNFxs12PCCaJFu7504lLpgtfLW9TPusY22885eyVDbk67iHqoXXaDvimoC4T5KC2ZIFRkHS51wiY4)yLeLzGi7CkMrlQ5UHBbZ(uPh3z)lcNSwIrhaIrW9nRzSi51enjrj5OHOABfv0AivUHfsAiFZW4tN0cVNS86EBhZYYS60ST5zfuN(NNwUzdf5PT7W3UTNA(QQYADNkOEIQOp3T5yDV(BOELs1SNLMrvAP9EPVIKPBHqyqvc81kQqWx78IYBtS5nRl9Ckgtydo4popCq9ragFVDPTIVvx43KUdu2PVUyoswEpEGcoY51UJJ87bCAzXY2SMP3KTQr8BZZsx3HSwssAwVOSQG43bw)Yy84hJBlmCE4HU7Dz2GBRg8TAbmJyOAoJXJYoF0ohJGHBtmpZW2y7HwON7GYh3v4MmI(XdDSLyEnANnqbUT59NZdU20dakItAaHeFac1jL(uZce7BEdx8pObFDYTemGUTy0s)(YFE313tAEkpuRj8Fktjpijf1ySPTiLPjOTGca1kwpG6()7IWllmz4(imaVJ5t77mSHs77mTGLcDulsMca567R0LCynWjcEEv1(GjeuIbzzNkB)sGc4y9JIWafjFxitu4wY4ZgL9ZtGJqExngQFGcbj553Z4y2MKrXSBlHVJZLCh1IriA4pfw8gMnE3LLtpSacaO)3s0OHIY2BwJbmN(CLWpbzwq7)qgYoRfRDXz5WE0UZgmalq8cgNPBUug03P8d2lQkzzgLJEE92KQ3hJFTUcPbY18Cuc7mu8AigTWcAnvyznMtE90WHScHWhGDq4sb4nbti0Y2nBrbtRArEsKVfGyjYRdLe4mTfmgpgftunUIH8M1PF6yedkf38(7PVLcHZbsFFhJuSJc4YFmBOXDU3Q91wIgUpVVfpADjL0qAWGov1UOVW)bFIO3xRDWqkwu5b6oIkeWhbhptLDpWpc)()uDhjijwdV8m3bWECHxEMllwOlxnPifrrBOs4nKlGrg4DL8t41)VTec49jCOI8HMQKUC((nmnklBR6KGyePblQoK5yoDD50DxdwiP(runw0NM5elzjp2g0VMBxeh0aTCq2U9)oHFQmJ98h(1FVmD(pfI8bAFgOESob8wN63HH9cC)P5Oxca1QMkS)ryvpHIQMRBKtX)exSGjbSNqRzUsZBaspqlq076KAeHVGqkeMJ2JmlW3pvIcBfbvksohjfKA2HA4PCfjpn1cEvXE4rPUnGzzzJUadTOjFUMdXAp38BBZlivjlYOoLiDsN(Bo2T3BZmTVXHkbdD9e14dUj590)UUf0Ll87OZKqqxl3WqK2qz(ZGNSJ(8umauq9VScFMkcu4hu9NapadVYWaS6NPaPVWr9g45OUGDpWz10Gb2ecvzBvb7F9VivLC7pXODbVGOS5CQvVQTfGIh33AIZoooRj2BL6X7wrG4EAQYXTNmApgh2mIAeYwzAkBuSvFJq9z9AQAArTeTGiezK0i5H4fjfWebe13kIBktCpN2aXDEzv52Ti9MhfuH7lmUwu6c(Vahzm4GwxXyHv4(aAD8ItmyyuKMqv)Q76zxHFOgM4a2HhjAmj)UK7Rzyse)XexM2tOzNkpa1jekdylQ8SMU4)lrLyuMdyWKteIVi8AfzflZxpBShBIuNyJ8(Xpo5rCL4q0Z52QZFwZT(h)OvkmL98OJostQFa9(bmBqeI1odjvmuf4AuZ5R65(W2tCaYy7PU8MW8zDgTlEyFOQkY2SHqv61qaX(OfcqqVUd9(mnbIRh7mmk74okvLWQcqihuvLBW4GPlD3sKLgPtE4E10BcTT0LnjvD1hja5GbKmO)1ICL1zMuSHwlCcwSNulG5s6uBpXyhPE0Xh51TVgUvWbhSLQLAkB2mhqJYKaAp03N6oNoE2DA5sXs64LwdDQldDvYlN0bauaIyPF1fV4CTVtWLAKYfNou6ucjcxNRgxztH)DWlM04QYggO6jDXE1FGESre2lXpaMdAfa9Rm75qBQeCDqohXqsVaRiyudkO0ER6o3EzLem0UEul4UAZ85D0URpv7aHvMZx5H3uvCSo0PkqU7NtHU5TuJEZNNS1XkVKKNC)CQEn8ryoi4KrNz1ND0jWT)iBO0JcJGeMZUFiMbwgEggqRU8jBbxNY20cVKIK5BvtJGTJqElNe3co(0gFgKrBuNu2lUtpyz3zk5tBSmd68JR1soOdKDa8h1xeXM6HIc4jUcrhYGlIk(c98f1Lf(2nWOVf(Xv9nr5SGxuK7u6iX8q)PmhmaZiKTrGw0Jtxey7xg1dRfajNgWeids9clvOGkg671W8HcpMBWUx9r8fh2C6W2KyihX5ZMHmTEChYLJ9yMbXYRpbM(GhNhFVPSOUDJimdKpSLANjGW2w2aXne6NoyRdXNaKek9ZlVeSzv5Pqp(I17j87eVS5YvbsrwJLcCX5wXwnw579AvMPMlz29N5Wr)z5KmGxcJFd2)7I2n6EnGHgFYR5r0QPI38GlskEpZFAmsZwQksS6fyjyMIV2iJ289pXW1fkJqwfz8BrZSgmXdE4pflDcJOmKZoSKFI1XCp9yYeiMERON4xdFP0gSMS03JHM4qeFAxge4tJc6YRCpvqG8bmhIhnHxUzJPlxgvSg8uJjY1IY0uNTepIQ4mSMlnyhNOeZH62TKkr5BgU8sYjm0tfHYuyjuQ9m01tdO1RatIkCa)n8mJ0AzwmbONZsG4cVGGbgGfvyQWBsfK7hwmcaGUEDjgbAiCbe9G0ZtWd8NcC3v6PknuMnunJ2jLy)yH11It5mS77LY3BnY1gSdgToI1y)7PTu(dlEHf(glPZSZ4(EfyZy5XPWSsYKg2P)ppXTpEizue3foAuiWC1xiIJvaRNStpKj23mvw3ancdRGfTMnRYv8pSl9YUurmM01TFe2j(X)aAnu(Tey1rfy39hRQKJWiI9UFtaS8J(0gN7QPDgho3pIwTGyyYJGmvdIJ4ZxdaSy1MlstaJEF978ZQ7dZh2WSbfkG9M2Hi8a1iVJAtbZLgIeBG1KJ2YkKHbvFELGfCc7z2WZWn4DBjq0ahWLLSQS4(0BXT7lBnl5fPEeWDI1yxXbw6iDNptkKsWCuUHU6lzZadzTYYakr3kyvd0qlv0xDHSqMuJVWFrj(cMF3R0k8CJWvGKj3wKpA4mq3Omca1K5rVZpTMFn0SzTSR1VCPI0QGRSKjXgrzY4ITw9ndfzHCX8KypLUdt)yX7u68pE2NznFgLld7(mH4KKTQTZj7ZaxMOhjGJk3LvtMA3yu3nZwGKc1nlf2)q971vpXpFsOy9)4WH63Av84C)fmM(YqmhmpehY3xezRteA(azLCFyduiSD7EsrE2nRBOhVx2Rfz89iAw(cY7(QJ3P1BVtcWM1He3)EVVJSgZR8cXqfW7BuD74ttUowWxAvTk(WBot7O2bBGw5ghG15wFCJ(t0t32k3CEs30ynWH9erKYrtQ1xKhDWEFdsEKox7IY8OB76FZyAZQiwBw9oiUBtO184U6RBl9RLHEESf(WE9u06Ec)hGQLMczOUFWAEWgwEht01vFDvBl7NdoI8uH7jWJvJYCzUkc9V1WJzxmhU4ZPh2CP5uFnnCmmGyejooQLZFcIJy3HCdgZmTpd22(4W8My5WrdBcWQ1MVasP4TLWitmNmVUm)wMoCfv7YhBBBDde6BcF2yQ6kE3jMCs9ACqFAlhYINsPFoTNdj05o1gWsXoy4SbZ4xJYk)oso0VpQT(a4Sx3Xe58sH(RnBbOi4IILz5KZJMr452sl7GyTveGPuqKwq02YAA3B6lMbgWbNF3m)ZGe6Yjt9ox2sXJYlAMm5PhmslGEESorXu(SK)P3vYBnmMsjm9TYEXadfdgQemVNOndSJDOwmZEokbdFcEgS5o5eXvjGm8i(Gnsccy3X7mn2aPfu8zV1F8ZHiXUu5M622WElHpH6Drs2kyAdAj2Kimu0SEahAHT1d(eL1MihYEbYqjw(vyxSh2q9aS)29oWALfmu2AH(brD7VTSUodqmpi6lIHfShj1lgoSljHuNG8z9gqT8kzOpCARMtC7Bz4xEV96Kp9uAOjW1UiHhEfk)ohxbnR16pUlC1Sf(gXat2Jeu2UnS0N9)SXfpuf4UVryGNAvjeUAagBozxmJP6t1i7AX72t5UmS2dXtSFIDZCWNvXUDrAy7TXopHDxVid79pqg8qGLBpog5COWTJftgFpg8hA5kEjGEMok7b(EFWubncoKXSwXLJUHmcH)Eq6KR9b)ftqDhQpbpqn7siC7dsxD5r5sKgCP5TuPDdaJmC3re48qg253uYWIE6cbsK7DV6jJPCb8Nc961zRqRF6wtxHlpMzt0wOPWDnHzswOmHekOFyhsqz8LigqqIwJOb9lKnzseZ5grSiqGw5wNznuGpScMDDBXnqzFWnvUlokADYqqXF95FJze3ilV8a15WbOaHISO3FS(PugY1dTTXYChjn2AuliAr)aGrVbf9PJHVNWe8SkPnVrw3hINoTSfA486TK8CZcvXNGXODbgea9IJnn1G(Hp)C7PimYL8C9moagPCdzJ)uoBJRwbXjhnKXK)p3Z3g8Tq(W6K2AK62nZa9g1A0WWYAgbEfGcMRjfhgjqQtSnqni8UTLQpFB5Oc9vK9wbsPQN)RTlVrKLNaDJWdgOSi5g8BRYsFFT)0n5Nnyb90aPccoSTucf9QKxwUC(Q2Q79NYe)RcKqxCLcKoe)RcCjWv3a8QPj0t52clFSlvBnzo9j3uBnkO8GlKMWY93QQe6IwqFVWvP2kf38C)i2IRN4xDtoOuRbYS4IKL30V)Q6PWkcdKWrl95(50ama7(HBoBl2ds8flkNtw6ETdzy7OePJnsvfkpotg7d4CR2z5g4A73xjwSyGHDpKXEA5y8(uv8rF73b8aa6gxWnDEgxsTTalk45OUtDV)FaXVUMYEu9q3tvHKpVSAzbCuFibc)I(P)ne705cqS)l2wiSKhERY2mNKMNTTEOq4xnia8ReWN2B0F0TIqgbC7WXD6tnkjLfqp(lPAvv03As98vj3wwzn2vDwSKMSOImVEnHYkTPTolDaqeSrpxHVtcs6x3lI2(gaCTdaD8zwadBri7qd2bay72eeJX2SymZlzLZJBwwRiGG3Cy(roUsQD96e6MNSeAOkKbvuEvkEEHVLlWuLWn9nQQFzsaHLoM2Na6ywSIvhMGGoZ3JyPNXEstZAd(BpsHv1f21waY0Cr)8PpNLmv0dCQRoarx2Mmx2LlKDx)tTlUh78)FfUXyZjotyIU3qUco1FwnHh6KOEd9vBt4tTFVs7i4(hxqsRkZPsKf)E(pougvF5oERUavHydinF31pJsKbuNubNCBcLkb7e7sLSi2N9S7KhN8kUTH1o8Xp)z(6HS4R20Z2g72UmvSG0c27wFFrcv6pvt(AynRmiBoJYrmNbEf3LS(Pr1Vse)gAQWV7ApylayI99sWzsSmqVLuxtYhGS5Gk5uvMmriV8crEHdlqpOyyXgV3MWx45Uj)(TismPUoBtwoVIY(mCBBFNylsAst)wuwahlMFB2GmQ6tN9S(oWw03e71ksDjiEzoeERQBlZQ(SCdBzB04Fi9UMUhtBQPIoVHu0sLDsnoO6ZtQT7DtJLrURQG)5z3uaB3bUTpn82wvdV8T0yzEZAXVD1uEhcj84DHgNtMEDQxAmgUDsby86PhcNBxWCtYfx2)ZHljE2vfppMf4LfFJLHlB3gBqqacZpAVu9p8nHyp4o20rblRnM4Zdgo4laclUJ3SD7LcFYAyi219B18bVFmULqBSmhBJEB9BmORaYUdd(VXWK09neSIOHFPhnOM6XMz8)EHiLd9xaMJOznmG5t(9aM7GHRW0CTTIKcncF)eDjLc1qOUrK2RRSvF262nuiBo)NSkHAmwwI51V3PQ)I0YIFTTImxMhk9N9m1NL1DmZZ20RdT9KhNdv3V0bg6T(IV7(YXqlrNOyxAMc1ZvLlZUr)r6uUGnsd4RtQrSpm1j5lwSkwlAwYaU5NvTnW(1Lx3U1xt)PGzv7Lq3bDm8l(rX(UXN2xeTDQQXZGa1DsRo0qUvdQChvtZJs9U65CB6MBWwXRkLYnTHzYHLPLq3AoOnk3KvW))9ONq))dnYmE49lnZ8dEZm7Tmo(DG3ny)mRISha8P62QkUqRNOJzCem8HprhznMzqYxoY8P6rg5uRiMcaA0SjQe(j6rooY6b9ZJ(bkcw3WnlKts0EDR1hicooJyHX(DDV7Y(bMmDfpPXvIrl6AeMH2GjdFnJndrr6JGk(mq4UK5DYSEwSEtCZ3GccGTbH8l9o4ADfxzFz2lIy6zVF2D3Lv71ez1FZZBT823RM1FI9xjtqHVwVXMFfsxX8yEEChbJ90LjJL7ZmQxOto8PWT9)rS(VyqW9l(S2SahL1BeV(iwYiW1wuU7GK62YyZIflIZkH6dpBtDhBEC6RTMhbyTVSqXWZkTfioQJR99GV40W9M2oh3Ax0)ZLM5eZ9R2iWYFXIVyzPIXIVgpvtQp11DYXmkgQH0DfCM0(TRHxEASPx81C1(SDkcGY5aliEPRheH7r6d2btYFKVV4is78plbChI4qlVtQtKhT(d7jDpuJrxSUFry74ibgdUjFvJHNkHCVGxd3k0yk21vgIFrS3qf7HVwwQzLziKSAvBnKATKISnjZtstj5KkOPw0a2oRFyXtil1Wv(J8P3ZZKwypIbtuHkaRr5Jd4ESNjtWEaF4HGqW2SXzZS9TH3G8)LZKda2233xC0Zh(ZK(5x8o0dIuh1Fqvz(BcvtL5bjybUYYCaOdIyole67tBc2KGN9GXkbVru8xb()grK1iAiv2D9)yU10UYMyHiJxORoV6NuSHR)0VWZu16qJb00e4UTLSpGO6JZ8mZSEaGCVQtXkT2qnqGqnyoxdD7ruVAe)HDJ6sNFinlUt4Wdmu2hCCkn1tTDhZlnIRA5ojM9VDn1U2y8un2Xakt8zCgxwS8bMz(apoSGW(6K0b)yUnpoqyY(j3JFlB6PcSXL3X1UAG8t6DAa4kBZnElCACbvAJVqJ7MqFVMKe8z6D)lRODU3ffB)RxBfJgewmZ6xLu9kCawhJIY8mP6FlF55kkPFKfVOw0D5v19EG1I3uGRjj5nRLJPUt1h(l2MIpEQfChuHp722QurClJ6WL64L3rSkyHgVv2TFfBt6VfvVZVIBZ9yR7niZ9N3FJ1DJCEACJHLbzUQ33xCdSLEVVJhGLODwSHp)ykYARYN5cfpZ7mU8H8EBUdI6F1lFwGbpJdr1HrfMhGt2SnpBvMycZ6)q8lcDi2Ra)ZCRdmg1pdbqpZpGe3KVYWfRyjM2u)F2yUjTd6gPUDDgNo9bpnYUb6qvW2mDfURbBZH1uN04ymmYDjeQnvwvV6V9F7A5AyfivkyjeTA9MI6nDIMAd3Qt1a1ZuhBJMEWeF9JDLB)90UUdTvufAGDC6nmIAIxH3x5XlA3AnCTKHVUog65tXnp)ad8EuHuNsGFrFrqD3n8AnCLf)X0inNO1YvQ4fZhmoXADerpCpolYKyMYNVR8735CkFkVhwz3fVIr2zJ4M49zD3M0Dd7tk8vGpM4x)wWle2DaH6hXhFjOgdYYNYUVsOF9tA439qiObxQeYbnQZ3j8tLfbd)HFn9xt5SkkiWnmrDj8upbUkVQX7eLSgZRFOX770a8OzsmoS8NIWZh)Lusaspqlq07A(98YccmzwzxWz9iZkt0vokSvCpOgjNJKc(tLVtHao1pFF8JgPdjk1ZiETSXvW96Iwz8U)t)nwUY(LcECvCVM2oy9Y7sETANiooqGR4hyabHOFCq8kpP2OEPl)3zMHQCHlhpvYMYLD8HfBug9I7CpdoI9AN3yCvTV34bDMYd3v7EuyPEkML9ea10CQfy4F3vtHQwSpNlxeHShpXgn9JFCYJ4cePQwRWpMSK)SM77p(rPQwJba8rhDK2jOaYqdicweOAJlXA7U5CHIJ8HLn)QOg(YEDvXtDb03Njd)g0(oD3gCBhPnyW1yCZ8zT2cj(CTlG3)kuN(NkaYJDbhSBp5aoBA3gYlSL(AEk0CzbPNCe7b018g4elb1r6F0Gl3xXs)QlEX5XuhVwGsDPl9nCS5kh3J7oUp15YAS5UPLTtpZkYb5Qq)uWMD74nlNG3PEh7IUR72wLAsXnWfs3AQWc(dtxC7UIbu8tnMYi(gVGXxFeUejZA3cR3W3A77FHa3yEfc7HymORf6pwr9p9EQbW)mODbTCcugDYZ5pj9fTMayf0CyazKYNfnRkR46KGWybkIw1WWT4hYfIVtC79r)i(XBMbxRWjz9uJOKjrD4lFUekXq0Q2(s9Jy26(Tg2ZfiBtPVMJ0aMDq6ZAh4vgGsEKQGkpY9CnZ86j2tNUTMz1lYm(w(OFD31)iF2VkVFFP8XRi3jnUvSAG9di2(UKm2OwSJIb4nfBqOOSQgfRqWHgPkLroKD7nbAzkg7gKN6SU9EC9aY(CwF38iP2T(p)YSAPrfJ5QhZPfH9g(HWb4to2xP1nMkKxYbbdVE0kfHOnDm1GsADyfECpp65LXmXjhBZ(r)7(iMPoFIThz5asY7AUPeLnG1zMsvsbplmN3VeBmxjDUAEtWzhdBTKDCVEwXloyDnZorVObgXegcUrvHj12sLewhbzWwgn)JmQ3AHA4oYOJRMkCh7ZypqgzTaPBud8jQ3guxiRJTj8XHJ)Hqep3xD6FTpiIgsj94wkGVA)kMHwJDey4l7V(DxH7KJ49WNkMAnfBa8PG)eb41CED56nkfymxNH36Z8Wmi31GjBV(D)5F6hFN4kscUNO)g4MYihVfMzxw0nYRYzk()nVPgNSZPPuyik3MIjrvdj2gw(cdZeb2xUDI(Ir0f9IIhdX0VwoWvzHaVaPRYwa1TZu6IAGrfIrb8H9vr5dYTmvWvCs2559Uz8zi5i8Q)uQmoez4CfFXTfoIxPg0KdQWMJSMPcFwrkrGpmOQd5wZJeq35ymGeWj(sGMTIYSlshICWPUxpixRHYA)XRblq6RjPnW6(Ql6D3KCWr590d4AYsBrXVfVbLbdMdBpL7ZoR4)bdRYAQj5RyIlOErHxi)GB7u3TlyzfrCtOBLCBWcdvJXX(1)BGdpa28DK1mmePctpXKBK511TDBXNnfGtdbpZir0BG)2pueobmxnmOxD8m(OLLkPuO8TYlTWAk5JWcUW9LTaVImHAYyXiIlKmup1eGQ2TgSTmW1r3dy8jepHueI83UQSTQz9OrQZgns1wiXTg5upbL5VZlZwwkJFAVelwVMEga22er2lPcGAWGjwQMVzrb1OgIg8YkYeNExueVrJnnh(MUlJ4XIXDBf8ia0XaeM0yTIRYtO(9ws3hEaXDMageEkIhoQQZmMR)1EcJ5UAhbmOxJcrS1EzupSwYZDNwKaZ1mpUkbxy3k50DbP5oSsf4af)GznZA92cuz838DmnWLTqnRaDVhEy(Ne)KlziVSLKK88dusD7zFFiZrfr1igFNqCOlRAu0yzb)r)yMQaiZYIQLbmQas2qxAyG6Jb(nX4b5lfkA9yVYj9lAy)5ETRWHdzHilBmbkoIXNMtBE4IVXrvucqcXGAtc1sJlhOGc6uiGMik0gMdM8sRznwDsKIY2BwJ1NtdrrtFjMvbqBvvj8tLQ71kANQYTpdOeSYCyaC4EQnbDNZDF1ehvicgzmjSFir8T4BBUa9Q9AnJwGZ7Ldp2L8jbTuVs2ybQaTDu8sqihKhwK1KbY3uEHW)aSgetDiln5gCg9QoQUOvmAR(vBjGHqN211gSpkaeCHwx32BtOzvdk)OxZYjO)bQBomfUliajwMix8EsxK7wTcJJ6Nw67bkWI2mWivowx5UpNUjtw1qywKEpNmTSDZwqsh4qhYci9lqkKjs50d6uL2Hvt7T8oH6BS0YF6hDkeMrN10Ifoq9tHiPXksZmmGAzL09u2)c24qbhr)V09EfyLo1h3kH8N(SSoKJh0GrDtfJljZJkKV238lultnTdLqJSCts1sMUAGdZWVW4i)XiuDyiPglDLOttSTylIfNXsQL(IXOYz9rwgU0HNLWtp9CxMf2LVVt3FeOPyoLmC0KPX8FovTionLswDsmZPCzyLDL0yXIOwHt3KxUijhwGMWD24O5WDhKvFoJ552TrPCK0RfmT5(yVgsmc7l(XoL5lyb3aiVMQoY4wHKKZdYlZgJ6sW8RvvGUi0vGyqG7vS8AS0bJA7N)dmWw8qHUXV3hipj1OgGXwRaY2l2Xh5n3bkEGPru5xLWU4unTCuBp)UQYv8kdc334Ezdy3xfei51zs7aFkqNOO4s4X2KqjKfLnMwmYkHoukr72TLqfeTMTIWNdqybmZbmDhEh0gfGDk6wlISwqdDjzWq0UYDual4xoia6TcgRvmLFN8Yzqb1GvtOYLzKNckShXDaUTH2ZRcm6NXuHh973itT26qo9Nsndb1L9dZoE2SJFXmk0CxsfKDake(lV(N(H)Xp8F(x3DnxwD2ggfcLd)ewp79eqzo1mWkWSg2z0K2MYnjy2rarY0d1t39TFxgOy8e6I9MYc6Rf)6N0fMW)RNWydu)iHlX0VAYjF4i5Qm7GSkNCMXYWtGKCne)TZf4RglCawe92F87mxVxCGxVVE0BWDFBaIV(v7ZVvmbZ8Wee)MJ97pD0qH6QyYdnUv58dYQCOzhnPtnux6EpPzAYI0PIGEjw4U0LUin4bTObmnc(4pUPl844dZYmEUyTLXKnCOSXZE(OHJzhKLXoZZSdSSSzpGYYe3GIpzVKIfpazx(Zq)97R2ltc0q)99ooU3lq0Si(1Aoy4y0SQhkTTowGXbhFQiFl4Xo4MnEyh56XYpyfsbbQLKvjT5nddUgJPiZo4gr03E2bJE4lKP5iJEHGtw)ZcUNDWsrDqBo1zNnIGDU76)BQtyvv3qD)PE(QKBlR(F(3bVdBQO(lk6BJdoVTbb7YSnVHsMVVObU9a(3yl031MaqWTzS44rn0fN1D0L6zhfy9glyDy4dMDySMC8weOVmhgP8J3EcDLUMmqXCu17cms4yFLUYxMrZ4z3kRtpmofDApg5)p27PT522gP)T4jtve9BvYok19zK8m9Px)q7DDUoj9U7BNcnfKetOe5rkzFUJg)B)2DbiiajajejDsM08PelsUyXIf7Ba7UTdm9dJ819dJ81DLrUrDpYUG9lpkTpLPsYc2PeTk(fBtUwcdZCr9d9UmuYV1esyi)bBqO1lyMNw9JeOUAKxJCrhTvvTXrgn)Y74mYSnuUdfvCPnovv337owmU3TNRFK(vEBKluKEzt0NH7De8R9Jk8QHhZbkR2(M2ZYRHh9dpBhcsNgyAd)MgvPnUrxlaA5ePmyCuLOUqT(HvRZ(Ilatx9tTV8ZPmyAfPTFy7RaMwHl9ZENkGPv4sfN3BEr2six7h74QaMwnR6h1qvatRWL(rzwfW0kCPFe2vbmTbx(sX1yDW0glk1aqxJt)ZLZ0hnE0p2J2z3OfGPFy8FvBmIRwa0s8OnQM5aOrhcfxtTx(S7uO6eQIzGhriKvn5OYHm1s4uMD54JMqf7SBbikVvUfGORBJRaGJhh6lRA7seEAKRxMk1p)89p)bd5yXIEs3xhXI(Xc9(r9x)O8QFSiUTgHQsB7hBq7hLOvcOYXYO09qP0vw1(k6h9dBwFjGTnKvna0g5qAaO9ARE6x(5n5NAmEhMZpV4OWSDO4AmrMOBI8)(TV4fp9UFLY0er(x8l4V8wwYo2M7WuUy84ZrI6vxHGfF2pYL6))X)RlE6Du2BS7XKWaS6iYppy4Dwer3FBm9eKjxG8wttOJwIB8YaSEEfnNJRV8sj45J3tIkIKS7ZYjc6ztaqaWKRAl7EeZFGHx55i6gqJzsI(76vmc)LyQiN)t0v0NjYYK)(p907EpLTE3Xc8Plh9JX7PRGoL5WioNkWm8Est3pBrzjOsQSumw)Wcoqfz0KyASxnby(nmteEjGWSLlzuULuKNSyMbr1XaK(9J)L3K)szxQVCKb26fSJxjIyzCSchiqr(ESQP9J)2)4jrvJCXEMajG1i8L9XRpWp8B)nYAGpOa5Fn(ECOZVX8zFlSGhSpkppCqyqh8)M4f7JywgqI9uKTwxkp1UZM9TY13DSOiykv7BT0FtyuOFQ5xcS9597tX8iER)8vSn8zGP3mFUCow2fNvozDopozgMXF7oxC1wMDTRGIDF88Kadqy8exbb8KK9rrZbqHGbbs4YzdleELfMSoE78mSPvmyy5cbnw1Tf)cSUMghfNUa)nt5pTN3537hTNnBm)FNZIYyZg5kMIPLa)2mVqbt1ZlQBNznNO60yxKbb5dnhAodaH8aEERqjQC1fTV3vGzSGevfEoJCPzgAYWvHNZ8uvGxACsFcoFdq7kNNTBI3nhFkJTG32BV7XQG7MJbAUq8gFu4xZZx33HBSfsxaqypK5TRcw8RhPUJX9Prdd7XVUvbGaHQlWdykTGHf7VVA0fdNCQfXzUoqg7R56JZKZUA0Pfcx3IPbjO(HD4WqZd(aD5EtTk2ZZ7SXtuanVp4nOq(UUbldCwEAxM(c5b605B6kDwKevCfYc1Iso8YuwXa)AyGf5g3C6xoD8Or96iYj4IH77N8mnCYLPYVZmX23z30PzkU5dSiEZ(nAJEBLjvUxjyyFSRGYu3lVdGtpTP05shF6q7LiDyRQWxjCVLSaMmi)xZZo9G1XhoCsj2dVbdpXKHu67gNn(WbZgM5n4KYwM5DmRTaCaVd0NTYP6g)a)7adDZwZGn)Ba3pcoCq(0vryEFUdBTz35VyfZ8J2Ww4hfbiIYJx4N(HnXWqVGb2eLSFhSKfWisP8DaV3cs3Vdr25)N9W0dyaxWUpu7LkxdcvF0(Tu98yomrbQs2oLN9G)hW14mSLdUDHYdwgbVnUccuRDGpggn)N3p3MhUb9R14BSe8EzDqQ)sAhQmIr3hJoZaZNS4iCT6Wb5JsaV2aNAMZY9E1iC14VmiRXUWDl6skKXBYWsNv1yHkKNwUiMEI8Jv87y2yfm2upHSwN0OuSToyFs9axDNN0PgjO0fh4vhRGAR9Rl4tzSzWjwWgZiJSgaOPwqdfuCo(m0p29yatYsapIv)DmtaY9TL8juiiJTLQsdOFydYtA9PG6Lbf9rMxpXtP3nz5dM4PoAjX54SX6bJYBsPq)d(Pj4lxqlXMHBCkiqgF0aciS)7AFyhvivGFqFxnxWy9xasmUlolBa1rmMlk5utN9QrAOinlYM)(9lwLVgxfxXbyGH6yf97wBbuuuxugQ78xrppnm4dzpRdeSWZsX2tSlKEUO0L7tF0L3gtwF6lC5LrvOz7W1VaqrHlFrEMQyH(y(L5A8Yc85BxxM6dS(OV9ySSwYm(j8GFnN(YYA7Sn2kDJSsCBeV1TZMmX(IuDOoQWmke9GnmBJjH)1oUtbh4g0C5WJETHNiviIpNQuPZZcxfgD4q(H8w6bxwuXb5aY7iMiLTjQPzYRDAI86bO8IBVEKr58UQAZ75MAuXCs(sB9lLtCIcGwLBByxLgUbmjnkmjZLr87CAa)UAw01sKSghXBbhdbJLlwwYTuTMrOQXQnomtNnbwFTOLfn6wWcvh3ClhwRdQjr77tm9RsOnC8OlMCQnheW5W0RhvJqX1(aQYwGLXZsMYGF7myLyOeDn1PSgmSsygk2byQcKoD2vnTj3nwC0CJga0vhoORDV23NCE6P3n5YxlkYvubWoHXwO0)SEBHBhp9U3q1(CWI63JNMsunCOL9wHSTssy1kqqNnKdKsKp16D5PL9Ku(nknOsZFG0ptVZeFsvsS6xoTuBX5BaYdiyuI6kbONIBvdoLKFgBEaVPoGV9QAezTLThTsa8rXheAbIUxJFcjlXyte1bgOBbd9imUbrr1PXUgzdlqtmPkx09SSmwuJcgSk2dLfnKVzSgjhwKauJwGOhtO5JFww4MWiP7dFwHLS7GFf27m)(qhuw9Xg7szzXi7(CmUtP3hh2bTBptOiGdRH)ka8HbaeB7EyZhi9o9ZpIjTmhUAlw1TCa5U2gYPfbmyu2YEyjyo3cCrcd(Y54Ws9pX5IY51SX2TOVMD)WdR0xUXkGiQAOeoGU3iU)bZXRkbFgRDi3)stVnwygoNF67CeUsmdSC(TvJfP7b8Vz0Qmw5mOhCYrOUOz8qeUlZuOgIrwZqVueIvhetQcTVZWLzD(9iPz0YD0Pxh2IZFwDK71HiV6)yEUvC0899ut5q)BAKhFCJSCSXDcZ0IqAJbhuijeffn30RWdOf3rbtpFonCw7660V3QWk55GLwWs0qBL7ZPLKpu6mDaVlmE(kfUyy2ROvblQ3jJBlS920b1QgjtlJC7GAdoSa4TNNgJIiKAfr8E4hbMfya5R9jfDdrjdlsBAah4ghuDIGGy(24791z6xLgMf948WGWGiwdu4RDjuwFLJRJCCDFHcxcgoS5LQBvnHR6nZyGTLXzgEGSgbR5yxDCjTKjb8E1I0YkKsbWZlWSny3wjZE0WpT6A9uhJdOI1aLVlANP6MFtHKXv44wqfoNQYUOP6gjwszDvOugmLLcsHAaUREWC5tdo5SA7NC61LrKMdEJfBTveykptUYaVcTPJWtDfRgqn0eSoCiPMguCfUzZh6PD(xxyrvx7mZ8Pem7AXJgaZnoHo38zOQJjTv7GdXi1OcaTZto)SuSWxvztQchv11PUmsTDDP5L9znYdcZNUG6FvxPbDLNxuQ7ZJ)Ikrtf51(DjURcGnBy4H0YQQvPzH5f7qlDIfODYntCiUdeRCGjcFTYiO2OQHp2hsnL3pBcEA06YwjvQrO2mzSIel70qJWOe5ZHDaFPT5RmbS4KJAK69Nv5vhVGL2AkHMzzAS2E)zCZ(GtgkVUKSLy7B7E2C)THB8N7heWI41awfDVRv7UTvw3mAa(r4vMdUAmD2Kr99Ws8s1nKx1gRymIvFLVTzO2mPUV5BvhiAvYbbHFHjdUNPOAdG8qanmSorQVY(e(tn5SH75W4RQ9Ko7Bg5S1Hlj9Ks6Ez0lo9ojYn5WHt0d8d2CHBALSd47aR(di2BR17zBYq0UGiwLdFlE3MAMNrhvDtryxWxBs)BsswpnMvMpwgxz3Iup6v6W1HUqoFRTTWy60Om0gjdKjKyPX0x7vsYMwdyptVmBReRub8vJWnbQQnmntHV1L7dTyGsk1ICLJKYLEBFsDq0(ZK7xONxEKkevFQi(2IPafP6lT4lo)YbfWkNlnVYRYDqEcExJRy53Dy6DKUanY7WbX1LBnZpA3AkJlUEIT1Aza0nqE(KIFz7tyP45kzdX5jYOe1nk5BOoYDXyVQgnRGAdQGztVUsaLDF8RvtE979ScYrnQFg1b6(LnOcDFld40bl(yBsy5hJmBjWbSgnAa1VYV3sETZaMoisW2COGp1Ftsu4YqEOwlZREJjE1oGoUmMVYf59wnvRf0rNaCTlqQkj1z11Zxu)Yb2wnaTfP61ntSf5vo4(91mSuXOxexEG6EWBIPk9c85XIBuT)IfuVcg(7HRJtyuFi2dFLaynHAX8lHLIqqG8JxQJUQdG0e48RdwLchsETaj06LKY0jKbM(wWJQggA10Yvlp08SJKN5q9nPz0uDLwXhLAfHm45CwyRsPu5Ev6od7jcpXIyC90PmwgQ(Qu26nCiYv(nM0kheNTZRr91IP5pfUJA67(I(GTOPxJSQRZRauXI2eU4ZLfPjEh39)VqJZrq5Q0DO1jGQKO1QN))TZqvcdpA9EhZQksRM7NqjjUS8YOu(bQYgIMxi5zv(CmnsOdiBMPNE24j5fjiUdvigklzv80v(P3TcebNrlq5Wa(Jn8oqovjWwKID3ESaAP22HXf0iSsAd69apkWzkVrMN3PZZc3eq5WX9HlWLBbRfk0Lfq15m4)LIv6k8lxeJibvrUEigB4XpGjqlV4wMKJQY6EfjFd2wKHvJRT(72t14R8ujbW4AKNvLvHpbawfGSLVxxNDXIvel2NsW80HJVy4OlhDTLIUby9)2p455DbD7UVW0A1XW)OLvlAvjJAQ8m1keRyrP7yXjThn4J()IZLrXtdRQz8gkoUSF1OmwGGP43J39RDbBbjILrjREVq2118RvSWQnAEtZlJoVfu9YYZUjmtOYairBLiYaiGeMN80Fuml)R86DICRxgApPYEGDpeJD9)TKyZGhdWnV04Y7P8Rqa5N3MYZBc5i3iF7s(oREHoMN4MLpVMkzUPDE1b1S)z6KbwRrfdC1cEtCyeNKrEmG3ZDkZAysI4t16EuTCo4jHBMVrY28M8Eb)B59cEfzLsBernRjOmAAEGSgAXyaNSOAfE7JhR4Ib8gkp2Z)qRa3JDlEC(J37uWvsei(7KTWF8)sq9pyPudUVe0fwz7mXcROIOyVfIW5PlT1EOBSf5gLNCD5ZhryPs58jbO0z4(XPIlpWPVIJ))90WvOzx51fZG1mU9koejhsZHqzjN(HRvuPQuOvJ6h)QMN7NaEeHRNV5T)tE91eaEuDgK)jLy6ir4iWEtj)QDJ1umlxyi72YhPqT3REL6)rjY2ZWuJ2vKl6rFovJ75hzA)Q4lt5BqQhLDeVan2JkDPH)btwWt5x5yGJJUZX5gB7NCPOCKIYi4)gkqduxG2X)qygwnqLt(81Rz9WfBUXBGokP0LZp9J31hSXJY8t2bK6zyr6SUE)ZPfad1HMII1LPItZTZ(Ur4bL5Gbu3o(t1b9nBunhnAXmr7mFlvb3QHI)10r6RBpDA7Pwcj9rGBP28rIR5WpmflJ0)EU2a0FSWLO(bSExdqI0MGwDMfJvn739ZlZvEG2yFhPXbnFI)5OX2NJwFIvuB81YijgOMO7WzlZmLXsrbZCO3CGxD6SV)IHxFkOpE(DXB3Nb2dYsV6M5VkjWRP76Z4rw)0Jb5TG70QL2TY1PQUWh1jKET4tUHWCzmPnLQppI37hOcONiEtfh2MMTki3g3af0FAIH)jQ0XJXpcy024)bmwuuaIq3Bky8eSB0xBYMMZkF1n7c3MAwfzzr41UGdwqH6ZMf7G7lqn9sjEpxA5FrE96N7mEOKbK6Pa(GGqFkAKBXark(1h4TRasyyIi4crXBxHrgh8tm3LWny)4N0MW5CjrRCExmAo4HvWDfpIrSXu0F4ZtQJnGQtfJi37toI9Gp3fuEBmaLgBHFVf3x(c1HgR75UQl1qvjFGLkaBd2fOEgek6In(Y1m(PXOQCJ2hy)SCV9QgC(0gVNzt1QWRLl3lpocjXzzHi6)mlhSf8f6jcqBmlSE5Ldp(KJ0IYSULGdMfs()AVR9EBJJJ4FwekGczLQdPEvBujby76I2I6gJOcK)Zk8XjXdHINkFefbyOp7DNz237SpUJ0okUb(FOPoU3UZo7883m7osiPJGONlqmDhienYorbW(pIo4p9joZMIFqT78Do4(ZSCtTAJrS4jcof)2wyXHQXLPhpNZchm84yhhSNNWCiWUWJUFY(bORpIZMLmtlVCPVC4a(jnpEllAIBhEu7L(o)f1c(k(3T9VdETLHpZSZ(E7Xn)T7Q6YvGlu1(k9432sI6YsUtH)WHqWNaPmHQ45dbGpomLB9gZYdSFBxswcel2rsILSXcudBzGk79Rs2GnLjyvDZeIScPlqSFur5HLWqY4lySr((M7itBx9F3uvbz6yeIPO1lhj(4ycIrOBwleFP99Z2d1leF4qiZ5ZRwjSzMs8c(0lap5MJypzj6oMYDlsjHbskZAEaSYEbKT9nlUfUq2wQYBRYvXv8lzo8xZktiKrkDtSnOSOZ9w3kiEln4qo)uGc1fe3(TL6YNAmlZIw8gErJLqLw)5mtxZe2QHL5TK1t3qCWg2GwmdZbUWmpZJOqzkEa4hqgvbDybYJwjbDhYTF1vec7(p)ae3GBRwmvCEsmjPyk8aYGcmWV7NBSCge(LqSA)wiEV0be7mdcEwcX7L(0338bi6eIpbvu5QzyJ9DdEFo(n)m6(AfIMm4RuHPWA150GlDS)1chEoyO0bHEoMgh5EIa84kIDaoLwPl1TFgTWiTh88wUo9w9(syuXMHHCCO(6LeZrlUx0GxjMWfJ5JRNr7RZTAw3EeoFxh6GrfPLRhtnwmxbtGm6ORGwLae8fNily2f6s4BQOmHQnW8vNMySyo(Bgq5FuLEFBI4mNgpf3FXOYTxS05fBptiHRF6ApKtwK3kZpAXe4SUvOHwnyZRNmlXaey2e)X28exzLEeLbgI3v2bXQ)xfFyIBbBkBIs8Y5t)dbB3tpO3WCzEjya3K(QFah5Z2X3Xcz2ATLeQWlggnBK4SIqB4BFRkC4YGzJrLMYoiMHXhRwFObEXeDf1nbYsqzM3TzXeswOYwnTspuL1FxHPCLMUc4kkqT9(HFVLzYoFVTvYXIaaBVgay9mAVcWVEQd6i1)FlgPric7bY79yEBP7bijjvL)wfa1mjPfn3atiHbKOyokgJy9dPSOoEC3ZyrWQyBdrS5iXEH5glAVi68te9HE8k97M3n9tX07kqS9wuK68KFCO3PJoVqOeMp37Sdok21FbNnNro(PopdmnkJlja3IGnDZD3)exTHKFbzFkWXlXWto8Uo0tPC)8EhbIHheC5p0xgxR2DUpf7J72vjt72Y9Y6JyX6s4I5E7zdJ3SsSR96Hw1p7(roANIwYgOQNRtwFU10XUvfsJp08(4H0qvipVHKERIpbNNBmIP1339tM1itFO9xjVOwqFiQMk9vu8NLkSLtnqJIoQkrFNWpvxvJYh(1VhD9zXIki5KaM0vf4aMJBHrMP0vWeEG9AxjWNpAg6eOyB7O7D)cocXlP9(AJIqI1mzw4hxvTqz1tWMMvWOSl2Q61fZhO3pekSDCEVD0toXiPI2YoHCL74J2(9W7hcvyfpv7Zt2oUWLdi9yv2rbXop5gmxOmkIIVhAAesJfCC1lRSnj6q0J9fsCHa1dhatoHkcyVeZTJSq4W9DW2mX(eCaeHthfQXXcjWy4BAWICtwdVqLxiTaddpa8ckYQRuB9HkKsyMP5(MS3jd41y2rLwXnTls4C2pQvIHDtHz6s)rCirAxXpUAMqv3uzv0yaPOUoxQwiVCQu4D8DACv5JWIPlBU)ECxqdbhYSAIxcp7IFsJ4hZ(6SLYsYYWtG8yTzhl(rvVTq5jvhGZAhuutmTcitJKqXdOui9He2mjqKJr8pqAuI0aQH4G)AtSf1KkkyyTE9gyoxKWJKsKfqisWUKBkz73ykvnmAMOG5y1)qiJuG3O9fuD)SQPU0vQlBq3vX55qH1QZF8A8)DnNVUxl1ihf)4WvZJoxDq4jLMSYgBvNRTtVyV3VFF)igiDs(qVAAdv)bboGq80KrqanOJquXclM6vyHmobc)8sijqqSeA1UhpAgANz)465Q1JwUwxInW0gm0HM6VwfJCldaA1Sm8LtXMiDuBJT1WT7wsibL)KmDELez7n)y72LDcN66OYomvuzDIgEe)cIi1O9TNJSljR01aw3Vy1M7fkTWYGFc0RGu3yo)fhnngm0Jf5ofENXlB(jLfgqizRXxTe2P3vT2R0bLYZUW51a0ePOdZrt5XFX83chxqKVLNuNdI1HPfzgeAkIjrS073uqaItY3cgSmtCGx(WIxLHujNx(7uABmU4yR76jEOL3HCCC(Wb9zm7tgRrLSFkEeQozakdcSfy4zMaAod76kOxsWQCcDREzzBivP2ZRUznr0uQ9asSgKUWogDeNuJEdcyWxWrLyKiyI7foRuQUOEcbSstKUHHNTVx)EY9c7LmB2(EfZYymUTdlHh6Odjfe(o59XluCTYlKxLTdG50vpODMrT6aHMir5Hr10vcQHWcRjll1wrYC1D)bSrMuibuhH8GBmy1n8QLLs(pIbkdN9Y93tDAk4PMwVcOzt5Msfg9fFJ78UHpHk4OBJEjHBENGCpH)Ho(cEu41uk)AiiQO)AoLTsPIp1eS1jARl4YlGEktX9hHlhoCad5PFBgdVvkdhJDCUn0B2OQPP5(LmDgcOlwP)6N4ztfYhLpgMD)SLwcZEbTs1yaHdAS19I0VwCUeqrV)GBj4jg59L1Mu9pdXs3AZH8841TRmZM1RDR8Wen2JW67bjsMeuGATF9ho47)UpOcFdyx2BM30mDowVnuzbTwx0ohbogSICzzcVAHi(8YyNDb5cOapL9(YzEneRZsfnbxhqLt5xew9hJvuemFeo0umSb1sxKItdTjCfD6jPr)2ZN9JCJHFjCx4M(G2LdpQVRRY(uVtJMfPlbp8785p(tsS0GahC26f8UDs3IJ)f24MnEPkd4qxXy8PzQEwDNZcU4K3k6sykvvDolPhiu5DHToQ1RQMFdDMryNPkMIJeopS4jRo3HnyMlvQOd)W5NoiMUdC12zn6PuFkdTqu50rJ5qkzH5FFLM2Gloo9T8COJtw7TtBQ1HR)1nVtdn4vckzf5x0JnBuo(JB7AFsvEEQDMCvfqKnJHbVmQ((L6j0AR0)2BA2SC9SYu(umL5Ooqz89vKGqq9ekpKk6Xut8(uUSliya2R1P)sTeDYPMxYXKXRwLAgY)97KrLgYPwd8W0mKnkkc6d4r6Hcc7fdEXGtAf5zihMiHjwBjBhoVgyXNEXrNg6SDab0JgOcPVjPhcXyRXG00yNfyvVX121BeRy(mCpueNDkwnDA9kNsAcB)m56TGlGDogWOZkNMdClMzFMMbXAwxT)99)th56vWtQ1r42WZtYxn05i4oviAbpyjA10hQIPhv4qCbVkdujYoZpVGh0dCeOpaqb7zXmnUA9dygsClFMvet0MfO5oV5Fr240aazelPjKb0VtjwpTcJzEr7qL6TueFiY5qUxhViWjldbbUfw09pcfkAGQ(ho643XDYBytu7LooMTDdhuuXPDkwTBCM5LY8UssPfNlQoHxMsCO0qhY)FHAm4kobBMWs5rKZJsG04IKJzuEd1gl1GXug0PTeHb7ywzBlBU)BX2VaLJ7TLTksEPDDPMVqQ4OqDe8fr5kP)co6xRiU6xtG)8pl2sCbtgflb0kA1lbflcYswuVUMYNM(fcF4g(IukU4vIg5JQjiMcDYpf46OOuYEF46Jk6EUuCtWutANcjagZV14kzFHHSaelKsvITCGFMWBVj)eMcYn1GjKAqCOlosanV3Sww7zpkPRakFrq9(OCpt7stamFn05CGsRykk)rfx95UP(kW3T)XcLnT1R3q96ZdnP3VELDXBnvv4wydZemzw4y8sTvnBPGXKg754IGJ0s25V(8eeCsx((FCz1TJwoL08a8e(oHwUKVwloQ4LOrHjJplaqlKUSq0)6qAGNNpitnYg7VALHt59QKtwOj7DJDTDR)VgsJDufqNp8mn4L0aVW(ZxF78MXJMBJaJDpdz8OrY9w5bZWqYr9VZOqIQXxjwaDLMwl1KoAUeLHKEYvna6rjio5Cr9Lr5eZcURXqtSe(ByKPKXx8U02Nkl2leFxUGhPmgJ9IeMA0yAnT94b9lMl5pGx8f3iHyaUiMtfUVqqawTkuRYhTf5qcoTurqDh0Q4r)H5GemE4JG0JtRaaMdlG7DVWUbWeQbS7AXcYACdIPyjdcsdn9Ktz0wYsnnvsl7F10Jpn)zcZsx8zRgC1yIAlQ92cRBfMFGv)Jm8pgKbc9l)KbCpUFww8bqiZVjSaSyEOvbn)IqJxq3ptMtcMbw(y(vrR3VEhu9RrXYxlRgsYdZxEcZiNiv6S1BtjBG8K5N(9kXXPsC(9kWPK9)8vGZZ3kV5lsf3O6RVVX2bK1bvDXdmfyQIuNQoVZv6aS0wo5OqlKj1njtrRefNG504NR1vRcDzIsIiFfr87vcXUTsikHUgmhTDDPWoqqxFtFbQZGph1xWoPUc(1TEcYuhbQhBzZACQ1Dq(3QE7OfNNZ3ZLDNkvTkY6hiFremAmMxh034k0vY)P)kLvmylBeNrKbYdr442PCCcqjfLlLVPpgjFmHJItx8RqQtl6yHjx1jVhEsSIBdyH3Qo75VviiDP8vkcxVFDWV0re7LAX39UcAPeTsnfUKh0Q(O51RWPD12uUun9UmwbME9(ShBbfm3XXJlXDBNtR5Cgl1C7lHD09YTekXqARdHbuq5WapIBZb45Q13clvERy)ydeDDY3TF5EHzjq0bUVzn0IhGkxdgziTsJK3tOyOGN3qDEw9tr1HCHBXblCB0pOMexRh9xK2oOsg5i0YiuTDbNcJP(DBQh(9z9)XjhOkx1xVuMY)X4TGlKexmzxkwKNm92mSlPrTOkXs)onk2E8B8msvSxvVS4t2LVcnH4PxKL)FSKTGdKTc307flQGewFn0nnQijK9wwDJ409mqcpCxOupbUrA6Ap(Gxfw6lmLuK0DHhIwJ6Qn3xTuLtc9pLTUJnLOSDQjYPQj8k3p2u)YJro2)kY5nXpRG3mIQjC0rkkBSIxF1siATYKrnd21AMBv212TUezZObbyMIPVmJBIDdyLMC1n2fxDCIn7a)hOI7WkWvw7lds2HtkAX(vXA98JheJxcrzOPv2ugr53wR(wfuddqnFjzuqamNviN)wiMmYEXGYMbBWoRd7RvoykJ8YPEor8PrAr3i69It1(0NIfb4DfDXoC4TGWSRcW9ZhkwAYKD6OEsEPBshzrJp3yDTAIuu1vwF5u0IODPvtxKtrDMQBvElijrwNairOgZahANdGUIfy(xK4rkAHgaEcPROodc4w9Kv7Shl)bxuEOLCcM7nd7a(GouPFvAldT2QBUtm6tX88BLmhCsPQAUc3PIJ)O4wqCHxV9akTb3V5sz7X1ZLXuZjhWT1HzvRW9w7NEEODX6bCcDFsvyfSLydzMFEudDvURpTaVbS1H8NDFQa7wt)qoK6ip6M0TbCRAnleHf76UdUCk5qOli05fClLLlSX5gHtcNLjAi7Nvsa)V4v7hA(GTZ5j2l7KuqMxNYf8JgS)Es5Vm4Oj(KYVM7y)XutWz4XBXGOMNxm8vfqAdErbNTIO2k)q3Mw)ItVOrotyRAAKN5IHhNJjLpaGzh5Jhuq7UWDlrtOkR3J8LKt9enNAoN9JWLvYM8lZMaLx1nlr8RyGZ(tC3Cdh5CZn4UoSJbK3G9QOd2wz4vjbORJrL1NAuYIoyiPaFLrrz0DRb5oeFAuokJXv45SUEzuxGyhzixz908Ot58dTq5zcXodJYQv0b5cr0zhg50eTOTI9pE1OnRN1S8JxDv9DBMJAZFlClZ)r4FF8)b]] )
|
|
|
|
|
|
end
|