From d69fd5d46a8b426e4eceabe60649ae32e8af22f4 Mon Sep 17 00:00:00 2001 From: ck Date: Tue, 12 Dec 2023 06:47:39 -0600 Subject: [PATCH] Various updates to types. --- .editorconfig | 5 + src/APL/APL.lua | 69 ++++--- src/Aura/Aura.lua | 8 +- src/AuraTable/AuraTable.lua | 2 +- src/Cacheable/Cacheable.lua | 6 +- src/EventManager/EventManager.lua | 55 ++++-- src/List/List.lua | 30 +-- src/ObjectManager/ObjectManager.lua | 8 +- src/Refreshable/Refreshable.lua | 5 +- src/Spell/Spell.lua | 23 ++- src/Unit/Unit.lua | 279 +++++++++++++++++++++------- src/UnitManager/UnitManager.lua | 41 +++- src/Vector3/Vector3.lua | 40 +++- src/_bastion.lua | 8 +- stylua.toml | 10 + 15 files changed, 422 insertions(+), 167 deletions(-) create mode 100644 stylua.toml diff --git a/.editorconfig b/.editorconfig index c5a2625..c98f997 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,11 @@ root = true [*.lua] +indent_type = Spaces +column_width = 180 +indent_width = 4 +quote_style = AutoPreferDouble +call_parentheses = Always indent_style = space indent_size = 4 end_of_line = lf diff --git a/src/APL/APL.lua b/src/APL/APL.lua index 19a71d6..001b580 100644 --- a/src/APL/APL.lua +++ b/src/APL/APL.lua @@ -45,6 +45,7 @@ end ---@class APLActor ---@field actor APLActorTable ---@field traits APLTrait[] +---@field name string local APLActor = {} APLActor.__index = APLActor @@ -52,7 +53,21 @@ APLActor.__index = APLActor ---@param actor APLActorTable function APLActor:New(actor) local self = setmetatable({}, APLActor) - + if actor.type == "spell" then + self.name = string.format("[%s] `%s`<%s>", actor.type, actor.spell:GetName(), actor.spell:GetID()) + elseif actor.type == "item" then + self.name = string.format("[%s] `%s`<%s>", actor.type, actor.item:GetName(), actor.item:GetID()) + elseif actor.type == "apl" then + self.name = string.format("[%s] `%s`", actor.type, actor.apl.name) + elseif actor.type == "sequencer" then + self.name = string.format("[%s]", actor.type) + elseif actor.type == "variable" then + self.name = string.format("[%s] `%s`", actor.type, actor.variable) + elseif actor.type == "action" then + self.name = string.format("[%s] `%s`", actor.type, actor.action) + else + self.name = string.format("[%s] Unknown", actor.type) + end self.actor = actor self.traits = {} @@ -120,31 +135,11 @@ function APLActor:Execute() end if actorTable.type == "spell" then ---@cast actorTable APLActorSpellTable - return actorTable.spell - :CastableIf(actorTable.castableFunc) - :OnCast(actorTable.onCastFunc) - :Cast(actorTable.target, actorTable.condition) - --[[ if actorTable.condition then - -- print("Bastion: APL:Execute: Condition for spell " .. actorTable.spell:GetName()) - actorTable.spell - :CastableIf(actorTable.castableFunc) - :OnCast(actorTable.onCastFunc) - :Cast(actorTable.target, actorTable.condition) - else - -- print("Bastion: APL:Execute: No condition for spell " .. actorTable.spell:GetName()) - actorTable.spell:CastableIf(actorTable.castableFunc):OnCast(actorTable.onCastFunc):Cast(actorTable.target) - end ]] + return actorTable.spell:CastableIf(actorTable.castableFunc):OnCast(actorTable.onCastFunc):Cast(actorTable.target, actorTable.condition) end if actorTable.type == "item" then ---@cast actorTable APLActorItemTable return actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target, actorTable.condition) - --[[ if actorTable.condition and type(actorTable.condition) == "string" then - -- print("Bastion: APL:Execute: Condition for spell " .. actorTable.spell:GetName()) - actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target, actorTable.condition) - else - -- print("Bastion: APL:Execute: No condition for spell " .. actorTable.spell:GetName()) - actorTable.item:UsableIf(actorTable.usableFunc):Use(actorTable.target) - end ]] end if actorTable.type == "action" then ---@cast actorTable APLActorActionTable @@ -168,7 +163,7 @@ end -- tostring ---@return string function APLActor:__tostring() - return "Bastion.__APLActor" + return string.format("Bastion.__APLActor(%s)", self.name) end -- APL (Attack priority list) class @@ -176,6 +171,7 @@ end ---@field apl APLActor[] ---@field variables table ---@field name string +---@field last { successful: { name: string, time: number, index: number }, attempted: { name: string, time: number, index: number } } local APL = {} APL.__index = APL @@ -188,6 +184,7 @@ function APL:New(name) self.apl = {} self.variables = {} self.name = name + self.last = {} return self end @@ -282,7 +279,7 @@ end -- Add an item to the APL ---@param item Item ----@param condition? string +---@param condition? string | fun(self: Item): boolean ---@return APLActor function APL:AddItem(item, condition) local usableFunc = item.UsableIfFunc @@ -324,17 +321,19 @@ end -- Execute the APL function APL:Execute() - for _, actor in ipairs(self.apl) do - if actor:HasTraits() then - if actor:Evaluate() and actor:Execute() then - return true - --break - end - else - if actor:Execute() then - return true - --break - end + for i, actor in ipairs(self.apl) do + self.last.attempted = { + name = actor.name, + time = GetTime(), + index = i, + } + if (not actor:HasTraits() or actor:Evaluate()) and actor:Execute() then + self.last.successful = { + name = actor.name, + time = GetTime(), + index = i, + } + return true end end end diff --git a/src/Aura/Aura.lua b/src/Aura/Aura.lua index fe39c1b..16b502d 100644 --- a/src/Aura/Aura.lua +++ b/src/Aura/Aura.lua @@ -266,7 +266,7 @@ function Aura:GetRemainingTime() end function Aura:GetElapsedPercent() - return ((self:GetDuration() - self:GetRemainingTime()) / self:GetDuration()) * 100 + return self:IsValid() and ((self:GetDuration() - self:GetRemainingTime()) / self:GetDuration()) * 100 or 0 end -- Get the auras expiration time @@ -350,11 +350,7 @@ end -- Check if the aura is dispelable by a spell ---@param spell Spell function Aura:IsDispelableBySpell(spell) - if - (self:GetDispelType() == "" or self:GetDispelType() == nil) - and self:GetIsStealable() - and spell:IsEnrageDispel() - then + if (self:GetDispelType() == "" or self:GetDispelType() == nil) and self:GetIsStealable() and spell:IsEnrageDispel() then return true end diff --git a/src/AuraTable/AuraTable.lua b/src/AuraTable/AuraTable.lua index 401d2f7..dec481b 100644 --- a/src/AuraTable/AuraTable.lua +++ b/src/AuraTable/AuraTable.lua @@ -104,7 +104,7 @@ function AuraTable:AddOrUpdateAuraInstanceID(instanceID, aura) self.instanceIDLookup[instanceID] = spellId - if Bastion.UnitManager["player"]:IsUnit(aura:GetSource()) then + if Bastion.UnitManager["player"] and Bastion.UnitManager["player"]:IsUnit(aura:GetSource()) then if not self.playerAuras[spellId] then self.playerAuras[spellId] = {} end diff --git a/src/Cacheable/Cacheable.lua b/src/Cacheable/Cacheable.lua index 1430651..4d8078d 100644 --- a/src/Cacheable/Cacheable.lua +++ b/src/Cacheable/Cacheable.lua @@ -40,8 +40,10 @@ function Cacheable:__tostring() end -- Create ----@param value any ----@param cb fun():any +---@generic V: Cacheable, V +---@param value V +---@param cb fun(): V +---@return V function Cacheable:New(value, cb) local self = setmetatable({}, Cacheable) diff --git a/src/EventManager/EventManager.lua b/src/EventManager/EventManager.lua index f1f64ce..8c0a62e 100644 --- a/src/EventManager/EventManager.lua +++ b/src/EventManager/EventManager.lua @@ -7,11 +7,14 @@ local Tinkr, Bastion = ... ---@field events table ---@field eventHandlers table ---@field wowEventHandlers table ----@field selfCLEUHandlers table +---@field selfCombatEventHandlers table +---@field CombatEventHandlers table local EventManager = { events = {}, eventHandlers = {}, wowEventHandlers = {}, + selfCombatEventHandlers = {}, + CombatEventHandlers = {}, frame = nil, } EventManager.__index = EventManager @@ -23,7 +26,8 @@ function EventManager:New() self.events = {} self.eventHandlers = {} self.wowEventHandlers = {} - self.selfCLEUHandlers = {} + self.selfCombatEventHandlers = {} + self.CombatEventHandlers = {} -- Frame for wow events self.frame = CreateFrame("Frame") @@ -93,21 +97,31 @@ end ---@param subevent string | string[] ---@param handler fun(...) -function EventManager:RegisterSelfCLEUEvent(subevent, handler) - if type(subevent) == "table" then - for _, e in ipairs(subevent) do - if not self.selfCLEUHandlers[e] then - self.selfCLEUHandlers[e] = {} - end - - table.insert(self.selfCLEUHandlers[e], handler) +function EventManager:RegisterSelfCombatEvent(subevent, handler) + if type(subevent) == "string" then + subevent = { subevent } + end + for _, e in ipairs(subevent) do + if not self.selfCombatEventHandlers[e] then + self.selfCombatEventHandlers[e] = {} end - else - if not self.selfCLEUHandlers[subevent] then - self.selfCLEUHandlers[subevent] = {} + + table.insert(self.selfCombatEventHandlers[e], handler) + end +end + +---@param subevent string | string[] +---@param handler fun(...) +function EventManager:RegisterCombatEvent(subevent, handler) + if type(subevent) == "string" then + subevent = { subevent } + end + for _, e in ipairs(subevent) do + if not self.CombatEventHandlers[e] then + self.CombatEventHandlers[e] = {} end - table.insert(self.selfCLEUHandlers[subevent], handler) + table.insert(self.CombatEventHandlers[e], handler) end end @@ -116,11 +130,14 @@ end ---@param subevent string ---@param ... any function EventManager:CLEUHandler(event, timestamp, subevent, ...) - if self.selfCLEUHandlers[subevent] then - if select(2, ...) == UnitGUID("player") then - for _, handler in pairs(self.selfCLEUHandlers[subevent]) do - handler(timestamp, subevent, ...) - end + if self.selfCombatEventHandlers[subevent] and select(2, ...) == UnitGUID("player") then + for _, handler in pairs(self.selfCombatEventHandlers[subevent]) do + handler(timestamp, subevent, ...) + end + end + if self.CombatEventHandlers[subevent] then + for _, handler in pairs(self.CombatEventHandlers[subevent]) do + handler(timestamp, subevent, ...) end end end diff --git a/src/List/List.lua b/src/List/List.lua index ac73f8d..eeb023c 100644 --- a/src/List/List.lua +++ b/src/List/List.lua @@ -1,4 +1,6 @@ ---@class List +---@operator add(any): List +---@operator sub(any): List local List = { -- Add overload ---@param self List @@ -54,7 +56,8 @@ function List:clear() self._list = {} end ----@param value any +---@generic I : any +---@param value I ---@return boolean function List:contains(value) for _, v in ipairs(self._list) do @@ -65,7 +68,8 @@ function List:contains(value) return false end ----@param value any +---@generic I : any +---@param value I ---@return boolean function List:remove(value) for i, v in ipairs(self._list) do @@ -77,8 +81,8 @@ function List:remove(value) return false end ----@param callback fun(value: any): boolean ----@return nil +---@generic I : any +---@param callback fun(value: I): boolean function List:each(callback) for _, v in ipairs(self._list) do if callback(v) then @@ -87,7 +91,8 @@ function List:each(callback) end end ----@param callback fun(value: any): boolean +---@generic I : any +---@param callback fun(value: I): boolean ---@return List function List:map(callback) local newList = List:New() @@ -97,7 +102,8 @@ function List:map(callback) return newList end ----@param callback fun(value: any): boolean +---@generic I : any +---@param callback fun(value: I): boolean ---@return List function List:filter(callback) local newList = List:New() @@ -109,10 +115,11 @@ function List:filter(callback) return newList end ----@generic I ----@param callback fun(result: I, value: I): I, boolean? ----@param initialValue I ----@return I +---@generic R : any +---@generic V : any +---@param callback fun(result: R, value: V): R, boolean? +---@param initialValue R +---@return R function List:reduce(callback, initialValue) local result = initialValue local done = false @@ -125,7 +132,8 @@ function List:reduce(callback, initialValue) return result end ----@param callback fun(value: any): boolean +---@generic I +---@param callback fun(value: I): boolean ---@return boolean | nil function List:find(callback) for _, v in ipairs(self._list) do diff --git a/src/ObjectManager/ObjectManager.lua b/src/ObjectManager/ObjectManager.lua index 2544119..3984a74 100644 --- a/src/ObjectManager/ObjectManager.lua +++ b/src/ObjectManager/ObjectManager.lua @@ -7,6 +7,7 @@ local Tinkr, Bastion = ... ---@field friends List ---@field activeEnemies List ---@field explosives List +---@field incorporeal List local ObjectManager = {} ObjectManager.__index = ObjectManager @@ -19,6 +20,7 @@ function ObjectManager:New() self.friends = Bastion.List:New() self.activeEnemies = Bastion.List:New() self.explosives = Bastion.List:New() + self.incorporeal = Bastion.List:New() return self end @@ -74,6 +76,7 @@ function ObjectManager:Refresh() self.friends:clear() self.activeEnemies:clear() self.explosives:clear() + self.incorporeal:clear() self:ResetLists() local objects = Objects() @@ -88,13 +91,14 @@ function ObjectManager:Refresh() Bastion.UnitManager:SetObject(unit) end - if unit:GetID() == 120651 then + if unit:GetID() == 204560 then + self.incorporeal:push(unit) + elseif unit:GetID() == 120651 then self.explosives:push(unit) elseif unit:IsPlayer() and (unit:IsInParty() or unit == Bastion.UnitManager["player"]) then self.friends:push(unit) elseif unit:IsEnemy() then self.enemies:push(unit) - if unit:InCombatOdds() > 80 then self.activeEnemies:push(unit) end diff --git a/src/Refreshable/Refreshable.lua b/src/Refreshable/Refreshable.lua index 80d434b..8a53698 100644 --- a/src/Refreshable/Refreshable.lua +++ b/src/Refreshable/Refreshable.lua @@ -29,9 +29,10 @@ function Refreshable:__tostring() end -- Create ----@param value any +---@generic V +---@param value V ---@param cb function ----@return Refreshable +---@return V function Refreshable:New(value, cb) local self = setmetatable({}, Refreshable) diff --git a/src/Spell/Spell.lua b/src/Spell/Spell.lua index 8a52048..1c4e773 100644 --- a/src/Spell/Spell.lua +++ b/src/Spell/Spell.lua @@ -71,6 +71,10 @@ function Spell:New(id) return self end +function Spell:GetDescription() + return GetSpellDescription(self:GetID()) or "" +end + -- Duplicator ---@return Spell function Spell:Fresh() @@ -78,9 +82,10 @@ function Spell:Fresh() end -- Get the spells id +---@param ignoreOverride? boolean ---@return number -function Spell:GetID() - return self:IsOverridden() and self:OverrideSpellID() or self.spellID +function Spell:GetID(ignoreOverride) + return ignoreOverride and self.spellID or self:IsOverridden() and self:OverrideSpellID() or self.spellID end -- Add post cast func @@ -258,7 +263,7 @@ function Spell:GetPostCastFunction() end function Spell:OverrideSpellID() - return FindSpellOverrideByID(self.spellID) + return C_SpellBook.GetOverrideSpell(self.spellID) end function Spell:IsOverridden() @@ -266,9 +271,11 @@ function Spell:IsOverridden() end -- Check if the spell is known +---@param includeOverrides? boolean ---@return boolean -function Spell:IsKnown() - local isKnown = IsSpellKnownOrOverridesKnown(self:GetID()) +function Spell:IsKnown(includeOverrides) + includeOverrides = includeOverrides ~= nil and includeOverrides or true + local isKnown = includeOverrides and IsSpellKnownOrOverridesKnown(self:GetID()) or IsSpellKnown(self:GetID()) local isPlayerSpell = IsPlayerSpell(self:GetID()) return isKnown or isPlayerSpell end @@ -601,4 +608,10 @@ function Spell:Damage() end end +---@param unit Unit +---@param source? Unit +function Spell:GetAura(unit, source) + return source and unit:GetAuras():FindFrom(self, source) or unit:GetAuras():FindAny(self) +end + return Spell diff --git a/src/Unit/Unit.lua b/src/Unit/Unit.lua index f74cf1b..d1cd614 100644 --- a/src/Unit/Unit.lua +++ b/src/Unit/Unit.lua @@ -60,7 +60,7 @@ function Unit:__tostring() end -- Constructor ----@param unit TinkrObjectReference +---@param unit? TinkrObjectReference ---@return Unit function Unit:New(unit) local self = setmetatable({}, Unit) @@ -214,6 +214,17 @@ function Unit:IsPet() return UnitIsUnit(self:GetOMToken(), "pet") end +function Unit:IsOtherPet() + local petName = self:GetName() + local ownerName = "" + ownerName = ( + string.match(petName, string.gsub(UNITNAME_TITLE_PET, "%%s", "(%.*)")) + or string.match(petName, string.gsub(UNITNAME_TITLE_MINION, "%%s", "(%.*)")) + or string.match(petName, string.gsub(UNITNAME_TITLE_GUARDIAN, "%%s", "(%.*)")) + ) + return ownerName ~= nil +end + -- Is the unit a friendly unit ---@return boolean function Unit:IsFriendly() @@ -300,6 +311,29 @@ function Unit:IsDamage() return UnitGroupRolesAssigned(self:GetOMToken()) == "DAMAGER" end +-- Get the units role +---@return "TANK" | "HEALER" | "DAMAGER" +function Unit:GetRole() + return UnitGroupRolesAssigned(self:GetOMToken()) +end + +function Unit:GetSpecializationID() + if CanInspect(self:GetOMToken(), false) then + return ObjectSpecializationID(self:GetOMToken()) + end + return false +end + +---@param fallback? boolean +---@return "TANK" | "HEALER" | "DAMAGER" | false +function Unit:GetSpecializationRole(fallback) + local specID = self:GetSpecializationID() + if specID then + return GetSpecializationRoleByID(specID) + end + return fallback and self:GetRole() or false +end + -- Is the unit a player ---@return boolean function Unit:IsPlayer() @@ -344,6 +378,109 @@ local isClassicWow = select(4, GetBuildInfo()) < 40000 -- return UnitInMelee(self:GetOMToken(), unit:GetOMToken()) -- end +-- 1048593 + +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 @@ -352,45 +489,49 @@ local isClassicWow = select(4, GetBuildInfo()) < 40000 Terrain = 0x100 WaterWalkableLiquid = 0x10000 Liquid = 0x20000 + WaterOrLiquid = 0x30000, EntityCollision = 0x100000 - Unknown = 0x200000 + Most = 0x100201, + Unknown = 0x200000, + All = 0x310003, ]] -local losFlag = bit.bor(0x1, 0x10, 0x100000) - --- Check if the unit can see another unit ----@param unit Unit ----@return boolean -function Unit:CanSee(unit) - local ax, ay, az = ObjectPosition(self:GetOMToken()) - local ah = ObjectHeight(self:GetOMToken()) - local attx, atty, attz = GetUnitAttachmentPosition(unit:GetOMToken(), 18) +local attachmentOverride = { + [131863] = AttachmentPoisitions.Head, -- WCM Raal the Gluttonous +} - local alwaysLos = { - [189727] = true, -- Khajin the Unyielding - } +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 - --if alwaysLos[unit:GetID()] then - --return true - --end +function Unit:GetLOSSourcePosition() + local src = self:GetPosition() + src.z = UnitIsMounted(self:GetOMToken()) and 3.081099 or 2.43808 + return src +end - if not attx or not ax then - return false +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 - if not ah then - return false - end + local src = self:GetLOSSourcePosition() + local dst = targetUnit:GetHitSphere() - if (ax == 0 and ay == 0 and az == 0) or (attx == 0 and atty == 0 and attz == 0) then + 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 - if not attx or not ax then - return false - end + local contactPoint = src + (dst - src):directionOrZero() * math.min(targetUnit:GetDistance(self), self:GetCombatReach()) - local x, y, z = TraceLine(ax, ay, az + ah, attx, atty, attz, losFlag) + 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 @@ -405,12 +546,10 @@ function Unit:IsCasting() end function Unit:GetTimeCastIsAt(percent) - local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = - UnitCastingInfo(self:GetOMToken()) + local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self:GetOMToken()) if not name then - name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = - UnitChannelInfo(self:GetOMToken()) + name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self:GetOMToken()) end if name and startTimeMS and endTimeMS then @@ -427,12 +566,10 @@ end -- Get Casting or channeling spell ---@return Spell | nil function Unit:GetCastingOrChannelingSpell() - local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = - UnitCastingInfo(self:GetOMToken()) + local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self:GetOMToken()) if not name then - name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = - UnitChannelInfo(self:GetOMToken()) + name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self:GetOMToken()) end if name then @@ -445,12 +582,10 @@ end -- Get the end time of the cast or channel ---@return number function Unit:GetCastingOrChannelingEndTime() - local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = - UnitCastingInfo(self:GetOMToken()) + local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self:GetOMToken()) if not name then - name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = - UnitChannelInfo(self:GetOMToken()) + name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self:GetOMToken()) end if name then @@ -472,6 +607,15 @@ function Unit:IsCastingOrChanneling() return self:IsCasting() or self:IsChanneling() end +function Unit:CastTarget() + return self:IsCastingOrChanneling() and Bastion.UnitManager:Get(ObjectCastingTarget(self:GetOMToken())) +end + +---@param unit Unit +function Unit:CastTargetIsUnit(unit) + return self:IsCastingOrChanneling() and self:CastTarget():IsUnit(unit) +end + function Unit:IsImmobilized() return bit.band(self:GetMovementFlag(), 0x400) > 0 end @@ -485,12 +629,10 @@ end ---@return number function Unit:GetChannelOrCastPercentComplete() - local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = - UnitCastingInfo(self:GetOMToken()) + local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self:GetOMToken()) if not name then - name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = - UnitChannelInfo(self:GetOMToken()) + name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self:GetOMToken()) end if name and startTimeMS and endTimeMS then @@ -506,12 +648,10 @@ end -- Check if unit is interruptible ---@return boolean function Unit:IsInterruptible() - local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = - UnitCastingInfo(self:GetOMToken()) + local name, text, texture, startTimeMS, endTimeMS, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo(self:GetOMToken()) if not name then - name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = - UnitChannelInfo(self:GetOMToken()) + name, text, texture, startTimeMS, endTimeMS, isTradeSkill, notInterruptible, spellId = UnitChannelInfo(self:GetOMToken()) end if name then @@ -552,13 +692,7 @@ function Unit:GetEnemies(range) local count = 0 Bastion.UnitManager:EnumEnemies(function(unit) - if - not self:IsUnit(unit) - and self:IsWithinCombatDistance(unit, range) - and unit:IsAlive() - and self:CanSee(unit) - and unit:IsEnemy() - then + if not self:IsUnit(unit) and self:IsWithinCombatDistance(unit, range) and unit:IsAlive() and self:CanSee(unit) and unit:IsEnemy() then count = count + 1 end return false @@ -569,8 +703,9 @@ function Unit:GetEnemies(range) end -- Get the number of melee attackers +---@param facing? boolean ---@return number -function Unit:GetMeleeAttackers() +function Unit:GetMeleeAttackers(facing) local enemies = self.cache:Get("melee_attackers") if enemies then return enemies @@ -579,7 +714,7 @@ function Unit:GetMeleeAttackers() local count = 0 Bastion.UnitManager:EnumEnemies(function(unit) - if not self:IsUnit(unit) and unit:IsAlive() and self:CanSee(unit) and self:InMelee(unit) and unit:IsEnemy() then + if not self:IsUnit(unit) and unit:IsAlive() and self:CanSee(unit) and self:InMelee(unit) and unit:IsEnemy() and (not facing or self:IsFacing(unit)) then count = count + 1 end return false @@ -596,13 +731,7 @@ function Unit:GetPartyHPAround(distance, percent) local count = 0 Bastion.UnitManager:EnumFriends(function(unit) - if - not self:IsUnit(unit) - and unit:GetDistance(self) <= distance - and unit:IsAlive() - and self:CanSee(unit) - and unit:GetHP() <= percent - then + if not self:IsUnit(unit) and unit:GetDistance(self) <= distance and unit:IsAlive() and self:CanSee(unit) and unit:GetHP() <= percent then count = count + 1 end return false @@ -668,8 +797,7 @@ end ---@param unit Unit ---@return boolean function Unit:IsTanking(unit) - local isTanking, status, threatpct, rawthreatpct, threatvalue = - UnitDetailedThreatSituation(self:GetOMToken(), unit:GetOMToken()) + local isTanking, status, threatpct, rawthreatpct, threatvalue = UnitDetailedThreatSituation(self:GetOMToken(), unit:GetOMToken()) return isTanking end @@ -742,6 +870,10 @@ function Unit:GetMeleeBoost() return 0 end +function Unit:GetModelId() + return ObjectModelId(self:GetOMToken()) +end + -- Melee calculation -- float fMaxDist = fmaxf((float)(*(float*)((uintptr_t)this + 0x1BF8) + 1.3333) + *(float*)((uintptr_t)target + 0x1BF8), 5.0); -- fMaxDist = fMaxDist + 1.0; @@ -790,6 +922,16 @@ function Unit:IsInParty() return UnitInParty(self:GetOMToken()) end +-- In party +---@return boolean +function Unit:IsInRaid() + return UnitInRaid(self:GetOMToken()) ~= nil +end + +function Unit:IsInPartyOrRaid() + return self:IsInParty() or self:IsInRaid() +end + -- Linear regression between time and percent to something ---@param time table ---@param percent table @@ -1110,9 +1252,9 @@ function Unit:GetStaggeredHealth() end -- get the units combat reach ----@return number | false +---@return number function Unit:GetCombatReach() - return ObjectCombatReach(self:GetOMToken()) + return ObjectCombatReach(self:GetOMToken()) or 0 end -- Get the units combat distance (distance - combat reach (realized distance)) @@ -1220,8 +1362,7 @@ function Unit:HasTarget() return ObjectTarget(self:GetOMToken()) ~= false end function Unit:Target() - return self:HasTarget() and Bastion.UnitManager:Get(ObjectTarget(self:GetOMToken()):unit()) - or Bastion.UnitManager:Get("none") + return self:HasTarget() and Bastion.UnitManager:Get(ObjectTarget(self:GetOMToken()):unit()) or Bastion.UnitManager:Get("none") end -- local empowering = {} diff --git a/src/UnitManager/UnitManager.lua b/src/UnitManager/UnitManager.lua index f31ae03..a4b6d4b 100644 --- a/src/UnitManager/UnitManager.lua +++ b/src/UnitManager/UnitManager.lua @@ -5,8 +5,14 @@ local ObjectManager = Tinkr.Util.ObjectManager local Unit = Bastion.Unit +---@class CacheableUnit : Cacheable, Unit + -- Create a new UnitManager class ----@class UnitManager +---@class UnitManager : { [UnitId]: Unit } +---@field units table +---@field customUnits table +---@field objects table +---@field cache Cache local UnitManager = { units = {}, customUnits = {}, @@ -14,6 +20,7 @@ local UnitManager = { cache = {}, } +---@param k UnitId function UnitManager:__index(k) if k == "none" then return self:Get("none") @@ -48,7 +55,7 @@ function UnitManager:__index(k) if self.objects[kguid] == nil then local o = Object(k) if o then - local unit = Unit:New(Object(k)) + local unit = Unit:New(o) self:SetObject(unit) end end @@ -59,6 +66,7 @@ end -- Constructor ---@return UnitManager function UnitManager:New() + ---@class UnitManager local self = setmetatable({}, UnitManager) self.units = {} self.customUnits = {} @@ -67,7 +75,7 @@ function UnitManager:New() end -- Get or create a unit ----@param token string +---@param token UnitId ---@return Unit function UnitManager:Get(token) -- if not Validate(token) then @@ -107,17 +115,17 @@ end -- Set a unit by guid ---@param unit Unit ----@return nil function UnitManager:SetObject(unit) self.objects[unit:GetGUID()] = unit end -- Create a custom unit and cache it for .5 seconds ---@param token string ----@param cb fun():Unit ----@return Unit +---@param cb fun(): Unit +---@return CacheableUnit function UnitManager:CreateCustomUnit(token, cb) local unit = cb() + ---@type CacheableUnit local cachedUnit = Bastion.Cacheable:New(unit, cb) if unit == nil then @@ -144,6 +152,7 @@ function UnitManager:EnumFriends(cb) if cb(unit) then return true end + return false end) end @@ -155,6 +164,7 @@ function UnitManager:EnumEnemies(cb) if cb(unit) then return true end + return false end) end @@ -166,12 +176,25 @@ function UnitManager:EnumUnits(cb) if cb(unit) then return true end + return false + end) +end + +-- Enum Incorporeal (object manager) +---@param cb fun(unit: Unit):boolean +---@return nil +function UnitManager:EnumIncorporeal(cb) + Bastion.ObjectManager.incorporeal:each(function(unit) + if cb(unit) then + return true + end + return false end) end -- Get the number of enemies with a debuff ---@param spell Spell ----@param range number +---@param range? number ---@return number function UnitManager:GetNumEnemiesWithDebuff(spell, range) local count = 0 @@ -216,8 +239,8 @@ end -- Get the friend with the most friends within a given radius (party/raid members) ---@param radius number ----@return Unit ----@return table +---@return Unit | nil +---@return Unit[] function UnitManager:GetFriendWithMostFriends(radius) local unit = nil local count = 0 diff --git a/src/Vector3/Vector3.lua b/src/Vector3/Vector3.lua index 433cb95..c1d920d 100644 --- a/src/Vector3/Vector3.lua +++ b/src/Vector3/Vector3.lua @@ -1,7 +1,12 @@ -- Create a Vector3 class ---@class Vector3 -local Vector3 = { } +---@operator add(Vector3): Vector3 +---@operator sub(Vector3): Vector3 +---@operator mul(Vector3): Vector3 +---@operator div(Vector3): Vector3 +---@operator unm(): Vector3 +local Vector3 = {} Vector3.__index = Vector3 ---@return string @@ -242,13 +247,15 @@ function Vector3:Distance(b) return FastDistance(self.x, self.y, self.z, b.x, b.y, b.z) end +---@param to Vector3 +function Vector3:GetAbsoluteAngle(to) + return self:NormalizeOrientation(math.atan2(to.y - self.y, to.x - self.x)) +end + ---@param to Vector3 ---@return number function Vector3:Angle(to) - return math.acos(self:Dot(to) / - ( - math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) * - math.sqrt(to.x * to.x + to.y * to.y + to.z * to.z))) + return math.acos(self:Dot(to) / (math.sqrt(self.x * self.x + self.y * self.y + self.z * self.z) * math.sqrt(to.x * to.x + to.y * to.y + to.z * to.z))) end ---@param maxLength number @@ -261,6 +268,18 @@ function Vector3:ClampMagnitude(maxLength) return self end +---@return Vector3 +function Vector3:directionOrZero() + local mag = self.magnitude + if mag < 0.0000001 then + return self.zero + elseif mag < 1.00001 and mag > 0.99999 then + return self + else + return self * (1.0 / mag) + end +end + -- Implement a clamp function ---@param x number ---@param min number @@ -330,4 +349,15 @@ function Vector3:Normalize() return Vector3:New(0, 0, 0) end +---@param o number +function Vector3:NormalizeOrientation(o) + if o < 0 then + mod = o * -1 + mod = math.fmod(mod, 2.0 * math.pi) + mod = -mod + 2.0 * math.pi + return mod + end + return math.fmod(o, 2.0 * math.pi) +end + return Vector3 diff --git a/src/_bastion.lua b/src/_bastion.lua index 32a7d6a..aee1228 100644 --- a/src/_bastion.lua +++ b/src/_bastion.lua @@ -183,8 +183,11 @@ Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", fun local args = { CombatLogGetCurrentEventInfo() } local subEvent = args[2] + ---@type string local sourceGUID = args[4] + ---@type string local destGUID = args[8] + ---@type number local spellID = args[12] -- if sourceGUID == pguid then @@ -253,8 +256,11 @@ function Bastion:FindModule(name) return nil end - +Bastion.PrintEnabled = false function Bastion:Print(...) + if not Bastion.PrintEnabled then + return + end local args = { ... } local str = "|cFFDF362D[Bastion]|r |cFFFFFFFF" for i = 1, #args do diff --git a/stylua.toml b/stylua.toml new file mode 100644 index 0000000..e7eaa6f --- /dev/null +++ b/stylua.toml @@ -0,0 +1,10 @@ +column_width = 180 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 4 +quote_style = "AutoPreferDouble" +call_parentheses = "Always" +collapse_simple_statement = "Never" + +[sort_requires] +enabled = false \ No newline at end of file