diff --git a/scripts/subtlety.lua b/scripts/subtlety.lua index 502c949..9c3d16f 100644 --- a/scripts/subtlety.lua +++ b/scripts/subtlety.lua @@ -37,7 +37,7 @@ 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(326509) +local Sanguine = Bastion.SpellBook:GetSpell(226512) local AtrophicPosion = Bastion.SpellBook:GetSpell(381637) local Evasion = Bastion.SpellBook:GetSpell(5277) local TricksOfTheTrade = Bastion.SpellBook:GetSpell(57934) @@ -65,6 +65,7 @@ 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), @@ -191,6 +192,7 @@ local RuptureTarget = Bastion.UnitManager:CreateCustomUnit('rupture', function() unit:GetAuras():FindMy(Rupture):GetRemainingTime() < 6 ) and unit:TimeToDie() > 12 + and unit:GetCombatTime() > 4 then target = unit return true @@ -209,17 +211,25 @@ 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 Player:InMelee(KickTarget) and + 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 Player:InMelee(KickTarget) and + return KickTarget:Exists() and self:IsInRange(KickTarget) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Kick:GetTimeSinceLastCast() > 2 and @@ -234,9 +244,10 @@ SpecialAPL:AddSpell( SpecialAPL:AddSpell( CheapShot:CastableIf(function(self) - return KickTarget:Exists() and Player:InMelee(KickTarget) and + 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) ) @@ -258,7 +269,7 @@ SpecialAPL:AddSpell( SpecialAPL:AddSpell( Shiv:CastableIf(function(self) - return PurgeTarget:Exists() and Player:InMelee(PurgeTarget) and + return PurgeTarget:Exists() and self:IsInRange(PurgeTarget) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and PurgeTarget:GetAuras():HasAnyStealableAura() end):SetTarget(PurgeTarget) @@ -326,6 +337,13 @@ SpecialAPL:AddItem( 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 @@ -345,7 +363,7 @@ SpecialAPL:AddItem( -- Use Shadowstrike during Shadow Dance. SpecialAPL:AddSpell( Shadowstrike:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + 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 @@ -454,7 +472,7 @@ DefaultAPL:AddSpell( -- 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 Player:InMelee(Target) and + return Target:Exists() and self:IsInRange(Target) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (Player:GetComboPoints(Target) >= 6 or @@ -466,22 +484,24 @@ DefaultAPL:AddSpell( end):SetTarget(Target) ) --- Secret Technique - Best is to use it during Shadow Dance. DefaultAPL:AddSpell( SecretTechnique:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + 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 Target:GetAuras():FindMy(Rupture):IsUp() + (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. +-- Cast Eviscerate if it is available. DefaultAPL:AddSpell( Eviscerate:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + return Target:Exists() and self:IsInRange(Target) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (Player:GetComboPoints(Target) >= 6 or @@ -504,7 +524,7 @@ DefaultAPL:AddSpell( -- Use Gloomblade outside of Shadow Dance. DefaultAPL:AddSpell( Gloomblade:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + return Target:Exists() and self:IsInRange(Target) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() @@ -514,7 +534,7 @@ DefaultAPL:AddSpell( -- Use Shadowstrike during Shadow Dance. DefaultAPL:AddSpell( Shadowstrike:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + return Target:Exists() and self:IsInRange(Target) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetAuras():FindMy(ShadowDanceAura):IsUp() @@ -587,7 +607,7 @@ AOEAPL:AddSpell( -- Use Thistle Tea with Shadow Dance. AOEAPL:AddSpell( ThistleTea:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + return Target:Exists() and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetPowerDeficit() >= 100 and @@ -614,7 +634,7 @@ AOEAPL:AddSpell( -- 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 Player:InMelee(Target) and + return Target:Exists() and self:IsInRange(Target) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (Player:GetComboPoints(Target) >= 5) and ( @@ -630,7 +650,7 @@ AOEAPL:AddSpell( -- Cast Rupture on all targets. (scam??) AOEAPL:AddSpell( Rupture:CastableIf(function(self) - return RuptureTarget:Exists() and Player:InMelee(RuptureTarget) and + return RuptureTarget:Exists() and self:IsInRange(RuptureTarget) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (Player:GetComboPoints(RuptureTarget) >= 6) and ( @@ -641,22 +661,25 @@ AOEAPL:AddSpell( 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 Player:InMelee(Target) and + 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 Target:GetAuras():FindMy(Rupture):IsUp() + (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 Target:Exists() and Player:InMelee(Target) and - self:IsKnownAndUsable() and + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and (Player:GetComboPoints(Target) >= 5) and (Player:GetEnemies(10) >= 3 or @@ -668,7 +691,7 @@ AOEAPL:AddSpell( -- Cast Eviscerate. AOEAPL:AddSpell( Eviscerate:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + return Target:Exists() and self:IsInRange(Target) and self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetComboPoints(Target) >= 5 @@ -689,8 +712,7 @@ AOEAPL:AddSpell( -- Use Shuriken Storm on 2 targets outside of Shadow Dance. AOEAPL:AddSpell( ShurikenStorm:CastableIf(function(self) - return Target:Exists() and Player:GetDistance(Target) <= 10 and - self:IsKnownAndUsable() and + return self:IsKnownAndUsable() and not Player:IsCastingOrChanneling() and Player:GetEnemies(10) == 2 and not Player:GetAuras():FindMy(ShadowDanceAura):IsUp() @@ -700,7 +722,7 @@ AOEAPL:AddSpell( -- Use Shadowstrike on 2 and 3 targets during Shadow Dance or to proc Premeditation. AOEAPL:AddSpell( Shadowstrike:CastableIf(function(self) - return Target:Exists() and Player:InMelee(Target) and + 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 @@ -708,15 +730,25 @@ AOEAPL:AddSpell( end):SetTarget(Target) ) --- Use Shuriken Storm +-- Use Shuriken Storm at > 2 targets. AOEAPL:AddSpell( ShurikenStorm:CastableIf(function(self) - return Target:Exists() and Player:GetDistance(Target) <= 10 and - self:IsKnownAndUsable() and - not Player:IsCastingOrChanneling() + 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 diff --git a/src/APL/APL.lua b/src/APL/APL.lua index d94918a..3ea8188 100644 --- a/src/APL/APL.lua +++ b/src/APL/APL.lua @@ -1,3 +1,116 @@ +-- Create an APL trait for the APL class +local APLTrait = {} +APLTrait.__index = APLTrait + +-- Constructor +function APLTrait:New(cb) + local self = setmetatable({}, APLTrait) + + self.cb = cb + self.lastcall = 0 + + return self +end + +-- Evaulate the APL trait +function APLTrait:Evaluate() + if GetTime() - self.lastcall > 0.1 then + self.lastresult = self.cb() + self.lastcall = GetTime() + return self.lastresult + end + + return self.lastresult +end + +-- tostring +function APLTrait:__tostring() + return "Bastion.__APLTrait" +end + +-- Create an APL actor for the APL class +local APLActor = {} +APLActor.__index = APLActor + +-- Constructor +function APLActor:New(actor) + local self = setmetatable({}, APLActor) + + self.actor = actor + self.traits = {} + + return self +end + +-- Add a trait to the APL actor +function APLActor:AddTraits(...) + for _, trait in ipairs({ ... }) do + table.insert(self.traits, trait) + end + + return self +end + +-- Get the actor +function APLActor:GetActor() + return self.actor +end + +-- Evaulate the APL actor +function APLActor:Evaluate() + for _, trait in ipairs(self.traits) do + if not trait:Evaluate() then + return false + end + end + + return true +end + +-- Execute +function APLActor:Execute() + if self:GetActor().apl then + if self:GetActor().condition() then + -- print("Bastion: APL:Execute: Executing sub APL " .. self:GetActor().apl.name) + self:GetActor().apl:Execute() + end + end + if self:GetActor().spell then + if self:GetActor().condition then + -- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName()) + self:GetActor().spell:CastableIf(self:GetActor().castableFunc):Cast(self:GetActor().target, + self:GetActor().condition) + end + + -- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName()) + self:GetActor().spell:CastableIf(self:GetActor().castableFunc):Cast(self:GetActor().target) + end + if self:GetActor().item then + if self:GetActor().condition then + -- print("Bastion: APL:Execute: Condition for spell " .. self:GetActor().spell:GetName()) + self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target, + self:GetActor().condition) + end + + -- print("Bastion: APL:Execute: No condition for spell " .. self:GetActor().spell:GetName()) + self:GetActor().item:UsableIf(self:GetActor().usableFunc):Use(self:GetActor().target) + end + if self:GetActor().action then + -- print("Bastion: APL:Execute: Executing action " .. self:GetActor().action) + self:GetActor().cb(self) + end +end + +-- has traits +function APLActor:HasTraits() + return #self.traits > 0 +end + +-- tostring +function APLActor:__tostring() + return "Bastion.__APLActor" +end + -- APL (Attack priority list) class local APL = {} @@ -26,7 +139,9 @@ end -- Add a manual action to the APL function APL:AddAction(action, cb) - table.insert(self.apl, { action = action, cb = cb }) + local actor = APLActor:New({ action = action, cb = cb }) + table.insert(self.apl, actor) + return actor end -- Add a spell to the APL @@ -34,7 +149,11 @@ function APL:AddSpell(spell, condition) local castableFunc = spell.CastableIfFunc local target = spell:GetTarget() - table.insert(self.apl, { spell = spell, condition = condition, castableFunc = castableFunc, target = target }) + local actor = APLActor:New({ spell = spell, condition = condition, castableFunc = castableFunc, target = target }) + + table.insert(self.apl, actor) + + return actor end -- Add an item to the APL @@ -42,46 +161,27 @@ function APL:AddItem(item, condition) local usableFunc = item.UsableIfFunc local target = item:GetTarget() - table.insert(self.apl, { item = item, condition = condition, usableFunc = usableFunc, target = target }) + local actor = APLActor:New({ item = item, condition = condition, usableFunc = usableFunc, target = target }) + + table.insert(self.apl, actor) + + return actor end -- Add an APL to the APL (for sub APLs) function APL:AddAPL(apl, condition) - table.insert(self.apl, { apl = apl, condition = condition }) + local actor = APLActor:New({ apl = apl, condition = condition }) + table.insert(self.apl, actor) + return actor end -- Execute the APL function APL:Execute() for _, actor in ipairs(self.apl) do - if actor.apl then - if actor.condition() then - -- print("Bastion: APL:Execute: Executing sub APL " .. actor.apl.name) - actor.apl:Execute() - end - end - if actor.spell then - if actor.condition then - -- print("Bastion: APL:Execute: Condition for spell " .. actor.spell:GetName()) - actor.spell:CastableIf(actor.castableFunc):Cast(actor.target, - actor.condition) - end - - -- print("Bastion: APL:Execute: No condition for spell " .. actor.spell:GetName()) - actor.spell:CastableIf(actor.castableFunc):Cast(actor.target) - end - if actor.item then - if actor.condition then - -- print("Bastion: APL:Execute: Condition for spell " .. actor.spell:GetName()) - actor.item:UsableIf(actor.usableFunc):Use(actor.target, - actor.condition) - end - - -- print("Bastion: APL:Execute: No condition for spell " .. actor.spell:GetName()) - actor.item:UsableIf(actor.usableFunc):Use(actor.target) - end - if actor.action then - -- print("Bastion: APL:Execute: Executing action " .. actor.action) - actor.cb(self) + if actor:HasTraits() and actor:Evaluate() then + actor:Execute() + else + actor:Execute() end end end @@ -91,4 +191,4 @@ function APL:__tostring() return "Bastion.__APL(" .. self.name .. ")" end -return APL +return APL, APLActor, APLTrait diff --git a/src/Unit/Unit.lua b/src/Unit/Unit.lua index 078f6de..211b639 100644 --- a/src/Unit/Unit.lua +++ b/src/Unit/Unit.lua @@ -325,7 +325,7 @@ function Unit:GetChannelOrCastPercentComplete() local start = startTimeMS / 1000 local finish = endTimeMS / 1000 local current = GetTime() - print(((current - start) / (finish - start)) * 100) + return ((current - start) / (finish - start)) * 100 end return 0 @@ -603,4 +603,19 @@ function Unit:TimeToDie() return timeto end +-- Set combat time if affecting combat and return the difference between now and the last time +function Unit:GetCombatTime() + if self:IsAffectingCombat() then + self.last_combat_time = GetTime() + elseif not self:IsAffectingCombat() and self.last_combat_time then + self.last_combat_time = nil + end + + if not self.last_combat_time then + return 0 + end + + return GetTime() - self.last_combat_time +end + return Unit diff --git a/src/_bastion.lua b/src/_bastion.lua index 57e49b5..4c54103 100644 --- a/src/_bastion.lua +++ b/src/_bastion.lua @@ -6,25 +6,20 @@ local Bastion = { Bastion.__index = Bastion function Bastion.require(class) - if Bastion[class] then - return Bastion[class] - end - - Bastion[class] = Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion) - return Bastion[class] + return Tinkr:require("scripts/bastion/src/" .. class .. "/" .. class, Bastion) end Bastion.ClassMagic = Bastion.require("ClassMagic") Bastion.List = Bastion.require("List") Bastion.NotificationsList, Bastion.Notification = Bastion.require("NotificationsList") Bastion.Vector3 = Bastion.require("Vector3") -Bastion.Commmand = Bastion.require("Command") +Bastion.Command = Bastion.require("Command") Bastion.Cache = Bastion.require("Cache") Bastion.Cacheable = Bastion.require("Cacheable") Bastion.Refreshable = Bastion.require("Refreshable") Bastion.Unit = Bastion.require("Unit") Bastion.Aura = Bastion.require("Aura") -Bastion.APL = Bastion.require("APL") +Bastion.APL, Bastion.APLActor, Bastion.APLTrait = Bastion.require("APL") Bastion.Module = Bastion.require("Module") Bastion.UnitManager = Bastion.require("UnitManager"):New() Bastion.ObjectManager = Bastion.require("ObjectManager"):New() @@ -115,7 +110,7 @@ function Bastion:Debug(...) print(str) end -local Command = Bastion.Commmand:New('bastion') +local Command = Bastion.Command:New('bastion') Command:Register('toggle', 'Toggle bastion on/off', function() Bastion.Enabled = not Bastion.Enabled