|
|
|
@ -1,43 +1,65 @@ |
|
|
|
|
---@type Tinkr, Bastion |
|
|
|
|
local Tinkr, Bastion = ... |
|
|
|
|
|
|
|
|
|
---@class Spell.Traits.Cast |
|
|
|
|
---@field moving boolean |
|
|
|
|
---@field dead boolean |
|
|
|
|
---@field global boolean |
|
|
|
|
---@field casting boolean |
|
|
|
|
---@field channeling boolean |
|
|
|
|
---@field override boolean |
|
|
|
|
|
|
|
|
|
---@class Spell.Traits.Target |
|
|
|
|
---@field exists boolean |
|
|
|
|
---@field player boolean |
|
|
|
|
|
|
|
|
|
---@class Spell.Traits |
|
|
|
|
---@field cast Spell.Traits.Cast |
|
|
|
|
---@field target Spell.Traits.Target |
|
|
|
|
|
|
|
|
|
---@class Spell.Traits.Cast.Params : Spell.Traits.Cast, { [string]?: boolean } |
|
|
|
|
---@class Spell.Traits.Target.Params : Spell.Traits.Target, { [string]?: boolean } |
|
|
|
|
|
|
|
|
|
---@class Spell.Traits.Params |
|
|
|
|
---@field cast? Spell.Traits.Cast.Params |
|
|
|
|
---@field target? Spell.Traits.Target.Params |
|
|
|
|
|
|
|
|
|
---@class Spell.Aura |
|
|
|
|
---@field spell Spell |
|
|
|
|
---@field source? Unit |
|
|
|
|
---@field target? Unit |
|
|
|
|
---@field lastApplied number |
|
|
|
|
|
|
|
|
|
-- Create a new Spell class |
|
|
|
|
---@class Spell |
|
|
|
|
---@field spellID number |
|
|
|
|
---@field PostCastFunc fun(self:Spell) | false |
|
|
|
|
---@field OnCastFunc fun(self:Spell) | false |
|
|
|
|
---@field PreCastFunc fun(self:Spell) | false |
|
|
|
|
---@field auras table<spellId, Spell.Aura> |
|
|
|
|
---@field CastableIfFunc false | fun(self:Spell):boolean |
|
|
|
|
---@field damage number |
|
|
|
|
---@field damageFormula false | fun(self:Spell):number |
|
|
|
|
---@field lastCastAt number | false |
|
|
|
|
---@field lastCastAttempt number | false |
|
|
|
|
---@field target Unit | false |
|
|
|
|
---@field damageFormula false | fun(self:Spell):number |
|
|
|
|
---@field OnCastFunc fun(self:Spell) | false |
|
|
|
|
---@field overrides { [spellId]: fun(self: Spell): Spell } |
|
|
|
|
---@field auras { [spellId]: { spell: Spell, source?: Unit, target?: Unit, lastApplied: number } } |
|
|
|
|
---@field PostCastFunc fun(self:Spell) | false |
|
|
|
|
---@field PreCastFunc fun(self:Spell) | false |
|
|
|
|
---@field release_at false |
|
|
|
|
---@field spellID number |
|
|
|
|
---@field target Unit | false |
|
|
|
|
---@field traits Spell.Traits |
|
|
|
|
---@field wasLooking boolean |
|
|
|
|
local Spell = { |
|
|
|
|
auras = {}, |
|
|
|
|
CastableIfFunc = false, |
|
|
|
|
PreCastFunc = false, |
|
|
|
|
OnCastFunc = false, |
|
|
|
|
PostCastFunc = false, |
|
|
|
|
lastCastAttempt = false, |
|
|
|
|
wasLooking = false, |
|
|
|
|
lastCastAt = false, |
|
|
|
|
conditions = {}, |
|
|
|
|
auras = {}, |
|
|
|
|
damage = 0, |
|
|
|
|
damageFormula = false, |
|
|
|
|
lastCastAt = false, |
|
|
|
|
lastCastAttempt = false, |
|
|
|
|
OnCastFunc = false, |
|
|
|
|
overrides = {}, |
|
|
|
|
traits = { |
|
|
|
|
cast = { |
|
|
|
|
moving = true, |
|
|
|
|
dead = false, |
|
|
|
|
global = false, |
|
|
|
|
casting = false, |
|
|
|
|
channeling = false, |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
target = false, |
|
|
|
|
PostCastFunc = false, |
|
|
|
|
PreCastFunc = false, |
|
|
|
|
release_at = false, |
|
|
|
|
damageFormula = false, |
|
|
|
|
damage = 0, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
local usableExcludes = { |
|
|
|
@ -60,24 +82,40 @@ 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) |
|
|
|
|
---@class Spell |
|
|
|
|
local self = setmetatable({}, Spell) |
|
|
|
|
|
|
|
|
|
self.auras = {} |
|
|
|
|
self.conditions = {} |
|
|
|
|
self.overrides = {} |
|
|
|
|
self.spellID = id |
|
|
|
|
self.traits = { |
|
|
|
|
cast = { |
|
|
|
|
moving = true, |
|
|
|
|
dead = false, |
|
|
|
|
global = false, |
|
|
|
|
casting = false, |
|
|
|
|
channeling = false, |
|
|
|
|
override = false, |
|
|
|
|
}, |
|
|
|
|
target = { |
|
|
|
|
exists = true, |
|
|
|
|
player = false, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
self.target = false |
|
|
|
|
self.wasLooking = false |
|
|
|
|
|
|
|
|
|
return self |
|
|
|
|
end |
|
|
|
@ -87,7 +125,6 @@ function Spell:GetDescription() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
-- Duplicator |
|
|
|
|
---@return Spell |
|
|
|
|
function Spell:Fresh() |
|
|
|
|
return Spell:New(self:GetID()) |
|
|
|
|
end |
|
|
|
@ -99,17 +136,8 @@ function Spell:GetID(ignoreOverride) |
|
|
|
|
return ignoreOverride and self.spellID or self:IsOverridden() and self:OverrideSpellID() or self.spellID |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
function Spell:SetTraits(traits) |
|
|
|
|
if traits.cast and type(traits.cast) == "table" then |
|
|
|
|
for k, v in pairs(traits.cast) do |
|
|
|
|
self.traits.cast[k] = v |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
-- Add post cast func |
|
|
|
|
---@param func fun(self:Spell) |
|
|
|
|
---@return Spell |
|
|
|
|
function Spell:PostCast(func) |
|
|
|
|
self.PostCastFunc = func |
|
|
|
|
return self |
|
|
|
@ -188,7 +216,7 @@ end |
|
|
|
|
|
|
|
|
|
-- Cast the spell |
|
|
|
|
---@param unit Unit |
|
|
|
|
---@param condition? string|fun(self:Spell):boolean |
|
|
|
|
---@param condition? string | fun(self:Spell):boolean |
|
|
|
|
---@return boolean |
|
|
|
|
function Spell:Cast(unit, condition) |
|
|
|
|
if condition then |
|
|
|
@ -212,7 +240,8 @@ function Spell:Cast(unit, condition) |
|
|
|
|
self.wasLooking = IsMouselooking() |
|
|
|
|
|
|
|
|
|
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast |
|
|
|
|
local u = unit:GetOMToken() |
|
|
|
|
|
|
|
|
|
local u = unit and unit:GetOMToken() or self.traits.target.player and "none" or "none" |
|
|
|
|
if type(u) == "string" and string.find(u, "nameplate") then |
|
|
|
|
u = Object(u) |
|
|
|
|
end |
|
|
|
@ -230,6 +259,7 @@ function Spell:Cast(unit, condition) |
|
|
|
|
if self:GetOnCastFunction() then |
|
|
|
|
self:GetOnCastFunction()(self) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
return true |
|
|
|
|
end |
|
|
|
|
|
|
|
|
@ -237,16 +267,11 @@ end |
|
|
|
|
---@param unit Unit |
|
|
|
|
---@return boolean |
|
|
|
|
function Spell:ForceCast(unit) |
|
|
|
|
-- Call pre cast function |
|
|
|
|
-- if self:GetPreCastFunction() then |
|
|
|
|
-- self:GetPreCastFunction()(self) |
|
|
|
|
-- end |
|
|
|
|
|
|
|
|
|
-- Check if the mouse was looking |
|
|
|
|
self.wasLooking = IsMouselooking() |
|
|
|
|
|
|
|
|
|
-- if unit:GetOMToken() contains 'nameplate' then we need to use Object wrapper to cast |
|
|
|
|
local u = unit:GetOMToken() |
|
|
|
|
local u = unit and unit:GetOMToken() or self.traits.target.player and "none" or "none" |
|
|
|
|
if type(u) == "string" and string.find(u, "nameplate") then |
|
|
|
|
u = Object(u) |
|
|
|
|
end |
|
|
|
@ -255,16 +280,11 @@ function Spell:ForceCast(unit) |
|
|
|
|
CastSpellByName(self:GetName(), u) |
|
|
|
|
SpellCancelQueuedSpell() |
|
|
|
|
|
|
|
|
|
Bastion:Debug("Casting", self) |
|
|
|
|
Bastion:Debug("Force Casting", self) |
|
|
|
|
|
|
|
|
|
-- Set the last cast time |
|
|
|
|
self.lastCastAttempt = GetTime() |
|
|
|
|
|
|
|
|
|
-- -- Call post cast function |
|
|
|
|
-- if self:GetOnCastFunction() then |
|
|
|
|
-- self:GetOnCastFunction()(self) |
|
|
|
|
-- end |
|
|
|
|
|
|
|
|
|
return true |
|
|
|
|
end |
|
|
|
|
|
|
|
|
@ -300,7 +320,7 @@ end |
|
|
|
|
---@param byId? boolean |
|
|
|
|
---@return boolean, boolean |
|
|
|
|
function Spell:IsUsableSpell(byId) |
|
|
|
|
return IsUsableSpell((byId ~= nil and byId) and self:GetID() or self:GetName()) |
|
|
|
|
return IsUsableSpell(byId and self:GetID() or self:GetName()) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
-- Check if the spell is usable |
|
|
|
@ -315,37 +335,67 @@ end |
|
|
|
|
---@param override? boolean |
|
|
|
|
---@return boolean |
|
|
|
|
function Spell:IsKnownAndUsable(override) |
|
|
|
|
return self:IsKnown(override) and not self:IsOnCooldown() and self:IsUsable(override and false or true) |
|
|
|
|
return self:IsKnown(override) and not self:IsOnCooldown() and self:IsUsable(override ~= nil and false or true) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
---@param traits Spell.Traits.Params |
|
|
|
|
function Spell:SetTraits(traits) |
|
|
|
|
for _, trait in pairs({ "cast", "target" }) do |
|
|
|
|
if type(traits[trait]) == "table" then |
|
|
|
|
for k, _ in pairs(self.traits[trait]) do |
|
|
|
|
if traits[trait][k] ~= nil then |
|
|
|
|
self.traits[trait][k] = traits[trait][k] |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
end |
|
|
|
|
return self |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
function Spell:EvaluateTraits() |
|
|
|
|
local player = Bastion.UnitManager["player"] |
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
(self.traits.cast.dead or player:IsAlive()) and |
|
|
|
|
(self.traits.cast.moving or not player:IsMoving()) and |
|
|
|
|
(self.traits.cast.global or player:GetGCD() == 0) and |
|
|
|
|
(self.traits.cast.casting or not player:IsCasting()) and |
|
|
|
|
(self.traits.cast.channeling or not player:IsChanneling()) |
|
|
|
|
) |
|
|
|
|
if not self.traits.cast.global and player:GetGCD() > 0 then |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if not self.traits.cast.moving and player:IsMoving() then |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if not self.traits.cast.dead and player:IsDead() then |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if not self.traits.cast.casting and player:IsCasting() then |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if not self.traits.cast.channeling and player:IsChanneling() then |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
if self.traits.target.exists and not self:TargetExists() then |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
return true |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
-- Check if the spell is castable |
|
|
|
|
---@return boolean |
|
|
|
|
function Spell:Castable() |
|
|
|
|
if not self:EvaluateTraits() then |
|
|
|
|
return true |
|
|
|
|
return false |
|
|
|
|
end |
|
|
|
|
if self:GetCastableFunction() then |
|
|
|
|
return self:GetCastableFunction()(self) |
|
|
|
|
return self:GetCastableFunction()(self) and self:IsKnownAndUsable(self.traits.cast.override or nil) |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
return self:IsKnownAndUsable() |
|
|
|
|
return self:IsKnownAndUsable(self.traits.cast.override or nil) |
|
|
|
|
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 |
|
|
|
@ -353,7 +403,6 @@ 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 |
|
|
|
@ -361,7 +410,6 @@ 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 |
|
|
|
@ -580,6 +628,10 @@ function Spell:GetTarget() |
|
|
|
|
return self.target |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
function Spell:TargetExists() |
|
|
|
|
return self:GetTarget() and self:GetTarget():Exists() |
|
|
|
|
end |
|
|
|
|
|
|
|
|
|
-- IsEnrageDispel |
|
|
|
|
---@return boolean |
|
|
|
|
function Spell:IsEnrageDispel() |
|
|
|
|