Basic rotations that work
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OkRotations/RestoShaman.lua

351 lines
12 KiB

local Tinkr, Bastion = ...
local RestoShamanModule = Bastion.Module:New('RestoShaman')
local Player = Bastion.UnitManager:Get('player')
local Target = Bastion.UnitManager:Get('target')
-- Initialize SpellBook
local SpellBook = Bastion.SpellBook:New()
-- Spells
local WaterShield = SpellBook:GetSpell(52127)
local EarthShield = SpellBook:GetSpell(974)
local HealingRain = SpellBook:GetSpell(73920)
local Riptide = SpellBook:GetSpell(61295)
local CloudburstTotem = SpellBook:GetSpell(157153)
local HealingStreamTotem = SpellBook:GetSpell(5394)
local PrimordialWave = SpellBook:GetSpell(375982)
local UnleashLife = SpellBook:GetSpell(73685)
local Downpour = SpellBook:GetSpell(207778)
local ChainHeal = SpellBook:GetSpell(1064)
local HealingWave = SpellBook:GetSpell(77472)
local HealingSurge = SpellBook:GetSpell(8004)
local Wellspring = SpellBook:GetSpell(197995)
local SpiritLinkTotem = SpellBook:GetSpell(98008)
local HealingTideTotem = SpellBook:GetSpell(108280)
local AncestralGuidance = SpellBook:GetSpell(108281)
local ManaTideTotem = SpellBook:GetSpell(16191)
local Ascendance = SpellBook:GetSpell(114052)
local NaturesSwiftness = SpellBook:GetSpell(378081)
local EarthenWallTotem = SpellBook:GetSpell(198838)
local PurifySpirit = SpellBook:GetSpell(77130)
local LightningBolt = SpellBook:GetSpell(403)
local ChainLightning = SpellBook:GetSpell(421)
local FlameShock = SpellBook:GetSpell(188389)
local LavaBurst = SpellBook:GetSpell(51505)
-- Buffs
local TidalWaves = SpellBook:GetSpell(53390)
local HighTide = SpellBook:GetSpell(288675)
-- Totem timers
local cloudburstTotemEnd = 0
local healingStreamTotemEnd = 0
local earthenWallTotemEnd = 0
local spiritLinkTotemEnd = 0
local healingTideTotemEnd = 0
-- Custom Units
local Lowest = Bastion.UnitManager:CreateCustomUnit('lowest', function(unit)
local lowest = nil
local lowestHP = math.huge
Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsDead() or Player:GetDistance(unit) > 40 or not Player:CanSee(unit) then
return false
end
local hp = unit:GetHP()
if hp < lowestHP then
lowest = unit
lowestHP = hp
end
end)
return lowest or Player
end)
-- APLs
local PrePullAPL = Bastion.APL:New('prepull')
local DefaultAPL = Bastion.APL:New('default')
local CooldownAPL = Bastion.APL:New('cooldown')
local DamageAPL = Bastion.APL:New('damage')
-- Debug function
local function Debug(message)
-- print("[RestoShaman Debug]: " .. message)
end
-- Helper Functions
local function IsTotemActive(totemEnd)
return GetTime() < totemEnd
end
local function UpdateTotemTimer(totemEnd, duration)
return GetTime() + duration
end
local function ShouldUseCloudburstTotem()
return not IsTotemActive(cloudburstTotemEnd) and (Player:GetEnemies(10) >= 3 or Player:GetPartyHPAround(30, 80) >= 3)
end
local function ShouldUseHealingStreamTotem()
return not IsTotemActive(healingStreamTotemEnd) and not CloudburstTotem:IsKnownAndUsable()
end
local function ShouldUseEarthenWallTotem()
return not IsTotemActive(earthenWallTotemEnd) and Player:GetPartyHPAround(40, 80) >= 3
end
local function ShouldUseSpiritLinkTotem()
return not IsTotemActive(spiritLinkTotemEnd) and Player:GetPartyHPAround(40, 60) >= 3
end
local function ShouldUseHealingTideTotem()
return not IsTotemActive(healingTideTotemEnd) and Player:GetPartyHPAround(40, 70) >= 3
end
local function GetRiptideCount()
local count = 0
Bastion.UnitManager:EnumFriends(function(unit)
if unit:GetAuras():FindMy(Riptide):IsUp() then
count = count + 1
end
end)
return count
end
local function ShouldUseAscendance()
return Player:GetPartyHPAround(40, 70) >= 3 and CloudburstTotem:GetTimeSinceLastCast() < 3
end
local function GetTanks()
local tanks = {}
Bastion.UnitManager:EnumFriends(function(unit)
if unit:IsTank() and not unit:IsDead() and Player:CanSee(unit) and Player:GetDistance(unit) <= 40 then
table.insert(tanks, unit)
end
end)
Debug("Found " .. #tanks .. " tanks")
return tanks
end
local function ApplyEarthShield()
local tanks = GetTanks()
for _, tank in ipairs(tanks) do
if not tank:GetAuras():FindMy(EarthShield):IsUp() then
Debug("Applying Earth Shield to " .. tank:GetName())
return EarthShield:Cast(tank)
end
end
Debug("No tanks need Earth Shield")
end
local function NeedsHealing(unit, threshold)
return unit:GetHP() < threshold
end
-- Pre-Pull APL
PrePullAPL:AddSpell(
WaterShield:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:GetAuras():FindMy(WaterShield):IsUp()
end):SetTarget(Player)
)
-- Default APL
DefaultAPL:AddSpell(
CloudburstTotem:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and ShouldUseCloudburstTotem()
end):SetTarget(Player):OnCast(function()
cloudburstTotemEnd = UpdateTotemTimer(cloudburstTotemEnd, 15)
end)
)
DefaultAPL:AddSpell(
HealingStreamTotem:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and ShouldUseHealingStreamTotem()
end):SetTarget(Player):OnCast(function()
healingStreamTotemEnd = UpdateTotemTimer(healingStreamTotemEnd, 15)
end)
)
DefaultAPL:AddSpell(
Riptide:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and NeedsHealing(Lowest, 90)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
PrimordialWave:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and NeedsHealing(Lowest, 85)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
UnleashLife:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and NeedsHealing(Lowest, 80)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
HealingRain:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetPartyHPAround(30, 85) >= 6 and not Player:IsMoving()
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
Downpour:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetPartyHPAround(30, 80) >= 3
end):SetTarget(Player)
)
DefaultAPL:AddSpell(
ChainHeal:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and (Player:GetAuras():FindMy(HighTide):IsUp() or Player:GetAuras():FindMy(NaturesSwiftness):IsUp())
and not Player:IsMoving() and NeedsHealing(Lowest, 75)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
HealingWave:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetAuras():FindMy(TidalWaves):IsUp() and not Player:IsMoving()
and NeedsHealing(Lowest, 85)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
HealingSurge:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and NeedsHealing(Lowest, 70)
end):SetTarget(Lowest)
)
DefaultAPL:AddSpell(
PurifySpirit:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Lowest:GetAuras():HasAnyDispelableAura(PurifySpirit)
end):SetTarget(Lowest)
)
-- Cooldown APL
CooldownAPL:AddSpell(
SpiritLinkTotem:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and ShouldUseSpiritLinkTotem()
end):SetTarget(Player):OnCast(function()
spiritLinkTotemEnd = UpdateTotemTimer(spiritLinkTotemEnd, 6)
end)
)
CooldownAPL:AddSpell(
HealingTideTotem:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and ShouldUseHealingTideTotem()
end):SetTarget(Player):OnCast(function()
healingTideTotemEnd = UpdateTotemTimer(healingTideTotemEnd, 10)
end)
)
CooldownAPL:AddSpell(
AncestralGuidance:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetPartyHPAround(40, 75) >= 3
end):SetTarget(Player)
)
CooldownAPL:AddSpell(
ManaTideTotem:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetPP() <= 80
end):SetTarget(Player)
)
CooldownAPL:AddSpell(
Ascendance:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and ShouldUseAscendance()
end):SetTarget(Player)
)
CooldownAPL:AddSpell(
NaturesSwiftness:CastableIf(function(self)
return Lowest:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and NeedsHealing(Lowest, 50)
end):SetTarget(Lowest)
)
CooldownAPL:AddSpell(
EarthenWallTotem:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and ShouldUseEarthenWallTotem()
end):SetTarget(Player):OnCast(function()
earthenWallTotemEnd = UpdateTotemTimer(earthenWallTotemEnd, 15)
end)
)
CooldownAPL:AddSpell(
Wellspring:CastableIf(function(self)
return Player:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetPartyHPAround(30, 85) >= 3
end):SetTarget(Player)
)
-- Damage APL
DamageAPL:AddSpell(
FlameShock:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and not Target:GetAuras():FindMy(FlameShock):IsUp()
end):SetTarget(Target)
)
DamageAPL:AddSpell(
LavaBurst:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Target:GetAuras():FindMy(FlameShock):IsUp()
end):SetTarget(Target)
)
DamageAPL:AddSpell(
ChainLightning:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
and Player:GetEnemies(10) > 2
end):SetTarget(Target)
)
DamageAPL:AddSpell(
LightningBolt:CastableIf(function(self)
return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling()
end):SetTarget(Target)
)
-- Module Sync
RestoShamanModule:Sync(function()
if not Player:IsAffectingCombat() then
-- Reset totem timers when leaving combat
cloudburstTotemEnd = 0
healingStreamTotemEnd = 0
earthenWallTotemEnd = 0
spiritLinkTotemEnd = 0
healingTideTotemEnd = 0
if not Player:GetAuras():FindMy(WaterShield):IsUp() then
WaterShield:Cast(Player)
end
ApplyEarthShield()
elseif Player:IsAffectingCombat() then
ApplyEarthShield()
CooldownAPL:Execute()
DefaultAPL:Execute()
-- Use damage abilities when healing isn't needed
if Player:GetPP() > 80 and Player:GetPartyHPAround(40, 95) == 0 then
DamageAPL:Execute()
end
end
end)
Bastion:Register(RestoShamanModule)