From 8c691161e429f8a7c940140c939903dae5fee5a3 Mon Sep 17 00:00:00 2001 From: Ofrex <30585460+Ofrex@users.noreply.github.com> Date: Fri, 10 Feb 2023 09:50:53 +0000 Subject: [PATCH] Objectmanager update To allow for healing tests in Proving Grounds --- scripts/discpriest.lua | 726 +++++++ scripts/feraldruid.lua | 400 ++++ scripts/guardiandruid.lua | 337 ++++ scripts/holypriest.lua | 476 +++++ scripts/outlawbak.lua | 608 ------ scripts/restodruid.lua | 906 ++++----- scripts/subtlety.lua | 1980 ------------------- src/APL/APL.lua | 39 +- src/Aura/Aura.lua | 40 +- src/AuraTable/AuraTable.lua | 37 +- src/Cache/Cache.lua | 10 + src/Cacheable/Cacheable.lua | 13 +- src/Class/Class.lua | 8 + src/ClassMagic/ClassMagic.lua | 4 + src/Command/Command.lua | 13 +- src/EventManager/EventManager.lua | 12 +- src/Item/Item.lua | 59 + src/ItemBook/ItemBook.lua | 4 + src/List/List.lua | 35 + src/Module/Module.lua | 13 + src/MythicPlusUtils/MythicPlusUtils.lua | 6 + src/NotificationsList/NotificationsList.lua | 45 +- src/ObjectManager/ObjectManager.lua | 13 +- src/Refreshable/Refreshable.lua | 10 + src/Spell/Spell.lua | 65 + src/SpellBook/SpellBook.lua | 22 + src/Timer/Timer.lua | 7 + src/Unit/Unit.lua | 253 ++- src/UnitManager/UnitManager.lua | 25 +- src/Vector3/Vector3.lua | 51 + src/_bastion.lua | 45 +- 31 files changed, 3013 insertions(+), 3249 deletions(-) create mode 100644 scripts/discpriest.lua create mode 100644 scripts/feraldruid.lua create mode 100644 scripts/guardiandruid.lua create mode 100644 scripts/holypriest.lua delete mode 100644 scripts/outlawbak.lua delete mode 100644 scripts/subtlety.lua diff --git a/scripts/discpriest.lua b/scripts/discpriest.lua new file mode 100644 index 0000000..67c45d3 --- /dev/null +++ b/scripts/discpriest.lua @@ -0,0 +1,726 @@ +local Tinkr, Bastion = ... + +local DiscModule = Bastion.Module:New('disc') +local Evaluator = Tinkr.Util.Evaluator +local Player = Bastion.UnitManager:Get('player') +local None = Bastion.UnitManager:Get('none') +local Target = Bastion.UnitManager:Get('target') + +local myconf = Tinkr.Util.Config:New('disc_priest') + +local AngelicFeather = Bastion.SpellBook:GetSpell(121536) +local DesperatePrayer = Bastion.SpellBook:GetSpell(19236) +local DispelMagic = Bastion.SpellBook:GetSpell(528) +local Fade = Bastion.SpellBook:GetSpell(586) +local FlashHeal = Bastion.SpellBook:GetSpell(2061) +local LeapofFaith = Bastion.SpellBook:GetSpell(73325) +local Levitate = Bastion.SpellBook:GetSpell(1706) +local MassDispel = Bastion.SpellBook:GetSpell(32375) +local MindBlast = Bastion.SpellBook:GetSpell(8092) +local MindSoothe = Bastion.SpellBook:GetSpell(453) +local MindVision = Bastion.SpellBook:GetSpell(2096) +local PowerInfusion = Bastion.SpellBook:GetSpell(10060) +local PowerWordFortitude = Bastion.SpellBook:GetSpell(21562) +local PowerWordLife = Bastion.SpellBook:GetSpell(373481) +local PowerWordShield = Bastion.SpellBook:GetSpell(17) +local PsychicScream = Bastion.SpellBook:GetSpell(8122) +local Renew = Bastion.SpellBook:GetSpell(139) +local Resurrection = Bastion.SpellBook:GetSpell(2006) +local ShadowWordDeath = Bastion.SpellBook:GetSpell(32379) +local Shadowfiend = Bastion.SpellBook:GetSpell(34433) +local Smite = Bastion.SpellBook:GetSpell(585) +local VampiricEmbrace = Bastion.SpellBook:GetSpell(15286) +local VoidTendrils = Bastion.SpellBook:GetSpell(108920) +local FocusedWill = Bastion.SpellBook:GetSpell(45243) +local BoonoftheAscended = Bastion.SpellBook:GetSpell(325013) + +local Apotheosis = Bastion.SpellBook:GetSpell(392941) +local DivineStar = Bastion.SpellBook:GetSpell(110744) +local MassResurrection = Bastion.SpellBook:GetSpell(212036) +local PainSuppression = Bastion.SpellBook:GetSpell(33206) +local Penance = Bastion.SpellBook:GetSpell(47540) +local PowerWordBarrier = Bastion.SpellBook:GetSpell(62618) +local PowerWordRadiance = Bastion.SpellBook:GetSpell(194509) +local PurgetheWicked = Bastion.SpellBook:GetSpell(204197) +local Purify = Bastion.SpellBook:GetSpell(527) +local Rapture = Bastion.SpellBook:GetSpell(47536) +local Schism = Bastion.SpellBook:GetSpell(214621) +local ShadowCovenant = Bastion.SpellBook:GetSpell(314867) +local Atonement = Bastion.SpellBook:GetSpell(81749) +local MasteryGrace = Bastion.SpellBook:GetSpell(271534) +local SinsoftheMany = Bastion.SpellBook:GetSpell(280391) +local Apotheosis = Bastion.SpellBook:GetSpell(392941) +local DivineStar = Bastion.SpellBook:GetSpell(110744) +local MassResurrection = Bastion.SpellBook:GetSpell(212036) +local Purify = Bastion.SpellBook:GetSpell(527) +local ShadowWordPain = Bastion.SpellBook:GetSpell(204197) +local DivineStar = Bastion.SpellBook:GetSpell(110744) +local ShadowWordPain = Bastion.SpellBook:GetSpell(204197) + +local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit) + local lowest = nil + local lowestHP = math.huge + + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then + return false + end + + if Player:GetDistance(unit) > 40 then + return false + end + + if not Player:CanSee(unit) then + return false + end + + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end) + + if not lowest then + lowest = Player + end + + return lowest +end) + +local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) + local purge = nil + + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then + return false + end + + if not Player:CanSee(unit) then + return false + end + + if Player:GetDistance(unit) > 40 then + return false + end + + if not unit:IsDead() and Player:CanSee(unit) and + unit:GetAuras():HasAnyStealableAura() then + purge = unit + return true + end + end) + + if purge == nil then + purge = None + end + + return purge +end) +local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit) + local lowest = nil + local lowestHP = math.huge + + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then + return false + end + + if not Player:CanSee(unit) then + return false + end + + if Player:GetDistance(unit) > 40 then + return false + end + + if not unit:IsDead() and Player:CanSee(unit) and + unit:GetAuras():HasAnyDispelableAura(Purify) then + + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end + end) + + if lowest == nil then + lowest = None + end + + return lowest +end) +local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) + local tank = nil + Bastion.UnitManager:EnumFriends(function(unit) + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + if unit:IsDead() then return false end + if unit:IsTank() then + tank = unit + return true + end + return false + end) + if tank == nil then + tank = None + end + return tank +end) +local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) + local explosive = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then + explosive = unit + return true + end + end) + if explosive == nil then + explosive = None + end + return explosive +end) +local RenewUnit = Bastion.UnitManager:CreateCustomUnit('renew', function(unit) + local lowest = nil + local lowestHP = math.huge + + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then + return false + end + + if not Player:CanSee(unit) then + return false + end + + if Player:GetDistance(unit) > 40 then + return false + end + + if not unit:IsDead() and Player:CanSee(unit) and + ( + not unit:GetAuras():FindMy(Renew):IsUp() or + unit:GetAuras():FindMy(Renew):GetRemainingTime() <= 1.6) then + + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end + + end) + + + if lowest == nil then + lowest = Player + end + + return lowest +end) + +local PshieldUnit = Bastion.UnitManager:CreateCustomUnit('pshield', function(unit) + local lowest = nil + local lowestHP = math.huge + + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then + return false + end + + if not Player:CanSee(unit) then + return false + end + + if Player:GetDistance(unit) > 40 then + return false + end + + if not unit:IsDead() and Player:CanSee(unit) and + ( + not unit:GetAuras():FindMy(PowerWordShield):IsUp()) then + + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end + + end) + + + if lowest == nil then + lowest = Player + end + + return lowest +end) + + + +local DefaultAPL = Bastion.APL:New('default') +DefaultAPL:AddSpell( + PowerWordFortitude:CastableIf(function(self) + return usepwf and self:IsKnownAndUsable() and + not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(PowerWordFortitude):IsUp() and not IsMounted() + end):SetTarget(Player) +) +DefaultAPL:AddSpell( + ShadowCovenant:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Player) +) +DefaultAPL:AddSpell( + ShadowWordPain:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Target:GetAuras():FindMy(ShadowWordPain):IsUp() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + PowerWordShield:CastableIf(function(self) + return self:IsKnownAndUsable() and + not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(PowerWordShield):IsUp() and not IsMounted() and Player:GetHealth() < 80 + end):SetTarget(Player) +) +DefaultAPL:AddSpell( + Shadowfiend:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + PowerInfusion:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Player) +) +DefaultAPL:AddSpell( + ShadowWordDeath:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Target:GetHealthPercent() <= 20 + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + MindBlast:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + Smite:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + PurgeTheWicked:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Target:GetAuras():PurgeTheWicked() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + Schism:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + Penance:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) +DefaultAPL:AddSpell( + Penance:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Lowest:GetHP() <= 70 + end):SetTarget(Lowest) +) +DefaultAPL:AddSpell( + DispelMagic:CastableIf(function(self) + return PurgeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(PurgeTarget) and PurgeTarget:GetAuras():HasAnyStealableAura() + end):SetTarget(PurgeTarget) +) +DefaultAPL:AddSpell( + Purify:CastableIf(function(self) + return DispelTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(DispelTarget) and DispelTarget:GetAuras():HasAnyDispelableAura(Purify) + end):SetTarget(DispelTarget) +) +DefaultAPL:AddSpell( + PainSuppression:CastableIf(function(self) + return Tank:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Tank) and Tank:GetHP() <= 30 + end):SetTarget(Tank) +) +DefaultAPL:AddSpell( + FlashHeal:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() <= 65 + end):SetTarget(Lowest) +) + +DefaultAPL:AddSpell( + PowerWordLife:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() <= 35 + end):SetTarget(Lowest) +) +DefaultAPL:AddSpell( + Renew:CastableIf(function(self) + return RenewUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(RenewUnit) and RenewUnit:GetHP() <= 95 + + end):SetTarget(RenewUnit) +) +DefaultAPL:AddSpell( + PowerWordShield:CastableIf(function(self) + return PshieldUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(PshieldUnit) and PshieldUnit:GetHP() <= 70 + + end):SetTarget(PshieldUnit) +) +DefaultAPL:AddSpell( + PowerWordRadiance:CastableIf(function(self) + return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:IsInParty() and + self:IsInRange(Player) and Player:GetPartyHPAround(50) >= 3 + end):SetTarget(Player) +) +DefaultAPL:AddSpell( + PowerWordBarrier:CastableIf(function(self) + return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:IsInParty() and + self:IsInRange(Player) and Player:GetPartyHPAround(40) >= 3 + end):SetTarget(Player) +) +OpenerAPL:AddSpell( + DivineStar:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:IsFacing(target) + end):SetTarget(None):OnCast(function(self) + local loc = Target:GetPosition() + self:Click(loc) + end) +) + + + + + + + + + + + +DiscModule:Sync(function() + if Player:IsAffectingCombat() then + DefaultAPL:Execute() + end +end) + + + + + + +Bastion:Register(DiscModule) + + +local tab1 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + + + [1] = { resto = { type = 'header', label = 'DPS Options' } }, + + [2] = { dps1 = { type = 'slider', label = 'DPS Health Threshold', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('dpshealth', 85), + onValueChanged = function(_, value) myconf:Write('dpshealth', value) end }, + + dps2 = { type = 'slider', label = 'Player Mana Threshold', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('playermana', 85), + onValueChanged = function(_, value) myconf:Write('playermana', value) end }, }, + + + + [3] = { autoform = { type = 'header', label = 'Natures Cure' } }, + [4] = { pummel = { type = 'checkbox', label = 'Dispell', column = 6, + initialValue = myconf:Read('naturescure1', false), + onValueChanged = function(_, flag) myconf:Write('naturescure1', flag) end }, }, + + [5] = { resto = { type = 'header', label = 'Soothe' } }, + + [6] = { incombat = { type = 'checkbox', label = 'Shoote Target', column = 12, order = 1, + initialValue = myconf:Read('soothe', false), + onValueChanged = function(_, flag) myconf:Write('soothe', flag) end }, }, + + [7] = { resto = { type = 'header', label = 'Dispel Delay Time' } }, + + [8] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, + min = 0.5, max = 2, precision = 1, + initialValue = myconf:Read('dispeldelay', 1), + onValueChanged = function(_, value) myconf:Write('dispeldelay', value) end }, }, + + [9] = { innervate1 = { type = 'checkbox', label = 'Innervate', column = 6, order = 1, + initialValue = myconf:Read('innervate', false), + onValueChanged = function(_, flag) myconf:Write('innervate', flag) end }, + + innervate2 = { type = 'slider', label = 'Player Mana', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('innervatemana', 35), + onValueChanged = function(_, value) myconf:Write('innervatemana', value) end }, }, + + [10] = { frostmage = { type = 'header', label = 'Trinkets' } }, + [11] = { pummel = { type = 'checkbox', label = 'Use Trinkets', column = 6, + initialValue = myconf:Read('trinkets', false), + onValueChanged = function(_, flag) myconf:Write('trinkets', flag) end }, }, + + [12] = { autoform = { type = 'header', label = 'Auto Form' } }, + [13] = { pummel = { type = 'checkbox', label = 'Use Auto Form', column = 6, + initialValue = myconf:Read('autoform', false), + onValueChanged = function(_, flag) myconf:Write('autoform', flag) end }, }, + + }, +} + +local tab2 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { resto = { type = 'header', label = 'Player Healing' } }, + + [2] = { incombat = { type = 'checkbox', label = 'In Combat Healing', column = 6, order = 1, + initialValue = myconf:Read('playericheal', false), + onValueChanged = function(_, flag) myconf:Write('playericheal', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat Healing', column = 6, order = 2, + initialValue = myconf:Read('playeroocheal', false), + onValueChanged = function(_, flag) myconf:Write('playeroocheal', flag) end }, }, + + [3] = { shadowmend1 = { type = 'checkbox', label = 'Regrowth', column = 6, order = 1, + initialValue = myconf:Read('shadowmend', false), + onValueChanged = function(_, flag) myconf:Write('shadowmend', flag) end }, + + shadowmend2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('shadowmendpercent', 35), + onValueChanged = function(_, value) myconf:Write('shadowmendpercent', value) end }, }, + + [4] = { resto = { type = 'header', label = 'Defensives' } }, + + [5] = { bark1 = { type = 'checkbox', label = 'Barkskin', column = 6, order = 1, + initialValue = myconf:Read('barkskin', false), + onValueChanged = function(_, flag) myconf:Write('barkskin', flag) end }, + + bark2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('barkskinpercent', 35), + onValueChanged = function(_, value) myconf:Write('barkskinpercent', value) end }, }, + + }, +} + +local tab3 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { incombat = { type = 'checkbox', label = 'In Combat Healing', column = 6, order = 1, + initialValue = myconf:Read('groupicheal', false), + onValueChanged = function(_, flag) myconf:Write('groupicheal', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat Healing', column = 6, order = 2, + initialValue = myconf:Read('groupoocheal', false), + onValueChanged = function(_, flag) myconf:Write('groupoocheal', flag) end }, }, + + [2] = { lifebloom1 = { type = 'dropdown', label = 'Lifebloom on', column = 6, order = 1, + options = { + { text = 'Tank Only', value = 'tank'}, + { text = 'All Party', value = 'party'}, + { text = 'Player', value = 'player'}, + { text = 'None', value = 'none'}, + }, + initialValue = myconf:Read('lifebloomtarget', 'tank'), + onValueChanged = function(_, value) myconf:Write('lifebloomtarget', value) end }, }, + + [3] = { resto = { type = 'header', label = 'AOE Heals' } }, + + [4] = { tranq1 = { type = 'checkbox', label = 'Tranquility', column = 6, order = 1, + initialValue = myconf:Read('grptranq', false), + onValueChanged = function(_, flag) myconf:Write('grptranq', flag) end }, }, + + [5] = { tranq2 = { type = 'slider', label = 'Number of Targets', column = 6, order = 2, + min = 1, max = 10, precision = 0, + initialValue = myconf:Read('tranqtargets', 3), + onValueChanged = function(_, value) myconf:Write('tranqtargets', value) end }, + + tranq3 = { type = 'slider', label = 'Target Health', column = 6, order = 1, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('tranqpercent', 50), + onValueChanged = function(_, value) myconf:Write('tranqpercent', value) end }, }, + + [6] = { flourish1 = { type = 'checkbox', label = 'Flourish', column = 6, order = 1, + initialValue = myconf:Read('flourish', false), + onValueChanged = function(_, flag) myconf:Write('flourish', flag) end }, }, + + [7] = { flourish2 = { type = 'slider', label = 'Number of Targets', column = 6, order = 2, + min = 1, max = 10, precision = 0, + initialValue = myconf:Read('flourishtargets', 3), + onValueChanged = function(_, value) myconf:Write('flourishtargets', value) end }, + + flourish3 = { type = 'slider', label = 'Target Health', column = 6, order = 1, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('flourishpercent', 50), + onValueChanged = function(_, value) myconf:Write('flourishpercent', value) end }, }, + + [8] = { convoke1 = { type = 'checkbox', label = 'Convoke The Spirits', column = 6, order = 1, + initialValue = myconf:Read('convoke', false), + onValueChanged = function(_, flag) myconf:Write('convoke', flag) end }, }, + + [9] = { convoke2 = { type = 'slider', label = 'Number of Targets', column = 6, order = 2, + min = 1, max = 10, precision = 0, + initialValue = myconf:Read('convoketargets', 3), + onValueChanged = function(_, value) myconf:Write('convoketargets', value) end }, + + convoke3 = { type = 'slider', label = 'Target Health', column = 6, order = 1, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('convokepercent', 50), + onValueChanged = function(_, value) myconf:Write('convokepercent', value) end }, }, + + [10] = { wild1 = { type = 'checkbox', label = 'Wild Growth', column = 6, order = 1, + initialValue = myconf:Read('grpwildgrowth', false), + onValueChanged = function(_, flag) myconf:Write('grpwildgrowth', flag) end }, }, + + [11] = { wild2 = { type = 'slider', label = 'Number of Targets', column = 6, order = 2, + min = 1, max = 10, precision = 0, + initialValue = myconf:Read('wildgrowthtargets', 3), + onValueChanged = function(_, value) myconf:Write('wildgrowthtargets', value) end }, + + wild3 = { type = 'slider', label = 'Target Health', column = 6, order = 1, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('wildgrowthpercent', 80), + onValueChanged = function(_, value) myconf:Write('wildgrowthpercent', value) end }, }, + + + + [12] = { incarnationtree1 = { type = 'checkbox', label = 'Incarnation: Tree of Life', column = 6, order = 1, + initialValue = myconf:Read('incarnationtree', false), + onValueChanged = function(_, flag) myconf:Write('incarnationtree', flag) end }, }, + + [13] = { convoke2 = { type = 'slider', label = 'Number of Targets', column = 6, order = 2, + min = 1, max = 10, precision = 0, + initialValue = myconf:Read('incarnationtreetargets', 3), + onValueChanged = function(_, value) myconf:Write('incarnationtreetargets', value) end }, + + convoke3 = { type = 'slider', label = 'Target Health', column = 6, order = 1, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('incarnationtreepercent', 50), + onValueChanged = function(_, value) myconf:Write('incarnationtreepercent', value) end }, } + + + }, +} +local tab4 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { resto = { type = 'header', label = 'AOE Heals ' } }, + + [2] = { ironbark1 = { type = 'dropdown', label = 'Ironbark on', column = 6, order = 1, + options = { + { text = 'Tank Only', value = 'tank'}, + { text = 'All Party', value = 'party'}, + { text = 'None', value = 'none'}, + }, + initialValue = myconf:Read('ironbarktarget', 'tank'), + onValueChanged = function(_, value) myconf:Write('ironbarktarget', value) end }, + + ironbark2 = { type = 'slider', label = 'Target Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('ironbarkpercent', 30), + onValueChanged = function(_, value) myconf:Write('ironbarkpercent', value) end }, }, + + [3] = { ward1 = { type = 'dropdown', label = 'Cenarion Ward on', column = 6, order = 1, + options = { + { text = 'Tank Only', value = 'tank'}, + { text = 'All Party', value = 'party'}, + { text = 'None', value = 'none'}, + }, + initialValue = myconf:Read('wardtarget', 'tank'), + onValueChanged = function(_, value) myconf:Write('wardtarget', value) end }, + + ward2 = { type = 'slider', label = 'Target Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('wardpercent', 30), + onValueChanged = function(_, value) myconf:Write('wardpercent', value) end }, }, + + [4] = { natswift1 = { type = 'checkbox', label = 'Natures Swiftness', column = 6, order = 1, + initialValue = myconf:Read('grpnatswift', false), + onValueChanged = function(_, flag) myconf:Write('grpnatswift', flag) end }, + + natswift2 = { type = 'slider', label = 'Target Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('grpnatswiftpercent', 40), + onValueChanged = function(_, value) myconf:Write('grpnatswiftpercent', value) end }, }, + + [5] = { swift1 = { type = 'checkbox', label = 'Swiftmend', column = 6, order = 1, + initialValue = myconf:Read('grpswiftmend', false), + onValueChanged = function(_, flag) myconf:Write('grpswiftmend', flag) end }, + + swift2 = { type = 'slider', label = 'Target Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('grpswiftmendpercent', 60), + onValueChanged = function(_, value) myconf:Write('grpswiftmendpercent', value) end }, }, + + [6] = { regrowth1 = { type = 'checkbox', label = 'Regrowth', column = 6, order = 1, + initialValue = myconf:Read('grpregrowth', false), + onValueChanged = function(_, flag) myconf:Write('grpregrowth', flag) end }, + + regrowth2 = { type = 'slider', label = 'Target Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('grpregrowthpercent', 65), + onValueChanged = function(_, value) myconf:Write('grpregrowthpercent', value) end }, }, + + [7] = { rejuv1 = { type = 'checkbox', label = 'Rejuvenation', column = 6, order = 1, + initialValue = myconf:Read('grprejuv', false), + onValueChanged = function(_, flag) myconf:Write('grprejuv', flag) end }, + + rejuv2 = { type = 'slider', label = 'Target Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('grprejuvpercent', 85), + onValueChanged = function(_, value) myconf:Write('grprejuvpercent', value) end }, }, + + }, +} + +local discpriestconfig = { + layoutConfig = { padding = { top = 30 } }, + rows = { + [1] = { + container = { + type = 'tab', + fullSize = true, + tabs = { + { + name = 'player', + title = 'Player Settings', + layout = tab1 + }, + { + name = 'playerhealing', + title = 'Player Healing', + layout = tab2 + }, + { + name = 'group', + title = 'Group Healing', + layout = tab3 + }, + { + name = 'group2', + title = 'AOE Healing ', + layout = tab4 + } + }, + } + }, + }, +} +Bastion.settingstemplate(discpriestconfig, 'Disc Priest', 460, 780, 1.00, 0.49, 0.04) \ No newline at end of file diff --git a/scripts/feraldruid.lua b/scripts/feraldruid.lua new file mode 100644 index 0000000..05f66ef --- /dev/null +++ b/scripts/feraldruid.lua @@ -0,0 +1,400 @@ +local Tinkr, Bastion = ... + +local FeralModule = Bastion.Module:New('feral') +local Evaluator = Tinkr.Util.Evaluator +local Player = Bastion.UnitManager:Get('player') +local None = Bastion.UnitManager:Get('none') +local Target = Bastion.UnitManager:Get('target') + +local myconf = Tinkr.Util.Config:New('feral_druid') -- for saving variables + +-- Spellbook spells +local Shadowmeld = Bastion.SpellBook:GetSpell(58984) +local Barkskin = Bastion.SpellBook:GetSpell(22812) +local BearForm = Bastion.SpellBook:GetSpell(5487) +--local Berserk = Bastion.SpellBook:GetSpell(50334) +local CatForm = Bastion.SpellBook:GetSpell(768) +local Dash = Bastion.SpellBook:GetSpell(1850) +local EntanglingRoots = Bastion.SpellBook:GetSpell(339) +local FeralFrenzy = Bastion.SpellBook:GetSpell(274837) +local FerociousBite = Bastion.SpellBook:GetSpell(22568) +--local FrenziedRegeneration = Bastion.SpellBook:GetSpell(22842) +local Growl = Bastion.SpellBook:GetSpell(6795) +--local Ironfur = Bastion.SpellBook:GetSpell(192081) +local Mangle = Bastion.SpellBook:GetSpell(33917) +local MarkoftheWild = Bastion.SpellBook:GetSpell(1126) +local Moonfire = Bastion.SpellBook:GetSpell(8921) +local MoonfireDebuff = Bastion.SpellBook:GetSpell(164812) +local PredatorySwiftness = Bastion.SpellBook:GetSpell(69369) +local PrimalWrath = Bastion.SpellBook:GetSpell(285381) +local Prowl = Bastion.SpellBook:GetSpell(5215) +local Rake = Bastion.SpellBook:GetSpell(1822) +local RakeDebuff = Bastion.SpellBook:GetSpell(155722) +local Regrowth = Bastion.SpellBook:GetSpell(8936) +local Revive = Bastion.SpellBook:GetSpell(50769) +local Rip = Bastion.SpellBook:GetSpell(1079) +local Shred = Bastion.SpellBook:GetSpell(5221) +local SkullBash = Bastion.SpellBook:GetSpell(106839) +--local Thrash = Bastion.SpellBook:GetSpell(106830) +local Thrash = Bastion.SpellBook:GetSpell(106832) +local ThrashDebuff = Bastion.SpellBook:GetSpell(106830) +local TigersFury = Bastion.SpellBook:GetSpell(5217) +local TravelForm = Bastion.SpellBook:GetSpell(783) +local Wrath = Bastion.SpellBook:GetSpell(5176) +--local Maul = Bastion.SpellBook:GetSpell(6807) +--local SurvivalInstincts = Bastion.SpellBook:GetSpell(61336) +--local Swipe = Bastion.SpellBook:GetSpell(106785) +local Swipe = Bastion.SpellBook:GetSpell(213764) +local BrutalSlash = Bastion.SpellBook:GetSpell(202028) +local ApexPredator = Bastion.SpellBook:GetSpell(391882) +local Incarnation = Bastion.SpellBook:GetSpell(102543) + +-- Define Units +local InterruptTarget = Bastion.UnitManager:CreateCustomUnit('skullbash', function(unit) + local interrupt = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then + interrupt = unit + return true + end + end) + if interrupt == nil then + interrupt = None + end + return interrupt +end) + +local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) + local tank = nil + Bastion.UnitManager:EnumFriends(function(unit) + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + if unit:IsDead() then return false end + if unit:IsTank() then + tank = unit + return true + end + return false + end) + if tank == nil then + tank = None + end + return tank +end) + +local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) + local explosive = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then + explosive = unit + return true + end + end) + if explosive == nil then + explosive = None + end + return explosive +end) + +-- Rotation code here +local function Buffs() + -- Mark of the Wild + local motw = myconf:Read('motw') + MarkoftheWild:Condition('Mark of the Wild', function() + return motw and MarkoftheWild:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(MarkoftheWild):IsUp() + and not Player:IsMounted() + end) + MarkoftheWild:Cast(Player, 'Mark of the Wild') +end + +local function DruidProwl() + -- Prowl + Prowl:Condition('Prowl', function() + return Prowl:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(Prowl):IsUp() + and Player:GetDistance(Target) <= 25 and not Player:IsMounted() and Target:IsHostile() and not Target:IsDead() + end) + Prowl:Cast(Player, 'Prowl') +end + +local function Opener() + -- Rake + Rake:Condition('Rake', function() + return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) + and not Target:GetAuras():FindMy(RakeDebuff):IsUp() and Target:Exists() and Target:IsHostile() and not Target:IsDead() + end) + Rake:Cast(Target, 'Rake') +end + +local function Interrupt() + -- Skull Bash + SkullBash:Condition('SkullBash', function() + return SkullBash:IsKnownAndUsable() and InterruptTarget:Exists() and not Player:IsCastingOrChanneling() + end) + C_Timer.After(1.5, function() + SkullBash:Cast(Target, 'SkullBash') + end) +end + +local function Damage() + -- Cat Form + CatForm:Condition('Cat Form', function() + return CatForm:IsKnownAndUsable() and not Player:GetAuras():FindMy(CatForm):IsUp() + end) + CatForm:Cast(Player, 'Cat Form') + -- Healing with Predatory Swiftness + Regrowth:Condition('Regrowth', function() + return Regrowth:IsKnownAndUsable() and Player:GetAuras():FindMy(PredatorySwiftness):IsUp() + end) + Regrowth:Cast(Player, 'Regrowth') + -- Cooldowns + -- Tiger's Fury + TigersFury:Condition('Tigers Fury', function() + return TigersFury:IsKnownAndUsable() and not Player:GetAuras():FindMy(TigersFury):IsUp() + end) + TigersFury:Cast(Player, 'Tigers Fury') + -- Feral Frenzy on cooldown and when combo points are zero + FeralFrenzy:Condition('Feral Frenzy', function() + return FeralFrenzy:IsKnownAndUsable() and Player:GetComboPoints(Target) == 0 + end) + FeralFrenzy:Cast(Player, 'Feral Frenzy') + -- Incarnation: Avatar of Ashamane + Incarnation:Condition('Incarnation', function() + return Incarnation:IsKnownAndUsable() + end) + Incarnation:Cast(Player, 'Incarnation') + + -- Multi Target Rotation + if Player:GetEnemies(8) >= 2 then + -- Primal Wrath at 5 combo points + PrimalWrath:Condition('Primal Wrath', function() + return PrimalWrath:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 5 + and (not Target:GetAuras():FindMy(Rip):IsUp() or Target:GetAuras():FindMy(Rip):GetRemainingTime() <= 7) + end) + PrimalWrath:Cast(Target, 'Primal Wrath') + -- Spend Apex Predator Procs on Ferocious Bite + FerociousBite:Condition('Ferocious Bite Apex Predator', function() + return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetAuras():FindMy(ApexPredator):IsUp() + end) + FerociousBite:Cast(Target, 'Ferocious Bite Apex Predator') + -- Maintain Rake + Rake:Condition('Maintain Rake', function() + return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) + and Target:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 4 + end) + Rake:Cast(Target, 'Maintain Rake') + -- Cast Thrash if its not up + Thrash:Condition('Thrash', function() + return Thrash:IsKnownAndUsable() and not Target:GetAuras():FindMy(ThrashDebuff):IsUp() and Player:InMelee(Target) + end) + Thrash:Cast(Target, 'Thrash') + -- Avoid capping Brutal Slash charges + BrutalSlash:Condition('Brutal Slash', function() + return BrutalSlash:IsKnownAndUsable() and Player:InMelee(Target) + end) + BrutalSlash:Cast(Target, 'Brutal Slash') + -- Shred to generate combo points + Shred:Condition('Shred', function() + return Shred:IsKnownAndUsable() and Player:InMelee(Target) + end) + Shred:Cast(Target, 'Shred') + end + + -- Single Target Rotation + if Player:GetEnemies(8) == 1 then + -- Maintain Rip - you'll want to cast this at 5 combo points + Rip:Condition('Rip', function() + return Rip:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 5 + and (not Target:GetAuras():FindMy(Rip):IsUp() or Target:GetAuras():FindMy(Rip):GetRemainingTime() <= 7) + end) + Rip:Cast(Target, 'Rip') + -- Spend Apex Predator Procs on Ferocious Bite + FerociousBite:Condition('Ferocious Bite Apex Predator', function() + return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetAuras():FindMy(ApexPredator):IsUp() + end) + FerociousBite:Cast(Target, 'Ferocious Bite Apex Predator') + -- If you have 5 combo points and Rip is not in Pandemic then cast Ferocious Bite + FerociousBite:Condition('Ferocious Bite', function() + return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 5 + end) + FerociousBite:Cast(Target, 'Ferocious Bite') + -- Maintain Rake + Rake:Condition('Maintain Rake', function() + return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) + and Target:GetAuras():FindMy(RakeDebuff):GetRemainingTime() <= 4 + end) + Rake:Cast(Target, 'Maintain Rake') + -- Avoid capping Brutal Slash charges + BrutalSlash:Condition('Brutal Slash', function() + return BrutalSlash:IsKnownAndUsable() and Player:InMelee(Target) + end) + BrutalSlash:Cast(Target, 'Brutal Slash') + -- Cast Thrash if its not up + Thrash:Condition('Thrash', function() + return Thrash:IsKnownAndUsable() and not Target:GetAuras():FindMy(ThrashDebuff):IsUp() and Player:InMelee(Target) + end) + Thrash:Cast(Target, 'Thrash') + -- Shred to generate combo points + Shred:Condition('Shred', function() + return Shred:IsKnownAndUsable() and Player:InMelee(Target) + end) + Shred:Cast(Target, 'Shred') + end +end + +-- Module that dictates workflow +FeralModule:Sync(function() + if not Player:IsAffectingCombat() then + if Buffs() then return end + if DruidProwl() then return end + if Opener() then return end + end + if Player:IsAffectingCombat() and Target:Exists() and Target:IsHostile() and not Target:IsDead() then + if Interrupt() then return end + if Damage() then return end + end +end) +Bastion:Register(FeralModule) + +local tab1 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { shadpr = { type = 'label', label = 'Open Combat' } }, + + [2] = { opener = { type = 'dropdown', label = 'Opener', column = 6, order = 1, + options = { + { text = 'Moonfire', value = 'moonfire'}, + { text = 'None', value = 'none'}, + }, + initialValue = myconf:Read('opener', 'moonfire'), + onValueChanged = function(_, value) myconf:Write('opener', value) end }, }, + + [3] = { shadpr = { type = 'label', label = 'Buffs' } }, + + [4] = { motw = { type = 'checkbox', label = 'Mark of the Wild', column = 12, order = 1, + initialValue = myconf:Read('motw', false), + onValueChanged = function(_, flag) myconf:Write('motw', flag) end }, }, + + [5] = { shadpr = { type = 'label', label = 'Defensives' } }, + + [6] = { shield1 = { type = 'checkbox', label = 'Ironfur', column = 6, order = 1, + initialValue = myconf:Read('ironfur', false), + onValueChanged = function(_, flag) myconf:Write('ironfur', flag) end }, + + shield2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('ironfurpercent', 35), + onValueChanged = function(_, value) myconf:Write('ironfurpercent', value) end }, }, + + [7] = { dispersion1 = { type = 'checkbox', label = 'Frenzied Regeneration', column = 6, order = 1, + initialValue = myconf:Read('fregen', false), + onValueChanged = function(_, flag) myconf:Write('fregen', flag) end }, + + dispersion2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('fregenpercent', 35), + onValueChanged = function(_, value) myconf:Write('fregenpercent', value) end }, }, + + [8] = { shadpr = { type = 'label', label = 'Interrupts' } }, + + [9] = { silence = { type = 'checkbox', label = 'Skull Bash on CD', column = 12, order = 1, + initialValue = myconf:Read('skullbash', false), + onValueChanged = function(_, flag) myconf:Write('skullbash', flag) end }, }, + + [10] = { shadpr = { type = 'header', label = 'Purify Disease' } }, + + [11] = { incombat = { type = 'checkbox', label = 'In Combat', column = 6, order = 1, + initialValue = myconf:Read('purifyic', false), + onValueChanged = function(_, flag) myconf:Write('purifyic', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat', column = 6, order = 2, + initialValue = myconf:Read('purifyooc', false), + onValueChanged = function(_, flag) myconf:Write('purifyooc', flag) end }, }, + + [12] = { shadpr = { type = 'header', label = 'Dispel Magic' } }, + + [13] = { incombat = { type = 'checkbox', label = 'In Combat', column = 12, order = 1, + initialValue = myconf:Read('dispelic', false), + onValueChanged = function(_, flag) myconf:Write('dispelic', flag) end }, }, + + [14] = { shadpr = { type = 'header', label = 'Dispel Delay Time' } }, + + [15] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, + min = 0.5, max = 2, precision = 1, + initialValue = myconf:Read('dispeldelay', 1), + onValueChanged = function(_, value) myconf:Write('dispeldelay', value) end }, }, + + }, +} + +local tab2 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { shadpr = { type = 'header', label = 'Player Healing' } }, + + [2] = { incombat = { type = 'checkbox', label = 'In Combat Healing', column = 6, order = 1, + initialValue = myconf:Read('playericheal', false), + onValueChanged = function(_, flag) myconf:Write('playericheal', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat Healing', column = 6, order = 2, + initialValue = myconf:Read('playeroocheal', false), + onValueChanged = function(_, flag) myconf:Write('playeroocheal', flag) end }, }, + + [3] = { shadowmend1 = { type = 'checkbox', label = 'Shadow Mend', column = 6, order = 1, + initialValue = myconf:Read('shadowmend', false), + onValueChanged = function(_, flag) myconf:Write('shadowmend', flag) end }, + + shadowmend2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('shadowmendpercent', 35), + onValueChanged = function(_, value) myconf:Write('shadowmendpercent', value) end }, }, + + }, +} + +local feralconfig = { + layoutConfig = { padding = { top = 30 } }, + rows = { + [1] = { + container = { + type = 'tab', + fullSize = true, + tabs = { + { + name = 'player', + title = 'Player Settings', + layout = tab1 + }, + { + name = 'group', + title = 'Placeholder', + layout = tab2 + } + }, + } + }, + }, +} +Bastion.settingstemplate(feralconfig, 'Feral Druid', 400, 600, 1.00, 0.49, 0.04) -- config, Title of Settings Frame, Width, Height, ClassRGB + +-- Class colours +-- Death Knight 0.77 0.12 0.23 Red +-- Demon Hunter 0.64 0.19 0.79 Dark Magenta +-- Druid 1.00 0.49 0.04 Orange +-- Evoker 0.20 0.58 0.50 Dark Emerald +-- Hunter 0.67 0.83 0.45 Green +-- Mage 0.25 0.78 0.92 Light Blue +-- Monk 0.00 1.00 0.60 Spring Green +-- Paladin 0.96 0.55 0.73 Pink +-- Priest 1.00 1.00 1.00 White +-- Rogue 1.00 0.96 0.41 Yellow +-- Shaman 0.00 0.44 0.87 Blue +-- Warlock 0.53 0.53 0.93 Purple +-- Warrior 0.78 0.61 0.43 Tan \ No newline at end of file diff --git a/scripts/guardiandruid.lua b/scripts/guardiandruid.lua new file mode 100644 index 0000000..4f35bb3 --- /dev/null +++ b/scripts/guardiandruid.lua @@ -0,0 +1,337 @@ +local Tinkr, Bastion = ... + +local GuardianModule = Bastion.Module:New('guardian') +local Evaluator = Tinkr.Util.Evaluator +local Player = Bastion.UnitManager:Get('player') +local None = Bastion.UnitManager:Get('none') +local Target = Bastion.UnitManager:Get('target') + +local myconf = Tinkr.Util.Config:New('guardian_druid') -- for saving variables + +local AutoAttack = Bastion.SpellBook:GetSpell(6603) +local WarStomp = Bastion.SpellBook:GetSpell(20549) +local Barkskin = Bastion.SpellBook:GetSpell(22812) +local BearForm = Bastion.SpellBook:GetSpell(5487) +local Berserk = Bastion.SpellBook:GetSpell(50334) +local CatForm = Bastion.SpellBook:GetSpell(768) +local Dash = Bastion.SpellBook:GetSpell(1850) +local EntanglingRoots = Bastion.SpellBook:GetSpell(339) +local FerociousBite = Bastion.SpellBook:GetSpell(22568) +local FrenziedRegeneration = Bastion.SpellBook:GetSpell(22842) +local Growl = Bastion.SpellBook:GetSpell(6795) +local Ironfur = Bastion.SpellBook:GetSpell(192081) +local Mangle = Bastion.SpellBook:GetSpell(33917) +local MarkoftheWild = Bastion.SpellBook:GetSpell(1126) +local Moonfire = Bastion.SpellBook:GetSpell(8921) +local MoonfireDebuff = Bastion.SpellBook:GetSpell(164812) +local Prowl = Bastion.SpellBook:GetSpell(5215) +local Regrowth = Bastion.SpellBook:GetSpell(8936) +local Revive = Bastion.SpellBook:GetSpell(50769) +local Shred = Bastion.SpellBook:GetSpell(5221) +local SkullBash = Bastion.SpellBook:GetSpell(106839) +local Thrash = Bastion.SpellBook:GetSpell(77758) +local ThrashDebuff = Bastion.SpellBook:GetSpell(192090) +local TravelForm = Bastion.SpellBook:GetSpell(783) +local Wrath = Bastion.SpellBook:GetSpell(5176) +local Maul = Bastion.SpellBook:GetSpell(6807) +local SurvivalInstincts = Bastion.SpellBook:GetSpell(61336) +local Swipe = Bastion.SpellBook:GetSpell(213771) + +local InterruptTarget = Bastion.UnitManager:CreateCustomUnit('skullbash', function(unit) + local interrupt = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then + interrupt = unit + return true + end + end) + if interrupt == nil then + interrupt = None + end + return interrupt +end) + +local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) + local tank = nil + Bastion.UnitManager:EnumFriends(function(unit) + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + if unit:IsDead() then return false end + if unit:IsTank() then + tank = unit + return true + end + return false + end) + if tank == nil then + tank = None + end + return tank +end) + +local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) + local explosive = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then + explosive = unit + return true + end + end) + if explosive == nil then + explosive = None + end + return explosive +end) + +local RestingAPL = Bastion.APL:New('resting') +local OpenerAPL = Bastion.APL:New('opener') +local DefensivesAPL = Bastion.APL:New('defensives') +local STAPL = Bastion.APL:New('st') +local UtilityAPL = Bastion.APL:New('utility') + +-- Resting - Out of Combat Actions +local motw = myconf:Read('motw') +RestingAPL:AddSpell( + MarkoftheWild:CastableIf(function(self) + return motw and self:IsKnownAndUsable() and + not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(MarkoftheWild):IsUp() and not IsMounted() + end):SetTarget(Player) +) + +-- Opener spells +local opener = myconf:Read('opener') +OpenerAPL:AddSpell( + Moonfire:CastableIf(function(self) + return opener == 'moonfire' and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) + +-- Defensive spells +local ironfur = myconf:Read('ironfur') +local ironfurpercent = myconf:Read('ironfurpercent') +DefensivesAPL:AddSpell( + Ironfur:CastableIf(function(self) + return ironfur and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(Ironfur):IsUp() + and Player:GetHP() <= ironfurpercent + end):SetTarget(Player) +) +-- Heal yourself with Frenzied Regeneration if your health ever dips low +local fregen = myconf:Read('fregen') +local fregenpercent = myconf:Read('fregenpercent') +DefensivesAPL:AddSpell( + FrenziedRegeneration:CastableIf(function(self) + return fregen and self:IsKnownAndUsable() and Player:GetHP() <= fregenpercent + end):SetTarget(Player) +) + +-- Utility spells +local skullbash = myconf:Read('skullbash') +UtilityAPL:AddSpell( + SkullBash:CastableIf(function(self) + return skullbash and InterruptTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(InterruptTarget) +) + +-- Single Target Action Priority List +-- Bear Form +STAPL:AddSpell( + BearForm:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(BearForm):IsUp() + end):SetTarget(Player) +) +-- Auto Attack +STAPL:AddSpell( + AutoAttack:CastableIf(function(self) + return self:IsKnownAndUsable() and not IsCurrentSpell(AutoAttack) and Player:InMelee(Target) + end):SetTarget(Target) +) +-- Opener spells +STAPL:AddSpell( + Moonfire:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Target:GetAuras():FindMy(MoonfireDebuff):IsUp() + end):SetTarget(Target) +) +-- Spend your rage on Maul as required +STAPL:AddSpell( + Maul:CastableIf(function(self) + return self:IsKnownAndUsable() and Player:InMelee(Target) + end):SetTarget(Target) +) +-- Maintain Moonfire on as many mobs as possible +-- Thrash on cooldown +STAPL:AddSpell( + Thrash:CastableIf(function(self) + return not Target:GetAuras():FindMy(ThrashDebuff):IsUp() and Player:InMelee(Target) + end):SetTarget(Target) +) +-- Mangle on cooldown +STAPL:AddSpell( + Mangle:CastableIf(function(self) + return self:IsKnownAndUsable() and Player:InMelee(Target) + end):SetTarget(Target) +) +-- Fill your empty GCDs with Swipe +STAPL:AddSpell( + Swipe:CastableIf(function(self) + return not self:IsOnCooldown() and Player:InMelee(Target) + end):SetTarget(Target) +) + +-- Module that dictates APL flow +GuardianModule:Sync(function() + if not Player:IsAffectingCombat() then + RestingAPL:Execute() + end + if not Player:IsAffectingCombat() and Target:Exists() and Target:IsHostile() and not Target:IsDead() then + OpenerAPL:Execute() + end + if Player:IsAffectingCombat() and Target:Exists() and Target:IsHostile() and not Target:IsDead() then + DefensivesAPL:Execute() + STAPL:Execute() + end +end) +Bastion:Register(GuardianModule) + +local tab1 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { shadpr = { type = 'label', label = 'Open Combat' } }, + + [2] = { opener = { type = 'dropdown', label = 'Opener', column = 6, order = 1, + options = { + { text = 'Moonfire', value = 'moonfire'}, + { text = 'None', value = 'none'}, + }, + initialValue = myconf:Read('opener', 'moonfire'), + onValueChanged = function(_, value) myconf:Write('opener', value) end }, }, + + [3] = { shadpr = { type = 'label', label = 'Buffs' } }, + + [4] = { motw = { type = 'checkbox', label = 'Mark of the Wild', column = 12, order = 1, + initialValue = myconf:Read('motw', false), + onValueChanged = function(_, flag) myconf:Write('motw', flag) end }, }, + + [5] = { shadpr = { type = 'label', label = 'Defensives' } }, + + [6] = { shield1 = { type = 'checkbox', label = 'Ironfur', column = 6, order = 1, + initialValue = myconf:Read('ironfur', false), + onValueChanged = function(_, flag) myconf:Write('ironfur', flag) end }, + + shield2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('ironfurpercent', 35), + onValueChanged = function(_, value) myconf:Write('ironfurpercent', value) end }, }, + + [7] = { dispersion1 = { type = 'checkbox', label = 'Frenzied Regeneration', column = 6, order = 1, + initialValue = myconf:Read('fregen', false), + onValueChanged = function(_, flag) myconf:Write('fregen', flag) end }, + + dispersion2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('fregenpercent', 35), + onValueChanged = function(_, value) myconf:Write('fregenpercent', value) end }, }, + + [8] = { shadpr = { type = 'label', label = 'Interrupts' } }, + + [9] = { silence = { type = 'checkbox', label = 'Skull Bash on CD', column = 12, order = 1, + initialValue = myconf:Read('skullbash', false), + onValueChanged = function(_, flag) myconf:Write('skullbash', flag) end }, }, + + [10] = { shadpr = { type = 'header', label = 'Purify Disease' } }, + + [11] = { incombat = { type = 'checkbox', label = 'In Combat', column = 6, order = 1, + initialValue = myconf:Read('purifyic', false), + onValueChanged = function(_, flag) myconf:Write('purifyic', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat', column = 6, order = 2, + initialValue = myconf:Read('purifyooc', false), + onValueChanged = function(_, flag) myconf:Write('purifyooc', flag) end }, }, + + [12] = { shadpr = { type = 'header', label = 'Dispel Magic' } }, + + [13] = { incombat = { type = 'checkbox', label = 'In Combat', column = 12, order = 1, + initialValue = myconf:Read('dispelic', false), + onValueChanged = function(_, flag) myconf:Write('dispelic', flag) end }, }, + + [14] = { shadpr = { type = 'header', label = 'Dispel Delay Time' } }, + + [15] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, + min = 0.5, max = 2, precision = 1, + initialValue = myconf:Read('dispeldelay', 1), + onValueChanged = function(_, value) myconf:Write('dispeldelay', value) end }, }, + + }, +} + +local tab2 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { shadpr = { type = 'header', label = 'Player Healing' } }, + + [2] = { incombat = { type = 'checkbox', label = 'In Combat Healing', column = 6, order = 1, + initialValue = myconf:Read('playericheal', false), + onValueChanged = function(_, flag) myconf:Write('playericheal', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat Healing', column = 6, order = 2, + initialValue = myconf:Read('playeroocheal', false), + onValueChanged = function(_, flag) myconf:Write('playeroocheal', flag) end }, }, + + [3] = { shadowmend1 = { type = 'checkbox', label = 'Shadow Mend', column = 6, order = 1, + initialValue = myconf:Read('shadowmend', false), + onValueChanged = function(_, flag) myconf:Write('shadowmend', flag) end }, + + shadowmend2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('shadowmendpercent', 35), + onValueChanged = function(_, value) myconf:Write('shadowmendpercent', value) end }, }, + + }, +} + +local guardianconfig = { + layoutConfig = { padding = { top = 30 } }, + rows = { + [1] = { + container = { + type = 'tab', + fullSize = true, + tabs = { + { + name = 'player', + title = 'Player Settings', + layout = tab1 + }, + { + name = 'group', + title = 'Placeholder', + layout = tab2 + } + }, + } + }, + }, +} +Bastion.settingstemplate(guardianconfig, 'Guardian Druid', 400, 600, 1.00, 0.49, 0.04) -- config, Title of Settings Frame, Width, Height, ClassRGB + +-- Class colours +-- Death Knight 0.77 0.12 0.23 Red +-- Demon Hunter 0.64 0.19 0.79 Dark Magenta +-- Druid 1.00 0.49 0.04 Orange +-- Evoker 0.20 0.58 0.50 Dark Emerald +-- Hunter 0.67 0.83 0.45 Green +-- Mage 0.25 0.78 0.92 Light Blue +-- Monk 0.00 1.00 0.60 Spring Green +-- Paladin 0.96 0.55 0.73 Pink +-- Priest 1.00 1.00 1.00 White +-- Rogue 1.00 0.96 0.41 Yellow +-- Shaman 0.00 0.44 0.87 Blue +-- Warlock 0.53 0.53 0.93 Purple +-- Warrior 0.78 0.61 0.43 Tan \ No newline at end of file diff --git a/scripts/holypriest.lua b/scripts/holypriest.lua new file mode 100644 index 0000000..795777c --- /dev/null +++ b/scripts/holypriest.lua @@ -0,0 +1,476 @@ +local Tinkr, Bastion = ... + +local HolyModule = Bastion.Module:New('holypriest') +local Evaluator = Tinkr.Util.Evaluator +local Player = Bastion.UnitManager:Get('player') +local None = Bastion.UnitManager:Get('none') +local Target = Bastion.UnitManager:Get('target') + +local myconf = Tinkr.Util.Config:New('holy_priest') -- for saving variables + +local ArcaneTorrent = Bastion.SpellBook:GetSpell(232633) +local AutoAttack = Bastion.SpellBook:GetSpell(6603) +local AngelicFeather = Bastion.SpellBook:GetSpell(121536) +local DesperatePrayer = Bastion.SpellBook:GetSpell(19236) +local DispelMagic = Bastion.SpellBook:GetSpell(528) +local Fade = Bastion.SpellBook:GetSpell(586) +local FlashHeal = Bastion.SpellBook:GetSpell(2061) +local HolyNova = Bastion.SpellBook:GetSpell(132157) +local HolyWordSalvation = Bastion.SpellBook:GetSpell(265202) +local LeapofFaith = Bastion.SpellBook:GetSpell(73325) +local Levitate = Bastion.SpellBook:GetSpell(1706) +local MassDispel = Bastion.SpellBook:GetSpell(32375) +local MindSoothe = Bastion.SpellBook:GetSpell(453) +local MindVision = Bastion.SpellBook:GetSpell(2096) +local PowerInfusion = Bastion.SpellBook:GetSpell(10060) +local PowerWordFortitude = Bastion.SpellBook:GetSpell(21562) +local PowerWordLife = Bastion.SpellBook:GetSpell(373481) +local PowerWordShield = Bastion.SpellBook:GetSpell(17) +local PrayerofMending = Bastion.SpellBook:GetSpell(33076) +local PsychicScream = Bastion.SpellBook:GetSpell(8122) +local Renew = Bastion.SpellBook:GetSpell(139) +local Resurrection = Bastion.SpellBook:GetSpell(2006) +local ShadowWordPain = Bastion.SpellBook:GetSpell(589) +local Shadowfiend = Bastion.SpellBook:GetSpell(34433) +local Smite = Bastion.SpellBook:GetSpell(585) +local FocusedWill = Bastion.SpellBook:GetSpell(45243) +local CircleofHealing = Bastion.SpellBook:GetSpell(204883) +local DivineHymn = Bastion.SpellBook:GetSpell(64843) +local DivineWord = Bastion.SpellBook:GetSpell(372760) +local GuardianSpirit = Bastion.SpellBook:GetSpell(47788) +local Halo = Bastion.SpellBook:GetSpell(120517) +local Heal = Bastion.SpellBook:GetSpell(2060) +local HolyFire = Bastion.SpellBook:GetSpell(14914) +local HolyWordSanctify = Bastion.SpellBook:GetSpell(34861) +local HolyWordSerenity = Bastion.SpellBook:GetSpell(2050) +local Lightweaver = Bastion.SpellBook:GetSpell(390993) +local MassResurrection = Bastion.SpellBook:GetSpell(212036) +local PrayerofHealing = Bastion.SpellBook:GetSpell(596) +local Purify = Bastion.SpellBook:GetSpell(527) +local SurgeofLight = Bastion.SpellBook:GetSpell(114255) +local SymbolofHope = Bastion.SpellBook:GetSpell(64901) +local MindBlast = Bastion.SpellBook:GetSpell(14914) + +local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit) + local lowest = nil + local lowestHP = math.huge + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then return false end + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end) + if not lowest then + lowest = Player + end + return lowest +end) + +local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) + local tank = nil + Bastion.UnitManager:EnumFriends(function(unit) + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + if unit:IsDead() then return false end + if unit:IsTank() then + tank = unit + return true + end + return false + end) + if tank == nil then + tank = Player + end + return tank +end) + +local Healer = Bastion.UnitManager:CreateCustomUnit('healer', function(unit) + local healer = nil + Bastion.UnitManager:EnumFriends(function(unit) + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + if unit:IsDead() then return false end + if unit:IsHealer() then + healer = unit + return true + end + return false + end) + if healer == nil then + healer = Player + end + return healer +end) + +local PurifyUnit = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit) + local lowest = nil + local lowestHP = math.huge + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if not unit:IsDead() and Player:CanSee(unit) and + unit:GetAuras():HasAnyDispelableAura(Purify) then + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end + end) + if lowest == nil then + lowest = None + end + return lowest +end) + +local SanctifyUnit = Bastion.UnitManager:CreateCustomUnit('sanctify', function(unit) + local lowest = nil + local lowestHP = math.huge + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:CanSee(unit) and unit:GetPartyHPAround(10, 70) >= 5 + then + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end + end) + if lowest == nil then + lowest = None + end + return lowest +end) + +local CoHUnit = Bastion.UnitManager:CreateCustomUnit('coh', function(unit) + local lowest = nil + local lowestHP = math.huge + Bastion.UnitManager:EnumFriends(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:CanSee(unit) and unit:GetPartyHPAround(30, 80) >= 4 + then + local hp = unit:GetHP() + if hp < lowestHP then + lowest = unit + lowestHP = hp + end + end + end) + if lowest == nil then + lowest = None + end + return lowest +end) + +local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) + local explosive = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then + explosive = unit + return true + end + end) + if explosive == nil then + explosive = None + end + return explosive +end) + +local RestingAPL = Bastion.APL:New('resting') +local HealAPL = Bastion.APL:New('heal') +local DamageAPL = Bastion.APL:New('damage') + +-- Resting - Out of Combat Actions +local usepwf = myconf:Read('pwf') +RestingAPL:AddSpell( + PowerWordFortitude:CastableIf(function(self) + return usepwf and self:IsKnownAndUsable() and + not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(PowerWordFortitude):IsUp() and not IsMounted() + end):SetTarget(Player) +) + +-- HealAPL - Healing here +-- Purify +HealAPL:AddSpell( + Purify:CastableIf(function(self) + return PurifyUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(PurifyUnit) and PurifyUnit:GetAuras():HasAnyDispelableAura(Purify) + end):SetTarget(PurifyUnit) +) +-- Symbol of Hope +HealAPL:AddSpell( + SymbolofHope:CastableIf(function(self) + return Healer:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Healer:GetPP() <= 50 + end):SetTarget(Player) +) +-- Guardian Spirit +HealAPL:AddSpell( + GuardianSpirit:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Tank:GetHP() <= 40 + end):SetTarget(Tank) +) +-- Flash Heal with Surge of Light +HealAPL:AddSpell( + FlashHeal:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(SurgeofLight):IsUp() + end):SetTarget(Lowest) +) +-- Holy Word: Salvation when the majority of the raid is heavily injured +HealAPL:AddSpell( + HolyWordSalvation:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetPartyHPAround(40, 50) >= 5 + and not Player:IsMoving() + end):SetTarget(Player) +) +-- Divine Hymn when the majority of the raid is heavily injured +HealAPL:AddSpell( + DivineHymn:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetPartyHPAround(40, 40) >= 5 + and not Player:IsMoving() + end):SetTarget(Player) +) +-- Divine Word followed by Holy Word: Sanctify (if available) on a cluster of injured allies +-- Divine Word followed by Holy Word: Serenity (if available) on an injured ally +HealAPL:AddSpell( + DivineWord:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(DivineWord):IsUp() + and (Lowest:GetHP() <= 60 or SanctifyUnit:GetPartyHPAround(10, 60) >= 5) + end):SetTarget(Player) +) +-- Holy Word: Sanctify when a cluster of your raid is injured +HealAPL:AddSpell( + HolyWordSanctify:CastableIf(function(self) + return SanctifyUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(SanctifyUnit) and SanctifyUnit:GetPartyHPAround(10, 70) >= 5 + end):SetTarget(None):OnCast(function(self) + local loc = SanctifyUnit:GetPosition() + self:Click(loc) + end) +) +-- Holy Word: Serenity on an injured ally +HealAPL:AddSpell( + HolyWordSerenity:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Lowest:GetHP() <= 70 + end):SetTarget(Lowest) +) +-- Circle of Healing on cooldown when at least 4 allies are injured +HealAPL:AddSpell( + CircleofHealing:CastableIf(function(self) + return CoHUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(CoHUnit) and CoHUnit:GetPartyHPAround(30, 80) >= 4 + end):SetTarget(CoHUnit) +) +-- Prayer of Mending on cooldown, usually on the active tank +HealAPL:AddSpell( + PrayerofMending:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Tank:GetHP() <= 95 + end):SetTarget(Tank) +) +-- Halo when most of your raid is injured +HealAPL:AddSpell( + Halo:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetPartyHPAround(30, 70) >= 5 + end):SetTarget(Player) +) +-- Power Word: Life for emergency triage on an ally below 35% hitpoints +HealAPL:AddSpell( + PowerWordLife:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Tank:GetHP() <= 35 + end):SetTarget(Tank) +) +-- Alternate between Flash Heal and Heal and use Lightweaver buff +HealAPL:AddSpell( + Heal:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Lightweaver):IsUp() + and Lowest:GetHP() <= 90 + end):SetTarget(Lowest) +) +HealAPL:AddSpell( + FlashHeal:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(Lightweaver):IsUp() + and Lowest:GetHP() <= 90 + end):SetTarget(Lowest) +) + +-- DamageAPL - DPS spells here +-- Cast Shadow Word: Pain if you're moving and nothing else is available +DamageAPL:AddSpell( + ShadowWordPain:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Target:GetAuras():FindMy(ShadowWordPain):IsUp() + end):SetTarget(Target) +) +-- Cast Smite +DamageAPL:AddSpell( + Smite:CastableIf(function(self) + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(Target) +) + +-- Module that dictates APL flow +HolyModule:Sync(function() + -- Bastion:Print(Lowest) + -- if not Player:IsAffectingCombat() then + -- RestingAPL:Execute() + -- end + HealAPL:Execute() + -- if Player:IsAffectingCombat() and Target:Exists() and Target:IsHostile() then + -- DamageAPL:Execute() + -- end +end) +Bastion:Register(HolyModule) + +local tab1 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { shadpr = { type = 'label', label = 'Open Combat' } }, + + [2] = { opener = { type = 'dropdown', label = 'Opener', column = 6, order = 1, + options = { + { text = 'Shadow Word: Pain', value = 'swp'}, + { text = 'Shadow Crash', value = 'crash'}, + { text = 'None', value = 'none'}, + }, + initialValue = myconf:Read('opener', 'shield'), + onValueChanged = function(_, value) myconf:Write('opener', value) end }, }, + + [3] = { shadpr = { type = 'label', label = 'Buffs' } }, + + [4] = { pwf = { type = 'checkbox', label = 'Power Word: Fortitude', column = 12, order = 1, + initialValue = myconf:Read('pwf', false), + onValueChanged = function(_, flag) myconf:Write('pwf', flag) end }, }, + + [5] = { shadpr = { type = 'header', label = 'Defensives' } }, + + [6] = { shield1 = { type = 'checkbox', label = 'Power Word: Shield', column = 6, order = 1, + initialValue = myconf:Read('pws', false), + onValueChanged = function(_, flag) myconf:Write('pws', flag) end }, + + shield2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('pwspercent', 35), + onValueChanged = function(_, value) myconf:Write('pwspercent', value) end }, }, + + [7] = { dispersion1 = { type = 'checkbox', label = 'Dispersion', column = 6, order = 1, + initialValue = myconf:Read('dispersion', false), + onValueChanged = function(_, flag) myconf:Write('dispersion', flag) end }, + + dispersion2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('dispersionpercent', 35), + onValueChanged = function(_, value) myconf:Write('dispersionpercent', value) end }, }, + + [8] = { shadpr = { type = 'header', label = 'Interrupts' } }, + + [9] = { silence = { type = 'checkbox', label = 'Silence on CD', column = 6, order = 1, + initialValue = myconf:Read('silence', false), + onValueChanged = function(_, flag) myconf:Write('silence', flag) end }, + + horror = { type = 'checkbox', label = 'Psychic Horror on CD', column = 6, order = 2, + initialValue = myconf:Read('horror', false), + onValueChanged = function(_, flag) myconf:Write('horror', flag) end }, }, + + [10] = { shadpr = { type = 'header', label = 'Purify Disease' } }, + + [11] = { incombat = { type = 'checkbox', label = 'In Combat', column = 6, order = 1, + initialValue = myconf:Read('purifyic', false), + onValueChanged = function(_, flag) myconf:Write('purifyic', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat', column = 6, order = 2, + initialValue = myconf:Read('purifyooc', false), + onValueChanged = function(_, flag) myconf:Write('purifyooc', flag) end }, }, + + [12] = { shadpr = { type = 'header', label = 'Dispel Magic' } }, + + [13] = { incombat = { type = 'checkbox', label = 'In Combat', column = 12, order = 1, + initialValue = myconf:Read('dispelic', false), + onValueChanged = function(_, flag) myconf:Write('dispelic', flag) end }, }, + + [14] = { shadpr = { type = 'header', label = 'Dispel Delay Time' } }, + + [15] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, + min = 0.5, max = 2, precision = 1, + initialValue = myconf:Read('dispeldelay', 1), + onValueChanged = function(_, value) myconf:Write('dispeldelay', value) end }, }, + + }, +} + +local tab2 = +{ + layoutConfig = { padding = { top = 40 } }, + rows = { + [1] = { shadpr = { type = 'header', label = 'Player Healing' } }, + + [2] = { incombat = { type = 'checkbox', label = 'In Combat Healing', column = 6, order = 1, + initialValue = myconf:Read('playericheal', false), + onValueChanged = function(_, flag) myconf:Write('playericheal', flag) end }, + + outcombat = { type = 'checkbox', label = 'Out of Combat Healing', column = 6, order = 2, + initialValue = myconf:Read('playeroocheal', false), + onValueChanged = function(_, flag) myconf:Write('playeroocheal', flag) end }, }, + + [3] = { shadowmend1 = { type = 'checkbox', label = 'Shadow Mend', column = 6, order = 1, + initialValue = myconf:Read('shadowmend', false), + onValueChanged = function(_, flag) myconf:Write('shadowmend', flag) end }, + + shadowmend2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, + min = 1, max = 100, precision = 0, + initialValue = myconf:Read('shadowmendpercent', 35), + onValueChanged = function(_, value) myconf:Write('shadowmendpercent', value) end }, }, + + }, +} + +local holypriestconfig = { + layoutConfig = { padding = { top = 30 } }, + rows = { + [1] = { + container = { + type = 'tab', + fullSize = true, + tabs = { + { + name = 'player', + title = 'Player Settings', + layout = tab1 + }, + { + name = 'group', + title = 'Placeholder', + layout = tab2 + } + }, + } + }, + }, +} +Bastion.settingstemplate(holypriestconfig, 'Holy Priest', 400, 600, 1.00, 1.00, 1.00) --, 0.00, 0.44, 0.87, 'enhsha') --Title of Settings Frame, Width, Height, ClassRGB, Rotation Name + +-- Class colours +-- Death Knight 0.77 0.12 0.23 Red +-- Demon Hunter 0.64 0.19 0.79 Dark Magenta +-- Druid 1.00 0.49 0.04 Orange +-- Evoker 0.20 0.58 0.50 Dark Emerald +-- Hunter 0.67 0.83 0.45 Green +-- Mage 0.25 0.78 0.92 Light Blue +-- Monk 0.00 1.00 0.60 Spring Green +-- Paladin 0.96 0.55 0.73 Pink +-- Priest 1.00 1.00 1.00 White +-- Rogue 1.00 0.96 0.41 Yellow +-- Shaman 0.00 0.44 0.87 Blue +-- Warlock 0.53 0.53 0.93 Purple +-- Warrior 0.78 0.61 0.43 Tan \ No newline at end of file diff --git a/scripts/outlawbak.lua b/scripts/outlawbak.lua deleted file mode 100644 index 959ecb5..0000000 --- a/scripts/outlawbak.lua +++ /dev/null @@ -1,608 +0,0 @@ -local Tinkr, Bastion = ... - -local OutlawModule = Bastion.Module:New('outlaw') -local Evaluator = Tinkr.Util.Evaluator -local Player = Bastion.UnitManager:Get('player') -local None = Bastion.UnitManager:Get('none') -local Target = Bastion.UnitManager:Get('target') - -local RollTheBones = Bastion.SpellBook:GetSpell(315508) -local SliceAndDice = Bastion.SpellBook:GetSpell(315496) -local BetweenTheEyes = Bastion.SpellBook:GetSpell(315341) -local BladeRush = Bastion.SpellBook:GetSpell(271877) -local Vanish = Bastion.SpellBook:GetSpell(1856) -local Dispatch = Bastion.SpellBook:GetSpell(2098) -local Ambush = Bastion.SpellBook:GetSpell(8676) -local Stealth = Bastion.SpellBook:GetSpell(1784) -local PistolShot = Bastion.SpellBook:GetSpell(185763) -local Opportunity = Bastion.SpellBook:GetSpell(195627) -local SinisterStrike = Bastion.SpellBook:GetSpell(193315) -local GrandMelee = Bastion.SpellBook:GetSpell(193358) -local Broadside = Bastion.SpellBook:GetSpell(193356) -local TrueBearing = Bastion.SpellBook:GetSpell(193359) -local RuthlessPrecision = Bastion.SpellBook:GetSpell(193357) -local SkullAndCrossbones = Bastion.SpellBook:GetSpell(199603) -local BuriedTreasure = Bastion.SpellBook:GetSpell(199600) -local AdrenalineRush = Bastion.SpellBook:GetSpell(13750) -local ShadowDance = Bastion.SpellBook:GetSpell(185313) -local Audacity = Bastion.SpellBook:GetSpell(381845) -local Flagellation = Bastion.SpellBook:GetSpell(323654) -local Dreadblades = Bastion.SpellBook:GetSpell(343142) -local JollyRoger = Bastion.SpellBook:GetSpell(199603) -local BladeFlurry = Bastion.SpellBook:GetSpell(13877) -local Kick = Bastion.SpellBook:GetSpell(1766) -local MarkedForDeath = Bastion.SpellBook:GetSpell(137619) -local CrimsonVial = Bastion.SpellBook:GetSpell(185311) -local Shiv = Bastion.SpellBook:GetSpell(5938) -local KidneyShot = Bastion.SpellBook:GetSpell(408) -local InstantPoison = Bastion.SpellBook:GetSpell(315584) -local AtrophicPosion = Bastion.SpellBook:GetSpell(381637) -local Evasion = Bastion.SpellBook:GetSpell(5277) -local TricksOfTheTrade = Bastion.SpellBook:GetSpell(57934) -local CheapShot = Bastion.SpellBook:GetSpell(1833) -local BagOfTricks = Bastion.SpellBook:GetSpell(312411) -local AutoAttack = Bastion.SpellBook:GetSpell(6603) - -local IrideusFragment = Bastion.ItemBook:GetItem(193743) -local Healthstone = Bastion.ItemBook:GetItem(5512) -local WindscarWhetstone = Bastion.ItemBook:GetItem(137486) - -local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) - local purge = nil - - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if unit:GetAuras():HasAnyStealableAura() then - purge = unit - return true - end - end) - - if purge == nil then - purge = None - end - - return purge -end) - -local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit) - local purge = nil - - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then - purge = unit - return true - end - end) - - if purge == nil then - purge = None - end - - return purge -end) - -local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) - local tank = nil - - Bastion.UnitManager:EnumFriends(function(unit) - if Player:GetDistance(unit) > 40 then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if unit:IsDead() then - return false - end - - if unit:IsTank() then - tank = unit - return true - end - - return false - end) - - if tank == nil then - tank = None - end - - return tank -end) - -local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) - local explosive = nil - - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then - - explosive = unit - return true - end - end) - - if explosive == nil then - explosive = None - end - - return explosive -end) - -local DefaultAPL = Bastion.APL:New('default') -local AOEAPL = Bastion.APL:New('aoe') -local SpecialAPL = Bastion.APL:New('special') - -SpecialAPL:AddSpell( - Kick:CastableIf(function(self) - return KickTarget:Exists() and Player:InMelee(KickTarget) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() - end):SetTarget(KickTarget) -) - -SpecialAPL:AddSpell( - SinisterStrike:CastableIf(function(self) - return Explosive:Exists() and not Player:IsCastingOrChanneling() - end):SetTarget(Explosive) -) - -SpecialAPL:AddSpell( - KidneyShot:CastableIf(function(self) - return KickTarget:Exists() and Player:InMelee(KickTarget) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - ( - Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp()))) - - end):SetTarget(KickTarget) -) - -SpecialAPL:AddSpell( - CheapShot:CastableIf(function(self) - return KickTarget:Exists() and Player:InMelee(KickTarget) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Stealth):IsUp() - end):SetTarget(KickTarget) -) - -SpecialAPL:AddSpell( - Stealth:CastableIf(function(self) - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and not Player:IsAffectingCombat() and - not Player:GetAuras():FindMy(Stealth):IsUp() and not IsMounted() - end):SetTarget(Player) -) - -SpecialAPL:AddSpell( - CrimsonVial:CastableIf(function(self) - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetHealthPercent() < 70 - end):SetTarget(Player) -) - -SpecialAPL:AddSpell( - Shiv:CastableIf(function(self) - return PurgeTarget:Exists() and Player:InMelee(PurgeTarget) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and PurgeTarget:GetAuras():HasAnyStealableAura() - end):SetTarget(PurgeTarget) -) - -SpecialAPL:AddSpell( - InstantPoison:CastableIf(function(self) - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - not Player:GetAuras():FindMy(InstantPoison):IsUp() and not Player:IsMoving() - end):SetTarget(Player) -) - -SpecialAPL:AddSpell( - AtrophicPosion:CastableIf(function(self) - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - not Player:GetAuras():FindMy(AtrophicPosion):IsUp() and not Player:IsMoving() - end):SetTarget(Player) -) - -SpecialAPL:AddItem( - Healthstone:UsableIf(function(self) - return self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetHealthPercent() < 40 - end):SetTarget(Player) -) - -SpecialAPL:AddSpell( - TricksOfTheTrade:CastableIf(function(self) - return Tank:Exists() and self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:IsTanking(Target) - end):SetTarget(Tank) -) - -SpecialAPL:AddSpell( - Evasion:CastableIf(function(self) - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetHealthPercent() < 40 - end):SetTarget(Player) -) - -SpecialAPL:AddItem( - IrideusFragment:UsableIf(function(self) - return self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) - end):SetTarget(Player) -) - -SpecialAPL:AddItem( - WindscarWhetstone:UsableIf(function(self) - return self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) - end):SetTarget(Player) -) - -SpecialAPL:AddSpell( - BagOfTricks:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() - end):SetTarget(Target) -) - --- Adrenaline Rush on cooldown. -DefaultAPL:AddSpell( - AdrenalineRush:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() - end):SetTarget(Player) -) - --- Roll the Bones if you have no combat enhancements active. -DefaultAPL:AddSpell( - RollTheBones:CastableIf(function(self) - local numBuffs = 0 - if Player:GetAuras():FindMy(Broadside):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(BuriedTreasure):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(GrandMelee):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(RuthlessPrecision):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(SkullAndCrossbones):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(TrueBearing):IsUp() then - numBuffs = numBuffs + 1 - end - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - ((not Player:GetAuras():FindMy(Broadside):IsUp() and - not Player:GetAuras():FindMy(TrueBearing):IsUp()) or numBuffs < 2) - end):SetTarget(Player) -) --- Slice and Dice if at max, or -1 from maximum combo points with Broadside or Opportunity active, if missing or has has 12 or less seconds remaining. -DefaultAPL:AddSpell( - SliceAndDice:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - ( - Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp()))) and - ( - not Player:GetAuras():FindMy(SliceAndDice):IsUp() or - Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() <= 12 - ) - end):SetTarget(Target) -) - --- Between the Eyes on cooldown if at max, or -1 from maximum combo points with Broadside or Opportunity active. -DefaultAPL:AddSpell( - BetweenTheEyes:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - ( - Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp()))) - end):SetTarget(Target) -) - --- Dispatch if at max, or -1 from maximum combo points with Broadside or Opportunity active. -DefaultAPL:AddSpell( - Dispatch:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - ( - Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp()))) - end):SetTarget(Target) -) - --- Shadow Dance at or below 3 combo points, and do not have Audacity or Opportunity active and wait until you have at least 80 energy. While active Ambush becomes your highest priority builder. -DefaultAPL:AddSpell( - ShadowDance:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetComboPoints(Target) <= 3 and - not Player:GetAuras():FindMy(Audacity):IsUp() and - not Player:GetAuras():FindMy(Opportunity):IsUp() and - Player:GetPower() >= 80 - end):SetTarget(Player) -) - --- Blade Rush if missing 50 or more energy and do not have Flagellation, Dreadblades or Shadow Dance active. -DefaultAPL:AddSpell( - BladeRush:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetPower() <= 50 and - not Player:GetAuras():FindMy(Flagellation):IsUp() and - not Player:GetAuras():FindMy(Dreadblades):IsUp() and - not Player:GetAuras():FindMy(ShadowDance):IsUp() - end):SetTarget(Player) -) - --- Vanish followed by Ambush if you won't overcap combo points and wait until you have at least 50 energy. -DefaultAPL:AddSpell( - Vanish:CastableIf(function(self) - return Tank:Exists() and Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetComboPoints(Target) <= 4 and - Player:GetPower() >= 50 and not Player:GetAuras():FindMy(Stealth):IsUp() - end):SetTarget(Player) -) - --- Pistol Shot if you have an Opportunity proc and won't overcap combo points. -DefaultAPL:AddSpell( - PistolShot:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetAuras():FindMy(Opportunity):IsUp() and - Player:GetComboPoints(Target) <= 4 - end):SetTarget(Target) -) - --- Use Ambush Icon Ambush instead of Sinister Strike Icon Sinister Strike whenever it is available to cast from any of the procs or cooldowns. -DefaultAPL:AddSpell( - Ambush:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() - end):SetTarget(Target) -) - --- Sinister Strike if you won't overcap combo points. -DefaultAPL:AddSpell( - SinisterStrike:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetComboPoints(Target) < 5 - end):SetTarget(Target) -) - --- AOE APL - --- Adrenaline Rush on cooldown. -AOEAPL:AddSpell( - AdrenalineRush:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() - end):SetTarget(Player) -) - --- Roll the Bones if you have no combat enhancements active. --- Roll the Bones has a large outcome of buffs which include; Broadside, Buried Treasure, Grand Melee, Ruthless Precision, Skull and Crossbones, and True Bearing. The buffs you want to keep this tier are going to be Broadside and True Bearing as single buffs or any combination of 2 or more buffs. Any of the other 4 buffs as a single means it's worth recasting Roll the Bones as soon as you are able to. - -AOEAPL:AddSpell( - RollTheBones:CastableIf(function(self) - local numBuffs = 0 - if Player:GetAuras():FindMy(Broadside):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(BuriedTreasure):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(GrandMelee):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(RuthlessPrecision):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(SkullAndCrossbones):IsUp() then - numBuffs = numBuffs + 1 - end - if Player:GetAuras():FindMy(TrueBearing):IsUp() then - numBuffs = numBuffs + 1 - end - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - ((not Player:GetAuras():FindMy(Broadside):IsUp() and - not Player:GetAuras():FindMy(TrueBearing):IsUp()) or numBuffs < 2) - end):SetTarget(Player) -) - --- Blade Flurry when missing. -AOEAPL:AddSpell( - BladeFlurry:CastableIf(function(self) - return self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (not Player:GetAuras():FindMy(self):IsUp() or Player:GetAuras():FindMy(self):GetRemainingTime() <= 2) - end):SetTarget(Player) -) - --- Slice and Dice if at max, or -1 from maximum combo points with Broadside or Opportunity active, if missing or has has 12 or less seconds remaining. -AOEAPL:AddSpell( - SliceAndDice:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - (Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or - Player:GetAuras():FindMy(Opportunity):IsUp()))) and - (not Player:GetAuras():FindMy(self):IsUp() or - Player:GetAuras():FindMy(self):GetRemainingTime() <= 12) - end):SetTarget(Player) -) - --- Between the Eyes on cooldown if at max, or -1 from maximum combo points with Broadside or Opportunity active. -AOEAPL:AddSpell( - BetweenTheEyes:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - (Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or - Player:GetAuras():FindMy(Opportunity):IsUp()))) - end):SetTarget(Target) -) - --- Dispatch if at max, or -1 from maximum combo points with Broadside or Opportunity active. -AOEAPL:AddSpell( - Dispatch:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - (Player:GetComboPoints(Target) >= 5 or - (Player:GetComboPoints(Target) >= 4 and - (Player:GetAuras():FindMy(Broadside):IsUp() or - Player:GetAuras():FindMy(Opportunity):IsUp()))) - end):SetTarget(Target) -) - --- Shadow Dance at or below 3 combo points, and do not have Audacity or Opportunity active and wait until you have at least 80 energy. While active Ambush becomes your highest priority builder. -AOEAPL:AddSpell( - ShadowDance:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetComboPoints(Target) <= 3 and - not Player:GetAuras():FindMy(Audacity):IsUp() and - not Player:GetAuras():FindMy(Opportunity):IsUp() and - Player:GetPower() >= 80 - end):SetTarget(Player) -) - --- Blade Rush if missing 50 or more energy and do not have Flagellation, Dreadblades or Shadow Dance active. -AOEAPL:AddSpell( - BladeRush:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetPower() >= 50 and - not Player:GetAuras():FindMy(Flagellation):IsUp() and - not Player:GetAuras():FindMy(Dreadblades):IsUp() and - not Player:GetAuras():FindMy(ShadowDance):IsUp() - end):SetTarget(Player) -) - --- Vanish followed by Ambush if you won't overcap combo points and wait until you have at least 50 energy. -AOEAPL:AddSpell( - Vanish:CastableIf(function(self) - return Tank:Exists() and Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetComboPoints(Target) < 5 and - Player:GetPower() >= 50 and not Player:GetAuras():FindMy(Stealth):IsUp() - end):SetTarget(Player) -) - --- Pistol Shot if you have an Opportunity proc and won't overcap combo points. -AOEAPL:AddSpell( - PistolShot:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetAuras():FindMy(Opportunity):IsUp() and - Player:GetComboPoints(Target) < 5 - end):SetTarget(Target) -) - -AOEAPL:AddSpell( - Ambush:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() - end):SetTarget(Target) -) - --- Sinister Strike if you won't overcap combo points. -AOEAPL:AddSpell( - SinisterStrike:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and - Player:GetComboPoints(Target) < 5 - end):SetTarget(Target) -) - -OutlawModule:Sync(function() - SpecialAPL:Execute() - if Player:GetMeleeAttackers() > 1 then - AOEAPL:Execute() - else - DefaultAPL:Execute() - end -end) - -Bastion:Register(OutlawModule) diff --git a/scripts/restodruid.lua b/scripts/restodruid.lua index 08fa91f..c9b3a28 100644 --- a/scripts/restodruid.lua +++ b/scripts/restodruid.lua @@ -1,53 +1,16 @@ local Tinkr, Bastion = ... -local RestoModule = Bastion.Module:New('resto_druid') +local RestoModule = Bastion.Module:New('resto') local Evaluator = Tinkr.Util.Evaluator local Player = Bastion.UnitManager:Get('player') local None = Bastion.UnitManager:Get('none') local Target = Bastion.UnitManager:Get('target') -local myconf = Tinkr.Util.Config:New('resto_druid') +local myconf = Tinkr.Util.Config:New('resto_druid') -- for saving variables -local AnomalyDetectionMarkI = Bastion.SpellBook:GetSpell(382499) +-- Spellbook spells local AutoAttack = Bastion.SpellBook:GetSpell(6603) -local MechanismBypass = Bastion.SpellBook:GetSpell(382501) -local OverloadElementalDeposit = Bastion.SpellBook:GetSpell(388213) -local ReviveBattlePets = Bastion.SpellBook:GetSpell(125439) -local WarStomp = Bastion.SpellBook:GetSpell(20549) -local ArmorSkills = Bastion.SpellBook:GetSpell(76275) -local Brawn = Bastion.SpellBook:GetSpell(154743) -local Cultivation = Bastion.SpellBook:GetSpell(20552) -local Endurance = Bastion.SpellBook:GetSpell(20550) -local Languages = Bastion.SpellBook:GetSpell(79746) -local MasterRiding = Bastion.SpellBook:GetSpell(90265) -local NatureResistance = Bastion.SpellBook:GetSpell(20551) -local WeaponSkills = Bastion.SpellBook:GetSpell(76300) -local ActivateEmpowerment = Bastion.SpellBook:GetSpell(357857) -local BlessingofOhnara = Bastion.SpellBook:GetSpell(384522) -local BronzeTimelock = Bastion.SpellBook:GetSpell(374990) -local ChampionAbility = Bastion.SpellBook:GetSpell(356550) local CenarionWard = Bastion.SpellBook:GetSpell(102351) -local CombatAlly = Bastion.SpellBook:GetSpell(211390) -local ConstructAbility = Bastion.SpellBook:GetSpell(347013) -local CovenantAbility = Bastion.SpellBook:GetSpell(313347) -local GarrisonAbility = Bastion.SpellBook:GetSpell(161691) -local HeartEssence = Bastion.SpellBook:GetSpell(296208) -local HuntingCompanion = Bastion.SpellBook:GetSpell(376280) -local SanityRestorationOrb = Bastion.SpellBook:GetSpell(314955) -local SignatureAbility = Bastion.SpellBook:GetSpell(326526) -local SkywardAscent = Bastion.SpellBook:GetSpell(372610) -local SummonPocopoc = Bastion.SpellBook:GetSpell(360078) -local SurgeForward = Bastion.SpellBook:GetSpell(372608) -local Throw = Bastion.SpellBook:GetSpell(385265) -local VenthyrAbility = Bastion.SpellBook:GetSpell(315594) -local WartimeAbility = Bastion.SpellBook:GetSpell(264739) -local WhirlingSurge = Bastion.SpellBook:GetSpell(361584) -local PocopocZoneAbilitySkill = Bastion.SpellBook:GetSpell(363942) -local DragonridingBasics = Bastion.SpellBook:GetSpell(376777) -local LiftOff = Bastion.SpellBook:GetSpell(383363) -local ThrilloftheSkies = Bastion.SpellBook:GetSpell(383366) -local Vigor = Bastion.SpellBook:GetSpell(383359) -local WindsoftheIsles = Bastion.SpellBook:GetSpell(373586) local Barkskin = Bastion.SpellBook:GetSpell(22812) local BearForm = Bastion.SpellBook:GetSpell(5487) local CatForm = Bastion.SpellBook:GetSpell(768) @@ -60,19 +23,19 @@ local Innervate = Bastion.SpellBook:GetSpell(29166) local Mangle = Bastion.SpellBook:GetSpell(33917) local MarkoftheWild = Bastion.SpellBook:GetSpell(1126) local Moonfire = Bastion.SpellBook:GetSpell(8921) -local MoonfireAura = Bastion.SpellBook:GetSpell(164812) +local MoonfireDebuff = Bastion.SpellBook:GetSpell(164812) local Prowl = Bastion.SpellBook:GetSpell(5215) local Rebirth = Bastion.SpellBook:GetSpell(20484) local Regrowth = Bastion.SpellBook:GetSpell(8936) local Rejuvenation = Bastion.SpellBook:GetSpell(774) -local RejuvenationAura = Bastion.SpellBook:GetSpell(25299) +local RejuvenationBuff = Bastion.SpellBook:GetSpell(25299) local Revive = Bastion.SpellBook:GetSpell(50769) local Rip = Bastion.SpellBook:GetSpell(1079) local Shred = Bastion.SpellBook:GetSpell(5221) local Soothe = Bastion.SpellBook:GetSpell(2908) local StampedingRoar = Bastion.SpellBook:GetSpell(106898) local Sunfire = Bastion.SpellBook:GetSpell(93402) -local SunfireAura = Bastion.SpellBook:GetSpell(164815) +local SunfireDebuff = Bastion.SpellBook:GetSpell(164815) local Swiftmend = Bastion.SpellBook:GetSpell(18562) local TeleportMoonglade = Bastion.SpellBook:GetSpell(18960) local Thrash = Bastion.SpellBook:GetSpell(106832) @@ -83,85 +46,90 @@ local WildGrowth = Bastion.SpellBook:GetSpell(48438) local Wrath = Bastion.SpellBook:GetSpell(5176) local AquaticForm = Bastion.SpellBook:GetSpell(276012) local FlightForm = Bastion.SpellBook:GetSpell(276029) -local TigerDash = Bastion.SpellBook:GetSpell(252216) local Efflorescence = Bastion.SpellBook:GetSpell(145205) local IncarnationTreeofLife = Bastion.SpellBook:GetSpell(33891) local Ironbark = Bastion.SpellBook:GetSpell(102342) local Lifebloom = Bastion.SpellBook:GetSpell(33763) -local LifebloomAura = Bastion.SpellBook:GetSpell(188550) +local LifebloomBuff = Bastion.SpellBook:GetSpell(188550) local NaturesCure = Bastion.SpellBook:GetSpell(88423) local NaturesSwiftness = Bastion.SpellBook:GetSpell(132158) -local MoonkinForm = Bastion.SpellBook:GetSpell(197625) local Revitalize = Bastion.SpellBook:GetSpell(212040) local Tranquility = Bastion.SpellBook:GetSpell(740) -local MasteryHarmony = Bastion.SpellBook:GetSpell(77495) -local Moonfire = Bastion.SpellBook:GetSpell(8921) -local Wrath = Bastion.SpellBook:GetSpell(5176) -local BearForm = Bastion.SpellBook:GetSpell(5487) local AdaptiveSwarm = Bastion.SpellBook:GetSpell(391888) local AdaptiveSwarmBuff = Bastion.SpellBook:GetSpell(391891) local ClearCasting = Bastion.SpellBook:GetSpell(16870) local ConvokeTheSpirits = Bastion.SpellBook:GetSpell(391528) local Flourish = Bastion.SpellBook:GetSpell(197721) local SoulOfTheForest = Bastion.SpellBook:GetSpell(114108) -local Bursting = Bastion.SpellBook:GetSpell(240443) local Rake = Bastion.SpellBook:GetSpell(1822) -local RakeAura = Bastion.SpellBook:GetSpell(155722) +local RakeDebuff = Bastion.SpellBook:GetSpell(155722) local Starsurge = Bastion.SpellBook:GetSpell(197626) local NaturesVigil = Bastion.SpellBook:GetSpell(124974) local SpringBlossoms = Bastion.SpellBook:GetSpell(207386) +-- Define Units local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit) local lowest = nil local lowestHP = math.huge - Bastion.UnitManager:EnumFriends(function(unit) - if unit:IsDead() then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if not Player:CanSee(unit) then - return false - end - + if unit:IsDead() then return false end + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end local hp = unit:GetHP() if hp < lowestHP then lowest = unit lowestHP = hp end + if not lowest then + lowest = Player + end end) + return lowest +end) - if not lowest then - lowest = Player +local InterruptTarget = Bastion.UnitManager:CreateCustomUnit('skullbash', function(unit) + local interrupt = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:IsInterruptible(5) and Player:IsFacing(unit) then + interrupt = unit + return true + end + end) + if interrupt == nil then + interrupt = None end + return interrupt +end) - return lowest +local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) + local tank = nil + Bastion.UnitManager:EnumFriends(function(unit) + if Player:GetDistance(unit) > 40 then return false end + if not Player:CanSee(unit) then return false end + if unit:IsDead() then return false end + if unit:IsTank() then + tank = unit + return true + end + return false + end) + if tank == nil then + tank = None + end + return tank end) local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(unit) local lowest = nil local lowestHP = math.huge - Bastion.UnitManager:EnumFriends(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if not unit:IsDead() and Player:CanSee(unit) and - unit:GetAuras():HasAnyDispelableAura(NaturesCure) then - + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if not unit:IsDead() and Player:CanSee(unit) and unit:GetAuras():HasAnyDispelableAura(NaturesCure) then local hp = unit:GetHP() if hp < lowestHP then lowest = unit @@ -169,488 +137,404 @@ local DispelTarget = Bastion.UnitManager:CreateCustomUnit('dispel', function(uni end end end) - if lowest == nil then lowest = None end - return lowest end) local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) local purge = nil - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if not unit:IsDead() and Player:CanSee(unit) and - unit:GetAuras():HasAnyStealableAura() then + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if not unit:IsDead() and Player:CanSee(unit) and unit:GetAuras():HasAnyStealableAura() then purge = unit return true end end) - if purge == nil then purge = None end - return purge end) -local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) - local tank = nil - - Bastion.UnitManager:EnumFriends(function(unit) - if Player:GetDistance(unit) > 40 then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if unit:IsDead() then - return false - end - - if unit:IsTank() then - tank = unit +local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) + local explosive = nil + Bastion.UnitManager:EnumEnemies(function(unit) + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:InMelee(unit) and unit:GetID() == 120651 and Player:IsFacing(unit) then + explosive = unit return true end - - return false end) - - if tank == nil then - tank = Player + if explosive == nil then + explosive = None end - - return tank + return explosive end) local RejuvUnit = Bastion.UnitManager:CreateCustomUnit('rejuv', function(unit) local lowest = nil local lowestHP = math.huge - Bastion.UnitManager:EnumFriends(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if not unit:IsDead() and Player:CanSee(unit) and - ( - not unit:GetAuras():FindMy(Rejuvenation):IsUp() or - unit:GetAuras():FindMy(Rejuvenation):GetRemainingTime() <= 3.6) then - + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if not unit:IsDead() and Player:CanSee(unit) and (not unit:GetAuras():FindMy(Rejuvenation):IsUp() or unit:GetAuras():FindMy(Rejuvenation):GetRemainingTime() <= 3.6) then local hp = unit:GetHP() if hp < lowestHP then lowest = unit lowestHP = hp end end - end) - - if lowest == nil then lowest = Player end - return lowest end) local SwiftmendUnit = Bastion.UnitManager:CreateCustomUnit('swiftmend', function(unit) local lowest = nil local lowestHP = math.huge - Bastion.UnitManager:EnumFriends(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if ( - Player:CanSee(unit) and ( - (unit:GetAuras():FindMy(Regrowth):IsUp()) - or - ( - unit:GetAuras():FindMy(Rejuvenation):IsUp() and - not unit:GetAuras():FindMy(WildGrowth):IsUp()) - ) - ) then - + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if (Player:CanSee(unit) and ((unit:GetAuras():FindMy(Regrowth):IsUp()) or (unit:GetAuras():FindMy(Rejuvenation):IsUp() and not unit:GetAuras():FindMy(WildGrowth):IsUp()))) then local hp = unit:GetHP() if hp < lowestHP then lowest = unit lowestHP = hp end end - end) - - if lowest == nil then lowest = None end - return lowest end) local WildGrowthUnit = Bastion.UnitManager:CreateCustomUnit('wildgrowth', function(unit) local lowest = nil local lowestHP = math.huge - Bastion.UnitManager:EnumFriends(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if Player:CanSee(unit) and ( - ( - Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and - ( - Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or - unit:GetPartyHPAround(30, 90) >= 2)) or - (unit:GetPartyHPAround(30, 90) >= 3 or unit:GetPartyHPAround(30, 85) >= 2)) - then + if unit:IsDead() then return false end + if not Player:CanSee(unit) then return false end + if Player:GetDistance(unit) > 40 then return false end + if Player:CanSee(unit) and ((Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and (Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or unit:GetPartyHPAround(30, 90) >= 2)) or + (unit:GetPartyHPAround(30, 90) >= 3 or unit:GetPartyHPAround(30, 85) >= 2)) then local hp = unit:GetHP() if hp < lowestHP then lowest = unit lowestHP = hp end end - end) - - if lowest == nil then lowest = None end - return lowest end) -local Explosive = Bastion.UnitManager:CreateCustomUnit('explosive', function(unit) - local explosive = nil - - Bastion.ObjectManager.explosives:each(function(unit) - if unit:IsDead() then - return false - end +-- Rotation code here +local function Buffs() + -- Mark of the Wild + local motw = myconf:Read('motw') + MarkoftheWild:Condition('Mark of the Wild', function() + return motw and MarkoftheWild:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(MarkoftheWild):IsUp() + and not Player:IsMounted() + end) + MarkoftheWild:Cast(Player, 'Mark of the Wild') +end + +local function DruidProwl() + -- Prowl + Prowl:Condition('Prowl', function() + return Prowl:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(Prowl):IsUp() + and Player:GetDistance(Target) <= 25 and not Player:IsMounted() and Target:IsHostile() and not Target:IsDead() + end) + Prowl:Cast(Player, 'Prowl') +end - if not Player:CanSee(unit) then - return false - end +local function Opener() + --Efflorescence + Efflorescence:Condition('Efflorescence', function() + return IsShiftKeyDown() and Player:Exists() and Efflorescence:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end) + Efflorescence:Cast(None, 'Efflorescence') + local mx, my, mz = Tinkr.Common.ScreenToWorld(GetCursorPosition()) + Efflorescence:OnCast(function(self) + Bastion:Print(mx,my,mz) + self:Click(mx, my, mz) + end) +end - if Player:GetDistance(unit) <= 40 then - explosive = unit - return true +local function Interrupt() + -- Skull Bash + SkullBash:Condition('SkullBash', function() + return SkullBash:IsKnownAndUsable() and InterruptTarget:Exists() and not Player:IsCastingOrChanneling() + end) + SkullBash:Cast(Target, 'SkullBash') +end + +local function Healing() + DefaultAPL:AddSpell( + Efflorescence:CastableIf(function(self) + return PLACE_EFFLO and Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + end):SetTarget(None):OnCast(function(self) + local loc = Bastion.UnitManager:FindFriendsCentroid(10, 40) + PLACE_EFFLO = false + self:Click(loc) + end) + ) + + CatForm:OnCast(function(self) + if not Player:GetAuras():FindMy(Prowl):IsUp() and not Player:IsAffectingCombat() then + Prowl:Cast(Player) end end) + + DefaultAPL:AddAction( + 'cat_form_shift', + function() + if (IsShiftKeyDown() or not Player:IsAffectingCombat()) and not Player:IsMounted() and + not Player:GetAuras():FindMy(CatForm):IsUp() and + not Player:IsCastingOrChanneling() then + CatForm:Cast(Player) + elseif (not IsShiftKeyDown() and Player:IsAffectingCombat()) and Player:GetAuras():FindMy(CatForm):IsUp() then + CancelShapeshiftForm() + end + end + ) + + DefaultAPL:AddSpell( + NaturesCure:CastableIf(function(self) + return DispelTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(DispelTarget) and DispelTarget:GetAuras():HasAnyDispelableAura(NaturesCure) + end):SetTarget(DispelTarget) + ) + + DefaultAPL:AddSpell( + Soothe:CastableIf(function(self) + return PurgeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(PurgeTarget) and PurgeTarget:GetAuras():HasAnyStealableAura() + end):SetTarget(PurgeTarget) + ) + + DefaultAPL:AddSpell( + NaturesSwiftness:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() < 70 + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + ConvokeTheSpirits:CastableIf(function(self) + return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(Player) and (Player:GetPartyHPAround(40, 70) >= 2 or Player:GetPartyHPAround(40, 75) >= 3) + and (Flourish:IsKnownAndUsable() or Flourish:GetTimeSinceLastCast() > 10) + end):SetTarget(Player) + ) + + DefaultAPL:AddSpell( + Flourish:CastableIf(function(self) + return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(Player) and (Player:GetPartyHPAround(40, 70) >= 2 or Player:GetPartyHPAround(40, 75) >= 3) and + (not ConvokeTheSpirits:IsKnownAndUsable() and ConvokeTheSpirits:GetTimeSinceLastCast() > 10) and + WildGrowth:GetTimeSinceLastCast() <= 6 + end):SetTarget(Player) + ) + + DefaultAPL:AddSpell( + NaturesVigil:CastableIf(function(self) + return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and + self:IsInRange(Player) and Flourish:GetTimeSinceLastCast() <= 5 + end):SetTarget(Player) + ) + + DefaultAPL:AddSpell( + AdaptiveSwarm:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + Swiftmend:CastableIf(function(self) + return SwiftmendUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(SwiftmendUnit) and + ( + SwiftmendUnit:GetHP() <= 60 or + ( + Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2 + ) + ) + end):SetTarget(SwiftmendUnit) + ) + + DefaultAPL:AddSpell( + WildGrowth:CastableIf(function(self) + return WildGrowthUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(WildGrowthUnit) and + ( + Player:GetAuras():FindMy(SoulOfTheForest):IsUp() + or + (WildGrowthUnit:GetPartyHPAround(30, 90) >= 3 or WildGrowthUnit:GetPartyHPAround(30, 85) >= 2) + ) and + not Player:IsMoving() + end):SetTarget(WildGrowthUnit) + ) + + DefaultAPL:AddSpell( + Regrowth:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() < 65 and + Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and not Player:IsMoving() + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + Regrowth:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() < 70 and + ( + NaturesSwiftness:GetTimeSinceLastCast() < 2 or Player:GetAuras():FindMy(NaturesSwiftness):IsUp() or + NaturesSwiftness:IsKnownAndUsable()) and not Player:IsMoving() and + not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + CenarionWard:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() <= 90 + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + Ironbark:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and Lowest:GetHP() <= 70 and not Lowest:GetAuras():FindMy(CenarionWard):IsUp() + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + Rejuvenation:CastableIf(function(self) + return RejuvUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(RejuvUnit) and (RejuvUnit:GetHP() <= 94) and + not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() + end):SetTarget(RejuvUnit) + ) + + DefaultAPL:AddSpell( + Lifebloom:CastableIf(function(self) + return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and + ( + not Player:GetAuras():FindMy(LifebloomAura):IsUp() or + Player:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Player:IsAffectingCombat() + end):SetTarget(Player) + ) + + DefaultAPL:AddSpell( + Lifebloom:CastableIf(function(self) + return Tank:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and + ( + not Tank:GetAuras():FindMy(LifebloomAura):IsUp() or + Tank:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Tank:IsAffectingCombat() + end):SetTarget(Tank) + ) + + DefaultAPL:AddSpell( + Regrowth:CastableIf(function(self) + return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(Lowest) and + ( + not Player:GetAuras():FindMy(Regrowth):IsUp() and Lowest:GetHP() < 70 or + (Lowest:GetHP() <= 85 and Player:GetAuras():FindMy(ClearCasting):IsUp())) and + not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and + not Player:IsMoving() + end):SetTarget(Lowest) + ) + + DefaultAPL:AddSpell( + Rejuvenation:CastableIf(function(self) + return RejuvUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() + and Player:CanSee(RejuvUnit) and (RejuvUnit:GetHP() <= 94 or Player:GetPartyHPAround(40, 90) >= 2) and + not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() + end):SetTarget(RejuvUnit) + ) - if explosive == nil then - explosive = None - end - - return explosive -end) --- local RestoCommands = Bastion.Command:New('rex') --- local PLACE_EFFLO = false --- RestoCommands:Register('efflo', 'Request the engine to place an Efflorescence', function() --- PLACE_EFFLO = true --- Bastion.Notifications:AddNotification(Efflorescence:GetIcon(), "Efflorescence requested") --- end) -local DefaultAPL = Bastion.APL:New('default') -local DamageAPL = Bastion.APL:New('damage') -DefaultAPL:AddSpell( - Moonfire:CastableIf(function(self) - return Explosive:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - end):SetTarget(Explosive) -) +end -DefaultAPL:AddSpell( - Efflorescence:CastableIf(function(self) - return IsShiftKeyDown() and Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - end):SetTarget(None):OnCast(function(self) - --local loc = Target:GetPosition() - self:Click(loc) - - +local function Damage() + -- Cat Form + CatForm:Condition('Cat Form', function() + return CatForm:IsKnownAndUsable() and not Player:GetAuras():FindMy(CatForm):IsUp() end) -) - -DefaultAPL:AddAction( - 'cat_form_shift', - function() - if IsAltKeyDown() and not Player:GetAuras():FindMy(MoonkinForm):IsUp() and not Player:IsCastingOrChanneling() then - MoonkinForm:Cast(Player) - elseif not IsAltKeyDown() and Player:GetAuras():FindMy(MoonkinForm):IsUp() then - CancelShapeshiftForm() - end - end -) - -DefaultAPL:AddSpell( - NaturesCure:CastableIf(function(self) - return DispelTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and - self:IsInRange(DispelTarget) and DispelTarget:GetAuras():HasAnyDispelableAura(NaturesCure) - end):SetTarget(DispelTarget) -) - -DefaultAPL:AddSpell( - Soothe:CastableIf(function(self) - return PurgeTarget:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and - self:IsInRange(PurgeTarget) and PurgeTarget:GetAuras():HasAnyStealableAura() - end):SetTarget(PurgeTarget) -) - -DefaultAPL:AddSpell( - NaturesSwiftness:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Lowest) and - (Lowest:GetHP() < 70 or (Player:GetPartyHPAround(40, 65) >= 2 or Player:GetPartyHPAround(40, 70)) - ) - end):SetTarget(Lowest) -) - -DefaultAPL:AddSpell( - ConvokeTheSpirits:CastableIf(function(self) - return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and - self:IsInRange(Player) and (Player:GetPartyHPAround(40, 65) >= 2 or Player:GetPartyHPAround(40, 60) >= 3) - end):SetTarget(Player) -) -DefaultAPL:AddSpell( - MarkoftheWild:CastableIf(function(self) - return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and - not Player:GetAuras():FindMy(MarkoftheWild):IsUp() - end):SetTarget(Player) -) - -DefaultAPL:AddSpell( - Flourish:CastableIf(function(self) - return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and - self:IsInRange(Player) and (Player:GetPartyHPAround(40, 65) >= 2 or Player:GetPartyHPAround(40, 70) >= 3) and - (not ConvokeTheSpirits:IsKnownAndUsable() and ConvokeTheSpirits:GetTimeSinceLastCast() > 7) and - WildGrowth:GetTimeSinceLastCast() <= 6 - end):SetTarget(Player) -) - -DefaultAPL:AddSpell( - NaturesVigil:CastableIf(function(self) - return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and - self:IsInRange(Player) and Flourish:GetTimeSinceLastCast() <= 5 - end):SetTarget(Player) -) - -DefaultAPL:AddSpell( - AdaptiveSwarm:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:IsAffectingCombat() - and Player:CanSee(Lowest) - end):SetTarget(Lowest) -) - -DefaultAPL:AddSpell( - Swiftmend:CastableIf(function(self) - return SwiftmendUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(SwiftmendUnit) and - ( - SwiftmendUnit:GetHP() <= 80 or - ( - Lowest:GetPartyHPAround(30, 90) >= 3 or Lowest:GetPartyHPAround(30, 85) >= 2 - ) - ) - end):SetTarget(SwiftmendUnit) -) - -DefaultAPL:AddSpell( - WildGrowth:CastableIf(function(self) - return WildGrowthUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(WildGrowthUnit) and - ( - ( - Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and - ( - Player:GetAuras():FindMy(SoulOfTheForest):GetRemainingTime() <= 5 or - WildGrowthUnit:GetPartyHPAround(30, 90) >= 2)) or - (WildGrowthUnit:GetPartyHPAround(30, 90) >= 3 or WildGrowthUnit:GetPartyHPAround(30, 85) >= 2)) and - not Player:IsMoving() - end):SetTarget(WildGrowthUnit) -) - -DefaultAPL:AddSpell( - Regrowth:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Lowest) and Lowest:GetHP() < 65 and - Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and not Player:IsMoving() - end):SetTarget(Lowest) -) - -DefaultAPL:AddSpell( - Regrowth:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Lowest) and Lowest:GetHP() < 70 and - ( - NaturesSwiftness:GetTimeSinceLastCast() < 2 or Player:GetAuras():FindMy(NaturesSwiftness):IsUp() or - NaturesSwiftness:IsKnownAndUsable()) and not Player:IsMoving() and - not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() - end):SetTarget(Lowest) -) - -DefaultAPL:AddSpell( - CenarionWard:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Lowest) and Lowest:GetHP() <= 90 - end):SetTarget(Lowest) -) - -DefaultAPL:AddSpell( - Ironbark:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Lowest) and Lowest:GetHP() <= 70 and not Lowest:GetAuras():FindMy(CenarionWard):IsUp() - end):SetTarget(Lowest) -) - -DefaultAPL:AddSpell( - Rejuvenation:CastableIf(function(self) - return RejuvUnit:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(RejuvUnit) and RejuvUnit:GetHP() <= 90 and - not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() - end):SetTarget(RejuvUnit) -) - -DefaultAPL:AddSpell( - Lifebloom:CastableIf(function(self) - return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and - ( - not Player:GetAuras():FindMy(LifebloomAura):IsUp() or - Player:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Player:IsAffectingCombat() - end):SetTarget(Player) -) - -DefaultAPL:AddSpell( - Lifebloom:CastableIf(function(self) - return Tank:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and - ( - not Tank:GetAuras():FindMy(LifebloomAura):IsUp() or - Tank:GetAuras():FindMy(LifebloomAura):GetRemainingTime() <= 4.5) and Tank:IsAffectingCombat() - end):SetTarget(Tank) -) - -DefaultAPL:AddSpell( - Regrowth:CastableIf(function(self) - return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Lowest) and - ( - not Player:GetAuras():FindMy(Regrowth):IsUp() and Lowest:GetHP() < 70 or - (Lowest:GetHP() <= 85 and Player:GetAuras():FindMy(ClearCasting):IsUp())) and - not Player:GetAuras():FindMy(SoulOfTheForest):IsUp() and - not Player:IsMoving() - end):SetTarget(Lowest) -) - -DamageAPL:AddSpell( - Sunfire:CastableIf(function(self) - return Bastion.UnitManager['target']:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Bastion.UnitManager['target']) and - ( - not Bastion.UnitManager['target']:GetAuras():FindMy(SunfireAura):IsUp() or - Bastion.UnitManager['target']:GetAuras():FindMy(SunfireAura):GetRemainingTime() <= 5.4) and - Bastion.UnitManager['target']:IsHostile() and - Bastion.UnitManager['target']:IsAffectingCombat() and Player:GetPP() >= 25 - end):SetTarget(Bastion.UnitManager['target']) -) -DefaultAPL:AddSpell( - Innervate:CastableIf(function(self) - return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:IsAffectingCombat() - and Player:GetPP() <= 60 - end):SetTarget(Player) -) -DefaultAPL:AddSpell( - TravelForm:CastableIf(function(self) - return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:IsAffectingCombat() and not IsMounted() - and Player:IsMoving() and not Player:GetAuras():FindMy(TravelForm):IsUp() and not Player:IsIndoors() - end):SetTarget(Player) -) -DefaultAPL:AddSpell( - CatForm:CastableIf(function(self) - return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:IsAffectingCombat() and not IsMounted() - and Player:IsMoving() and not Player:GetAuras():FindMy(CatForm):IsUp() and Player:IsIndoors() - end):SetTarget(Player) -) -DamageAPL:AddSpell( - Moonfire:CastableIf(function(self) - return Bastion.UnitManager['target']:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Bastion.UnitManager['target']) and - ( - not Bastion.UnitManager['target']:GetAuras():FindMy(MoonfireAura):IsUp() or - Bastion.UnitManager['target']:GetAuras():FindMy(MoonfireAura):GetRemainingTime() <= 5.4) and - Bastion.UnitManager['target']:IsHostile() and - Bastion.UnitManager['target']:IsAffectingCombat() and Player:GetPP() >= 25 - end):SetTarget(Bastion.UnitManager['target']) -) - -DamageAPL:AddSpell( - Starsurge:CastableIf(function(self) - return Bastion.UnitManager['target']:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Bastion.UnitManager['target']) and Bastion.UnitManager['target']:IsHostile() and - Bastion.UnitManager['target']:IsAffectingCombat() and Player:GetPP() >= 25 - end):SetTarget(Bastion.UnitManager['target']) -) - -DamageAPL:AddSpell( - Wrath:CastableIf(function(self) - return Bastion.UnitManager['target']:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() - and Player:CanSee(Bastion.UnitManager['target']) and not Player:IsMoving() and - Bastion.UnitManager['target']:IsHostile() and - Bastion.UnitManager['target']:IsAffectingCombat() and Player:GetPP() >= 25 - end):SetTarget(Bastion.UnitManager['target']) -) + CatForm:Cast(Player, 'Cat Form') + -- Tiger's Fury + TigersFury:Condition('Tigers Fury', function() + return TigersFury:IsKnownAndUsable() and not Player:GetAuras():FindMy(TigersFury):IsUp() + end) + TigersFury:Cast(Player, 'Tigers Fury') + -- Primal Wrath + PrimalWrath:Condition('Primal Wrath', function() + return PrimalWrath:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 4 + and not Target:GetAuras():FindMy(Rip):IsUp() + end) + PrimalWrath:Cast(Target, 'Primal Wrath') + -- Ferocious Bite + FerociousBite:Condition('Ferocious Bite', function() + return FerociousBite:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetComboPoints(Target) >= 4 + end) + FerociousBite:Cast(Target, 'Ferocious Bite') + -- Thrash + Thrash:Condition('Thrash', function() + return Thrash:IsKnownAndUsable() and not Target:GetAuras():FindMy(ThrashDebuff):IsUp() and Player:InMelee(Target) + and Player:GetEnemies(8) >= 2 + end) + Thrash:Cast(Target, 'Thrash') + -- Rake + Rake:Condition('Rake', function() + return Rake:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:InMelee(Target) + and not Target:GetAuras():FindMy(RakeDebuff):IsUp() + end) + Rake:Cast(Target, 'Rake') + -- Swipe + Swipe:Condition('Swipe', function() + return Swipe:IsKnownAndUsable() and Player:InMelee(Target) and Player:GetEnemies(8) >= 2 + end) + Swipe:Cast(Target, 'Swipe') + -- Shred + Shred:Condition('Shred', function() + return Shred:IsKnownAndUsable() and Player:InMelee(Target) + end) + Shred:Cast(Target, 'Shred') +end +-- Module that dictates workflow RestoModule:Sync(function() - if IsAltKeyDown() and Player:GetAuras():FindMy(MoonkinForm):IsUp() then - return DamageAPL:Execute() + if not Player:IsAffectingCombat() then + if Buffs() then return end + if DruidProwl() then return end + if Opener() then return end + end + if Player:IsAffectingCombat() and Target:Exists() and Target:IsHostile() and not Target:IsDead() then + if Interrupt() then return end + if Damage() then return end end - DefaultAPL:Execute() end) Bastion:Register(RestoModule) @@ -658,59 +542,51 @@ local tab1 = { layoutConfig = { padding = { top = 40 } }, rows = { - [1] = { shadpr = { type = 'header', label = 'Open Combat' } }, + [1] = { shadpr = { type = 'label', label = 'Open Combat' } }, - [2] = { opener = { type = 'dropdown', label = 'Healing', column = 6, order = 1, + [2] = { opener = { type = 'dropdown', label = 'Opener', column = 6, order = 1, options = { - { text = 'Healing 1', value = 'swp'}, - { text = 'Healing 2', value = 'crash'}, + { text = 'Moonfire', value = 'moonfire'}, { text = 'None', value = 'none'}, }, - initialValue = myconf:Read('opener', 'shield'), + initialValue = myconf:Read('opener', 'moonfire'), onValueChanged = function(_, value) myconf:Write('opener', value) end }, }, - [3] = { swp1 = { type = 'checkbox', label = 'Spread SWP', column = 6, order = 1, - initialValue = myconf:Read('swpspread', false), - onValueChanged = function(_, flag) myconf:Write('swpspread', flag) end }, + [3] = { shadpr = { type = 'label', label = 'Buffs' } }, - swp2 = { type = 'slider', label = 'Max Targets', column = 6, order = 2, - min = 1, max = 10, precision = 0, - initialValue = myconf:Read('swptargets', 3), - onValueChanged = function(_, value) myconf:Write('swptargets', value) end }, }, + [4] = { motw = { type = 'checkbox', label = 'Mark of the Wild', column = 12, order = 1, + initialValue = myconf:Read('motw', false), + onValueChanged = function(_, flag) myconf:Write('motw', flag) end }, }, - [4] = { shadpr = { type = 'header', label = 'Defensives' } }, + [5] = { shadpr = { type = 'label', label = 'Defensives' } }, - [5] = { shield1 = { type = 'checkbox', label = 'Power Word: Shield', column = 6, order = 1, - initialValue = myconf:Read('pws', false), - onValueChanged = function(_, flag) myconf:Write('pws', flag) end }, + [6] = { shield1 = { type = 'checkbox', label = 'Ironfur', column = 6, order = 1, + initialValue = myconf:Read('ironfur', false), + onValueChanged = function(_, flag) myconf:Write('ironfur', flag) end }, shield2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, min = 1, max = 100, precision = 0, - initialValue = myconf:Read('pwspercent', 35), - onValueChanged = function(_, value) myconf:Write('pwspercent', value) end }, }, + initialValue = myconf:Read('ironfurpercent', 35), + onValueChanged = function(_, value) myconf:Write('ironfurpercent', value) end }, }, - [6] = { dispersion1 = { type = 'checkbox', label = 'Dispersion', column = 6, order = 1, - initialValue = myconf:Read('dispersion', false), - onValueChanged = function(_, flag) myconf:Write('dispersion', flag) end }, + [7] = { dispersion1 = { type = 'checkbox', label = 'Frenzied Regeneration', column = 6, order = 1, + initialValue = myconf:Read('fregen', false), + onValueChanged = function(_, flag) myconf:Write('fregen', flag) end }, dispersion2 = { type = 'slider', label = 'Player Health', column = 6, order = 2, min = 1, max = 100, precision = 0, - initialValue = myconf:Read('dispersionpercent', 35), - onValueChanged = function(_, value) myconf:Write('dispersionpercent', value) end }, }, - - [7] = { shadpr = { type = 'header', label = 'Interrupts' } }, + initialValue = myconf:Read('fregenpercent', 35), + onValueChanged = function(_, value) myconf:Write('fregenpercent', value) end }, }, - [8] = { silence = { type = 'checkbox', label = 'Silence on CD', column = 6, order = 1, - initialValue = myconf:Read('silence', false), - onValueChanged = function(_, flag) myconf:Write('silence', flag) end }, + [8] = { shadpr = { type = 'label', label = 'Interrupts' } }, - horror = { type = 'checkbox', label = 'Psychic Horror on CD', column = 6, order = 2, - initialValue = myconf:Read('horror', false), - onValueChanged = function(_, flag) myconf:Write('horror', flag) end }, }, + [9] = { silence = { type = 'checkbox', label = 'Skull Bash on CD', column = 12, order = 1, + initialValue = myconf:Read('skullbash', false), + onValueChanged = function(_, flag) myconf:Write('skullbash', flag) end }, }, - [9] = { shadpr = { type = 'header', label = 'Purify Disease' } }, + [10] = { shadpr = { type = 'header', label = 'Purify Disease' } }, - [10] = { incombat = { type = 'checkbox', label = 'In Combat', column = 6, order = 1, + [11] = { incombat = { type = 'checkbox', label = 'In Combat', column = 6, order = 1, initialValue = myconf:Read('purifyic', false), onValueChanged = function(_, flag) myconf:Write('purifyic', flag) end }, @@ -718,15 +594,15 @@ local tab1 = initialValue = myconf:Read('purifyooc', false), onValueChanged = function(_, flag) myconf:Write('purifyooc', flag) end }, }, - [11] = { shadpr = { type = 'header', label = 'Dispel Magic' } }, + [12] = { shadpr = { type = 'header', label = 'Dispel Magic' } }, - [12] = { incombat = { type = 'checkbox', label = 'In Combat', column = 12, order = 1, + [13] = { incombat = { type = 'checkbox', label = 'In Combat', column = 12, order = 1, initialValue = myconf:Read('dispelic', false), onValueChanged = function(_, flag) myconf:Write('dispelic', flag) end }, }, - [13] = { shadpr = { type = 'header', label = 'Dispel Delay Time' } }, + [14] = { shadpr = { type = 'header', label = 'Dispel Delay Time' } }, - [14] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, + [15] = { delay = { type = 'slider', label = 'In seconds', column = 6, order = 1, min = 0.5, max = 2, precision = 1, initialValue = myconf:Read('dispeldelay', 1), onValueChanged = function(_, value) myconf:Write('dispeldelay', value) end }, }, @@ -760,7 +636,7 @@ local tab2 = }, } -local restodruidconfig = { +local restoconfig = { layoutConfig = { padding = { top = 30 } }, rows = { [1] = { @@ -775,7 +651,7 @@ local restodruidconfig = { }, { name = 'group', - title = 'Healing', + title = 'Placeholder', layout = tab2 } }, @@ -783,7 +659,7 @@ local restodruidconfig = { }, }, } -Bastion.settingstemplate(restodruidconfig, 'Resto Druid', 400, 600, 1.00, 1.00, 1.00) --, 0.00, 0.44, 0.87, 'enhsha') --Title of Settings Frame, Width, Height, ClassRGB, Rotation Name +Bastion.settingstemplate(restoconfig, 'Resto Druid', 400, 600, 1.00, 0.49, 0.04) -- config, Title of Settings Frame, Width, Height, ClassRGB -- Class colours -- Death Knight 0.77 0.12 0.23 Red diff --git a/scripts/subtlety.lua b/scripts/subtlety.lua deleted file mode 100644 index 55c0350..0000000 --- a/scripts/subtlety.lua +++ /dev/null @@ -1,1980 +0,0 @@ --- local Tinkr, Bastion = ... - --- local SubModulue = Bastion.Module:New('sub') --- local Evaluator = Tinkr.Util.Evaluator --- local Player = Bastion.UnitManager:Get('player') --- local None = Bastion.UnitManager:Get('none') --- local Target = Bastion.UnitManager:Get('target') - --- local RollTheBones = Bastion.SpellBook:GetSpell(315508) --- local SliceAndDice = Bastion.SpellBook:GetSpell(315496) --- local BetweenTheEyes = Bastion.SpellBook:GetSpell(315341) --- local BladeRush = Bastion.SpellBook:GetSpell(271877) --- local Vanish = Bastion.SpellBook:GetSpell(1856) --- local Dispatch = Bastion.SpellBook:GetSpell(2098) --- local Ambush = Bastion.SpellBook:GetSpell(8676) --- local Stealth = Bastion.SpellBook:GetSpell(1784) --- local PistolShot = Bastion.SpellBook:GetSpell(185763) --- local Opportunity = Bastion.SpellBook:GetSpell(195627) --- local SinisterStrike = Bastion.SpellBook:GetSpell(193315) --- local GrandMelee = Bastion.SpellBook:GetSpell(193358) --- local Broadside = Bastion.SpellBook:GetSpell(193356) --- local TrueBearing = Bastion.SpellBook:GetSpell(193359) --- local RuthlessPrecision = Bastion.SpellBook:GetSpell(193357) --- local SkullAndCrossbones = Bastion.SpellBook:GetSpell(199603) --- local BuriedTreasure = Bastion.SpellBook:GetSpell(199600) --- local AdrenalineRush = Bastion.SpellBook:GetSpell(13750) --- local ShadowDance = Bastion.SpellBook:GetSpell(185313) --- local ShadowDanceAura = Bastion.SpellBook:GetSpell(185422) --- local Audacity = Bastion.SpellBook:GetSpell(381845) --- local Flagellation = Bastion.SpellBook:GetSpell(323654) --- local Dreadblades = Bastion.SpellBook:GetSpell(343142) --- local JollyRoger = Bastion.SpellBook:GetSpell(199603) --- local BladeFlurry = Bastion.SpellBook:GetSpell(13877) --- local Kick = Bastion.SpellBook:GetSpell(1766) --- local MarkedForDeath = Bastion.SpellBook:GetSpell(137619) --- local CrimsonVial = Bastion.SpellBook:GetSpell(185311) --- local Shiv = Bastion.SpellBook:GetSpell(5938) --- local KidneyShot = Bastion.SpellBook:GetSpell(408) --- local InstantPoison = Bastion.SpellBook:GetSpell(315584) --- local Sanguine = Bastion.SpellBook:GetSpell(226512) --- local AtrophicPosion = Bastion.SpellBook:GetSpell(381637) --- local Evasion = Bastion.SpellBook:GetSpell(5277) --- local TricksOfTheTrade = Bastion.SpellBook:GetSpell(57934) --- local CheapShot = Bastion.SpellBook:GetSpell(1833) --- local BagOfTricks = Bastion.SpellBook:GetSpell(312411) --- local AutoAttack = Bastion.SpellBook:GetSpell(6603) --- local SymbolsOfDeath = Bastion.SpellBook:GetSpell(212283) --- local ShadowBlades = Bastion.SpellBook:GetSpell(121471) --- local ColdBlood = Bastion.SpellBook:GetSpell(382245) --- local ShurikenTornado = Bastion.SpellBook:GetSpell(277925) --- local ThistleTea = Bastion.SpellBook:GetSpell(381623) --- local Gloomblade = Bastion.SpellBook:GetSpell(200758) --- local Shadowstrike = Bastion.SpellBook:GetSpell(185438) --- local Rupture = Bastion.SpellBook:GetSpell(1943) --- local Eviscerate = Bastion.SpellBook:GetSpell(196819) --- local NumbingPoison = Bastion.SpellBook:GetSpell(5761) --- local ShurikenStorm = Bastion.SpellBook:GetSpell(197835) --- local BlackPowder = Bastion.SpellBook:GetSpell(319175) --- local SecretTechnique = Bastion.SpellBook:GetSpell(280719) --- local DarkBrew = Bastion.SpellBook:GetSpell(310454) --- local Premeditation = Bastion.SpellBook:GetSpell(343173) --- local DanseMacabre = Bastion.SpellBook:GetSpell(393969) - --- local IrideusFragment = Bastion.ItemBook:GetItem(193743) --- local Healthstone = Bastion.ItemBook:GetItem(5512) --- local WindscarWhetstone = Bastion.ItemBook:GetItem(137486) --- local DarkMoonRime = Bastion.ItemBook:GetItem(198477) --- local AlgetharsPuzzleBox = Bastion.ItemBook:GetItem(193701) - --- local RimeCards = { --- One = Bastion.SpellBook:GetSpell(382844), --- Two = Bastion.SpellBook:GetSpell(382845), --- Three = Bastion.SpellBook:GetSpell(382846), --- Four = Bastion.SpellBook:GetSpell(382847), --- Five = Bastion.SpellBook:GetSpell(382848), --- Six = Bastion.SpellBook:GetSpell(382849), --- Seven = Bastion.SpellBook:GetSpell(382850), --- Eight = Bastion.SpellBook:GetSpell(382851), --- } - --- local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) --- local purge = nil - --- Bastion.UnitManager:EnumEnemies(function(unit) --- if unit:IsDead() then --- return false --- end - --- if not Player:CanSee(unit) then --- return false --- end - --- if Player:GetDistance(unit) > 40 then --- return false --- end - --- if unit:GetAuras():HasAnyStealableAura() then --- purge = unit --- return true --- end --- end) - --- if purge == nil then --- purge = None --- end - --- return purge --- end) - --- local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit) --- local kick = nil - --- Bastion.UnitManager:EnumEnemies(function(unit) --- if unit:IsDead() then --- return false --- end - --- if not Player:CanSee(unit) then --- return false --- end - --- if Player:GetDistance(unit) > 40 then --- return false --- end - --- if Player:InMelee(unit) and Player:IsFacing(unit) and Bastion.MythicPlusUtils:CastingCriticalKick(unit, 5) then --- kick = unit --- return true --- end --- end) - --- if kick == nil then --- kick = None --- end - --- return kick --- end) - --- local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) --- local tank = nil - --- Bastion.UnitManager:EnumFriends(function(unit) --- if Player:GetDistance(unit) > 40 then --- return false --- end - --- if not Player:CanSee(unit) then --- return false --- end - --- if unit:IsDead() then --- return false --- end - --- if unit:IsTank() then --- tank = unit --- return true --- end - --- return false --- end) - --- if tank == nil then --- tank = None --- end - --- return tank --- end) - --- local RuptureTarget = Bastion.UnitManager:CreateCustomUnit('rupture', function() --- local target = nil - --- Bastion.UnitManager:EnumEnemies(function(unit) --- if unit:IsDead() then --- return false --- end - --- if not Player:CanSee(unit) then --- return false --- end - --- if not Player:InMelee(unit) then --- return false --- end - --- if not Player:IsFacing(unit) then --- return false --- end - --- if ( --- not unit:GetAuras():FindMy(Rupture):IsUp() or --- unit:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 --- ) --- and unit:TimeToDie() > 12 --- and unit:GetCombatTime() > 4 --- then --- target = unit --- return true --- end --- end) - --- if target == nil then --- target = None --- end - --- return target --- end) - --- local DefaultAPL = Bastion.APL:New('default') --- local AOEAPL = Bastion.APL:New('aoe') --- local SpecialAPL = Bastion.APL:New('special') --- local RacialsAPL = Bastion.APL:New('racials') - --- local Facing = function(t) --- return Bastion.APLTrait:New(function() --- return Player:IsFacing(t) --- end) --- end - --- SpecialAPL:AddSpell( --- Kick:CastableIf(function(self) --- return KickTarget:Exists() and self:IsInRange(KickTarget) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() --- end):SetTarget(KickTarget) --- ):AddTraits( --- Facing(KickTarget) --- ) - --- SpecialAPL:AddSpell( --- KidneyShot:CastableIf(function(self) --- return KickTarget:Exists() and self:IsInRange(KickTarget) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Kick:GetTimeSinceLastCast() > 2 and --- (Player:GetComboPoints(Target) >= 5 or --- ( --- Player:GetComboPoints(Target) >= 4 and --- (Player:GetAuras():FindMy(Broadside):IsUp() or Player:GetAuras():FindMy(Opportunity):IsUp()))) --- and not Target:GetAuras():Find(Sanguine):IsUp() - --- end):SetTarget(KickTarget) --- ) - --- SpecialAPL:AddSpell( --- CheapShot:CastableIf(function(self) --- return KickTarget:Exists() and self:IsInRange(KickTarget) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Stealth):IsUp() --- and not Target:GetAuras():Find(Sanguine):IsUp() --- end):SetTarget(KickTarget) --- ) - --- SpecialAPL:AddSpell( --- Stealth:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and not Player:IsAffectingCombat() and --- not Player:GetAuras():FindMy(Stealth):IsUp() and not IsMounted() --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddSpell( --- CrimsonVial:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetHealthPercent() < 70 --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddSpell( --- Shiv:CastableIf(function(self) --- return PurgeTarget:Exists() and self:IsInRange(PurgeTarget) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and PurgeTarget:GetAuras():HasAnyStealableAura() --- end):SetTarget(PurgeTarget) --- ) - --- SpecialAPL:AddSpell( --- InstantPoison:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- not Player:GetAuras():FindMy(InstantPoison):IsUp() and not Player:IsMoving() --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddSpell( --- AtrophicPosion:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- not Player:GetAuras():FindMy(AtrophicPosion):IsUp() and not Player:IsMoving() --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddSpell( --- NumbingPoison:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- not Player:GetAuras():FindMy(NumbingPoison):IsUp() and not Player:IsMoving() --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddItem( --- Healthstone:UsableIf(function(self) --- return self:IsEquippedAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetHealthPercent() < 40 --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddSpell( --- TricksOfTheTrade:CastableIf(function(self) --- return Tank:Exists() and self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:IsTanking(Target) --- end):SetTarget(Tank) --- ) - --- SpecialAPL:AddSpell( --- Evasion:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetHealthPercent() < 40 --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddItem( --- IrideusFragment:UsableIf(function(self) --- return self:IsEquippedAndUsable() and --- not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddItem( --- WindscarWhetstone:UsableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and --- not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddItem( --- AlgetharsPuzzleBox:UsableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and --- not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 3 or Target:IsBoss()) --- end):SetTarget(Player) --- ) - --- SpecialAPL:AddItem( --- DarkMoonRime:UsableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and --- not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) and --- (Player:GetAuras():FindMy(RimeCards.One):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Two):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Three):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Four):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Five):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Six):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Seven):IsUp() or --- Player:GetAuras():FindMy(RimeCards.Eight):IsUp() --- ) --- end):SetTarget(Target) --- ) - --- -- Use Shadowstrike during Shadow Dance. --- SpecialAPL:AddSpell( --- Shadowstrike:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(Premeditation):IsUp() and --- Player:GetEnemies(10) <= 3 --- end):SetTarget(Target) --- ) - --- RacialsAPL:AddSpell( --- BagOfTricks:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() --- end):SetTarget(Target) --- ) - --- -- Use Symbols of Death on cooldown as much as possible. --- DefaultAPL:AddSpell( --- SymbolsOfDeath:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() --- end):SetTarget(Player):OnCast(function() --- ShurikenTornado:Cast(Target) --- end) --- ) - --- -- Use Shadow Blades on cooldown. --- DefaultAPL:AddSpell( --- ShadowBlades:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() --- end):SetTarget(Player) --- ) - --- -- Use Cold Blood before a finishing move, ideally before Secret Technique. --- DefaultAPL:AddSpell( --- ColdBlood:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetComboPoints(Target) >= 5 and SecretTechnique:IsKnownAndUsable() and --- Player:GetAuras():FindMy(SliceAndDice):IsUp() and --- Target:GetAuras():FindMy(Rupture):IsUp() --- end):SetTarget(Player):OnCast(function() --- SecretTechnique:Cast(Target) --- end) --- ) - --- -- Line up Shuriken Tornado with Symbols of Death. --- DefaultAPL:AddSpell( --- ShurikenTornado:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetAuras():FindMy(SymbolsOfDeath):IsUp() --- end):SetTarget(Player) --- ) - --- -- Use Shadow Dance on cooldown as much as possible. --- DefaultAPL:AddSpell( --- ShadowDance:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and Gloomblade:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) <= 2 --- end):SetTarget(Player):OnCast(function() --- Gloomblade:Cast(Target) -- We want to cast gloomblade immediately with shadow dance to trigger 1 stack of danse macabre --- end) --- ) - --- -- Use Thistle Tea when low on energy. --- -- actions.cds+=/thistle_tea,if=cooldown.symbols_of_death.remains>=3&!buff.thistle_tea.up&(energy.deficit>=100|cooldown.thistle_tea.charges_fractional>=2.75&buff.shadow_dance.up)|buff.shadow_dance.remains>=4&!buff.thistle_tea.up&spell_targets.shuriken_storm>=3|!buff.thistle_tea.up&fight_remains<=(6*cooldown.thistle_tea.charges) --- DefaultAPL:AddSpell( --- ThistleTea:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetPowerDeficit() >= 100 and --- ThistleTea:GetTimeSinceLastCast() >= 3 --- end):SetTarget(Player) - --- ) - --- -- Use Finishing moves with 6 or more combo points (5 or more during Shadow Dance) with the following priority: --- -- Cast Slice and Dice if it needs to be refreshed for maintenance or if it is not up. --- DefaultAPL:AddSpell( --- SliceAndDice:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 6 or --- (Player:GetComboPoints(Target) >= 5 and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and --- ( --- not Player:GetAuras():FindMy(SliceAndDice):IsUp() or --- Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 12 --- ) --- end):SetTarget(Player) --- ) - --- -- Cast Rupture if it needs to be refreshed for maintenance or if it is not up. --- DefaultAPL:AddSpell( --- Rupture:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 6 or --- (Player:GetComboPoints(Target) >= 5 and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) and ( --- not Target:GetAuras():FindMy(Rupture):IsUp() or --- Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 --- ) and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- end):SetTarget(Target) --- ) - --- DefaultAPL:AddSpell( --- SecretTechnique:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 5) and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and --- (Player:GetAuras():FindMy(DanseMacabre):GetCount() >= 3 or --- not DanseMacabre:IsKnown()) and --- (not ColdBlood:IsKnown() or --- ColdBlood:GetCooldownRemaining() > Player:GetAuras():FindMy(ShadowDanceAura):GetRemainingTime() - 2) --- end):SetTarget(Target) --- ) - --- -- Cast Eviscerate if it is available. --- DefaultAPL:AddSpell( --- Eviscerate:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 6 or --- (Player:GetComboPoints(Target) >= 5 and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp())) --- end):SetTarget(Target) --- ) - --- -- Vanish - Is a fairly weak cooldown. It is best to use on low combo points for a Shadowstrike cast. Use it after Secret Technique in Shadow Dance when playing with Danse Macabre. --- DefaultAPL:AddSpell( --- Vanish:CastableIf(function(self) --- return Tank:Exists() and Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetComboPoints(Target) < 4 --- end):SetTarget(Player) --- ) - --- -- Use Combo Point builder with the following priority: --- -- Use Gloomblade outside of Shadow Dance. --- DefaultAPL:AddSpell( --- Gloomblade:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- end):SetTarget(Target) --- ) - --- -- Use Shadowstrike during Shadow Dance. --- DefaultAPL:AddSpell( --- Shadowstrike:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- end):SetTarget(Target) --- ) - --- -- AOE - --- -- Use Symbols of Death on cooldown as much as possible. --- AOEAPL:AddSpell( --- SymbolsOfDeath:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() --- end):SetTarget(Player):OnCast(function() --- ShurikenTornado:Cast(Target) --- end) --- ) - --- -- Use Shadow Blades on cooldown. --- AOEAPL:AddSpell( --- ShadowBlades:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() --- end):SetTarget(Player) --- ) - --- -- Use Cold Blood before a finishing move. --- AOEAPL:AddSpell( --- ColdBlood:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetComboPoints(Target) >= 5 and SecretTechnique:IsKnownAndUsable() and --- Player:GetAuras():FindMy(SliceAndDice):IsUp() and --- Target:GetAuras():FindMy(Rupture):IsUp() --- end):SetTarget(Player):OnCast(function() --- SecretTechnique:Cast(Target) --- end) --- ) - --- -- Line up Shuriken Tornado with Symbols of Death. --- AOEAPL:AddSpell( --- ShurikenTornado:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetAuras():FindMy(SymbolsOfDeath):IsUp() --- end):SetTarget(Target) --- ) - --- -- Use Shadow Dance on cooldown as much as possible. --- AOEAPL:AddSpell( --- ShadowDance:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and Gloomblade:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) <= 2 --- end):SetTarget(Player):OnCast(function() --- Gloomblade:Cast(Target) -- We want to cast gloomblade immediately with shadow dance to trigger 1 stack of danse macabre --- end) --- ) - --- -- Use Thistle Tea with Shadow Dance. --- AOEAPL:AddSpell( --- ThistleTea:CastableIf(function(self) --- return Target:Exists() and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetPowerDeficit() >= 100 and --- ThistleTea:GetTimeSinceLastCast() >= 3 --- end):SetTarget(Player) --- ) - --- -- Use Finishing moves with 5 or more combo points with the following priority: --- -- Cast Slice and Dice if it needs to be refreshed for maintenance or if it is not up. --- AOEAPL:AddSpell( --- SliceAndDice:CastableIf(function(self) --- return Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 5) and --- ( --- not Player:GetAuras():FindMy(SliceAndDice):IsUp() or --- Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < 6 --- ) --- and Player:GetEnemies(10) < 6 --- end):SetTarget(Target) --- ) - --- -- Cast Rupture if it needs to be refreshed for maintenance or if it is not up. --- AOEAPL:AddSpell( --- Rupture:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 5) and ( --- not Target:GetAuras():FindMy(Rupture):IsUp() or --- Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 --- ) --- and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- and not Player:GetAuras():FindMy(SymbolsOfDeath):IsUp() --- and not Player:GetAuras():FindMy(ThistleTea):IsUp() --- end):SetTarget(Target) --- ) - --- -- Cast Rupture on all targets. (scam??) --- AOEAPL:AddSpell( --- Rupture:CastableIf(function(self) --- return RuptureTarget:Exists() and self:IsInRange(RuptureTarget) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(RuptureTarget) >= 6) and ( --- not RuptureTarget:GetAuras():FindMy(Rupture):IsUp() or --- RuptureTarget:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 --- ) --- and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- end):SetTarget(RuptureTarget) --- ) - --- -- actions.finish+=/secret_technique,if=buff.shadow_dance.up&(buff.danse_macabre.stack>=3|!talent.danse_macabre)&(!talent.cold_blood|cooldown.cold_blood.remains>buff.shadow_dance.remains-2) --- AOEAPL:AddSpell( --- SecretTechnique:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 5) and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and --- (Player:GetAuras():FindMy(DanseMacabre):GetCount() >= 3 or --- not DanseMacabre:IsKnown()) and --- (not ColdBlood:IsKnown() or --- ColdBlood:GetCooldownRemaining() > Player:GetAuras():FindMy(ShadowDanceAura):GetRemainingTime() - 2) --- end):SetTarget(Target) --- ) - --- -- Cast Black Powder with 3 or more targets, 2 or more when talented into Dark Brew. --- AOEAPL:AddSpell( --- BlackPowder:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- (Player:GetComboPoints(Target) >= 5) and --- (Player:GetEnemies(10) >= 3 or --- (Player:GetEnemies(10) >= 2 and --- DarkBrew:IsKnown())) --- end):SetTarget(Target) --- ) - --- -- Cast Eviscerate. --- AOEAPL:AddSpell( --- Eviscerate:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetComboPoints(Target) >= 5 --- end):SetTarget(Target) --- ) - --- -- Vanish - Is a fairly weak cooldown. It is best to use on low combo points for a Shadowstrike cast. Use it after Secret Technique in Shadow Dance when playing with Danse Macabre. --- AOEAPL:AddSpell( --- Vanish:CastableIf(function(self) --- return Tank:Exists() and Target:Exists() and Player:InMelee(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetComboPoints(Target) < 4 --- end):SetTarget(Player) --- ) - --- -- Use Combo Point builder with the following priority: --- -- Use Shuriken Storm on 2 targets outside of Shadow Dance. --- AOEAPL:AddSpell( --- ShurikenStorm:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetEnemies(10) == 2 and --- not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- end):SetTarget(Player) --- ) - --- -- Use Shadowstrike on 2 and 3 targets during Shadow Dance or to proc Premeditation. --- AOEAPL:AddSpell( --- Shadowstrike:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetEnemies(10) >= 2 and Player:GetEnemies(10) <= 3 and --- Player:GetAuras():FindMy(ShadowDanceAura):IsUp() --- end):SetTarget(Target) --- ) - --- -- Use Shuriken Storm at > 2 targets. --- AOEAPL:AddSpell( --- ShurikenStorm:CastableIf(function(self) --- return self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetEnemies(10) > 2 --- end):SetTarget(Player) --- ) - --- -- Use gloomblade at <= 2 targets. --- AOEAPL:AddSpell( --- Gloomblade:CastableIf(function(self) --- return Target:Exists() and self:IsInRange(Target) and --- self:IsKnownAndUsable() and --- not Player:IsCastingOrChanneling() and --- Player:GetEnemies(10) <= 2 --- end):SetTarget(Target) --- ) - --- SubModulue:Sync(function() --- SpecialAPL:Execute() --- if Player:GetEnemies(10) >= 2 then --- AOEAPL:Execute() --- else --- DefaultAPL:Execute() --- end --- RacialsAPL:Execute() --- end) - --- Bastion:Register(SubModulue) - -local Tinkr, Bastion = ... - -local SubModulue = Bastion.Module:New('sub') -local Evaluator = Tinkr.Util.Evaluator -local Player = Bastion.UnitManager:Get('player') -local None = Bastion.UnitManager:Get('none') -local Target = Bastion.UnitManager:Get('target') - -Player:WatchForSwings() - -local RollTheBones = Bastion.SpellBook:GetSpell(315508) -local SliceAndDice = Bastion.SpellBook:GetSpell(315496) -local BetweenTheEyes = Bastion.SpellBook:GetSpell(315341) -local BladeRush = Bastion.SpellBook:GetSpell(271877) -local Vanish = Bastion.SpellBook:GetSpell(1856) -local Dispatch = Bastion.SpellBook:GetSpell(2098) -local Ambush = Bastion.SpellBook:GetSpell(8676) -local FlagellationPersist = Bastion.SpellBook:GetSpell(345569) -local Stealth = Bastion.SpellBook:GetSpell(1784) -local PistolShot = Bastion.SpellBook:GetSpell(185763) -local Opportunity = Bastion.SpellBook:GetSpell(195627) -local SinisterStrike = Bastion.SpellBook:GetSpell(193315) -local GrandMelee = Bastion.SpellBook:GetSpell(193358) -local Broadside = Bastion.SpellBook:GetSpell(193356) -local TrueBearing = Bastion.SpellBook:GetSpell(193359) -local RuthlessPrecision = Bastion.SpellBook:GetSpell(193357) -local SkullAndCrossbones = Bastion.SpellBook:GetSpell(199603) -local ShadowFocus = Bastion.SpellBook:GetSpell(108209) -local BuriedTreasure = Bastion.SpellBook:GetSpell(199600) -local AdrenalineRush = Bastion.SpellBook:GetSpell(13750) -local ShadowDance = Bastion.SpellBook:GetSpell(185313) -local ShadowDanceAura = Bastion.SpellBook:GetSpell(185422) -local Shadowmeld = Bastion.SpellBook:GetSpell(58984) -local Audacity = Bastion.SpellBook:GetSpell(381845) -local SealFate = Bastion.SpellBook:GetSpell(14190) -local Flagellation = Bastion.SpellBook:GetSpell(323654) -local Dreadblades = Bastion.SpellBook:GetSpell(343142) -local MasterOfShadows = Bastion.SpellBook:GetSpell(196976) -local JollyRoger = Bastion.SpellBook:GetSpell(199603) -local BladeFlurry = Bastion.SpellBook:GetSpell(13877) -local Kick = Bastion.SpellBook:GetSpell(1766) -local FinalityRupture = Bastion.SpellBook:GetSpell(385951) -local MarkedForDeath = Bastion.SpellBook:GetSpell(137619) -local CrimsonVial = Bastion.SpellBook:GetSpell(185311) -local TheRotten = Bastion.SpellBook:GetSpell(394203) -local Shiv = Bastion.SpellBook:GetSpell(5938) -local KidneyShot = Bastion.SpellBook:GetSpell(408) -local InstantPoison = Bastion.SpellBook:GetSpell(315584) -local Sanguine = Bastion.SpellBook:GetSpell(226512) -local AtrophicPosion = Bastion.SpellBook:GetSpell(381637) -local Evasion = Bastion.SpellBook:GetSpell(5277) -local TricksOfTheTrade = Bastion.SpellBook:GetSpell(57934) -local Backstab = Bastion.SpellBook:GetSpell(53) -local CheapShot = Bastion.SpellBook:GetSpell(1833) -local BagOfTricks = Bastion.SpellBook:GetSpell(312411) -local Alacrity = Bastion.SpellBook:GetSpell(193539) -local PerforatedVeins = Bastion.SpellBook:GetSpell(394254) -local AutoAttack = Bastion.SpellBook:GetSpell(6603) -local DeeperStratagem = Bastion.SpellBook:GetSpell(193531) -local SecretStratagem = Bastion.SpellBook:GetSpell(394320) -local SymbolsOfDeath = Bastion.SpellBook:GetSpell(212283) -local ShadowBlades = Bastion.SpellBook:GetSpell(121471) -local Vigor = Bastion.SpellBook:GetSpell(14983) -local ColdBlood = Bastion.SpellBook:GetSpell(382245) -local ShurikenTornado = Bastion.SpellBook:GetSpell(277925) -local ThistleTea = Bastion.SpellBook:GetSpell(381623) -local Gloomblade = Bastion.SpellBook:GetSpell(200758) -local Shadowstrike = Bastion.SpellBook:GetSpell(185438) -local Rupture = Bastion.SpellBook:GetSpell(1943) -local Eviscerate = Bastion.SpellBook:GetSpell(196819) -local ResoundingClarity = Bastion.SpellBook:GetSpell(381622) -local ArcanePulse = Bastion.SpellBook:GetSpell(260364) -local NumbingPoison = Bastion.SpellBook:GetSpell(5761) -local ShurikenStorm = Bastion.SpellBook:GetSpell(197835) -local BlackPowder = Bastion.SpellBook:GetSpell(319175) -local Sepsis = Bastion.SpellBook:GetSpell(385408) -local SecretTechnique = Bastion.SpellBook:GetSpell(280719) -local DarkBrew = Bastion.SpellBook:GetSpell(310454) -local Premeditation = Bastion.SpellBook:GetSpell(343173) -local ArcaneTorrent = Bastion.SpellBook:GetSpell(50613) -local DanseMacabre = Bastion.SpellBook:GetSpell(393969) -local LingeringShadow = Bastion.SpellBook:GetSpell(385960) -local EchoingReprimand = Bastion.SpellBook:GetSpell(385616) -local LightsJudgment = Bastion.SpellBook:GetSpell(255647) -local Subterfuge = Bastion.SpellBook:GetSpell(108208) -local EchoingReprimand2 = Bastion.SpellBook:GetSpell(323558) -local EchoingReprimand3 = Bastion.SpellBook:GetSpell(323559) -local EchoingReprimand4 = Bastion.SpellBook:GetSpell(323560) -local EchoingReprimand5 = Bastion.SpellBook:GetSpell(354838) -local SilentStorm = Bastion.SpellBook:GetSpell(385722) -local FindWeakness = Bastion.SpellBook:GetSpell(91023) -local ImprovedShurikenStorm = Bastion.SpellBook:GetSpell(319951) - - - -local IrideusFragment = Bastion.ItemBook:GetItem(193743) -local Healthstone = Bastion.ItemBook:GetItem(5512) -local WindscarWhetstone = Bastion.ItemBook:GetItem(137486) -local DarkMoonRime = Bastion.ItemBook:GetItem(198477) -local AlgetharsPuzzleBox = Bastion.ItemBook:GetItem(193701) - -local RimeCards = { - One = Bastion.SpellBook:GetSpell(382844), - Two = Bastion.SpellBook:GetSpell(382845), - Three = Bastion.SpellBook:GetSpell(382846), - Four = Bastion.SpellBook:GetSpell(382847), - Five = Bastion.SpellBook:GetSpell(382848), - Six = Bastion.SpellBook:GetSpell(382849), - Seven = Bastion.SpellBook:GetSpell(382850), - Eight = Bastion.SpellBook:GetSpell(382851), -} - -local PurgeTarget = Bastion.UnitManager:CreateCustomUnit('purge', function(unit) - local purge = nil - - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if unit:GetAuras():HasAnyStealableAura() then - purge = unit - return true - end - end) - - if purge == nil then - purge = None - end - - return purge -end) - -local KickTarget = Bastion.UnitManager:CreateCustomUnit('kick', function(unit) - local kick = nil - - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if Player:GetDistance(unit) > 40 then - return false - end - - if Player:InMelee(unit) and Player:IsFacing(unit) and Bastion.MythicPlusUtils:CastingCriticalKick(unit, 5) then - kick = unit - return true - end - end) - - if kick == nil then - kick = None - end - - return kick -end) - -local Tank = Bastion.UnitManager:CreateCustomUnit('tank', function(unit) - local tank = nil - - Bastion.UnitManager:EnumFriends(function(unit) - if Player:GetDistance(unit) > 40 then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if unit:IsDead() then - return false - end - - if unit:IsTank() then - tank = unit - return true - end - - return false - end) - - if tank == nil then - tank = None - end - - return tank -end) - -local RuptureTarget = Bastion.UnitManager:CreateCustomUnit('rupture', function() - local target = nil - - Bastion.UnitManager:EnumEnemies(function(unit) - if unit:IsDead() then - return false - end - - if not Player:CanSee(unit) then - return false - end - - if not Player:InMelee(unit) then - return false - end - - if not Player:IsFacing(unit) then - return false - end - - if ( - not unit:GetAuras():FindMy(Rupture):IsUp() or - unit:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 - ) - and unit:TimeToDie() > 12 - and unit:GetCombatTime() > 4 - then - target = unit - return true - end - end) - - if target == nil then - target = None - end - - return target -end) - -local DefaultAPL = Bastion.APL:New('default') -local CDsAPL = Bastion.APL:New('cds') -local StealthedAPL = Bastion.APL:New('stealthed') -local StealthCDsAPL = Bastion.APL:New('stealthed_cds') -local FinishAPL = Bastion.APL:New('finish') -local BuildAPL = Bastion.APL:New('build') -local ItemsAPL = Bastion.APL:New('items') - -ItemsAPL:AddItem( - IrideusFragment:UsableIf(function(self) - return self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) - end):SetTarget(Player) -) - -ItemsAPL:AddItem( - WindscarWhetstone:UsableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) - end):SetTarget(Player) -) - -ItemsAPL:AddItem( - AlgetharsPuzzleBox:UsableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 3 or Target:IsBoss()) - end):SetTarget(Player) -) - -ItemsAPL:AddItem( - DarkMoonRime:UsableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and self:IsEquippedAndUsable() and - not Player:IsCastingOrChanneling() and (Player:GetMeleeAttackers() > 2 or Target:IsBoss()) and - (Player:GetAuras():FindMy(RimeCards.One):IsUp() or - Player:GetAuras():FindMy(RimeCards.Two):IsUp() or - Player:GetAuras():FindMy(RimeCards.Three):IsUp() or - Player:GetAuras():FindMy(RimeCards.Four):IsUp() or - Player:GetAuras():FindMy(RimeCards.Five):IsUp() or - Player:GetAuras():FindMy(RimeCards.Six):IsUp() or - Player:GetAuras():FindMy(RimeCards.Seven):IsUp() or - Player:GetAuras():FindMy(RimeCards.Eight):IsUp() - ) - end):SetTarget(Target) -) - --- # Executed every time the actor is available. --- # Restealth if possible (no vulnerable enemies in combat) --- actions=stealth -DefaultAPL:AddSpell( - Stealth:CastableIf( - function(self) - return self:IsKnownAndUsable() and not Player:GetAuras():FindMy(Stealth):IsUp() and - not Player:IsAffectingCombat() and not IsMounted() - end - ):SetTarget(Player) -) - --- # Interrupt on cooldown to allow simming interactions with that --- actions+=/kick -DefaultAPL:AddSpell( - Kick:CastableIf(function(self) - return KickTarget:Exists() and self:IsInRange(KickTarget) and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() and Player:IsFacing(Target) - end):SetTarget(KickTarget) -) - --- double consume_cp_max() const --- { --- return COMBO_POINT_MAX + as( talent.rogue.deeper_stratagem->effectN( 2 ).base_value() + --- talent.outlaw.devious_stratagem->effectN( 2 ).base_value() + --- talent.subtlety.secret_stratagem->effectN( 2 ).base_value() ); --- } - --- # Used to determine whether cooldowns wait for SnD based on targets. --- actions+=/variable,name=snd_condition,value=buff.slice_and_dice.up|spell_targets.shuriken_storm>=cp_max_spend -DefaultAPL:AddVariable( - 'snd_condition', - function() - return Player:GetAuras():FindMy(SliceAndDice):IsUp() or - Player:GetEnemies(10) >= Player:GetComboPointsMax() - end -) - --- # Check to see if the next CP (in the event of a ShT proc) is Animacharged --- actions+=/variable,name=is_next_cp_animacharged,if=talent.echoing_reprimand.enabled,value=combo_points=1&buff.echoing_reprimand_2.up|combo_points=2&buff.echoing_reprimand_3.up|combo_points=3&buff.echoing_reprimand_4.up|combo_points=4&buff.echoing_reprimand_5.up -DefaultAPL:AddVariable( - 'is_next_cp_animacharged', - function() - if not EchoingReprimand:IsKnown() then - return false - end - - local comboPoints = Player:GetComboPoints() - - if comboPoints == 1 and Player:GetAuras():FindMy(EchoingReprimand2):IsUp() then - return true - end - - if comboPoints == 2 and Player:GetAuras():FindMy(EchoingReprimand3):IsUp() then - return true - end - - if comboPoints == 3 and Player:GetAuras():FindMy(EchoingReprimand4):IsUp() then - return true - end - - if comboPoints == 4 and Player:GetAuras():FindMy(EchoingReprimand5):IsUp() then - return true - end - - return false - end -) - --- # Account for ShT reaction time by ignoring low-CP animacharged matches in the 0.5s preceeding a potential ShT proc --- actions+=/variable,name=effective_combo_points,value=effective_combo_points -DefaultAPL:AddVariable( - 'effective_combo_points', - function() - local cur = Player:GetComboPoints() or 0 - if not EchoingReprimand:IsKnown() then - return cur - end - - if cur < 2 or cur > 5 then - return cur - end - - if Player:GetAuras():FindMy(EchoingReprimand):IsUp() or Player:GetAuras():FindMy(EchoingReprimand2):IsUp() or - Player:GetAuras():FindMy(EchoingReprimand3):IsUp() or - Player:GetAuras():FindMy(EchoingReprimand4):IsUp() or - Player:GetAuras():FindMy(EchoingReprimand5):IsUp() - then - return 7 - end - - return cur - end -) - --- actions+=/variable,name=effective_combo_points,if=talent.echoing_reprimand.enabled&effective_combo_points>combo_points&combo_points.deficit>2&time_to_sht.4.plus<0.5&!variable.is_next_cp_animacharged,value=combo_points -DefaultAPL:AddVariable( - 'effective_combo_points', - function() - if not EchoingReprimand:IsKnown() then - return 0 - end - - local cur = Player:GetComboPoints() or 0 - local deficit = Player:GetComboPointsDeficit() or 0 - - if cur > Player:GetComboPoints() and deficit > 2 and - Player:GetAuras():FindMy(EchoingReprimand4):GetRemainingTime() < 0.5 and - not DefaultAPL:GetVariable('is_next_cp_animacharged') - then - return cur - end - - return 0 - end -) - --- # Check CDs at first --- actions+=/call_action_list,name=cds -DefaultAPL:AddAPL( - CDsAPL, - function() - return true - end -) - --- # Apply Slice and Dice at 4+ CP if it expires within the next GCD or is not up --- actions+=/slice_and_dice,if=spell_targets.shuriken_storm6&combo_points>=4 -DefaultAPL:AddSpell( - SliceAndDice:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:GetEnemies(10) < Player:GetComboPointsMax() and - Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() < Player:GetGCD() and - Player:GetAuras():FindMy(SliceAndDice):GetRemainingTime() > 6 and - Player:GetComboPoints() >= 4 - end - ):SetTarget(Player) -) - --- # Run fully switches to the Stealthed Rotation (by doing so, it forces pooling if nothing is available). --- actions+=/run_action_list,name=stealthed,if=stealthed.all -DefaultAPL:AddAPL( - StealthedAPL, - function() - return Player:GetAuras():FindMy(Stealth):IsUp() or Player:GetAuras():FindMy(Subterfuge):IsUp() or - Player:GetAuras():FindMy(ShadowDanceAura):IsUp() - end -) - --- # Only change rotation if we have priority_rotation set. --- actions+=/variable,name=priority_rotation,value=priority_rotation -DefaultAPL:AddVariable( - 'priority_rotation', - function() - return false - end -) - --- # Used to define when to use stealth CDs or builders --- actions+=/variable,name=stealth_threshold,value=25+talent.vigor.enabled*20+talent.master_of_shadows.enabled*20+talent.shadow_focus.enabled*25+talent.alacrity.enabled*20+25*(spell_targets.shuriken_storm>=4) -DefaultAPL:AddVariable( - 'stealth_threshold', - function() - return 25 + (Vigor:IsKnown() and 20 or 0) + (MasterOfShadows:IsKnown() and 20 or 0) + - (ShadowFocus:IsKnown() and 25 or 0) + (Alacrity:IsKnown() and 20 or 0) + - (25 * (Player:GetEnemies(10) >= 4 and 1 or 0)) - end -) - --- # Consider using a Stealth CD when reaching the energy threshold --- actions+=/call_action_list,name=stealth_cds,if=energy.deficit<=variable.stealth_threshold -DefaultAPL:AddAPL( - StealthCDsAPL, - function() - return Player:GetPowerDeficit() <= DefaultAPL:GetVariable('stealth_threshold') - end -) - --- actions+=/call_action_list,name=finish,if=variable.effective_combo_points>=cp_max_spend -DefaultAPL:AddAPL( - FinishAPL, - function() - return DefaultAPL:GetVariable('effective_combo_points') >= Player:GetComboPointsMax() - end -) - --- # Finish at maximum or close to maximum combo point value --- actions+=/call_action_list,name=finish,if=combo_points.deficit<=1+buff.the_rotten.up|fight_remains<=1&variable.effective_combo_points>=3 -DefaultAPL:AddAPL( - FinishAPL, - function() - return Player:GetComboPointsDeficit() <= 1 + (((Player:GetAuras():FindMy(TheRotten):IsUp()) or - (Player:TimeToDie() <= 1 and DefaultAPL:GetVariable('effective_combo_points') >= 3)) and 1 or 0) - end -) - --- # Finish at 4+ against 4 targets (outside stealth) --- actions+=/call_action_list,name=finish,if=spell_targets.shuriken_storm>=(4-talent.seal_fate)&variable.effective_combo_points>=4 -DefaultAPL:AddAPL( - FinishAPL, - function() - return Player:GetEnemies(10) >= (4 - (SealFate:IsKnown() and 1 or 0)) and - DefaultAPL:GetVariable('effective_combo_points') >= 4 - end -) - --- # Use a builder when reaching the energy threshold --- actions+=/call_action_list,name=build,if=energy.deficit<=variable.stealth_threshold -DefaultAPL:AddAPL( - BuildAPL, - function() - return Player:GetPowerDeficit() <= DefaultAPL:GetVariable('stealth_threshold') - end -) - --- # Lowest priority in all of the APL because it causes a GCD --- actions+=/arcane_torrent,if=energy.deficit>=15+energy.regen -DefaultAPL:AddSpell( - ArcaneTorrent:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:InMelee(Target) and - Player:GetPowerDeficit() >= 15 + Player:GetPowerRegen() - end - ):SetTarget(Player) -) - --- actions+=/arcane_pulse -DefaultAPL:AddSpell( - ArcanePulse:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and true - end - ):SetTarget(Target) -) - --- actions+=/lights_judgment -DefaultAPL:AddSpell( - LightsJudgment:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and true - end - ):SetTarget(Target) -) - --- actions+=/bag_of_tricks -DefaultAPL:AddSpell( - BagOfTricks:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and true - end - ):SetTarget(Target) -) - --- # Builders --- actions.build=shuriken_storm,if=spell_targets>=2+(buff.lingering_shadow.remains>=6|buff.perforated_veins.up) -BuildAPL:AddSpell( - ShurikenStorm:CastableIf( - function(self) - return self:IsKnownAndUsable() and - Player:GetEnemies(10) >= 2 + ((Player:GetAuras():FindMy(LingeringShadow):GetRemainingTime() >= 6 or - Player:GetAuras():FindMy(PerforatedVeins):IsUp()) and 1 or 0) - end - ):SetTarget(Target) -) - --- # Build immediately unless the next CP is Animacharged and we won't cap energy waiting for it. --- actions.build+=/variable,name=anima_helper,value=!talent.echoing_reprimand.enabled|!(variable.is_next_cp_animacharged&(time_to_sht.3.plus<0.5|time_to_sht.4.plus<1)&energy<60) -BuildAPL:AddVariable( - 'anima_helper', - function() - return not EchoingReprimand:IsKnown() or (not (DefaultAPL:GetVariable('is_next_cp_animacharged') and - (Player:GetTimeToShurikenTornado(3) < 0.5 or Player:GetTimeToShurikenTornado(4) < 1) and - Player:GetPower() < 60)) - end -) - --- actions.build+=/gloomblade,if=variable.anima_helper -BuildAPL:AddSpell( - Gloomblade:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and BuildAPL:GetVariable('anima_helper') - end - ):SetTarget(Target) -) - --- actions.build+=/backstab,if=variable.anima_helper -BuildAPL:AddSpell( - Backstab:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and BuildAPL:GetVariable('anima_helper') - end - ):SetTarget(Target) -) - --- # Cooldowns Use Dance off-gcd before the first Shuriken Storm from Tornado comes in. --- actions.cds=shadow_dance,use_off_gcd=1,if=!buff.shadow_dance.up&buff.shuriken_tornado.up&buff.shuriken_tornado.remains<=3.5 -CDsAPL:AddSpell( - ShadowDance:CastableIf( - function(self) - return Player:IsAffectingCombat() and self:IsKnownAndUsable() and - not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and - Player:GetAuras():FindMy(ShurikenTornado):IsUp() and - Player:GetAuras():FindMy(ShurikenTornado):GetRemainingTime() <= 3.5 - end - ):SetTarget(Player) -) - --- # (Unless already up because we took Shadow Focus) use Symbols off-gcd before the first Shuriken Storm from Tornado comes in. --- actions.cds+=/symbols_of_death,use_off_gcd=1,if=buff.shuriken_tornado.up&buff.shuriken_tornado.remains<=3.5 -CDsAPL:AddSpell( - SymbolsOfDeath:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:GetAuras():FindMy(ShurikenTornado):IsUp() and - Player:GetAuras():FindMy(ShurikenTornado):GetRemainingTime() <= 3.5 - end - ):SetTarget(Player) -) - --- # Vanish for Shadowstrike with Danse Macabre at adaquate stacks --- actions.cds+=/vanish,if=buff.danse_macabre.stack>3&combo_points<=2 -CDsAPL:AddSpell( - Vanish:CastableIf( - function(self) - return Player:IsAffectingCombat() and Player:GetAuras():FindMy(Vanish):IsUp() and self:IsKnownAndUsable() and - Player:GetAuras():FindMy(DanseMacabre):GetCount() > 3 and - DefaultAPL:GetVariable('effective_combo_points') <= 2 - end - ):SetTarget(Player) -) - --- # Cold Blood on 5 combo points when not playing Secret Technique --- actions.cds+=/cold_blood,if=!talent.secret_technique&combo_points>=5 -CDsAPL:AddSpell( - ColdBlood:CastableIf( - function(self) - return self:IsKnownAndUsable() and not SecretTechnique:IsKnown() and - DefaultAPL:GetVariable('effective_combo_points') >= 5 - end - ):SetTarget(Player) -) - --- actions.cds+=/flagellation,target_if=max:target.time_to_die,if=variable.snd_condition&combo_points>=5&target.time_to_die>10 -CDsAPL:AddSpell( - Flagellation:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and DefaultAPL:GetVariable('snd_condition') and - DefaultAPL:GetVariable('effective_combo_points') >= 5 and - Target:TimeToDie() > 10 - end - ):SetTarget(Target) -) - --- # Pool for Tornado pre-SoD with ShD ready when not running SF. --- actions.cds+=/pool_resource,for_next=1,if=talent.shuriken_tornado.enabled&!talent.shadow_focus.enabled - - --- # Use Tornado pre SoD when we have the energy whether from pooling without SF or just generally. --- actions.cds+=/shuriken_tornado,if=spell_targets.shuriken_storm<=1&energy>=60&variable.snd_condition&cooldown.symbols_of_death.up&cooldown.shadow_dance.charges>=1&(!talent.flagellation.enabled&!cooldown.flagellation.up|buff.flagellation_buff.up|spell_targets.shuriken_storm>=5)&combo_points<=2&!buff.premeditation.up -CDsAPL:AddSpell( - ShurikenTornado:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:GetEnemies(10) <= 1 and - Player:GetPower() >= 60 and - DefaultAPL:GetVariable('snd_condition') and - SymbolsOfDeath:OnCooldown() and - ShadowDance:GetCharges() >= 1 and - (not Flagellation:IsKnown() and not Flagellation:OnCooldown() or - Player:GetAuras():FindMy(Flagellation):IsUp() or - Player:GetEnemies(10) >= 5) and - DefaultAPL:GetVariable('effective_combo_points') <= 2 and - not Player:GetAuras():FindMy(Premeditation):IsUp() - end - ):SetTarget(Target) -) - --- actions.cds+=/sepsis,if=variable.snd_condition&combo_points.deficit>=1&target.time_to_die>=16 -CDsAPL:AddSpell( - Sepsis:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and DefaultAPL:GetVariable('snd_condition') and - Player:GetComboPointsDeficit() >= 1 and - Target:TimeToDie() >= 16 - end - ):SetTarget(Target) -) - --- # Use Symbols on cooldown (after first SnD) unless we are going to pop Tornado and do not have Shadow Focus. --- actions.cds+=/symbols_of_death,if=variable.snd_condition&(!talent.flagellation|cooldown.flagellation.remains>10|cooldown.flagellation.up&combo_points>=5) -CDsAPL:AddSpell( - SymbolsOfDeath:CastableIf( - function(self) - return self:IsKnownAndUsable() and DefaultAPL:GetVariable('snd_condition') and - (not Flagellation:IsKnown() or - Flagellation:GetCooldownRemaining() > 10 or - Flagellation:OnCooldown() and DefaultAPL:GetVariable('effective_combo_points') >= 5) - end - ):SetTarget(Player) -) - --- # If adds are up, snipe the one with lowest TTD. Use when dying faster than CP deficit or not stealthed without any CP. --- actions.cds+=/marked_for_death,line_cd=1.5,target_if=min:target.time_to_die,if=raid_event.adds.up&(target.time_to_die=cp_max_spend) --- # If no adds will die within the next 30s, use MfD on boss without any CP. --- actions.cds+=/marked_for_death,if=raid_event.adds.in>30-raid_event.adds.duration&combo_points.deficit>=cp_max_spend - --- actions.cds+=/shadow_blades,if=variable.snd_condition&combo_points.deficit>=2&target.time_to_die>=10&(dot.sepsis.ticking|cooldown.sepsis.remains<=8|!talent.sepsis)|fight_remains<=20 -CDsAPL:AddSpell( - ShadowBlades:CastableIf( - function(self) - return Player:IsAffectingCombat() and self:IsKnownAndUsable() and - DefaultAPL:GetVariable('snd_condition') and - Player:GetComboPointsDeficit() >= 2 and - Target:TimeToDie() >= 10 and - ((Target:GetAuras():FindMy(Sepsis):IsUp() or - Sepsis:GetCooldownRemaining() <= 8 or - not Sepsis:IsKnown()) or - Target:TimeToDie() <= 20) - end - ):SetTarget(Target) -) - --- actions.cds+=/echoing_reprimand,if=variable.snd_condition&combo_points.deficit>=3&(variable.priority_rotation|spell_targets.shuriken_storm<=4|talent.resounding_clarity)&(buff.shadow_dance.up|!talent.danse_macabre) -CDsAPL:AddSpell( - EchoingReprimand:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and DefaultAPL:GetVariable('snd_condition') and - Player:GetComboPointsDeficit() >= 3 and - (DefaultAPL:GetVariable('priority_rotation') or - Player:GetEnemies(10) <= 4 or - ResoundingClarity:IsKnown()) and - (Player:GetAuras():FindMy(ShadowDance):IsUp() or - not DanseMacabre:IsKnown()) - end - ):SetTarget(Target) -) - --- # With SF, if not already done, use Tornado with SoD up. --- actions.cds+=/shuriken_tornado,if=variable.snd_condition&buff.symbols_of_death.up&combo_points<=2&(!buff.premeditation.up|spell_targets.shuriken_storm>4) -CDsAPL:AddSpell( - ShurikenTornado:CastableIf( - function(self) - return self:IsKnownAndUsable() and DefaultAPL:GetVariable('snd_condition') and - Player:GetAuras():FindMy(SymbolsOfDeath):IsUp() and - DefaultAPL:GetVariable('effective_combo_points') <= 2 and - (not Player:GetAuras():FindMy(Premeditation):IsUp() or - Player:GetEnemies(10) > 4) - end - ):SetTarget(Target) -) - --- actions.cds+=/shuriken_tornado,if=cooldown.shadow_dance.ready&!stealthed.all&spell_targets.shuriken_storm>=3&!talent.flagellation.enabled -CDsAPL:AddSpell( - ShurikenTornado:CastableIf( - function(self) - return self:IsKnownAndUsable() and ShadowDance:GetCooldownRemaining() == 0 and - not Player:IsStealthed() and - Player:GetEnemies(10) >= 3 and - not Flagellation:IsKnown() - end - ):SetTarget(Target) -) - --- actions.cds+=/shadow_dance,if=!buff.shadow_dance.up&fight_remains<=8+talent.subterfuge.enabled -CDsAPL:AddSpell( - ShadowDance:CastableIf( - function(self) - return Player:IsAffectingCombat() and self:IsKnownAndUsable() and - not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and - Target:TimeToDie() <= 8 + (Subterfuge:IsKnown() and 1 or 0) - end - ):SetTarget(Target) -) - --- actions.cds+=/thistle_tea,if=cooldown.symbols_of_death.remains>=3&!buff.thistle_tea.up&(energy.deficit>=100|cooldown.thistle_tea.charges_fractional>=2.75&buff.shadow_dance.up)|buff.shadow_dance.remains>=4&!buff.thistle_tea.up&spell_targets.shuriken_storm>=3|!buff.thistle_tea.up&fight_remains<=(6*cooldown.thistle_tea.charges) -CDsAPL:AddSpell( - ThistleTea:CastableIf( - function(self) - return Player:IsAffectingCombat() and self:IsKnownAndUsable() and - (SymbolsOfDeath:GetCooldownRemaining() >= 3 - and not Player:GetAuras():FindMy(ThistleTea):IsUp() and - (Player:GetPowerDeficit() >= 100 or - self:GetChargesFractional() >= 2.75 and - Player:GetAuras():FindMy(ShadowDance):IsUp()) or - (Player:GetAuras():FindMy(ShadowDance):GetRemainingTime() >= 4 and - not Player:GetAuras():FindMy(ThistleTea):IsUp() and - Player:GetEnemies(10) >= 3) or - (not Player:GetAuras():FindMy(ThistleTea):IsUp() and - Player:TimeToDie() <= (6 * self:GetCharges()))) - end - ):SetTarget(Player) -) - --- actions.cds+=/potion,if=buff.bloodlust.react|fight_remains<30|buff.symbols_of_death.up&(buff.shadow_blades.up|cooldown.shadow_blades.remains<=10) --- actions.cds+=/blood_fury,if=buff.symbols_of_death.up --- actions.cds+=/berserking,if=buff.symbols_of_death.up --- actions.cds+=/fireblood,if=buff.symbols_of_death.up --- actions.cds+=/ancestral_call,if=buff.symbols_of_death.up --- actions.cds+=/use_item,name=manic_grieftorch,use_off_gcd=1,if=gcd.remains>gcd.max-0.1,if=!stealthed.all --- # Default fallback for usable items: Use with Symbols of Death. --- actions.cds+=/use_items,if=buff.symbols_of_death.up|fight_remains<20 -CDsAPL:AddAPL( - ItemsAPL, - function() - return Player:GetAuras():FindMy(SymbolsOfDeath):IsUp() or (Target:IsBoss() and Target:TimeToDie() < 20) - end -) - --- # Finishers While using Premeditation, avoid casting Slice and Dice when Shadow Dance is soon to be used, except for Kyrian --- actions.finish=variable,name=premed_snd_condition,value=talent.premeditation.enabled&spell_targets.shuriken_storm<5 -FinishAPL:AddVariable( - 'premed_snd_condition', - function() - return Premeditation:IsKnown() and Player:GetEnemies(10) < 5 - end -) - --- actions.finish+=/slice_and_dice,if=!variable.premed_snd_condition&spell_targets.shuriken_storm<6&!buff.shadow_dance.up&buff.slice_and_dice.remains=2) -FinishAPL:AddVariable( - 'skip_rupture', - function() - return (Player:GetAuras():FindMy(ThistleTea):IsUp() and Player:GetEnemies(10) == 1) or - (Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and - (Player:GetEnemies(10) == 1 or - Target:GetAuras():FindMy(Rupture):IsUp() and Player:GetEnemies(10) >= 2)) - end -) - --- # Keep up Rupture if it is about to run out. --- actions.finish+=/rupture,if=(!variable.skip_rupture|variable.priority_rotation)&target.time_to_die-remains>6&refreshable -FinishAPL:AddSpell( - Rupture:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and - (not DefaultAPL:GetVariable('skip_rupture') or - DefaultAPL:GetVariable('priority_rotation')) and - Target:TimeToDie() - Target:GetAuras():FindMy(Rupture):GetRemainingTime() > 6 and - Target:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 - end - ):SetTarget(Target) -) - --- # Refresh Rupture early for Finality --- actions.finish+=/rupture,if=!variable.skip_rupture&buff.finality_rupture.up&cooldown.shadow_dance.remains<12&cooldown.shadow_dance.charges_fractional<=1&spell_targets.shuriken_storm=1&(talent.dark_brew|talent.danse_macabre) -FinishAPL:AddSpell( - Rupture:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and not DefaultAPL:GetVariable('skip_rupture') and - Player:GetAuras():FindMy(FinalityRupture):IsUp() and - ShadowDance:GetCooldownRemaining() < 12 and - ShadowDance:GetChargesFractional() <= 1 and - Player:GetEnemies(10) == 1 and - (DarkBrew:IsKnown() or DanseMacabre:IsKnown()) - end - ):SetTarget(Target) -) - --- # Sync Cold Blood with Secret Technique when possible --- actions.finish+=/cold_blood,if=buff.shadow_dance.up&(buff.danse_macabre.stack>=3|!talent.danse_macabre)&cooldown.secret_technique.ready -FinishAPL:AddSpell( - ColdBlood:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and - (Player:GetAuras():FindMy(DanseMacabre):GetCount() >= 3 or - not DanseMacabre:IsKnown()) and - SecretTechnique:OnCooldown() - end - ):SetTarget(Target) -) - --- actions.finish+=/secret_technique,if=buff.shadow_dance.up&(buff.danse_macabre.stack>=3|!talent.danse_macabre)&(!talent.cold_blood|cooldown.cold_blood.remains>buff.shadow_dance.remains-2) -FinishAPL:AddSpell( - SecretTechnique:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and - Player:GetAuras():FindMy(ShadowDanceAura):IsUp() and - (Player:GetAuras():FindMy(DanseMacabre):GetCount() >= 3 or - not DanseMacabre:IsKnown()) and - (not ColdBlood:IsKnown() or - ColdBlood:GetCooldownRemaining() > Player:GetAuras():FindMy(ShadowDanceAura):GetRemainingTime() - 2) - end - ):SetTarget(Target) -) - --- # Multidotting targets that will live for the duration of Rupture, refresh during pandemic. --- actions.finish+=/rupture,cycle_targets=1,if=!variable.skip_rupture&!variable.priority_rotation&spell_targets.shuriken_storm>=2&target.time_to_die>=(2*combo_points)&refreshable -FinishAPL:AddSpell( - Rupture:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(RuptureTarget) and - not DefaultAPL:GetVariable('skip_rupture') and - not DefaultAPL:GetVariable('priority_rotation') and - Player:GetEnemies(10) >= 2 and - RuptureTarget:TimeToDie() >= (2 * Player:GetComboPoints()) and - RuptureTarget:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 - end - ):SetTarget(RuptureTarget) -) - - --- # Refresh Rupture early if it will expire during Symbols. Do that refresh if SoD gets ready in the next 5s. --- actions.finish+=/rupture,if=!variable.skip_rupture&remainscooldown.symbols_of_death.remains+5 -FinishAPL:AddSpell( - Rupture:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and not DefaultAPL:GetVariable('skip_rupture') and - Target:GetAuras():FindMy(Rupture):GetRemainingTime() < - SymbolsOfDeath:GetCooldownRemaining() + 10 and - SymbolsOfDeath:GetCooldownRemaining() <= 5 and - Target:TimeToDie() - Target:GetAuras():FindMy(Rupture):GetRemainingTime() > - SymbolsOfDeath:GetCooldownRemaining() + 5 - end - ):SetTarget(Target) -) - --- actions.finish+=/black_powder,if=!variable.priority_rotation&spell_targets>=3 -FinishAPL:AddSpell( - BlackPowder:CastableIf( - function(self) - return self:IsKnownAndUsable() and not DefaultAPL:GetVariable('priority_rotation') and - Player:GetEnemies(10) >= 3 - end - ):SetTarget(Target) -) - --- actions.finish+=/eviscerate -FinishAPL:AddSpell( - Eviscerate:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and true - end - ):SetTarget(Target) -) - --- # Stealth Cooldowns Helper Variable --- actions.stealth_cds=variable,name=shd_threshold,value=cooldown.shadow_dance.charges_fractional>=0.75+talent.shadow_dance -StealthCDsAPL:AddVariable( - 'shd_threshold', - function(self) - return ShadowDance:GetChargesFractional() >= 0.75 + (ShadowDance:IsKnown() and 1 or 0) - end -) - --- # Vanish if we are capping on Dance charges. Early before first dance if we have no Nightstalker but Dark Shadow in order to get Rupture up (no Master Assassin). --- actions.stealth_cds+=/vanish,if=(!talent.danse_macabre|spell_targets.shuriken_storm>=3)&!variable.shd_threshold&combo_points.deficit>1 -StealthCDsAPL:AddSpell( - Vanish:CastableIf( - function(self) - return Player:IsAffectingCombat() and not Player:GetAuras():FindMy(Stealth):IsUp() and - self:IsKnownAndUsable() and - (not DanseMacabre:IsKnown() or Player:GetEnemies(10) >= 3) and - not StealthCDsAPL:GetVariable('shd_threshold') and - Player:GetComboPointsDeficit() > 1 - end - ):SetTarget(Target) -) - --- # Pool for Shadowmeld + Shadowstrike unless we are about to cap on Dance charges. Only when Find Weakness is about to run out. --- actions.stealth_cds+=/pool_resource,for_next=1,extra_amount=40,if=race.night_elf - - --- actions.stealth_cds+=/shadowmeld,if=energy>=40&energy.deficit>=10&!variable.shd_threshold&combo_points.deficit>4 -StealthCDsAPL:AddSpell( - Shadowmeld:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:GetEnergy() >= 40 and - Player:GetPowerDeficit() >= 10 and - not StealthCDsAPL:GetVariable('shd_threshold') and - Player:GetComboPointsDeficit() > 4 - end - ):SetTarget(Target) -) - --- # CP thresholds for entering Shadow Dance Default to start dance with 0 or 1 combo point --- actions.stealth_cds+=/variable,name=shd_combo_points,value=combo_points<=1 -StealthCDsAPL:AddVariable( - 'shd_combo_points', - function(self) - return Player:GetComboPoints() <= 1 - end -) - --- # Use stealth cooldowns with high combo points when playing shuriken tornado or with high target counts --- actions.stealth_cds+=/variable,name=shd_combo_points,value=combo_points.deficit<=1,if=spell_targets.shuriken_storm>(4-2*talent.shuriken_tornado.enabled)|variable.priority_rotation&spell_targets.shuriken_storm>=4 -StealthCDsAPL:AddVariable( - 'shd_combo_points', - function(self) - return Player:GetComboPointsDeficit() <= 1 and - ((Player:GetEnemies(10) > (4 - 2 * (ShurikenTornado:IsKnown() and 1 or 0))) or - (DefaultAPL:GetVariable('priority_rotation') and - Player:GetEnemies(10) >= 4)) - end -) - --- # Use stealth cooldowns on any combo point on 4 targets --- actions.stealth_cds+=/variable,name=shd_combo_points,value=1,if=spell_targets.shuriken_storm=(4-talent.seal_fate) -StealthCDsAPL:AddVariable( - 'shd_combo_points', - function(self) - return Player:GetEnemies(10) == (4 - (SealFate:IsKnown() and 1 or 0)) - end -) - --- # Dance during Symbols or above threshold. --- actions.stealth_cds+=/shadow_dance,if=(variable.shd_combo_points&(buff.symbols_of_death.remains>=(2.2-talent.flagellation.enabled)|variable.shd_threshold)|buff.flagellation.up|buff.flagellation_persist.remains>=6|spell_targets.shuriken_storm>=4&cooldown.symbols_of_death.remains>10)&!buff.the_rotten.up -StealthCDsAPL:AddSpell( - ShadowDance:CastableIf( - function(self) - return Player:IsAffectingCombat() and self:IsKnownAndUsable() and - ((StealthCDsAPL:GetVariable('shd_combo_points') and - (Player:GetAuras():FindMy(SymbolsOfDeath):GetRemainingTime() >= - (2.2 - (Flagellation:IsKnown() and 1 or 0)) or - StealthCDsAPL:GetVariable('shd_threshold'))) or - Player:GetAuras():FindMy(Flagellation):IsUp() or - Player:GetAuras():FindMy(FlagellationPersist):GetRemainingTime() >= 6 or - Player:GetEnemies(10) >= 4 and - SymbolsOfDeath:GetCooldownRemaining() > 10) and - not Target:GetAuras():FindMy(TheRotten):IsUp() - end - ):SetTarget(Target) -) - --- # Burn Dances charges if before the fight ends if SoD won't be ready in time. --- actions.stealth_cds+=/shadow_dance,if=variable.shd_combo_points&fight_remains=cp_max_spend -StealthedAPL:AddAPL( - FinishAPL, - function(self) - return DefaultAPL:GetVariable('effective_combo_points') >= Player:GetComboPointsMax() - end -) - --- # Finish earlier with Shuriken tornado up. --- actions.stealthed+=/call_action_list,name=finish,if=buff.shuriken_tornado.up&combo_points.deficit<=2 -StealthedAPL:AddAPL( - FinishAPL, - function(self) - return Player:GetAuras():FindMy(ShurikenTornado):IsUp() and - Player:GetComboPointsDeficit() <= 2 - end -) - --- # Also safe to finish at 4+ CP with exactly 4 targets. (Same as outside stealth.) --- actions.stealthed+=/call_action_list,name=finish,if=spell_targets.shuriken_storm>=4-talent.seal_fate&variable.effective_combo_points>=4 -StealthedAPL:AddAPL( - FinishAPL, - function() - return Player:GetEnemies(10) >= 4 - ((SealFate:IsKnown() and - DefaultAPL:GetVariable('effective_combo_points') >= 4) and 1 or 0) - end -) - --- # Finish at lower combo points if you are talented in DS, SS or Seal Fate --- actions.stealthed+=/call_action_list,name=finish,if=combo_points.deficit<=1+(talent.seal_fate|talent.deeper_stratagem|talent.secret_stratagem) -StealthedAPL:AddAPL( - FinishAPL, - function() - return Player:GetComboPointsDeficit() <= - 1 + ((SealFate:IsKnown() or DeeperStratagem:IsKnown() or SecretStratagem:IsKnown()) and 1 or 0) - end -) - --- # Use Gloomblade or Backstab when close to hitting max PV stacks --- actions.stealthed+=/gloomblade,if=buff.perforated_veins.stack>=5&spell_targets.shuriken_storm<3 -StealthedAPL:AddSpell( - Gloomblade:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and - Player:GetAuras():FindMy(PerforatedVeins):GetCount() >= 5 and - Player:GetEnemies(10) < 3 - end - ):SetTarget(Target) -) - --- actions.stealthed+=/backstab,if=buff.perforated_veins.stack>=5&spell_targets.shuriken_storm<3 -StealthedAPL:AddSpell( - Backstab:CastableIf( - function(self) - return self:IsKnownAndUsable() and self:IsInRange(Target) and - Player:GetAuras():FindMy(PerforatedVeins):GetCount() >= 5 and - Player:GetEnemies(10) < 3 - end - ):SetTarget(Target) -) - --- actions.stealthed+=/shadowstrike,if=stealthed.sepsis&spell_targets.shuriken_storm<4 -StealthedAPL:AddSpell( - Shadowstrike:CastableIf( - function(self) - return self:IsKnownAndUsable() and Player:GetAuras():FindMy(Sepsis):IsUp() and - Player:GetEnemies(10) < 4 - end - ):SetTarget(Target) -) - --- actions.stealthed+=/shuriken_storm,if=spell_targets>=3+buff.the_rotten.up&(!buff.premeditation.up|spell_targets>=7) -StealthedAPL:AddSpell( - ShurikenStorm:CastableIf( - function(self) - return self:IsKnownAndUsable() and - Player:GetEnemies(10) >= 3 + ((Player:GetAuras():FindMy(TheRotten):IsUp() and - (not Player:GetAuras():FindMy(Premeditation):IsUp() or Player:GetEnemies(10) >= 7)) and 1 or 0) - end - ):SetTarget(Target) -) - --- # Shadowstrike to refresh Find Weakness and to ensure we can carry over a full FW into the next SoD if possible. --- actions.stealthed+=/shadowstrike,if=debuff.find_weakness.remains<=1|cooldown.symbols_of_death.remains<18&debuff.find_weakness.remains 0.1 then self.lastresult = self.cb() @@ -24,15 +30,18 @@ function APLTrait:Evaluate() end -- tostring +---@return string function APLTrait:__tostring() return "Bastion.__APLTrait" end -- Create an APL actor for the APL class +---@class APLActor local APLActor = {} APLActor.__index = APLActor -- Constructor +---@param actor table function APLActor:New(actor) local self = setmetatable({}, APLActor) @@ -43,6 +52,8 @@ function APLActor:New(actor) end -- Add a trait to the APL actor +---@param ... APLTrait +---@return APLActor function APLActor:AddTraits(...) for _, trait in ipairs({ ... }) do table.insert(self.traits, trait) @@ -52,11 +63,13 @@ function APLActor:AddTraits(...) end -- Get the actor +---@return table function APLActor:GetActor() return self.actor end -- Evaulate the APL actor +---@return boolean function APLActor:Evaluate() for _, trait in ipairs(self.traits) do if not trait:Evaluate() then @@ -106,21 +119,25 @@ function APLActor:Execute() end -- has traits +---@return boolean function APLActor:HasTraits() return #self.traits > 0 end -- tostring +---@return string function APLActor:__tostring() return "Bastion.__APLActor" end -- APL (Attack priority list) class - +---@class APL local APL = {} APL.__index = APL -- Constructor +---@param name string +---@return APL function APL:New(name) local self = setmetatable({}, APL) @@ -132,16 +149,23 @@ function APL:New(name) end -- Add a variable to the APL +---@param name string +---@param value any function APL:SetVariable(name, value) self.variables[name] = value end -- Get and evaluate a variable +---@param name string +---@return boolean function APL:GetVariable(name) return self.variables[name] end -- Add variable +---@param name string +---@param cb fun(...):any +---@return APLActor function APL:AddVariable(name, cb) local actor = APLActor:New({ variable = name, cb = cb, _apl = self }) table.insert(self.apl, actor) @@ -149,6 +173,9 @@ function APL:AddVariable(name, cb) end -- Add a manual action to the APL +---@param action string +---@param cb fun(...):any +---@return APLActor function APL:AddAction(action, cb) local actor = APLActor:New({ action = action, cb = cb }) table.insert(self.apl, actor) @@ -156,6 +183,9 @@ function APL:AddAction(action, cb) end -- Add a spell to the APL +---@param spell Spell +---@param condition fun(...):boolean +---@return APLActor function APL:AddSpell(spell, condition) local castableFunc = spell.CastableIfFunc local target = spell:GetTarget() @@ -168,6 +198,9 @@ function APL:AddSpell(spell, condition) end -- Add an item to the APL +---@param item Item +---@param condition fun(...):boolean +---@return APLActor function APL:AddItem(item, condition) local usableFunc = item.UsableIfFunc local target = item:GetTarget() @@ -180,6 +213,9 @@ function APL:AddItem(item, condition) end -- Add an APL to the APL (for sub APLs) +---@param apl APL +---@param condition fun(...):boolean +---@return APLActor function APL:AddAPL(apl, condition) if not condition then error("Bastion: APL:AddAPL: No condition for APL " .. apl.name) @@ -201,6 +237,7 @@ function APL:Execute() end -- tostring +---@return string function APL:__tostring() return "Bastion.__APL(" .. self.name .. ")" end diff --git a/src/Aura/Aura.lua b/src/Aura/Aura.lua index c8a3de5..72bf052 100644 --- a/src/Aura/Aura.lua +++ b/src/Aura/Aura.lua @@ -1,3 +1,5 @@ +-- Document with emmy lua: https://emmylua.github.io/ + local Tinkr, Bastion = ... -- Create a new Aura class @@ -19,6 +21,8 @@ function Aura:__index(k) end -- Equals +---@param other Aura|Spell +---@return boolean function Aura:__eq(other) if getmetatable(other) == Aura then return self:GetSpell():GetID() == other:GetSpell():GetID() @@ -31,11 +35,16 @@ function Aura:__eq(other) return false end +-- tostring +---@return string function Aura:__tostring() return "Bastion.__Aura(" .. self:GetSpell():GetID() .. ")" .. " - " .. (self:GetName() or "''") end -- Constructor +---@param unit Unit +---@param index number +---@param type string function Aura:New(unit, index, type) if unit == nil then local self = setmetatable({}, Aura) @@ -68,7 +77,8 @@ function Aura:New(unit, index, type) end local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal, - spellId, canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod = UnitAura(unit:GetOMToken(), index, type) + spellId, canApplyAura, isBossDebuff, castByPlayer, nameplateShowAll, timeMod = UnitAura(unit:GetOMToken(), index, + type) local self = setmetatable({}, Aura) self.aura = { @@ -98,6 +108,9 @@ function Aura:New(unit, index, type) return self end +-- Constructor +---@param unitAuraInfo UnitAuraInfo +---@return Aura function Aura:CreateFromUnitAuraInfo(unitAuraInfo) local self = setmetatable({}, Aura) self.aura = { @@ -128,56 +141,67 @@ function Aura:CreateFromUnitAuraInfo(unitAuraInfo) end -- Check if the aura is valid +---@return boolean function Aura:IsValid() return self.aura.name ~= nil end -- Check if the aura is up +---@return boolean function Aura:IsUp() return self:IsValid() and (self:GetDuration() == 0 or self:GetRemainingTime() > 0) end -- Check if the aura is down +---@return boolean function Aura:IsDown() return not self:IsUp() end -- Get the auras index +---@return number function Aura:GetIndex() return self.aura.index end -- Get the auras type +---@return string function Aura:GetType() return self.aura.type end -- Get the auras name +---@return string function Aura:GetName() return self.aura.name end -- Get the auras icon +---@return string function Aura:GetIcon() return self.aura.icon end -- Get the auras count +---@return number function Aura:GetCount() return self.aura.count end -- Get the auras dispel type +---@return string function Aura:GetDispelType() return self.aura.dispelType end -- Get the auras duration +---@return number function Aura:GetDuration() return self.aura.duration end -- Get the auras remaining time +---@return number function Aura:GetRemainingTime() local remainingTime = self.aura.expirationTime - GetTime() @@ -189,71 +213,85 @@ function Aura:GetRemainingTime() end -- Get the auras expiration time +---@return number function Aura:GetExpirationTime() return self.aura.expirationTime end -- Get the auras source +---@return Unit function Aura:GetSource() return Bastion.UnitManager[self.aura.source] end -- Get the auras stealable status +---@return boolean function Aura:GetIsStealable() return self.aura.isStealable end -- Get the auras nameplate show personal status +---@return boolean function Aura:GetNameplateShowPersonal() return self.aura.nameplateShowPersonal end -- Get the auras spell id +---@return Spell function Aura:GetSpell() return Bastion.SpellBook:GetSpell(self.aura.spellId) end -- Get the auras can apply aura status +---@return boolean function Aura:GetCanApplyAura() return self.aura.canApplyAura end -- Get the auras is boss debuff status +---@return boolean function Aura:GetIsBossDebuff() return self.aura.isBossDebuff end -- Get the auras cast by player status +---@return boolean function Aura:GetCastByPlayer() return self.aura.castByPlayer end -- Get the auras nameplate show all status +---@return boolean function Aura:GetNameplateShowAll() return self.aura.nameplateShowAll end -- Get the auras time mod +---@return number function Aura:GetTimeMod() return self.aura.timeMod end -- Check if the aura is a buff +---@return boolean function Aura:IsBuff() return self.aura.type == "HELPFUL" end -- Check if the aura is a debuff +---@return boolean function Aura:IsDebuff() return self.aura.type == "HARMFUL" end -- Get aura instance id +---@return number function Aura:GetAuraInstanceID() return self.aura.auraInstanceID end -- Check if the aura is dispelable by a spell +---@param spell Spell function Aura:IsDispelableBySpell(spell) if self:GetDispelType() == nil then return false diff --git a/src/AuraTable/AuraTable.lua b/src/AuraTable/AuraTable.lua index fddcca6..28239d7 100644 --- a/src/AuraTable/AuraTable.lua +++ b/src/AuraTable/AuraTable.lua @@ -5,15 +5,9 @@ local Tinkr, Bastion = ... local AuraTable = {} AuraTable.__index = AuraTable -local function tsize(t) - local keys = {} - for k, v in pairs(t) do - table.insert(keys, k) - end - return keys[#keys] -end - -- Constructor +---@param unit Unit +---@return AuraTable function AuraTable:New(unit) local self = setmetatable({}, AuraTable) @@ -28,6 +22,8 @@ function AuraTable:New(unit) return self end +---@param auras UnitAuraUpdateInfo +---@return nil function AuraTable:OnUpdate(auras) if not auras then self:Update() @@ -73,6 +69,8 @@ function AuraTable:OnUpdate(auras) end end +---@param instanceID number +---@return nil function AuraTable:RemoveInstanceID(instanceID) if not self.instanceIDLookup[instanceID] then return @@ -93,6 +91,10 @@ function AuraTable:RemoveInstanceID(instanceID) end end +-- Update the aura table +---@param instanceID number +---@param aura Aura +---@return nil function AuraTable:AddOrUpdateAuraInstanceID(instanceID, aura) local spellId = aura:GetSpell():GetID() @@ -114,6 +116,7 @@ function AuraTable:AddOrUpdateAuraInstanceID(instanceID, aura) end -- Get a units buffs +---@return nil function AuraTable:GetUnitBuffs() if Tinkr.classic then for i = 1, 40 do @@ -152,6 +155,7 @@ function AuraTable:GetUnitBuffs() end -- Get a units debuffs +---@return nil function AuraTable:GetUnitDebuffs() if Tinkr.classic then for i = 1, 40 do @@ -190,6 +194,7 @@ function AuraTable:GetUnitDebuffs() end -- Update auras +---@return nil function AuraTable:Update() -- print("Updating auras for " .. tostring(self.unit)) self:Clear() @@ -203,6 +208,7 @@ function AuraTable:Update() end -- Get a units auras +---@return table function AuraTable:GetUnitAuras() if not self.did then self.did = true @@ -226,6 +232,7 @@ function AuraTable:GetUnitAuras() end -- Get a units auras +---@return table function AuraTable:GetMyUnitAuras() if not self.did then self.did = true @@ -249,6 +256,7 @@ function AuraTable:GetMyUnitAuras() end -- Clear the aura table +---@return nil function AuraTable:Clear() self.auras = {} self.playerAuras = {} @@ -256,6 +264,7 @@ function AuraTable:Clear() end -- Check if the unit has a specific aura +---@param spell Spell ---@return Aura function AuraTable:Find(spell) local auras = self:GetUnitAuras() @@ -281,6 +290,8 @@ function AuraTable:Find(spell) return Bastion.Aura:New() end +-- Check if the unit has a specific aura +---@param spell Spell ---@return Aura function AuraTable:FindMy(spell) local auras = self:GetMyUnitAuras() @@ -305,6 +316,10 @@ function AuraTable:FindMy(spell) return Bastion.Aura:New() end +-- Check if the unit has a specific aura +---@param spell Spell +---@param source Unit +---@return Aura function AuraTable:FindFrom(spell, source) local auras = self:GetUnitAuras() local aurasub = auras[spell:GetID()] @@ -331,6 +346,8 @@ function AuraTable:FindFrom(spell, source) end -- Find the aura from the current unit +---@param spell Spell +---@return Aura function AuraTable:FindTheirs(spell) local auras = self:GetUnitAuras() local aurasub = auras[spell:GetID()] @@ -357,6 +374,7 @@ function AuraTable:FindTheirs(spell) end -- Find any +---@param spell Spell ---@return Aura function AuraTable:FindAny(spell) local a = self:Find(spell) @@ -368,6 +386,7 @@ function AuraTable:FindAny(spell) end -- Has any stealable aura +---@return boolean function AuraTable:HasAnyStealableAura() for _, auras in pairs(self:GetUnitAuras()) do for _, aura in pairs(auras) do @@ -385,6 +404,8 @@ function AuraTable:HasAnyStealableAura() end -- Has any dispelable aura +---@param spell Spell +---@return boolean function AuraTable:HasAnyDispelableAura(spell) for _, auras in pairs(self:GetUnitAuras()) do for _, aura in pairs(auras) do diff --git a/src/Cache/Cache.lua b/src/Cache/Cache.lua index a88f0b0..2e4d6fb 100644 --- a/src/Cache/Cache.lua +++ b/src/Cache/Cache.lua @@ -1,13 +1,19 @@ +---@class Cache local Cache = {} Cache.__index = Cache -- Constructor +---@return Cache function Cache:New() local self = setmetatable({}, Cache) self.cache = {} return self end +---@param key any +---@param value any +---@param duration number +---@return nil function Cache:Set(key, value, duration) self.cache = self.cache or {} self.cache[key] = { @@ -17,6 +23,8 @@ function Cache:Set(key, value, duration) } end +---@param key any +---@return any function Cache:Get(key) self.cache = self.cache or {} local cache = self.cache[key] @@ -30,6 +38,8 @@ function Cache:Get(key) return nil end +---@param key any +---@return boolean function Cache:IsCached(key) self.cache = self.cache or {} local cache = self.cache[key] diff --git a/src/Cacheable/Cacheable.lua b/src/Cacheable/Cacheable.lua index b344814..3dd0214 100644 --- a/src/Cacheable/Cacheable.lua +++ b/src/Cacheable/Cacheable.lua @@ -1,10 +1,14 @@ local Tinkr, Bastion = ... -- Define a Cacheable class +---@class Cacheable local Cacheable = { cache = nil, callback = nil, - value = nil + value = nil, + -- __eq = function(self, other) + -- return self.value.__eq(self.value, other) + -- end } -- On index check the cache to be valid and return the value or reconstruct the value and return it @@ -26,11 +30,14 @@ function Cacheable:__index(k) end -- When the object is accessed return the value +---@return string function Cacheable:__tostring() return "Bastion.__Cacheable(" .. tostring(self.value) .. ")" end -- Create +---@param value any +---@param cb fun():any function Cacheable:New(value, cb) local self = setmetatable({}, Cacheable) @@ -44,6 +51,7 @@ function Cacheable:New(value, cb) end -- Try to update the value +---@return nil function Cacheable:TryUpdate() if self.cache:IsCached("value") then self.value = self.callback() @@ -51,16 +59,19 @@ function Cacheable:TryUpdate() end -- Update the value +---@return nil function Cacheable:Update() self.value = self.callback() end -- Set a new value +---@param value any function Cacheable:Set(value) self.value = value end -- Set a new callback +---@param cb fun():any function Cacheable:SetCallback(cb) self.callback = cb end diff --git a/src/Class/Class.lua b/src/Class/Class.lua index d9c4a97..b1f3d11 100644 --- a/src/Class/Class.lua +++ b/src/Class/Class.lua @@ -1,6 +1,7 @@ local Tinkr, Bastion = ... -- Create a new Class class +---@class Class local Class = {} function Class:__index(k) @@ -18,6 +19,9 @@ function Class:__index(k) end -- Constructor +---@param locale string +---@param name string +---@param id number function Class:New(locale, name, id) local self = setmetatable({}, Class) self.class = { @@ -29,21 +33,25 @@ function Class:New(locale, name, id) end -- Get the classes locale +---@return string function Class:GetLocale() return self.class.locale end -- Get the classes name +---@return string function Class:GetName() return self.class.name end -- Get the classes id +---@return number function Class:GetID() return self.class.id end -- Return the classes color +---@return ColorMixin classColor function Class:GetColor() return C_ClassColor.GetClassColor(self.class.name) end diff --git a/src/ClassMagic/ClassMagic.lua b/src/ClassMagic/ClassMagic.lua index b4bef57..3a9ec61 100644 --- a/src/ClassMagic/ClassMagic.lua +++ b/src/ClassMagic/ClassMagic.lua @@ -1,6 +1,10 @@ +---@class ClassMagic local ClassMagic = {} ClassMagic.__index = ClassMagic +---@param Class table +---@param key string +---@return any function ClassMagic:Resolve(Class, key) if Class[key] or Class[key] == false then return Class[key] diff --git a/src/Command/Command.lua b/src/Command/Command.lua index 1341ed9..0e04436 100644 --- a/src/Command/Command.lua +++ b/src/Command/Command.lua @@ -1,12 +1,14 @@ -- Create a wow command handler class - +---@class Command local Command = {} Command.__index = Command +---@return string function Command:__tostring() return "Command(" .. self.command .. ")" end +---@param prefix string function Command:New(prefix) local self = setmetatable({}, Command) @@ -21,6 +23,10 @@ function Command:New(prefix) return self end +---@param command string +---@param helpmsg string +---@param cb fun(args: table) +---@return nil function Command:Register(command, helpmsg, cb) self.commands[command] = { helpmsg = helpmsg, @@ -28,6 +34,8 @@ function Command:Register(command, helpmsg, cb) } end +---@param msg string +---@return table function Command:Parse(msg) local args = {} for arg in msg:gmatch("%S+") do @@ -37,6 +45,8 @@ function Command:Parse(msg) return args end +---@param msg string +---@return nil function Command:OnCommand(msg) local args = self:Parse(msg) @@ -51,6 +61,7 @@ function Command:OnCommand(msg) end end +---@return nil function Command:PrintHelp() for k, v in pairs(self.commands) do print('/' .. self.prefix .. ' ' .. k .. " - " .. v.helpmsg) diff --git a/src/EventManager/EventManager.lua b/src/EventManager/EventManager.lua index 28775bc..409a445 100644 --- a/src/EventManager/EventManager.lua +++ b/src/EventManager/EventManager.lua @@ -1,5 +1,5 @@ -- Create an EventManager class - +---@class EventManager local EventManager = { events = {}, eventHandlers = {}, @@ -10,6 +10,7 @@ local EventManager = { EventManager.__index = EventManager -- Constructor +---@return EventManager function EventManager:New() local self = setmetatable({}, EventManager) self.events = {} @@ -31,6 +32,9 @@ function EventManager:New() end -- Register an event +---@param event string +---@param handler fun(...) +---@return nil function EventManager:RegisterEvent(event, handler) if not self.events[event] then self.events[event] = {} @@ -40,6 +44,9 @@ function EventManager:RegisterEvent(event, handler) end -- Register a wow event +---@param event string +---@param handler fun(...) +---@return nil function EventManager:RegisterWoWEvent(event, handler) if not self.wowEventHandlers[event] then self.wowEventHandlers[event] = {} @@ -50,6 +57,9 @@ function EventManager:RegisterWoWEvent(event, handler) end -- Trigger an event +---@param event string +---@param ... any +---@return nil function EventManager:TriggerEvent(event, ...) if self.events[event] then for _, handler in pairs(self.events[event]) do diff --git a/src/Item/Item.lua b/src/Item/Item.lua index 1ae6282..72c8ca0 100644 --- a/src/Item/Item.lua +++ b/src/Item/Item.lua @@ -31,16 +31,20 @@ function Item:__index(k) end -- Equals +---@param other Item +---@return boolean function Item:__eq(other) return self:GetID() == other:GetID() end -- tostring +---@return string function Item:__tostring() return "Bastion.__Item(" .. self:GetID() .. ")" .. " - " .. self:GetName() end -- Constructor +---@param id number function Item:New(id) local self = setmetatable({}, Item) @@ -57,47 +61,58 @@ function Item:New(id) end -- Get the Items id +---@return number function Item:GetID() return self.ItemID end -- Get the Items name +---@return string function Item:GetName() return GetItemInfo(self:GetID()) end -- Get the Items icon +---@return number function Item:GetIcon() return select(3, GetItemInfo(self:GetID())) end -- Get the Items cooldown +---@return number function Item:GetCooldown() return select(2, C_Container.GetItemCooldown(self:GetID())) end -- Return the Usable function +---@return function | boolean function Item:GetUsableFunction() return self.UsableIfFunc end -- Return the preUse function +---@return function | boolean function Item:GetPreUseFunction() return self.PreUseFunc end -- Get the on Use func +---@return function | boolean function Item:GetOnUseFunction() return self.OnUseFunc end -- Get the Items cooldown remaining +---@return number function Item:GetCooldownRemaining() local start, duration = C_Container.GetItemCooldown(self:GetID()) return start + duration - GetTime() end -- Use the Item +---@param unit Unit +---@param condition string +---@return boolean function Item:Use(unit, condition) if condition and not self:EvaluateCondition(condition) then return false @@ -127,46 +142,56 @@ function Item:Use(unit, condition) if self:GetOnUseFunction() then self:GetOnUseFunction()(self) end + + return true end -- Last use attempt +---@return number function Item:GetLastUseAttempt() return self.lastUseAttempt end -- Time since last attepmt +---@return number function Item:GetTimeSinceLastUseAttempt() return GetTime() - self:GetLastUseAttempt() end -- Check if the Item is known +---@return boolean function Item:IsEquipped() return IsEquippedItem(self:GetID()) end -- Check if the Item is on cooldown +---@return boolean function Item:IsOnCooldown() return select(2, C_Container.GetItemCooldown(self:GetID())) > 0 end -- Check if the Item is usable +---@return boolean function Item:IsUsable() local usable, noMana = IsUsableItem(self:GetID()) return usable or usableExcludes[self:GetID()] end -- Check if the Item is Usable +---@return boolean function Item:IsEquippedAndUsable() return ((self:IsEquippable() and self:IsEquipped()) or (not self:IsEquippable() and self:IsUsable())) and not self:IsOnCooldown() end -- Is equippable +---@return boolean function Item:IsEquippable() return IsEquippableItem(self:GetID()) end -- Check if the Item is Usable +---@return boolean function Item:Usable() if self:GetUsableFunction() then return self:GetUsableFunction()(self) @@ -177,6 +202,7 @@ end -- Set a script to check if the Item is Usable ---@param func fun(self:Item):boolean +---@return Item function Item:UsableIf(func) self.UsableIfFunc = func return self @@ -184,6 +210,7 @@ end -- Set a script to run before the Item has been Use ---@param func fun(self:Item) +---@return Item function Item:PreUse(func) self.PreUseFunc = func return self @@ -191,17 +218,23 @@ end -- Set a script to run after the Item has been Use ---@param func fun(self:Item) +---@return Item function Item:OnUse(func) self.OnUseFunc = func return self end -- Get was looking +---@return boolean function Item:GetWasLooking() return self.wasLooking end -- Click the Item +---@param x number +---@param y number +---@param z number +---@return boolean function Item:Click(x, y, z) if type(x) == 'table' then x, y, z = x.x, x.y, x.z @@ -218,6 +251,8 @@ function Item:Click(x, y, z) end -- Check if the Item is Usable and Use it +---@param unit Unit +---@return boolean function Item:Call(unit) if self:Usable() then self:Use(unit) @@ -227,6 +262,8 @@ function Item:Call(unit) end -- Check if the Item is in range of the unit +---@param unit Unit +---@return boolean function Item:IsInRange(unit) local name, rank, icon, UseTime, Itemmin, Itemmax, ItemID = GetItemInfo(self:GetID()) @@ -263,11 +300,13 @@ function Item:IsInRange(unit) end -- Get the last use time +---@return number function Item:GetLastUseTime() return Bastion.SpellBook:GetSpell(self:GetID()):GetLastCastTime() end -- Get time since last use +---@return number function Item:GetTimeSinceLastUse() if not self:GetLastUseTime() then return math.huge @@ -276,11 +315,13 @@ function Item:GetTimeSinceLastUse() end -- Get the Items charges +---@return number function Item:GetCharges() return GetItemCharges(self:GetID()) end -- Get the Items charges remaining +---@return number function Item:GetChargesRemaining() local charges, maxCharges, start, duration = GetItemCharges(self:GetID()) return charges @@ -289,6 +330,7 @@ end -- Create a condition for the Item ---@param name string ---@param func fun(self:Item) +---@return Item function Item:Condition(name, func) self.conditions[name] = { func = func @@ -297,6 +339,8 @@ function Item:Condition(name, func) end -- Get a condition for the Item +---@param name string +---@return function | nil function Item:GetCondition(name) local condition = self.conditions[name] if condition then @@ -307,6 +351,8 @@ function Item:GetCondition(name) end -- Evaluate a condition for the Item +---@param name string +---@return boolean function Item:EvaluateCondition(name) local condition = self:GetCondition(name) if condition then @@ -317,6 +363,8 @@ function Item:EvaluateCondition(name) end -- Check if the Item has a condition +---@param name string +---@return boolean function Item:HasCondition(name) local condition = self:GetCondition(name) if condition then @@ -327,17 +375,21 @@ function Item:HasCondition(name) end -- Set the Items target +---@param unit Unit +---@return Item function Item:SetTarget(unit) self.target = unit return self end -- Get the Items target +---@return Unit | nil function Item:GetTarget() return self.target end -- IsMagicDispel +---@return boolean function Item:IsMagicDispel() return ({ [88423] = true @@ -345,6 +397,7 @@ function Item:IsMagicDispel() end -- IsCurseDispel +---@return boolean function Item:IsCurseDispel() return ({ [88423] = true @@ -352,6 +405,7 @@ function Item:IsCurseDispel() end -- IsPoisonDispel +---@return boolean function Item:IsPoisonDispel() return ({ [88423] = true @@ -359,16 +413,21 @@ function Item:IsPoisonDispel() end -- IsDiseaseDispel +---@return boolean function Item:IsDiseaseDispel() return ({ })[self:GetID()] end +---@param item Item +---@return boolean function Item:IsItem(item) return self:GetID() == item:GetID() end +-- Get the Items spell +---@return Spell | nil function Item:GetSpell() if self.spellID then return Bastion.SpellBook:GetSpell(self.spellID) diff --git a/src/ItemBook/ItemBook.lua b/src/ItemBook/ItemBook.lua index 9b3eebb..535bfa9 100644 --- a/src/ItemBook/ItemBook.lua +++ b/src/ItemBook/ItemBook.lua @@ -1,10 +1,12 @@ local Tinkr, Bastion = ... -- Create a new ItemBook class +---@class ItemBook local ItemBook = {} ItemBook.__index = ItemBook -- Constructor +---@return ItemBook function ItemBook:New() local self = setmetatable({}, ItemBook) self.items = {} @@ -12,6 +14,8 @@ function ItemBook:New() end -- Get a spell from the ItemBook +---@param id number +---@return Item function ItemBook:GetItem(id) if self.items[id] == nil then self.items[id] = Bastion.Item:New(id) diff --git a/src/List/List.lua b/src/List/List.lua index f26a86b..f7bb988 100644 --- a/src/List/List.lua +++ b/src/List/List.lua @@ -22,32 +22,42 @@ local List = { } List.__index = List +---@param from table | nil +---@return List function List:New(from) local self = setmetatable({}, List) self._list = from or {} return self end +---@param value any +---@return nil function List:push(value) table.insert(self._list, value) end +---@return any function List:pop() return table.remove(self._list) end +---@return any function List:peek() return self._list[#self._list] end +---@return number function List:count() return #self._list end +---@return nil function List:clear() self._list = {} end +---@param value any +---@return boolean function List:contains(value) for _, v in ipairs(self._list) do if v == value then @@ -57,6 +67,8 @@ function List:contains(value) return false end +---@param value any +---@return boolean function List:remove(value) for i, v in ipairs(self._list) do if v == value then @@ -67,6 +79,8 @@ function List:remove(value) return false end +---@param callback fun(value: any): boolean +---@return nil function List:each(callback) for _, v in ipairs(self._list) do if callback(v) then @@ -75,6 +89,8 @@ function List:each(callback) end end +---@param callback fun(value: any): boolean +---@return boolean function List:map(callback) local newList = List.new() for _, v in ipairs(self._list) do @@ -83,6 +99,8 @@ function List:map(callback) return newList end +---@param callback fun(value: any): boolean +---@return boolean function List:filter(callback) local newList = List.new() for _, v in ipairs(self._list) do @@ -93,6 +111,9 @@ function List:filter(callback) return newList end +---@param callback fun(value: any): boolean +---@param initialValue any +---@return boolean function List:reduce(callback, initialValue) local result = initialValue for _, v in ipairs(self._list) do @@ -101,6 +122,8 @@ function List:reduce(callback, initialValue) return result end +---@param callback fun(value: any): boolean +---@return boolean | nil function List:find(callback) for _, v in ipairs(self._list) do if callback(v) then @@ -110,6 +133,8 @@ function List:find(callback) return nil end +---@param callback fun(value: any): boolean +---@return number | nil function List:findIndex(callback) for i, v in ipairs(self._list) do if callback(v) then @@ -119,10 +144,13 @@ function List:findIndex(callback) return nil end +---@param callback fun(value: any): boolean +---@return nil function List:sort(callback) table.sort(self._list, callback) end +---@return List function List:reverse() local newList = List.new() for i = #self._list, 1, -1 do @@ -131,6 +159,7 @@ function List:reverse() return newList end +---@return List function List:clone() local newList = List.new() for _, v in ipairs(self._list) do @@ -139,6 +168,8 @@ function List:clone() return newList end +---@param list List +---@return List function List:concat(list) local newList = List.new() for _, v in ipairs(self._list) do @@ -150,6 +181,8 @@ function List:concat(list) return newList end +---@param separator string +---@return string function List:join(separator) local result = "" for i, v in ipairs(self._list) do @@ -161,10 +194,12 @@ function List:join(separator) return result end +---@return string function List:toString() return self:join(", ") end +---@return string function List:__tostring() return self:toString() end diff --git a/src/Module/Module.lua b/src/Module/Module.lua index 6c19d98..e7b7919 100644 --- a/src/Module/Module.lua +++ b/src/Module/Module.lua @@ -1,13 +1,18 @@ -- Create a module class for a bastion module +---@class Module local Module = {} Module.__index = Module -- __tostring +---@return string function Module:__tostring() return "Bastion.__Module(" .. self.name .. ")" end +-- Constructor +---@param name string +---@return Module function Module:New(name) local module = {} setmetatable(module, Module) @@ -20,16 +25,19 @@ function Module:New(name) end -- Enable the module +---@return nil function Module:Enable() self.enabled = true end -- Disable the module +---@return nil function Module:Disable() self.enabled = false end -- Toggle the module +---@return nil function Module:Toggle() if self.enabled then self:Disable() @@ -39,11 +47,15 @@ function Module:Toggle() end -- Add a function to the sync list +---@param func function +---@return nil function Module:Sync(func) table.insert(self.synced, func) end -- Remove a function from the sync list +---@param func function +---@return nil function Module:Unsync(func) for i = 1, #self.synced do if self.synced[i] == func then @@ -54,6 +66,7 @@ function Module:Unsync(func) end -- Sync +---@return nil function Module:Tick() if self.enabled then for i = 1, #self.synced do diff --git a/src/MythicPlusUtils/MythicPlusUtils.lua b/src/MythicPlusUtils/MythicPlusUtils.lua index dbafc79..6384383 100644 --- a/src/MythicPlusUtils/MythicPlusUtils.lua +++ b/src/MythicPlusUtils/MythicPlusUtils.lua @@ -12,6 +12,7 @@ local MythicPlusUtils = { MythicPlusUtils.__index = MythicPlusUtils +---@return MythicPlusUtils function MythicPlusUtils:New() local self = setmetatable({}, MythicPlusUtils) @@ -148,14 +149,19 @@ function MythicPlusUtils:New() return self end +---@return nil function MythicPlusUtils:ToggleDebuffLogging() self.debuffLogging = not self.debuffLogging end +---@return nil function MythicPlusUtils:ToggleCastLogging() self.castLogging = not self.castLogging end +---@param unit Unit +---@param percent number +---@return boolean function MythicPlusUtils:CastingCriticalKick(unit, percent) local castingSpell = unit:GetCastingOrChannelingSpell() diff --git a/src/NotificationsList/NotificationsList.lua b/src/NotificationsList/NotificationsList.lua index 7572d07..7b416b5 100644 --- a/src/NotificationsList/NotificationsList.lua +++ b/src/NotificationsList/NotificationsList.lua @@ -1,24 +1,26 @@ -- Create a NotificationsList class +---@class NotificationsList local NotificationsList = { notifications = {} } NotificationsList.__index = NotificationsList -- Constructor +---@return NotificationsList function NotificationsList:New() local self = setmetatable({}, NotificationsList) -- Create a frame for the notifications self.frame = CreateFrame("Frame", "BastionNotificationsList", UIParent) - self.frame:SetSize(300, 100) + self.frame:SetSize(600, 60) self.frame:SetPoint("TOP", UIParent, "TOP", 0, -100) self.frame:SetFrameStrata("HIGH") -- Remove notifications after 5 seconds C_Timer.NewTicker(0.1, function() for i, notification in ipairs(self.notifications) do - if GetTime() - notification.addedAt > 2 then + if GetTime() - notification.addedAt > notification.duration then notification:Remove() table.remove(self.notifications, i) end @@ -29,18 +31,26 @@ function NotificationsList:New() end -- Create a notification class for the notifications list (takes icon and text) +---@class Notification local Notification = { } Notification.__index = Notification -- Constructor -function Notification:New(list, icon, text) +---@param list NotificationsList +---@param icon string +---@param text string +---@param duration number +---@return Notification +function Notification:New(list, icon, text, duration) local self = setmetatable({}, Notification) + if not duration then duration = 2 end + -- Create a frame for the notification self.frame = CreateFrame("Frame", nil, list.frame) - self.frame:SetSize(300, 100) - self.frame:SetPoint("TOP", list.frame, "TOP", 0, 0) + self.frame:SetSize(5, 5) + self.frame:SetPoint("CENTER", list.frame, "CENTER", 0, 0) self.frame:SetFrameStrata("HIGH") -- Create a texture for the icon @@ -50,18 +60,23 @@ function Notification:New(list, icon, text) self.icon:SetTexture(icon) -- Create a fontstring for the text - self.text = self.frame:CreateFontString(nil, "BACKGROUND", "GameFontNormal") - self.text:SetPoint("CENTER", self.frame, "CENTER", 10, 0) + self.text = self.frame:CreateFontString(nil, "BACKGROUND", "NumberFontNormal") + self.text:SetPoint("LEFT", self.frame, "LEFT", 32 + 16, 0) self.text:SetText(text) - self.text:SetFont("Fonts\\FRIZQT__.TTF", 14) + self.text:SetFont("Fonts\\OpenSans-Bold.ttf", 18) + + -- set the frame size to the size of the text + icon + self.frame:SetSize(self.text:GetStringWidth() + 32 + 16, 32) self.addedAt = GetTime() + self.duration = duration self.list = list return self end -- Remove notification +---@return nil function Notification:Remove() -- Fade out the notification frame and remove it after the fade UIFrameFadeOut(self.frame, 0.2, 1, 0) @@ -75,9 +90,13 @@ function Notification:Remove() end -- Add a notification to the list -function NotificationsList:AddNotification(icon, text) +---@param icon string +---@param text string +---@param duration number +---@return nil +function NotificationsList:AddNotification(icon, text, duration) -- Create a new notification - local notification = Notification:New(self, icon, text) + local notification = Notification:New(self, icon, text, duration) -- Add the notification to the list table.insert(self.notifications, notification) @@ -88,15 +107,18 @@ function NotificationsList:AddNotification(icon, text) end -- Update the notifications +---@return nil function NotificationsList:Update() -- Loop through the notifications for i, notification in ipairs(self.notifications) do -- Set the position of the notification - notification.frame:SetPoint("TOP", self.frame, "TOP", 0, -50 * (i - 1)) + notification.frame:SetPoint("CENTER", self.frame, "CENTER", 0, -42 * (i - 1)) end end -- Remove a notification from the list +---@param notification Notification +---@return nil function NotificationsList:RemoveNotification(notification) -- Loop through the notifications for i, v in ipairs(self.notifications) do @@ -111,6 +133,7 @@ function NotificationsList:RemoveNotification(notification) end -- Remove all notifications from the list +---@return nil function NotificationsList:RemoveAllNotifications() -- Loop through the notifications for i, v in ipairs(self.notifications) do diff --git a/src/ObjectManager/ObjectManager.lua b/src/ObjectManager/ObjectManager.lua index cd0bbc4..5cd2bc4 100644 --- a/src/ObjectManager/ObjectManager.lua +++ b/src/ObjectManager/ObjectManager.lua @@ -18,6 +18,9 @@ function ObjectManager:New() end -- Register a custom list with a callback +---@param name string +---@param cb function +---@return List function ObjectManager:RegisterList(name, cb) if self._lists[name] then return false @@ -32,6 +35,7 @@ function ObjectManager:RegisterList(name, cb) end -- reset custom lists +---@return nil function ObjectManager:ResetLists() for _, list in pairs(self._lists) do list.list:clear() @@ -39,6 +43,8 @@ function ObjectManager:ResetLists() end -- Refresh custom lists +---@param object table +---@return nil function ObjectManager:EnumLists(object) for _, list in pairs(self._lists) do local r = list.cb(object) @@ -49,10 +55,14 @@ function ObjectManager:EnumLists(object) end -- Get a list +---@param name string +---@return List function ObjectManager:GetList(name) return self._lists[name].list end +-- Refresh all lists +---@return nil function ObjectManager:Refresh() self.enemies:clear() self.friends:clear() @@ -61,6 +71,7 @@ function ObjectManager:Refresh() self:ResetLists() local objects = Objects() + local _, _, _, _, _, _, _, instanceID = GetInstanceInfo() for _, object in pairs(objects) do self:EnumLists(object) @@ -74,7 +85,7 @@ function ObjectManager:Refresh() if unit:GetID() == 120651 then self.explosives:push(unit) - elseif unit:IsPlayer() and unit:IsInParty() then + elseif (unit:IsPlayer() and unit:IsInParty()) or (instanceID == 1148 and unit:IsInParty()) then self.friends:push(unit) elseif unit:IsEnemy() then self.enemies:push(unit) diff --git a/src/Refreshable/Refreshable.lua b/src/Refreshable/Refreshable.lua index 5f8a365..3542c8d 100644 --- a/src/Refreshable/Refreshable.lua +++ b/src/Refreshable/Refreshable.lua @@ -22,11 +22,15 @@ function Refreshable:__index(k) end -- When the object is accessed return the value +---@return string function Refreshable:__tostring() return "Bastion.__Refreshable(" .. tostring(rawget(self, 'value')) .. ")" end -- Create +---@param value any +---@param cb function +---@return Refreshable function Refreshable:New(value, cb) local self = setmetatable({}, Refreshable) @@ -40,6 +44,7 @@ function Refreshable:New(value, cb) end -- Try to update the value +---@return nil function Refreshable:TryUpdate() if self.cache:IsCached("value") then self.value = self.callback() @@ -47,16 +52,21 @@ function Refreshable:TryUpdate() end -- Update the value +---@return nil function Refreshable:Update() self.value = self.callback() end -- Set a new value +---@param value any +---@return nil function Refreshable:Set(value) self.value = value end -- Set a new callback +---@param cb function +---@return nil function Refreshable:SetCallback(cb) self.callback = cb end diff --git a/src/Spell/Spell.lua b/src/Spell/Spell.lua index 851964c..6d4bdc5 100644 --- a/src/Spell/Spell.lua +++ b/src/Spell/Spell.lua @@ -32,16 +32,21 @@ function Spell:__index(k) end -- Equals +---@param other Spell +---@return boolean function Spell:__eq(other) return self:GetID() == other:GetID() end -- tostring +---@return string function Spell:__tostring() return "Bastion.__Spell(" .. self:GetID() .. ")" .. " - " .. self:GetName() end -- Constructor +---@param id number +---@return Spell function Spell:New(id) local self = setmetatable({}, Spell) @@ -51,59 +56,72 @@ function Spell:New(id) end -- Get the spells id +---@return number function Spell:GetID() return self.spellID end -- Add post cast func ---@param func fun(self:Spell) +---@return Spell function Spell:PostCast(func) self.PostCastFunc = func return self end -- Get the spells name +---@return string function Spell:GetName() return GetSpellInfo(self:GetID()) end -- Get the spells icon +---@return number function Spell:GetIcon() return select(3, GetSpellInfo(self:GetID())) end -- Get the spells cooldown +---@return number function Spell:GetCooldown() return select(2, GetSpellCooldown(self:GetID())) end -- Return the castable function +---@return fun(self:Spell):boolean function Spell:GetCastableFunction() return self.CastableIfFunc end -- Return the precast function +---@return fun(self:Spell) function Spell:GetPreCastFunction() return self.PreCastFunc end -- Get the on cast func +---@return fun(self:Spell) function Spell:GetOnCastFunction() return self.OnCastFunc end -- Get the spells cooldown remaining +---@return number function Spell:GetCooldownRemaining() local start, duration = GetSpellCooldown(self:GetID()) return start + duration - GetTime() end -- On cooldown +---@return boolean function Spell:OnCooldown() return self:GetCooldownRemaining() > 0 end -- Cast the spell +---@param unit Unit +---@param condition string +---@return boolean function Spell:Cast(unit, condition) if condition and not self:EvaluateCondition(condition) then return false @@ -140,14 +158,18 @@ function Spell:Cast(unit, condition) if self:GetOnCastFunction() then self:GetOnCastFunction()(self) end + + return true end -- Get post cast func +---@return fun(self:Spell) function Spell:GetPostCastFunction() return self.PostCastFunc end -- Check if the spell is known +---@return boolean function Spell:IsKnown() local isKnown = IsSpellKnown(self:GetID()) local isPlayerSpell = IsPlayerSpell(self:GetID()) @@ -155,22 +177,26 @@ function Spell:IsKnown() end -- Check if the spell is on cooldown +---@return boolean function Spell:IsOnCooldown() return select(2, GetSpellCooldown(self:GetID())) > 0 end -- Check if the spell is usable +---@return boolean function Spell:IsUsable() local usable, noMana = IsUsableSpell(self:GetID()) return usable or usableExcludes[self:GetID()] end -- Check if the spell is castable +---@return boolean function Spell:IsKnownAndUsable() return self:IsKnown() and not self:IsOnCooldown() and self:IsUsable() end -- Check if the spell is castable +---@return boolean function Spell:Castable() if self:GetCastableFunction() then return self:GetCastableFunction()(self) @@ -181,6 +207,7 @@ end -- Set a script to check if the spell is castable ---@param func fun(spell:Spell):boolean +---@return Spell function Spell:CastableIf(func) self.CastableIfFunc = func return self @@ -188,6 +215,7 @@ end -- Set a script to run before the spell has been cast ---@param func fun(spell:Spell) +---@return Spell function Spell:PreCast(func) self.PreCastFunc = func return self @@ -195,17 +223,23 @@ end -- Set a script to run after the spell has been cast ---@param func fun(spell:Spell) +---@return Spell function Spell:OnCast(func) self.OnCastFunc = func return self end -- Get was looking +---@return boolean function Spell:GetWasLooking() return self.wasLooking end -- Click the spell +---@param x number +---@param y number +---@param z number +---@return boolean function Spell:Click(x, y, z) if type(x) == 'table' then x, y, z = x.x, x.y, x.z @@ -222,6 +256,8 @@ function Spell:Click(x, y, z) end -- Check if the spell is castable and cast it +---@param unit Unit +---@return boolean function Spell:Call(unit) if self:Castable() then self:Cast(unit) @@ -230,11 +266,15 @@ function Spell:Call(unit) return false end +-- Check if the spell is castable and cast it +---@return boolean function Spell:HasRange() return SpellHasRange(self:GetName()) end -- Check if the spell is in range of the unit +---@param unit Unit +---@return boolean function Spell:IsInRange(unit) local hasRange = self:HasRange() local inRange = IsSpellInRange(self:GetName(), unit:GetOMToken()) @@ -251,11 +291,13 @@ function Spell:IsInRange(unit) end -- Get the last cast time +---@return number function Spell:GetLastCastTime() return self.lastCastAt end -- Get time since last cast +---@return number function Spell:GetTimeSinceLastCast() if not self:GetLastCastTime() then return math.huge @@ -264,10 +306,13 @@ function Spell:GetTimeSinceLastCast() end -- Get the spells charges +---@return number function Spell:GetCharges() return GetSpellCharges(self:GetID()) end +-- Get the spells charges +---@return number function Spell:GetChargesFractional() local charges, maxCharges, start, duration = GetSpellCharges(self:GetID()) @@ -288,12 +333,16 @@ function Spell:GetChargesFractional() end -- Get the spells charges remaining +---@return number function Spell:GetChargesRemaining() local charges, maxCharges, start, duration = GetSpellCharges(self:GetID()) return charges end -- Create a condition for the spell +---@param name string +---@param func fun(self:Spell):boolean +---@return Spell function Spell:Condition(name, func) self.conditions[name] = { func = func @@ -302,6 +351,8 @@ function Spell:Condition(name, func) end -- Get a condition for the spell +---@param name string +---@return function | nil function Spell:GetCondition(name) local condition = self.conditions[name] if condition then @@ -312,6 +363,8 @@ function Spell:GetCondition(name) end -- Evaluate a condition for the spell +---@param name string +---@return boolean function Spell:EvaluateCondition(name) local condition = self:GetCondition(name) if condition then @@ -322,6 +375,8 @@ function Spell:EvaluateCondition(name) end -- Check if the spell has a condition +---@param name string +---@return boolean function Spell:HasCondition(name) local condition = self:GetCondition(name) if condition then @@ -332,17 +387,21 @@ function Spell:HasCondition(name) end -- Set the spells target +---@param unit Unit +---@return Spell function Spell:SetTarget(unit) self.target = unit return self end -- Get the spells target +---@return Unit function Spell:GetTarget() return self.target end -- IsMagicDispel +---@return boolean function Spell:IsMagicDispel() return ({ [88423] = true @@ -350,6 +409,7 @@ function Spell:IsMagicDispel() end -- IsCurseDispel +---@return boolean function Spell:IsCurseDispel() return ({ [88423] = true @@ -357,6 +417,7 @@ function Spell:IsCurseDispel() end -- IsPoisonDispel +---@return boolean function Spell:IsPoisonDispel() return ({ [88423] = true @@ -364,12 +425,16 @@ function Spell:IsPoisonDispel() end -- IsDiseaseDispel +---@return boolean function Spell:IsDiseaseDispel() return ({ })[self:GetID()] end +-- IsSpell +---@param spell Spell +---@return boolean function Spell:IsSpell(spell) return self:GetID() == spell:GetID() end diff --git a/src/SpellBook/SpellBook.lua b/src/SpellBook/SpellBook.lua index b06f69f..c99fc8f 100644 --- a/src/SpellBook/SpellBook.lua +++ b/src/SpellBook/SpellBook.lua @@ -1,10 +1,12 @@ local Tinkr, Bastion = ... -- Create a new SpellBook class +---@class SpellBook local SpellBook = {} SpellBook.__index = SpellBook -- Constructor +---@return SpellBook function SpellBook:New() local self = setmetatable({}, SpellBook) self.spells = {} @@ -12,6 +14,7 @@ function SpellBook:New() end -- Get a spell from the spellbook +---@return Spell function SpellBook:GetSpell(id) if self.spells[id] == nil then self.spells[id] = Bastion.Spell:New(id) @@ -20,6 +23,25 @@ function SpellBook:GetSpell(id) return self.spells[id] end +---@param ... number[] +---@return Spell, ... Spell +function SpellBook:GetSpells(...) + local spells = {} + for _, id in ipairs({ ... }) do + table.insert(spells, self:GetSpell(id)) + end + + return unpack(spells) +end + +---@param name string +---@return Spell +function SpellBook:GetSpellByName(name) + local _, rank, icon, castTime, minRange, maxRange, spellID, originalIcon = GetSpellInfo(name) + return self:GetSpell(spellID) +end + +---@return Spell function SpellBook:GetIfRegistered(id) return self.spells[id] end diff --git a/src/Timer/Timer.lua b/src/Timer/Timer.lua index 14ec866..c14b84d 100644 --- a/src/Timer/Timer.lua +++ b/src/Timer/Timer.lua @@ -1,6 +1,7 @@ local Tinkr, Bastion = ... -- Create a new Timer class +---@class Timer local Timer = { startTime = nil, resetAfterCombat = false, @@ -8,6 +9,8 @@ local Timer = { Timer.__index = Timer -- Constructor +---@param type string +---@return Timer function Timer:New(type) local self = setmetatable({}, Timer) self.startTime = nil @@ -16,11 +19,13 @@ function Timer:New(type) end -- Start the timer +---@return nil function Timer:Start() self.startTime = GetTime() end -- Get the time since the timer was started +---@return number function Timer:GetTime() if not self:IsRunning() then return 0 @@ -29,11 +34,13 @@ function Timer:GetTime() end -- Check if the timer is running +---@return boolean function Timer:IsRunning() return self.startTime ~= nil end -- Reset the timer +---@return nil function Timer:Reset() self.startTime = nil end diff --git a/src/Unit/Unit.lua b/src/Unit/Unit.lua index e115515..256c078 100644 --- a/src/Unit/Unit.lua +++ b/src/Unit/Unit.lua @@ -34,16 +34,26 @@ function Unit:__index(k) end -- Equals +---@param other Unit +---@return boolean function Unit:__eq(other) return UnitIsUnit(self:GetOMToken(), other.unit) end -- tostring +---ToString +--- +---```lua +---print(Unit:New('player')) +---``` +---@return string function Unit:__tostring() return "Bastion.__Unit(" .. tostring(self:GetOMToken()) .. ")" .. " - " .. (self:GetName() or '') end -- Constructor +---@param unit string +---@return Unit function Unit:New(unit) local self = setmetatable({}, Unit) self.unit = unit @@ -54,85 +64,107 @@ function Unit:New(unit) end -- Check if the unit is valid +---@return boolean function Unit:IsValid() return self:GetOMToken() ~= nil and self:Exists() end -- Check if the unit exists +---@return boolean function Unit:Exists() return Object(self:GetOMToken()) end -- Get the units token +---@return string function Unit:Token() return self:GetOMToken() end -- Get the units name +---@return string function Unit:GetName() return UnitName(self:GetOMToken()) end -- Get the units GUID +---@return string function Unit:GetGUID() return ObjectGUID(self:GetOMToken()) end -- Get the units health +---@return number function Unit:GetHealth() return UnitHealth(self:GetOMToken()) end -- Get the units max health +---@return number function Unit:GetMaxHealth() return UnitHealthMax(self:GetOMToken()) end -- Get the units health percentage +---@return number function Unit:GetHP() return self:GetHealth() / self:GetMaxHealth() * 100 end +-- Get the units health deficit +---@return number function Unit:GetHealthPercent() return self:GetHP() end -- Get the units power type +---@return number function Unit:GetPowerType() return UnitPowerType(self:GetOMToken()) end -- Get the units power +---@param powerType number | nil +---@return number function Unit:GetPower(powerType) local powerType = powerType or self:GetPowerType() return UnitPower(self:GetOMToken(), powerType) end -- Get the units max power +---@param powerType number | nil +---@return number function Unit:GetMaxPower(powerType) local powerType = powerType or self:GetPowerType() return UnitPowerMax(self:GetOMToken(), powerType) end -- Get the units power percentage +---@param powerType number | nil +---@return number function Unit:GetPP(powerType) local powerType = powerType or self:GetPowerType() return self:GetPower(powerType) / self:GetMaxPower(powerType) * 100 end -- Get the units power deficit +---@param powerType number | nil +---@return number function Unit:GetPowerDeficit(powerType) local powerType = powerType or self:GetPowerType() return self:GetMaxPower(powerType) - self:GetPower(powerType) end -- Get the units position +---@return Vector3 function Unit:GetPosition() local x, y, z = ObjectPosition(self:GetOMToken()) return Bastion.Vector3:New(x, y, z) end -- Get the units distance from another unit +---@param unit Unit +---@return number function Unit:GetDistance(unit) local pself = self:GetPosition() local punit = unit:GetPosition() @@ -141,36 +173,43 @@ function Unit:GetDistance(unit) end -- Is the unit dead +---@return boolean function Unit:IsDead() return UnitIsDeadOrGhost(self:GetOMToken()) end -- Is the unit alive +---@return boolean function Unit:IsAlive() return not UnitIsDeadOrGhost(self:GetOMToken()) end -- Is the unit a pet +---@return boolean function Unit:IsPet() return UnitIsUnit(self:GetOMToken(), "pet") end -- Is the unit a friendly unit +---@return boolean function Unit:IsFriendly() return UnitIsFriend("player", self:GetOMToken()) end -- IsEnemy +---@return boolean function Unit:IsEnemy() return UnitCanAttack("player", self:GetOMToken()) end -- Is the unit a hostile unit +---@return boolean function Unit:IsHostile() return UnitCanAttack(self:GetOMToken(), 'player') end -- Is the unit a boss +---@return boolean function Unit:IsBoss() if UnitClassification(self:GetOMToken()) == "worldboss" then return true @@ -187,6 +226,7 @@ function Unit:IsBoss() return false end +---@return string function Unit:GetOMToken() if not self.unit then return "none" @@ -195,51 +235,61 @@ function Unit:GetOMToken() end -- Is the unit a target +---@return boolean function Unit:IsTarget() return UnitIsUnit(self:GetOMToken(), "target") end -- Is the unit a focus +---@return boolean function Unit:IsFocus() return UnitIsUnit(self:GetOMToken(), "focus") end -- Is the unit a mouseover +---@return boolean function Unit:IsMouseover() return UnitIsUnit(self:GetOMToken(), "mouseover") end -- Is the unit a tank +---@return boolean function Unit:IsTank() return UnitGroupRolesAssigned(self:GetOMToken()) == "TANK" end -- Is the unit a healer +---@return boolean function Unit:IsHealer() return UnitGroupRolesAssigned(self:GetOMToken()) == "HEALER" end -- Is the unit a damage dealer +---@return boolean function Unit:IsDamage() return UnitGroupRolesAssigned(self:GetOMToken()) == "DAMAGER" end -- Is the unit a player +---@return boolean function Unit:IsPlayer() return UnitIsPlayer(self:GetOMToken()) end -- Is the unit a player controlled unit +---@return boolean function Unit:IsPCU() return UnitPlayerControlled(self:GetOMToken()) end -- Get if the unit is affecting combat +---@return boolean function Unit:IsAffectingCombat() return UnitAffectingCombat(self:GetOMToken()) end -- Get the units class id +---@return Class function Unit:GetClass() local locale, class, classID = UnitClass(self:GetOMToken()) return Bastion.Class:New(locale, class, classID) @@ -252,6 +302,7 @@ function Unit:GetAuras() end -- Get the raw unit +---@return string function Unit:GetRawUnit() return self:GetOMToken() end @@ -266,6 +317,8 @@ local isClassicWow = select(4, GetBuildInfo()) < 40000 local losFlag = bit.bor(0x1, 0x10, 0x100000) -- Check if the unit can see another unit +---@param unit Unit +---@return boolean function Unit:CanSee(unit) -- mechagon smoke cloud -- local mechagonID = 2097 @@ -311,11 +364,13 @@ function Unit:CanSee(unit) end -- Check if the unit is casting a spell +---@return boolean function Unit:IsCasting() return UnitCastingInfo(self:GetOMToken()) ~= nil end -- Get Casting or channeling spell +---@return Spell | nil function Unit:GetCastingOrChannelingSpell() local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self .unit) @@ -333,20 +388,25 @@ function Unit:GetCastingOrChannelingSpell() end -- Check if the unit is channeling a spell +---@return boolean function Unit:IsChanneling() return UnitChannelInfo(self:GetOMToken()) ~= nil end -- Check if the unit is casting or channeling a spell +---@return boolean function Unit:IsCastingOrChanneling() return self:IsCasting() or self:IsChanneling() end -- Check if the unit can attack the target +---@param unit Unit +---@return boolean function Unit:CanAttack(unit) return UnitCanAttack(self:GetOMToken(), unit:GetOMToken()) end +---@return number function Unit:GetChannelOrCastPercentComplete() local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self .unit) @@ -366,6 +426,8 @@ function Unit:GetChannelOrCastPercentComplete() return 0 end +-- Check if unit is interruptible +---@return boolean function Unit:IsInterruptible() local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self .unit) @@ -383,6 +445,8 @@ function Unit:IsInterruptible() end -- Check if unit is interruptible +---@param percent number +---@return boolean function Unit:IsInterruptibleAt(percent) if not self:IsInterruptible() then return false @@ -399,6 +463,8 @@ function Unit:IsInterruptibleAt(percent) end -- Get the number of enemies in a given range of the unit and cache the result for .5 seconds +---@param range number +---@return number function Unit:GetEnemies(range) local enemies = self.cache:Get("enemies_" .. range) if enemies then @@ -419,6 +485,7 @@ function Unit:GetEnemies(range) end -- Get the number of melee attackers +---@return number function Unit:GetMeleeAttackers() local enemies = self.cache:Get("melee_attackers") if enemies then @@ -438,6 +505,9 @@ function Unit:GetMeleeAttackers() return count end +---@param distance number +---@param percent number +---@return number function Unit:GetPartyHPAround(distance, percent) local count = 0 @@ -452,33 +522,57 @@ function Unit:GetPartyHPAround(distance, percent) end -- Is moving +---@return boolean function Unit:IsMoving() return GetUnitSpeed(self:GetOMToken()) > 0 end +-- Is moving at all +---@return boolean function Unit:IsMovingAtAll() return ObjectMovementFlag(self:GetOMToken()) ~= 0 end -function Unit:GetComboPoints() +---@param unit Unit | nil +---@return number +function Unit:GetComboPoints(unit) + if Tinkr.classic then + if not unit then + return 0 + end + return GetComboPoints(self:GetOMToken(), unit:GetOMToken()) + end return UnitPower(self:GetOMToken(), 4) end +---@return number function Unit:GetComboPointsMax() + if Tinkr.classic then + return 5 + end return UnitPowerMax(self:GetOMToken(), 4) end -- Get combopoints deficit -function Unit:GetComboPointsDeficit() +---@param unit Unit | nil +---@return number +function Unit:GetComboPointsDeficit(unit) + if Tinkr.classic then + return self:GetComboPointsMax() - self:GetComboPoints(unit) + end return self:GetComboPointsMax() - self:GetComboPoints() end -- IsUnit +---@param unit Unit +---@return boolean function Unit:IsUnit(unit) return UnitIsUnit(self:GetOMToken(), unit and unit:GetOMToken() or 'none') end -- IsTanking +---@param unit Unit +---@return boolean function Unit:IsTanking(unit) local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(), unit:GetOMToken()) @@ -486,6 +580,8 @@ function Unit:IsTanking(unit) end -- IsFacing +---@param unit Unit +---@return boolean function Unit:IsFacing(unit) local rot = ObjectRotation(self:GetOMToken()) local x, y, z = ObjectPosition(self:GetOMToken()) @@ -506,6 +602,8 @@ function Unit:IsFacing(unit) end -- IsBehind +---@param unit Unit +---@return boolean function Unit:IsBehind(unit) local rot = ObjectRotation(unit:GetOMToken()) local x, y, z = ObjectPosition(unit:GetOMToken()) @@ -525,6 +623,7 @@ function Unit:IsBehind(unit) return math.abs(angle) > 90 end +---@return number function Unit:GetMeleeBoost() if IsPlayerSpell(196924) then return 3 @@ -540,6 +639,8 @@ end -- return ((myPos.x - targetPos.x) * (myPos.x - targetPos.x)) + ((myPos.y - targetPos.y) * (myPos.y - targetPos.y)) + ((myPos.z - targetPos.z) * (myPos.z - targetPos.z)) <= (float)(fMaxDist * fMaxDist); -- InMelee +---@param unit Unit +---@return boolean function Unit:InMelee(unit) local x, y, z = ObjectPosition(self:GetOMToken()) local x2, y2, z2 = ObjectPosition(unit:GetOMToken()) @@ -556,16 +657,21 @@ function Unit:InMelee(unit) end -- Get object id +---@return number function Unit:GetID() return ObjectID(self:GetOMToken()) end -- In party +---@return boolean function Unit:IsInParty() return UnitInParty(self:GetOMToken()) end -- Linear regression between time and percent to something +---@param time table +---@param percent table +---@return number, number function Unit:LinearRegression(time, percent) local x = time local y = percent @@ -592,6 +698,8 @@ function Unit:LinearRegression(time, percent) end -- Use linear regression to get the health percent at a given time in the future +---@param time number +---@return number function Unit:PredictHealth(time) local x = {} local y = {} @@ -613,6 +721,8 @@ function Unit:PredictHealth(time) end -- Use linear regression to guess the time until a given health percent +---@param percent number +---@return number function Unit:PredictTime(percent) local x = {} local y = {} @@ -634,6 +744,7 @@ function Unit:PredictTime(percent) end -- Time until death +---@return number function Unit:TimeToDie() if self:IsDead() then self.regression_history = {} @@ -650,16 +761,21 @@ function Unit:TimeToDie() end -- Set combat time if affecting combat and return the difference between now and the last time +---@return number function Unit:GetCombatTime() return GetTime() - self.last_combat_time end +-- Set last combat time +---@param time number +---@return nil function Unit:SetLastCombatTime(time) self.last_combat_time = time end -- Get combat odds (if the last combat time is less than 1 minute ago return 1 / time, else return 0) -- the closer to 0 the more likely the unit is to be in combat (0 = 100%) 60 = 0% +---@return number function Unit:InCombatOdds() local time = self:GetCombatTime() local percent = 1 - (time / 60) @@ -668,6 +784,7 @@ function Unit:InCombatOdds() end -- Get units gcd time +---@return number function Unit:GetGCD() local start, duration = GetSpellCooldown(61304) if start == 0 then @@ -686,6 +803,7 @@ The GCD won't drop below 1 second More than 50% Haste will drop a spell below 1 second ]] +---@return number function Unit:GetMaxGCD() local haste = UnitSpellHaste(self:GetOMToken()) if haste > 50 then @@ -696,6 +814,7 @@ function Unit:GetMaxGCD() end -- IsStealthed +---@return boolean function Unit:IsStealthed() local Stealth = Bastion.SpellBook:GetSpell(1784) local Vanish = Bastion.SpellBook:GetSpell(1856) @@ -709,6 +828,7 @@ function Unit:IsStealthed() end -- Get unit swing timers +---@return number, number function Unit:GetSwingTimers() local main_speed, off_speed = UnitAttackSpeed(self:GetOMToken()) local main_speed = main_speed or 2 @@ -728,6 +848,7 @@ function Unit:GetSwingTimers() return main_speed_remains, off_speed_remains end +---@return nil function Unit:WatchForSwings() Bastion.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function() local _, subtype, _, sourceGUID, sourceName, _, _, destGUID, destName, destFlags, _, spellID, spellName, _, amount, interrupt, a, b, c, d, offhand, multistrike = CombatLogGetCurrentEventInfo() @@ -759,114 +880,34 @@ function Unit:WatchForSwings() end) end --- GetTimeToShurikenTornado ---[[ - spec:RegisterStateTable( "time_to_sht", setmetatable( {}, { - __index = function( t, k ) - local n = tonumber( k ) - n = n - ( n % 1 ) - - if not n or n > 5 then return 3600 end - - if n <= swings_since_sht then return 0 end - - local mh_speed = swings.mainhand_speed - local mh_next = ( swings.mainhand > now - 3 ) and ( swings.mainhand + mh_speed ) or now + ( mh_speed * 0.5 ) - - local oh_speed = swings.offhand_speed - local oh_next = ( swings.offhand > now - 3 ) and ( swings.offhand + oh_speed ) or now - - table.wipe( sht ) - - if mh_speed and mh_speed > 0 then - for i = 1, 4 do - insert( sht, mh_next + ( i * mh_speed ) ) - end - end - - if oh_speed and oh_speed > 0 then - for i = 1, 4 do - insert( sht, oh_next + ( i * oh_speed ) ) - end - end - - local i = 1 - - while( sht[i] ) do - if sht[i] < last_shadow_techniques + 3 then - table.remove( sht, i ) - else - i = i + 1 - end - end - - if #sht > 0 and n - swings_since_sht < #sht then - table.sort( sht ) - return max( 0, sht[ n - swings_since_sht ] - query_time ) - else - return 3600 - end - end -} ) ) -]] -function Unit:GetTimeToShurikenTornado(n) - local now = GetTime() - local sht = {} - local swings = self:GetSwingTimers() - - if not self.swings_since_sht then - self.swings_since_sht = 0 - end - - if not self.last_shadow_techniques then - self.last_shadow_techniques = 0 - end - - if n <= self.swings_since_sht then - return 0 - end - - local mh_speed = swings[1] - local mh_next = (self.last_mh > now - 3) and (self.last_mh + mh_speed) or now + (mh_speed * 0.5) - - local oh_speed = swings[2] - local oh_next = (self.last_oh > now - 3) and (self.last_oh + oh_speed) or now - - table.wipe(sht) - - if mh_speed and mh_speed > 0 then - for i = 1, 4 do - table.insert(sht, mh_next + (i * mh_speed)) - end - end - - if oh_speed and oh_speed > 0 then - for i = 1, 4 do - table.insert(sht, oh_next + (i * oh_speed)) - end - end +-- ismounted +---@return boolean +function Unit:IsMounted() + return UnitIsMounted(self.unit) +end - local i = 1 +-- isindoors +---@return boolean +function Unit:IsOutdoors() + return ObjectIsOutdoors(self.unit) +end - while (sht[i]) do - if sht[i] < self.last_shadow_techniques + 3 then - table.remove(sht, i) - else - i = i + 1 - end - end +-- IsIndoors +---@return boolean +function Unit:IsIndoors() + return not ObjectIsOutdoors(self.unit) +end - if #sht > 0 and n - self.swings_since_sht < #sht then - table.sort(sht) - return math.max(0, sht[n - self.swings_since_sht] - now) - else - return 3600 - end +-- IsSubmerged +---@return boolean +function Unit:IsSubmerged() + return ObjectIsSubmerged(self.unit) end --- Is the unit indoors -function Unit:IsIndoors() - return IsIndoors() +-- IsDry +---@return boolean +function Unit:IsDry() + return not ObjectIsSubmerged(self.unit) end -return Unit \ No newline at end of file +return Unit diff --git a/src/UnitManager/UnitManager.lua b/src/UnitManager/UnitManager.lua index 6031459..059fd92 100644 --- a/src/UnitManager/UnitManager.lua +++ b/src/UnitManager/UnitManager.lua @@ -99,6 +99,7 @@ function UnitManager:__index(k) end -- Constructor +---@return UnitManager function UnitManager:New() local self = setmetatable({}, UnitManager) self.units = {} @@ -112,6 +113,7 @@ function UnitManager:Validate(token) end -- Get or create a unit +---@param token string ---@return Unit function UnitManager:Get(token) -- if not Validate(token) then @@ -142,10 +144,16 @@ function UnitManager:Get(token) end) end +-- Get a unit by guid +---@param guid string +---@return Unit function UnitManager:GetObject(guid) return self.objects[guid] end +-- Set a unit by guid +---@param unit Unit +---@return Unit function UnitManager:SetObject(unit) self.objects[unit:GetGUID()] = unit end @@ -174,7 +182,9 @@ function UnitManager:CreateCustomUnit(token, cb) return cachedUnit end --- Enum Friends (party/raid members) +---@description Enumerates all friendly units in the battlefield +---@param cb fun(unit: Unit):boolean +---@return nil function UnitManager:EnumFriends(cb) Bastion.ObjectManager.friends:each(function(unit) if cb(unit) then @@ -185,6 +195,7 @@ end -- Enum Enemies (object manager) ---@param cb fun(unit: Unit):boolean +---@return nil function UnitManager:EnumEnemies(cb) Bastion.ObjectManager.activeEnemies:each(function(unit) if cb(unit) then @@ -194,6 +205,8 @@ function UnitManager:EnumEnemies(cb) end -- Enum Units (object manager) +---@param cb fun(unit: Unit):boolean +---@return nil function UnitManager:EnumUnits(cb) Bastion.ObjectManager.enemies:each(function(unit) if cb(unit) then @@ -203,6 +216,8 @@ function UnitManager:EnumUnits(cb) end -- Get the number of friends with a buff (party/raid members) +---@param spell Spell +---@return number function UnitManager:GetNumFriendsWithBuff(spell) local count = 0 self:EnumFriends(function(unit) @@ -214,6 +229,7 @@ function UnitManager:GetNumFriendsWithBuff(spell) end -- Get the number of friends alive (party/raid members) +---@return number function UnitManager:GetNumFriendsAlive() local count = 0 self:EnumFriends(function(unit) @@ -225,7 +241,9 @@ function UnitManager:GetNumFriendsAlive() end -- Get the friend with the most friends within a given radius (party/raid members) --- Return unit, friends +---@param radius number +---@return Unit +---@return table function UnitManager:GetFriendWithMostFriends(radius) local unit = nil local count = 0 @@ -254,6 +272,9 @@ function UnitManager:GetFriendWithMostFriends(radius) end -- Find the centroid of the most dense area of friends (party/raid members) of a given radius within a given range +---@param radius number +---@param range number +---@return Vector3 | nil function UnitManager:FindFriendsCentroid(radius, range) local unit, friends = self:GetFriendWithMostFriends(radius) if unit == nil then diff --git a/src/Vector3/Vector3.lua b/src/Vector3/Vector3.lua index 406d9be..878c888 100644 --- a/src/Vector3/Vector3.lua +++ b/src/Vector3/Vector3.lua @@ -1,16 +1,22 @@ -- Create a Vector3 class +---@class Vector3 local Vector3 = {} Vector3.__index = Vector3 +---@return string function Vector3:__tostring() return "Vector3(" .. self.x .. ", " .. self.y .. ", " .. self.z .. ")" end +---@param other Vector3 +---@return Vector3 function Vector3:__add(other) return Vector3:New(self.x + other.x, self.y + other.y, self.z + other.z) end +---@param other Vector3 +---@return Vector3 function Vector3:__sub(other) if type(other) == "number" then return Vector3:New(self.x - other, self.y - other, self.z - other) @@ -18,30 +24,42 @@ function Vector3:__sub(other) return Vector3:New(self.x - other.x, self.y - other.y, self.z - other.z) end +---@param other number +---@return Vector3 function Vector3:__mul(other) return Vector3:New(self.x * other, self.y * other, self.z * other) end +---@param other number +---@return Vector3 function Vector3:__div(other) return Vector3:New(self.x / other, self.y / other, self.z / other) end +---@param other Vector3 +---@return boolean function Vector3:__eq(other) return self.x == other.x and self.y == other.y and self.z == other.z end +---@param other Vector3 +---@return boolean function Vector3:__lt(other) return self.x < other.x and self.y < other.y and self.z < other.z end +---@param other Vector3 +---@return boolean function Vector3:__le(other) return self.x <= other.x and self.y <= other.y and self.z <= other.z end +---@return Vector3 function Vector3:__unm() return Vector3:New(-self.x, -self.y, -self.z) end +---@return number function Vector3:__len() return math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) end @@ -151,6 +169,10 @@ function Vector3:__newindex(k, v) end end +---@param x number +---@param y number +---@param z number +---@return Vector3 function Vector3:New(x, y, z) if x == false then return Vector3:New(0, 0, 0) @@ -160,18 +182,26 @@ function Vector3:New(x, y, z) return self end +---@param rhs Vector3 +---@return number function Vector3:Dot(rhs) return self.x * rhs.x + self.y * rhs.y + self.z * rhs.z end +---@param rhs Vector3 +---@return Vector3 function Vector3:Cross(rhs) return Vector3:New(self.y * rhs.z - self.z * rhs.y, self.z * rhs.x - self.x * rhs.z, self.x * rhs.y - self.y * rhs.x) end +---@param b Vector3 +---@return number function Vector3:Distance(b) return FastDistance(self.x, self.y, self.z, b.x, b.y, b.z) end +---@param to Vector3 +---@return number function Vector3:Angle(to) return math.acos(self:Dot(to) / ( @@ -179,6 +209,8 @@ function Vector3:Angle(to) math.sqrt(to.x * to.x + to.y * to.y + to.z * to.z))) end +---@param maxLength number +---@return Vector3 function Vector3:ClampMagnitude(maxLength) if self:Dot(self) > maxLength * maxLength then return self.normalized * maxLength @@ -188,15 +220,25 @@ function Vector3:ClampMagnitude(maxLength) end -- Implement a clamp function +---@param x number +---@param min number +---@param max number +---@return number local function clamp(x, min, max) return x < min and min or (x > max and max or x) end +---@param b Vector3 +---@param t number +---@return Vector3 function Vector3:Lerp(b, t) t = clamp(t, 0, 1) return Vector3:New(self.x + (b.x - self.x) * t, self.y + (b.y - self.y) * t, self.z + (b.z - self.z) * t) end +---@param target Vector3 +---@param maxDistanceDelta number +---@return Vector3 function Vector3:MoveTowards(target, maxDistanceDelta) local toVector = target - self local distance = toVector.magnitude @@ -207,10 +249,14 @@ function Vector3:MoveTowards(target, maxDistanceDelta) return self + toVector / distance * maxDistanceDelta end +---@param b Vector3 +---@return Vector3 function Vector3:Scale(b) return Vector3:New(self.x * b.x, self.y * b.y, self.z * b.z) end +---@param onNormal Vector3 +---@return Vector3 function Vector3:Project(onNormal) local num = onNormal:Dot(onNormal) if num < 1.401298E-45 then @@ -220,14 +266,19 @@ function Vector3:Project(onNormal) return onNormal * self:Dot(onNormal) / num end +---@param planeNormal Vector3 +---@return Vector3 function Vector3:ProjectOnPlane(planeNormal) return self - self:Project(planeNormal) end +---@param inDirection Vector3 +---@return Vector3 function Vector3:Reflect(inNormal) return -2 * inNormal:Dot(self) * inNormal + self end +---@return Vector3 function Vector3:Normalize() local num = self:Dot(self) if num > 1E-05 then diff --git a/src/_bastion.lua b/src/_bastion.lua index 76c123b..346723d 100644 --- a/src/_bastion.lua +++ b/src/_bastion.lua @@ -1,5 +1,6 @@ local Tinkr = ... +---@class Bastion local Bastion = { DebugMode = false } @@ -9,32 +10,56 @@ function Bastion.require(class) return Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion) end +---@type ClassMagic Bastion.ClassMagic = Bastion.require("ClassMagic") +---@type List Bastion.List = Bastion.require("List") +---@type NotificationsList, Notification Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList") +---@type Vector3 Bastion.Vector3 = Bastion.require("Vector3") +---@type Command Bastion.Command = Bastion.require("Command") +---@type Cache Bastion.Cache = Bastion.require("Cache") +---@type Cacheable Bastion.Cacheable = Bastion.require("Cacheable") +---@type Refreshable Bastion.Refreshable = Bastion.require("Refreshable") +---@type Unit Bastion.Unit = Bastion.require("Unit") +---@type Aura Bastion.Aura = Bastion.require("Aura") +---@type APL, APLActor, APLTrait Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL") Bastion.Module = Bastion.require("Module") +---@type UnitManager Bastion.UnitManager = Bastion.require("UnitManager"):New() +---@type ObjectManager Bastion.ObjectManager = Bastion.require("ObjectManager"):New() +---@type EventManager Bastion.EventManager = Bastion.require("EventManager"):New() +---@type Spell Bastion.Spell = Bastion.require("Spell") +---@type SpellBook Bastion.SpellBook = Bastion.require("SpellBook"):New() +---@type Item Bastion.Item = Bastion.require("Item") +---@type ItemBook Bastion.ItemBook = Bastion.require("ItemBook"):New() +---@type AuraTable Bastion.AuraTable = Bastion.require("AuraTable") +---@type Class Bastion.Class = Bastion.require("Class") +---@type Timer Bastion.Timer = Bastion.require("Timer") -Bastion.Gui = Bastion.require("Gui") +---@type Timer Bastion.CombatTimer = Bastion.Timer:New('combat') +---@type MythicPlusUtils Bastion.MythicPlusUtils = Bastion.require("MythicPlusUtils"):New() +---@type NotificationsList Bastion.Notifications = Bastion.NotificationsList:New() +Bastion.Gui = Bastion.require("Gui") Bastion.modules = {} Bastion.Enabled = false @@ -211,27 +236,21 @@ Command:Register('interface', 'Opens interface menu', function() else mainmenu:Show() end - end) - --- local files = ListFiles("scripts/bastion/scripts") - --- for i = 1, #files do --- local file = files[i] --- if file:sub(-4) == ".lua" or file:sub(-5) == '.luac' then --- Tinkr:require("scripts/bastion/scripts/" .. file:sub(1, -5), Bastion) --- end --- end +end) if UnitClass('player') == 'Priest' and GetSpecialization() == 3 then Tinkr:require("scripts/bastion/scripts/shadowpriest", Bastion) Eval('RunMacroText("/bastion module shadow")', 'bastion') elseif UnitClass('player') == 'Priest' and GetSpecialization() == 2 then Tinkr:require("scripts/bastion/scripts/holypriest", Bastion) - Eval('RunMacroText("/bastion module holypriest")', 'bastion') + Eval('RunMacroText("/bastion module holypriest")', 'bastion') +elseif UnitClass('player') == 'Druid' and GetSpecialization() == 2 then + Tinkr:require("scripts/bastion/scripts/feraldruid", Bastion) + Eval('RunMacroText("/bastion module feral")', 'bastion') elseif UnitClass('player') == 'Druid' and GetSpecialization() == 3 then Tinkr:require("scripts/bastion/scripts/guardiandruid", Bastion) Eval('RunMacroText("/bastion module guardian")', 'bastion') elseif UnitClass('player') == 'Druid' and GetSpecialization() == 4 then Tinkr:require("scripts/bastion/scripts/restodruid", Bastion) - Eval('RunMacroText("/bastion module resto_druid")', 'bastion') + Eval('RunMacroText("/bastion module resto")', 'bastion') end \ No newline at end of file