main
ck 1 year ago
parent 09a2fd2e63
commit a572688294
  1. 2
      src/AuraTable/AuraTable.lua
  2. 192
      src/Spell/Spell.lua
  3. 199
      src/Unit/Unit.lua
  4. 6
      src/_bastion.lua

@ -330,7 +330,7 @@ end
---@param source "any" | Unit
---@return Aura
function AuraTable:FindFrom(spell, source)
if type(source) == "string" or source == "any" then
if type(source) == "string" and source == "any" then
return self:FindAny(spell)
end

@ -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()

@ -268,7 +268,7 @@ function Unit:IsBoss()
return false
end
---@return UnitId
---@return UnitIds
function Unit:GetOMToken()
if not self.unit then
return "none"
@ -379,183 +379,27 @@ local isClassicWow = select(4, GetBuildInfo()) < 40000
-- return UnitInMelee(self:GetOMToken(), unit:GetOMToken())
-- end
-- 1048593
local losFlag = bit.bor(0x1, 0x10)
local losBlacklist = {
[131863] = true, -- Raal the Gluttonous (WCM)
}
local AttachmentPoisitions = {
MountMain = 0,
HandRight = 1,
HandLeft = 2,
ElbowRight = 3,
ElbowLeft = 4,
ShoulderRight = 5,
ShoulderLeft = 6,
KneeRight = 7,
KneeLeft = 8,
HipRight = 9,
HipLeft = 10,
Helm = 11,
Back = 12,
ShoulderFlapRight = 13,
ShoulderFlapLeft = 14,
ChestBloodFront = 15,
ChestBloodBack = 16,
Breath = 17,
PlayerName = 18,
Base = 19,
Head = 20,
SpellLeftHand = 21,
SpellRightHand = 22,
Special1 = 23,
Special2 = 24,
Special3 = 25,
SheathMainHand = 26,
SheathOffHand = 27,
SheathShield = 28,
PlayerNameMounted = 29,
LargeWeaponLeft = 30,
LargeWeaponRight = 31,
HipWeaponLeft = 32,
HipWeaponRight = 33,
Chest = 34,
HandArrow = 35,
Bullet = 36,
SpellHandOmni = 37,
SpellHandDirected = 38,
VehicleSeat1 = 39,
VehicleSeat2 = 40,
VehicleSeat3 = 41,
VehicleSeat4 = 42,
VehicleSeat5 = 43,
VehicleSeat6 = 44,
VehicleSeat7 = 45,
VehicleSeat8 = 46,
LeftFoot = 47,
RightFoot = 48,
ShieldNoGlove = 49,
SpinLow = 50,
AlteredShoulderR = 51,
AlteredShoulderL = 52,
BeltBuckle = 53,
SheathCrossbow = 54,
HeadTop = 55,
VirtualSpellDirected = 56,
Backpack = 57,
Unknown = 60,
}
--[[
M2Collision = 1, -- 0x1
M2Render = 2, -- 0x2
WMOCollision = 16, -- 0x10
WMORender = 32, -- 0x20
Terrain = 256, -- 0x100
WaterWalkableLiquid = 65536, -- 0x10000
Liquid = 131072, -- 0x20000
WaterOrLiquid = 196608, -- 0x30000
EntityCollision = 1048576, -- 0x100000
Most = 1048849, -- 0x100201
Unknown = 2097152, -- 0x200000
All = 3211571, -- 0x310003
]]
--[[
public enum IntersectFlags {
None,
DoodadCollision = 0x00000001,
DoodadRender = 0x00000002,
WmoCollision = 0x00000010,
WmoRender = 0x00000020,
WmoNoCamCollision = 0x00000040,
Terrain = 0x00000100,
IgnoreWmoDoodad = 0x00002000,
LiquidWaterWalkable = 0x00010000,
LiquidAll = 0x00020000,
Cull = 0x00080000,
EntityCollision = 0x00100000,
EntityRender = 0x00200000,
Collision = DoodadCollision | WmoCollision | Terrain | EntityCollision,
LineOfSight = WmoCollision | EntityCollision
]]
--[[
M2Collision = 0x1
M2Render = 0x2
WMOCollision = 0x10
WMORender = 0x20
Terrain = 0x100
WaterWalkableLiquid = 0x10000
Liquid = 0x20000
WaterOrLiquid = 0x30000,
EntityCollision = 0x100000
Most = 0x100201,
Unknown = 0x200000,
All = 0x310003,
]]
local attachmentOverride = {
[131863] = AttachmentPoisitions.Head, -- WCM Raal the Gluttonous
}
function Unit:GetHitSphere()
--local srcX, srcY, srcZ = GetUnitAttachmentPosition(self:GetOMToken(), attachmentOverride[self:GetID()] or AttachmentPoisitions.PlayerName)
--local srcHeight = UnitIsMounted(self:GetOMToken()) and 3.081099 or 2.43808
return Bastion.Vector3:New(GetUnitAttachmentPosition(self:GetOMToken(),
attachmentOverride[self:GetID()] or AttachmentPoisitions.PlayerName))
end
function Unit:GetLOSSourcePosition()
local src = self:GetPosition()
src.z = UnitIsMounted(self:GetOMToken()) and 3.081099 or 2.43808
return src
end
local losFlag = bit.bor(0x10)
-- Check if the unit can see another unit
---@param targetUnit Unit
---@return boolean
function Unit:CanSee(targetUnit)
local npcId = targetUnit:GetID()
if npcId and losBlacklist[npcId] then
return true
end
local src = self:GetLOSSourcePosition()
local dst = targetUnit:GetHitSphere()
if (src.x == 0 and src.y == 0 and src.z == 0) or (dst.x == 0 and dst.y == 0 and dst.z == 0) then
return true
end
local contactPoint = src +
(dst - src):directionOrZero() * math.min(targetUnit:GetDistance(self), self:GetCombatReach())
local x, y, z = TraceLine(src.x, src.y, src.z, contactPoint.x, contactPoint.y, contactPoint.z, losFlag)
if x ~= 0 or y ~= 0 or z ~= 0 then
return false
else
return true
end
end
---@param destination Unit
---@return Vector3
function Unit:GetHitSpherePointFor(destination)
local vThis = Bastion.Vector3:New(self:GetPosition().x, self:GetPosition().y,
self:GetPosition().z + ObjectHeight(self:GetOMToken()))
local vObj = Bastion.Vector3:New(destination:GetPosition().x, destination:GetPosition().y,
destination:GetPosition().z)
local contactPoint = vThis +
(vObj - vThis):directionOrZero() * math.min(destination:GetDistance(self), self:GetCombatReach())
return contactPoint
end
---@param unit Unit
function Unit:CanSee2(unit)
---@return boolean
function Unit:CanSee(unit)
-- mechagon smoke cloud
-- local mechagonID = 2097
-- local smokecloud = 298602
-- local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceID, instanceGroupSize, LfgDungeonID =
-- GetInstanceInfo()
-- otherUnit = otherUnit and otherUnit or "player"
-- if instanceID == 2097 then
-- if (self:debuff(smokecloud, unit) and not self:debuff(smokecloud, otherUnit))
-- or (self:debuff(smokecloud, otherUnit) and not self:debuff(smokecloud, unit))
-- then
-- return false
-- end
-- end
local ax, ay, az = ObjectPosition(self:GetOMToken())
local ah = ObjectHeight(self:GetOMToken())
local attx, atty, attz = GetUnitAttachmentPosition(unit:GetOMToken(), 34)
@ -1135,11 +979,8 @@ end
---@return number
function Unit:GetGCD()
local start, duration = GetSpellCooldown(61304)
if start == 0 then
return 0
end
return duration - (GetTime() - start)
return start == 0 and 0 or duration - (GetTime() - start)
end
-- Get units max gcd time

@ -186,6 +186,7 @@ Bastion.Globals.SpellName = {}
Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function()
local args = { CombatLogGetCurrentEventInfo() }
---@type string
local subEvent = args[2]
---@type string
local sourceGUID = args[4]
@ -196,9 +197,12 @@ Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", fun
---@type string
local spellName = args[13]
if not Bastion.Globals.SpellName[spellID] or Bastion.Globals.SpellName[spellID] ~= spellName then
if subEvent:find("SPELL") == 1 or subEvent:find("RANGE") == 1 then
if (not Bastion.Globals.SpellName[spellID] or Bastion.Globals.SpellName[spellID] ~= spellName) then
Bastion.Globals.SpellName[spellID] = spellName
end
end
-- if sourceGUID == pguid then
-- local args = { CombatLogGetCurrentEventInfo() }

Loading…
Cancel
Save