Parry Leveling Weakaura
Automatically allocates talents for a parry tank.
Made this to solo/duo/trio prestige trials. Designed around Undaunted/CS LRE.
|
|
| Quick Facts |
|---|
Automatically allocates talents for a parry tank.
Made this to solo/duo/trio prestige trials. Designed around Undaunted/CS LRE.
Contains custom functions.
Code has been reviewed and is safe to use, custom functions might affect in-game performance.
{
"m": "d",
"d": {
"iconSource": -1,
"authorOptions": [
{
"fontSize": "large",
"type": "description",
"text": "Allow Purges",
"width": 1
},
{
"type": "description",
"fontSize": "large",
"text": "Debug",
"width": 1
},
{
"type": "space",
"variableWidth": true,
"height": 1,
"useHeight": false,
"width": 2
},
{
"type": "toggle",
"key": "allowPurges",
"desc": "If enabled, will use a purge if there aren't sufficient ability or talent essences to learn all specified abilities or talents. If disabled, will learn abilities and talents as the essence to learn them becomes available from leveling, starting with the lowest level unlearned abilities and talents first.",
"useDesc": true,
"name": "Enabled",
"default": false,
"width": 1
},
{
"type": "toggle",
"key": "debugEnabled",
"width": 1,
"default": false,
"name": "Enabled",
"useDesc": true,
"desc": ""
},
{
"type": "space",
"variableWidth": true,
"height": 1,
"width": 2,
"useHeight": false
},
{
"subOptions": [
{
"type": "input",
"useDesc": false,
"width": 1,
"default": "",
"name": "Character Name - Server Name",
"multiline": false,
"length": 10,
"key": "characterName",
"useLength": false
},
{
"type": "input",
"useDesc": false,
"width": 1,
"desc": "",
"key": "profileName",
"multiline": false,
"default": "",
"length": 10,
"name": "Profile Name",
"useLength": false
}
],
"type": "group",
"useDesc": false,
"nameSource": 1,
"width": 1,
"useCollapse": true,
"key": "profiles",
"name": "Character Profiles",
"hideReorder": true,
"limitType": "none",
"groupType": "array",
"collapse": false,
"size": 10
}
],
"yOffset": 0,
"anchorPoint": "CENTER",
"cooldownEdge": false,
"icon": true,
"triggers": {
"1": {
"trigger": {
"type": "custom",
"subeventSuffix": "_CAST_START",
"event": "Health",
"subeventPrefix": "SPELL",
"debuffType": "HELPFUL",
"unit": "player",
"custom": "function(event)\n if event == \"OPTIONS\" then return end\n if aura_env.enabled then\n aura_env.ensureCorrectAdvancements()\n end\nend",
"names": [],
"custom_type": "event",
"events": "PLAYER_ENTERING_WORLD",
"spellIds": [],
"custom_hide": "timed"
},
"untrigger": []
},
"2": {
"trigger": {
"type": "custom",
"events": "UNIT_LEVEL, POLI_LEARN_ADVANCEMENTS",
"custom_type": "event",
"custom_hide": "timed",
"custom": "function(event, unit)\n if event == \"OPTIONS\" or unit ~= \"player\" then return end\n local level = UnitLevel(\"player\")\n if aura_env.enabled and level ~= 70 then\n if level >= 10 then\n if not InCombatLockdown() then\n aura_env.ensureCorrectAdvancements()\n else\n aura_env.pendingAdvancement = true\n end\n end\n elseif level == 1 and aura_env.selectedProfile then\n aura_env.enabled = true\n aura_env.ensureCorrectAdvancements()\n elseif level == 70 and aura_env.enabled then\n aura_env.enabled = false\n else\n print(\"[Leveling Spell and Talent Automation] No leveling profile selected\")\n end\nend",
"debuffType": "HELPFUL",
"unit": "player"
},
"untrigger": []
},
"3": {
"trigger": {
"type": "custom",
"events": "PLAYER_REGEN_ENABLED",
"custom_type": "event",
"unit": "player",
"debuffType": "HELPFUL",
"custom": "function(event)\n if event == \"OPTIONS\" then return end\n \n if aura_env.enabled and aura_env.pendingAdvancement then\n aura_env.ensureCorrectAdvancements()\n aura_env.pendingAdvancement = false\n end\nend",
"custom_hide": "timed"
},
"untrigger": []
},
"activeTriggerMode": -10
},
"internalVersion": 52,
"keepAspectRatio": false,
"selfPoint": "CENTER",
"desaturate": false,
"subRegions": [
{
"type": "subbackground"
},
{
"text_shadowXOffset": 0,
"text_text_format_s_format": "none",
"text_text": "%s",
"text_shadowColor": [
0,
0,
0,
1
],
"text_selfPoint": "AUTO",
"text_automaticWidth": "Auto",
"text_fixedWidth": 64,
"anchorYOffset": 0,
"text_justify": "CENTER",
"type": "subtext",
"text_color": [
1,
1,
1,
1
],
"text_font": "Friz Quadrata TT",
"text_shadowYOffset": 0,
"text_wordWrap": "WordWrap",
"text_visible": true,
"text_anchorPoint": "INNER_BOTTOMRIGHT",
"text_fontSize": 12,
"anchorXOffset": 0,
"text_fontType": "OUTLINE"
},
{
"glowFrequency": 0.25,
"type": "subglow",
"useGlowColor": false,
"glowType": "buttonOverlay",
"glowLength": 10,
"glowYOffset": 0,
"glowColor": [
1,
1,
1,
1
],
"glowXOffset": 0,
"glow": false,
"glowThickness": 1,
"glowScale": 1,
"glowLines": 8,
"glowBorder": false
}
],
"height": 64,
"load": {
"use_level": false,
"talent": {
"multi": []
},
"spec": {
"multi": []
},
"use_spellknown": false,
"level_operator": "<",
"class": {
"multi": []
},
"use_never": false,
"level": "70",
"spellknown": 25780,
"size": {
"multi": []
}
},
"source": "import",
"regionType": "icon",
"actions": {
"start": [],
"finish": [],
"init": {
"do_custom": true,
"custom": "-- Made by Poli\n\n-----------------------------------------------\n-- DON'T TOUCH ANYTHING BETWEEN THE ARROWS --\n-----------------------------------------------\n\n-- | | | | | | | | | | | | | | | | | | | | -- \n-- v v v v v v v v v v v v v v v v v v v v --\n\n\n\nlocal CA_LearnSpell, CA_LearnTalent, CA_GetTalentInfo = CA_LearnSpell, CA_LearnTalent, CA_GetTalentInfo\nlocal GetSpellInfo, UnitLevel = GetSpellInfo, UnitLevel\nlocal setmetatable, pairs, ipairs, error, wipe = setmetatable, pairs, ipairs, error, wipe\nlocal sformat, slower, tostring = string.format, string.lower, tostring\nlocal CAO_Spell_References, C_PrimaryStat = CAO_Spell_References, C_PrimaryStat\nlocal GetItemCount, ItemData, C_Spell, CA_GetSpellInfo = GetItemCount, ItemData, C_Spell, CA_GetSpellInfo\nlocal type, assert, tinsert, unpack = type, assert, table.insert, unpack\n\nlocal c = aura_env.config\nlocal print = function(text) if c.debugEnabled then print(text) end end\n\nlocal meta = {\n __unm = function(t)\n t.op = \"remove\"\n return t\n end,\n __mul = function(val1, val2)\n local returns = {}\n local self = type(val1) == \"table\" and val1 or val2\n local num = self ~= val1 and val1 or val2\n assert(type(self) == \"table\" and type(num) == \"number\" and num > 1)\n for i=1, num do\n tinsert(returns, self)\n end\n return returns\n end\n}\n\nlocal talentData = { type = \"talent\" }\nfor _, talentInfo in pairs(CAO_Talents) do\n local talentID = talentInfo[2][1]\n local talentName = GetSpellInfo(talentID)\n talentData[slower(talentName)] = talentID\nend\ntalentData[\"dual wield specialization\"] = nil\ntalentData[\"dual wield specialization - warrior\"] = 23584\ntalentData[\"dual wield specialization - rogue\"] = 13715\ntalentData[\"vengeance\"] = nil\ntalentData[\"vengeance - druid\"] = 16909\ntalentData[\"vengeance - paladin\"] = 20049\ntalentData[\"intensity - warlock\"] = 18135\ntalentData[\"intensity - druid\"] = 17106\n\n\nlocal talentIDs = {}\nfor talentName, talentID in pairs(talentData) do\n talentIDs[talentID] = talentName\nend\n\nlocal abilityData = { type = \"ability\" }\nfor abilityID in pairs(CAO_Spells) do\n local abilityName = GetSpellInfo(abilityID)\n abilityData[slower(abilityName)] = abilityID\nend\n\nlocal abilityIDs = {}\nfor abilityName, abilityID in pairs(abilityData) do\n abilityIDs[abilityID] = abilityName\nend\n\n\nlocal psData = {\n strength = 1,\n agility = 2,\n intellect = 3,\n spirit = 4\n}\n\nlocal function createAdvancement(data, k)\n local lowercase = slower(k)\n if k == \"\" then return {} end\n if lowercase == \"remove all\" then\n return { type = data.type, op = lowercase }\n end\n \n local advancementID = data[lowercase]\n if not advancementID then\n error(sformat(\"ID for %s \\\"%s\\\" not found\", data.type, tostring(k)))\n end\n local d = setmetatable({}, meta)\n \n d.id = advancementID\n d.type = data.type\n d.op = \"add\"\n return d\nend\nlocal function createTalent(t, k)\n return createAdvancement(talentData, k)\nend\nlocal function createAbility(t, k)\n return createAdvancement(abilityData, k)\nend\nlocal function createPrimaryStat(t, k)\n local stat = psData[slower(k)]\n if not stat then\n error(sformat(\"Primary stat %s not found. Acceptable values: Strength, Agility, Intellect, Spirit\", tostring(k)))\n end\n return { type = \"primary stat\", stat = stat }\nend\n\nlocal t = setmetatable({}, { __index = createTalent })\nlocal a = setmetatable({}, { __index = createAbility })\nlocal ps = setmetatable({}, { __index = createPrimaryStat })\n\n\n-- ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ --\n-- | | | | | | | | | | | | | | | | | | | | --\n\naura_env.profiles = {\n [\"Arcane Leveling\"] = {\n startingAbilities = { a[\"\"], a[\"\"], ps[\"Strength\"] },\n levels = {\n [10] = { t[\"Improved Consecration\"] },\n [11] = { t[\"Improved Consecration\"] },\n [12] = { t[\"Improved Consecration\"] },\n [13] = { t[\"Deflection\"] },\n [14] = { t[\"Deflection\"] },\n [15] = { t[\"Deflection\"] },\n [16] = { t[\"Tactical Mastery\"] },\n [17] = { t[\"Tactical Mastery\"] },\n [18] = { t[\"Tactical Mastery\"] },\n [19] = { t[\"Iron Will\"] },\n [20] = { t[\"Iron Will\"] }, \n [21] = { t[\"Iron Will\"] },\n [22] = { t[\"Light's Vengeance\"] },\n [23] = { t[\"Light's Vengeance\"] },\n [24] = { t[\"Light's Vengeance\"] },\n [25] = { t[\"Improved Riposte\"] },\n [26] = { t[\"Improved Riposte\"] },\n [27] = { t[\"Improved Riposte\"] },\n [28] = { t[\"Vindication\"] },\n [29] = { t[\"Vindication\"] },\n [30] = { t[\"Vindication\"] }, \n [31] = { t[\"Reckoning\"] },\n [32] = { t[\"Reckoning\"] },\n [33] = { t[\"Reckoning\"] },\n [34] = { t[\"Reckoning\"] },\n [35] = { t[\"Nether Protection\"] },\n [36] = { t[\"Adaptive Defense\"] },\n [37] = { t[\"Adaptive Defense\"] },\n [38] = { t[\"Adaptive Defense\"] },\n [39] = { t[\"Adaptive Defense\"] },\n [40] = { t[\"Adaptive Defense\"] },\n [41] = { t[\"Spiritual Attunement\"] },\n [42] = { t[\"Nether Protection\"] },\n [43] = { t[\"Divinity\"] },\n [44] = { t[\"Divinity\"] },\n [45] = { t[\"Divinity\"] },\n [46] = { t[\"Enveloping Shadows\"] },\n [47] = { t[\"Enveloping Shadows\"] },\n [48] = { t[\"Unfair Advantage\"] },\n [49] = { t[\"Nether Protection\"] },\n [50] = { t[\"Setup\"] },\n [51] = { t[\"Setup\"] },\n [52] = { t[\"Setup\"] },\n [53] = { t[\"Improved Victory Rush\"] },\n [54] = { t[\"Enveloping Shadows\"] },\n [55] = { t[\"Improved Righteous Fury\"] },\n [56] = { t[\"Improved Righteous Fury\"] },\n [57] = { t[\"Ardent Defender\"] },\n [58] = { t[\"Ardent Defender\"] },\n [59] = { t[\"Ardent Defender\"] },\n [60] = { t[\"Deadened Nerves\"] },\n [61] = { t[\"Deadened Nerves\"] },\n [62] = { t[\"Deadened Nerves\"] },\n [63] = { t[\"Prismatic Cloak\"] },\n [64] = { t[\"Prismatic Cloak\"] },\n [65] = { t[\"Survival Instincts\"] },\n [66] = { t[\"Survival Instincts\"] },\n [67] = { t[\"Survival Instincts\"] },\n [68] = { t[\"Improved Righteous Fury\"] },\n [69] = { t[\"Astral Shift\"] },\n [70] = { t[\"\"] }, \n \n }\n }\n}\n\n\ndo -- set up leveling profile \n local specifiedCharacterProfileName\n for _, profile in pairs(c.profiles) do\n local name, server = select(3, profile.characterName:find(\"^(.-) %- (.-)$\"))\n if name == UnitName(\"player\") and GetRealmName():find(server) then\n specifiedCharacterProfileName = profile.profileName\n end\n end\n \n if not specifiedCharacterProfileName or specifiedCharacterProfileName == \"\" then\n if UnitLevel(\"player\") ~= 70 then\n error(\"No leveling profile specified for character\", 2)\n end\n end\n \n local selectedProfile = aura_env.profiles[specifiedCharacterProfileName]\n aura_env.selectedProfile = selectedProfile\n if not selectedProfile then\n if UnitLevel(\"player\") ~= 70 then\n error(sformat(\"Profile \\\"%s\\\" not found\", specifiedCharacterProfileName), 2)\n end\n end\n aura_env.enabled = not not (UnitLevel(\"player\") ~= 70 and selectedProfile)\n \nend\n\n\n-- write and run function to ensure valid leveling profile\n\n\nlocal function collectAdvancementInRow(abilities, talents, row)\n for i, advancement in ipairs(row) do\n if #advancement > 1 then\n collectAdvancementInRow(abilities, talents, advancement)\n elseif advancement.type == \"talent\" then\n tinsert(talents, advancement)\n elseif advancement.type == \"ability\" then\n tinsert(abilities, advancement)\n end\n end\nend\n\nlocal function processAdvancements(advancements)\n local purge = false\n local toRemove = {}\n \n for i=#advancements, 1, -1 do\n if purge then\n advancements[i] = nil\n else\n local advancement = advancements[i]\n if advancement.op == \"remove\" then\n toRemove[advancement.id] = (toRemove[advancement.id] or 0) + 1\n advancements[i] = nil\n elseif advancement.op == \"remove all\" then\n purge = true\n advancements[i] = nil\n elseif advancement.op == \"add\" and toRemove[advancement.id]\n and toRemove[advancement.id] > 0 then\n toRemove[advancement.id] = toRemove[advancement.id] - 1\n advancements[i] = nil\n end\n end\n end\nend\n\nlocal function processTalentsAndAbilities(abilities, talents)\n processAdvancements(abilities)\n processAdvancements(talents)\n \n local expectedTalentsAndAbilities = {}\n print(\"Expected Talents:\")\n for _, talent in pairs(talents) do\n print(talentIDs[talent.id], talent.id)\n tinsert(expectedTalentsAndAbilities, talent)\n end\n print(\"Expected Spells:\")\n for _, ability in pairs(abilities) do\n print(abilityIDs[ability.id], ability.id)\n tinsert(expectedTalentsAndAbilities, ability)\n end\n return expectedTalentsAndAbilities\nend\n\nlocal function collectExpectedTalentsAndAbilities()\n local abilities, talents = {}, {}\n \n collectAdvancementInRow(abilities, talents, aura_env.selectedProfile.startingAbilities)\n for level=10, UnitLevel(\"player\") do\n collectAdvancementInRow(abilities, talents, aura_env.selectedProfile.levels[level])\n end\n return processTalentsAndAbilities(abilities, talents)\nend\n\n-- returns how many ADDITIONAL essences are required to know all talents in list\nlocal function countRequiredEssences(expectedTalentsAndAbilities)\n \n local expectedTalents = {}\n for _, advancement in ipairs(expectedTalentsAndAbilities) do\n if advancement.type == \"talent\" then\n expectedTalents[advancement.id] = (expectedTalents[advancement.id] or 0) + 1\n end\n end\n \n local numRequiredAbilityEssences, numRequiredTalentEssences = 0, 0\n for _, advancement in ipairs(expectedTalentsAndAbilities) do\n local ae, te\n if advancement.type == \"ability\" and not C_Spell:IsAnyRankKnown(advancement.id) then\n ae, te = CA_GetSpellInfo(advancement.id)\n numRequiredAbilityEssences = numRequiredAbilityEssences + ae\n numRequiredTalentEssences = numRequiredTalentEssences + te\n end\n end\n for talentID, expectedRank in pairs(expectedTalents) do\n local currentRank, _, _, ae, te = CA_GetTalentInfo(talentID)\n if currentRank < expectedRank then\n numRequiredAbilityEssences = numRequiredAbilityEssences + ae * (expectedRank - currentRank)\n numRequiredTalentEssences = numRequiredTalentEssences + te * (expectedRank - currentRank)\n end\n end\n \n print(\"numRequiredAbilityEssences: \" .. numRequiredAbilityEssences)\n print(\"numRequiredTalentEssences: \" .. numRequiredTalentEssences)\n return numRequiredAbilityEssences, numRequiredTalentEssences\nend\n\n-- returns what kind of reset is required\nlocal function isResetRequired(expectedTalentsAndAbilities)\n local numRequiredAbilityEssences, numRequiredTalentEssences = countRequiredEssences(expectedTalentsAndAbilities)\n return numRequiredAbilityEssences > GetItemCount(ItemData.ABILITY_ESSENCE),\n numRequiredTalentEssences > GetItemCount(ItemData.TALENT_ESSENCE)\nend\n\nlocal function handleUnlearn(abilityResetRequired, talentResetRequired)\n if abilityResetRequired then\n print(\"unlearn spells\")\n CA_UnLearnAllSpells()\n end\n if talentResetRequired then\n print(\"unlearn talents\")\n CA_UnLearnAllTalents()\n end\n if aura_env.resetHandle then\n aura_env.resetHandle:Cancel()\n end\n aura_env.runTwiceFlag = true\n aura_env.resetHandle = Timer.NewTimer(1, function() WeakAuras.ScanEvents(\"POLI_LEARN_ADVANCEMENTS\", \"player\") end)\nend\n\nlocal function calculateExpectedTalentRank(expectedTalentsAndAbilities, talentID, index)\n local rank = 1\n for i=index-1, 1, -1 do\n if expectedTalentsAndAbilities[i].id == talentID then\n rank = rank + 1\n end\n end\n return rank\nend\n\nlocal function canAndShouldLearn(expectedTalentsAndAbilities, advancement, index)\n \n -- Can learn (have enough essences)\n local numRequiredAbilityEssences, numRequiredTalentEssences\n if advancement.type == \"talent\" then\n numRequiredAbilityEssences, numRequiredTalentEssences = select(4, CA_GetTalentInfo(advancement.id))\n else\n numRequiredAbilityEssences, numRequiredTalentEssences = CA_GetSpellInfo(advancement.id)\n end\n \n if numRequiredAbilityEssences > GetItemCount(ItemData.ABILITY_ESSENCE)\n and numRequiredTalentEssences > GetItemCount(ItemData.TALENT_ESSENCE) then\n return false -- can't learn\n end\n \n -- Should learn\n if advancement.type == \"talent\" then\n local currentTalentRank = CA_GetTalentInfo(advancement.id)\n local expectedTalentRank = calculateExpectedTalentRank(expectedTalentsAndAbilities, advancement.id, index)\n \n if currentTalentRank >= expectedTalentRank then\n return false -- expected talent rank already known\n end\n else\n if C_Spell:IsAnyRankKnown(advancement.id) then\n return false -- ability already known\n end\n end\n return true\nend\n\n-- Learns as many talents in the list as possible\nfunction learnAdvancements(expectedTalentsAndAbilities)\n for i, advancement in ipairs(expectedTalentsAndAbilities) do\n if canAndShouldLearn(expectedTalentsAndAbilities, advancement, i) then\n \n if advancement.type == \"talent\" then\n CA_LearnTalent(CAO_Talent_References[advancement.id])\n print(sformat(\"T %s %s\", talentIDs[advancement.id], advancement.id))\n \n else\n print(sformat(\"A %s %s\", abilityIDs[advancement.id], advancement.id))\n CA_LearnSpell(CAO_Spell_References[advancement.id])\n end\n end\n end\nend\n\nlocal function ensureCorrectTalentsAndAbilities()\n print(\"Start ensuring correct talents and abilities\")\n local expectedTalentsAndAbilities = collectExpectedTalentsAndAbilities()\n if c.allowPurges then\n local abilityResetRequired, talentResetRequired = isResetRequired(expectedTalentsAndAbilities)\n print(\"abilityResetRequired: \" .. tostring(abilityResetRequired))\n print(\"talentResetRequired: \" .. tostring(talentResetRequired))\n if abilityResetRequired or talentResetRequired then\n handleUnlearn(abilityResetRequired, talentResetRequired)\n return\n end\n end\n learnAdvancements(expectedTalentsAndAbilities)\nend\n\nlocal function setPrimaryStat(stat)\n C_PrimaryStat:SetPrimaryStat(stat)\nend\n\nlocal function ensureCorrectPrimaryStat()\n local levels = aura_env.selectedProfile.levels\n for i=UnitLevel(\"player\"), 10, -1 do\n local row = levels[i]\n for j=#row, 1, -1 do\n if row[j].type == \"primary stat\" then\n setPrimaryStat(row[j].stat)\n end\n return\n end\n end\n local row = aura_env.selectedProfile.startingAbilities\n for i=#row, 1, -1 do\n if row[i].type == \"primary stat\" then\n setPrimaryStat(row[i].stat)\n end\n return\n end\nend\n\nfunction aura_env.ensureCorrectAdvancements()\n ensureCorrectTalentsAndAbilities()\n ensureCorrectPrimaryStat()\nend"
}
},
"information": [],
"zoom": 0,
"anchorFrameType": "SCREEN",
"tocversion": 30300,
"id": "Leveling Spell and Talent Automation Arcane",
"color": [
1,
1,
1,
1
],
"frameStrata": 1,
"width": 64,
"xOffset": 0,
"config": {
"allowPurges": false,
"profiles": [
{
"characterName": "Slowdiver-Thrall",
"profileName": "Parry Leveling"
},
{
"characterName": "",
"profileName": ""
}
],
"debugEnabled": true
},
"inverse": false,
"uid": "3vzzCZ6ofJv",
"alpha": 1,
"conditions": [],
"cooldown": false,
"animation": {
"start": {
"easeStrength": 3,
"type": "none",
"duration_type": "seconds",
"easeType": "none"
},
"main": {
"easeStrength": 3,
"type": "none",
"duration_type": "seconds",
"easeType": "none"
},
"finish": {
"easeStrength": 3,
"type": "none",
"duration_type": "seconds",
"easeType": "none"
}
}
},
"s": "4.1.4",
"v": 1421
}