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.

1879 lines
148 KiB

5 years ago
-- MageArcane.lua
-- June 2018
local addon, ns = ...
local Hekili = _G[ addon ]
local class = Hekili.Class
local state = Hekili.State
local PTR = ns.PTR
-- 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 )
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 pvptalent.arcane_empowerment.enabled and 5 or 1 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 = 8,
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
}
} )
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
x:RegisterHook( "reset_precast", function ()
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
end )
x:RegisterStateFunction( "update_disciplinary_command", function( elem )
if not legendary.disciplinary_command.enabled or cooldown.buff_disciplinary_command.remains > 0 then return end
if elem == "arcane" then applyBuff( "disciplinary_command_arcane" ) end
if elem == "fire" then applyBuff( "disciplinary_command_fire" ) end
if elem == "frost" then applyBuff( "disciplinary_command_frost" ) end
if cooldown.buff_disciplinary_command.remains == 0 and 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 )
end
end )
x:RegisterHook( "runHandler", function( action )
local a = class.abilities[ action ]
if a then
update_disciplinary_command( a.discipline or state.spec.key )
end
end )
x:RegisterHook( "COMBAT_LOG_EVENT_UNFILTERED", function( event, _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName )
if sourceGUID == GUID then
if subtype == "SPELL_CAST_SUCCESS" then
local ability = class.abilities[ spellID ]
if ability then
if ability.discipline == "frost" then
__last_frost = GetTime()
elseif ability.discipline == "fire" then
__last_fire = GetTime()
else
__last_arcane = GetTime()
end
end
elseif subtype == "SPELL_AURA_APPLIED" and spellID == class.auras.disciplinary_command.id then
__last_disciplinary_command = GetTime()
end
end
end )
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
-- 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( event, _, 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." )
end
elseif subtype == "SPELL_AURA_REMOVED" and ( spellID == 276743 or spellID == 263725 ) then
-- Clearcasting was consumed.
clearcasting_consumed = GetTime()
end
end
end )
spec:RegisterVariable( "have_opened", function ()
local val = 0
if active_enemies > 2 then
val = 1
end
-- 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)
if val == 0 and prev_gcd[1].evocation and not ( runeforge.siphon_storm.enabled or runeforge.temporal_warp.enabled ) then
val = 1
end
-- 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)
if val == 0 and buff.arcane_power.down and cooldown.arcane_power.remains > 0 and ( runeforge.siphon_storm.enabled or runeforge.temporal_warp.enabled ) then
val = 1
end
return val
end )
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
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 = nil
incanters_flow.reset()
end )
-- 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
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
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" )
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
5 years ago
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
5 years ago
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 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,
gcd = "spell",
spend = 0.18,
spendType = "mana",
startsCombat = false,
texture = 134132,
usable = function ()
if GetItemCount( 36799 ) ~= 0 or 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 IsUsableItem( 36799 ) or state.fake_mana_gem
end,
cast = 0,
cooldown = 120,
gcd = "off",
startsCombat = false,
texture = 134132,
item = 36799,
bagItem = true,
usable = function ()
if GetItemCount( 36799 ) == 0 and not fake_mana_gem then return false, "requires mana_gem in bags" end
return true
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
5 years ago
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" )
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 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 ()
applyDebuff( "target", "radiant_spark" )
applyDebuff( "target", "radiant_spark_vulnerability" )
end,
auras = {
radiant_spark = {
id = 307443,
duration = 8,
max_stack = 1
},
radiant_spark_vulnerability = {
id = 307454,
duration = 8,
max_stack = 4
}
}
},
-- 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" )
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,
5 years ago
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", 20210707, [[da1k6gqiPK6rQQuUerfPKnrr(ecAuirNcjSkPeYRqOAwuuDlPKGDrYViImmIOogrPLruPNHqPPruHUgcfBJOI6BsjIXjLiDoPeyDeH6DevKsnpvv5EiP9ruLdkLOwOQQ6HQQKjsurkUirfXgjQiv(irfjgjrfjDsPKOvsr5LevKQMjri3ukHANeb)uvLQAOsjPLQQsLNsjtvvfxLOc6RevGXsuv7vv(lPgSshg1IrQhtyYaUm0MLQplfJMsDAqRwkj0RrGzRWTr0UP63IgofoUucA5cpxrtxLRd02jsFxvz8efNxk16vvPkZhHSFj)K99ZZcGp8jb5kz5kRKBjsULOKClqwjl3wWZ6ABGpldwqa3GplNjXNvlhc2XNLb3EKmW7NN1mbdb(SSVZykXsssnWZgKwjssjnHKGd(GPlcUFsAcjfs6zrdchxR0F0pla(WNeKRKLRSsULi5wIsYTazLSCL7ZAAGINeKZY9zzdbaq)r)SaWP4z1I5gS2woeSJLzMboAxBlX8ALRKLRSLzLz)YM9gCkXLzTc1khoXAV2gqbpQ1cs(RATzhya9MAZETcB2DCul0pmcqJdMETqFEiduB2RLqb7cCOzXbtNqvzwRqT)YM9gSwoeSJAO3Ho8Ax7L1YHGDuBZbz6TRLs4vRJsXO2p0VAhqPyT8SwoeSJAO3Ho8AtH6znGZB((5zLgOJX7NNeK99ZZcDMEGaV)FwIaEya5Nva6ypJgubaNcOXa6C0wlsss2buOZ0deOwt1sd27ka4uangqNJ2ArssYoGUh58uGgplwCW0FwDyGA6bpV39KGCF)8SqNPhiW7)NLiGhgq(zfGo2ZObvnbCoARHcOyGk0z6bcuRPAjzNvgIRw5vBlGyEwS4GP)S6ropTNs539KaX((5zXIdM(Zca5ZModhFwOZ0de49)7Esqo((5zHotpqG3)plrapmG8ZIKDwziUALxTYrj)SyXbt)zfmaK9tpn4GG39KaX8(5zXIdM(ZIegrgtD21xgKOFpl0z6bc8()DpjiNF)8SqNPhiW7)NLiGhgq(zrd27koeSJAJ8ddfq(51AQwrMdG8ZvCiyh1g5hgQajzOpFwS4GP)SM2W(b9gTr(HX7EsOL8(5zHotpqG3)plrapmG8ZsK5ai)Cfhc2rTr(HHkqgODTMQLgS3vCiyh1cBoAq18ybb1(xT0G9UIdb7OwyZrdQizz0ZJfe8SyXbt)zXHGDuNb97EsOL((5zHotpqG3)plrapmG8ZsKsrN9tjf9ZUDuRPAfzoaYpxrcJiJPo76lds0pvGKm0N1kVABPYXNfloy6ploeSJA6bpV39Kql49ZZIfhm9N1LGcBD21NnQj5g4ZcDMEGaV)F3tcYk53pplwCW0FwCiyh1g5hgpl0z6bc8()DpjiRSVFEwOZ0de49)ZseWddi)SOb7Dfhc2rTr(HHci)8Nfloy6pRa0rD21g5hgV7jbzL77NNf6m9abE))SyXbt)zze4eDbQZUMe6aplaCkcOXbt)zjhoXAB1Sfx7L1oBHGi(7H1YETOmxW12YHGDS2)h88QfamGEtTNnw7p51ILul3Q1(bDG8RwqFGZzTbO7qVP2woeSJ1kNiStvTTYETTCiyhRvoryN1cN1E8a9dbmV2pSwb7eE1coXAB1Sfx7h8SHETNnw7p51ILul3Q1(bDG8RwqFGZzTFyTq)WianUApBS2wUfxRWMDhhMx7mR9djCmQDYsXAHN6zjc4HbKFwTU2JhOFkoeSJAuyNk0z6bcuRPAbqAWExDjOWwND9zJAsUbQanQ1uTainyVRUeuyRZU(Srnj3avbsYqFw7FuRLYAzXbtxXHGDutp45Pqzqb4H6dsI12IQLgS3vgborxG6SRjHoGIKLrppwqqTu8UNeKLyF)8SqNPhiW7)Nfloy6plJaNOlqD21Kqh4zbGtranoy6pRwzV2wnBX1AZtNWRwAe9AbNiqTaGb0BQ9SXA)jVwCTFqhi)mV2pKWXOwWjwl8Q9YANTqqe)9WAzVwuMl4AB5qWow7)dEE1c9ApBS2Fx2QsQLB1A)Goq(PEwIaEya5NfnyVR4qWoQnYpmuGg1AQwAWExfGoQZU2i)Wqfijd9zT)rTwkRLfhmDfhc2rn9GNNcLbfGhQpijwBlQwAWExze4eDbQZUMe6akswg98ybb1sX7Esqw547NNf6m9abE))Seb8WaYplG8ubdaz)0tdoiqfijd9zTYRwIPwIiQwaKgS3vbdaz)0tdoiqlfC4yW0Wb8ARMhliOw5vRKFwS4GP)S4qWoQPh88E3tcYsmVFEwOZ0de49)ZIfhm9Nfhc2rnnhb3GplaCkcOXbt)z1YJpU9S2)5i4gSw(Q9SXArhO2SxBl3Q1(zJETbO7qVP2ZgRTLdb7yTYPYbz6TRDGnOdWr7NLiGhgq(zrd27koeSJAJ8ddfOrTMQLgS3vCiyh1g5hgQajzOpR9VABea1AQ2a0XEgnOIdb7O2MdY0BRqNPhiW7Esqw587NNf6m9abE))SyXbt)zXHGDutZrWn4ZcaNIaACW0FwT84JBpR9FocUbRLVApBSw0bQn71E2yT)USvR9d6a5xTF2OxBa6o0BQ9SXAB5qWowRCQCqME7Ahyd6aC0(zjc4HbKFw0G9UkaDuNDTr(HHc0Owt1sd27koeSJAJ8ddfq(51AQwAWExfGoQZU2i)Wqfijd9zT)rT2gbqTMQnaDSNrdQ4qWoQT5Gm92k0z6bc8UNeKTL8(5zHotpqG3)plHnd9NLSplKJrBTWMHUg2Fw0G9Usmqoe88GEJwyZUJdfq(5MOKgS3vCiyh1g5hgkqdIiIYwF8a9tLsXWi)WabmrjnyVRcqh1zxBKFyOaniIirMdG8ZvO0uWhmDvGmqBkOGINLiGhgq(zbG0G9U6sqHTo76Zg1KCdubAuRPApEG(P4qWoQrHDQqNPhiqTMQLYAPb7DfaYNnDgoQaYpVwIiQwwCqPOgDKeIZAPwRS1srTMQfaPb7D1LGcBD21NnQj5gOkqsg6ZALxTS4GPR4qWoQjHZjCGtfkdkapuFqs8zXIdM(ZIdb7OMeoNWboF3tcY2sF)8SqNPhiW7)NLiGhgq(zrd27kXa5qWZd6nQ5XccQLAT0G9Usmqoe88GEJIKLrppwqqTMQvKsrN9tjf9ZUD8SyXbt)zXHGDutcNt4aNV7jbzBbVFEwOZ0de49)ZIfhm9Nfhc2rnjCoHdC(Se2m0FwY(Seb8WaYplAWExjgihcEEqVrfilUAnvRiZbq(5koeSJAJ8ddvGKm0N1AQwkRLgS3vbOJ6SRnYpmuGg1ser1sd27koeSJAJ8ddfOrTu8UNeKRKF)8SqNPhiW7)NLiGhgq(zrd27koeSJAHnhnOAESGGA)JATs5aY0duD5rQjzz0cBoAW5ZIfhm9Nfhc2rDg0V7jb5k77NNf6m9abE))Seb8WaYplAWExfGoQZU2i)WqbAulrevlj7SYqC1kVALLyEwS4GP)S4qWoQPh88E3tcYvUVFEwOZ0de49)ZIfhm9Nfknf8bt)zb9dJa040W(ZIKDwzio5rTLsmplOFyeGgNgssIaq(WNLSplrapmG8ZIgS3vbOJ6SRnYpmua5NxRPAPb7Dfhc2rTr(HHci)839KGCj23pplwCW0FwCiyh10CeCd(SqNPhiW7)39UNvhoTHEJonqhJ3ppji77NNf6m9abE))SyXbt)zHstbFW0Fwa4ueqJdM(ZsoWg9Adq3HEtTi8SXO2ZgR1YQ2mQ9h5GAhyd6aCaXP51(H1(X(v7L1kNinRLg7zG1E2yT)KxlwsTCRw7h0bYpvTYHtSw4vlpRDMPxlpR93LTAT28S2o0HtBeO2emQ9djukw70a9R2emQvyZrdoFwIaEya5NfL1gGo2ZObvhsAKbp0FCyOqNPhiqTeruTuwBa6ypJgunHg2PRNxgKk0z6bcuRPABDTs5aY0duzeOb4yOrPzTuRv2APOwkQ1uTuwlnyVRcqh1zxBKFyOaYpVwIiQwJaLQBeakzvCiyh10CeCdwlf1AQwrMdG8ZvbOJ6SRnYpmubsYqF(U
end