From 09a2fd2e63254e15001022ea6ff895b88f99615e Mon Sep 17 00:00:00 2001 From: ck Date: Thu, 11 Jan 2024 21:19:21 -0600 Subject: [PATCH] Type Updates, Spell optimizations, New (HeroLib) TTD calc --- src/Aura/Aura.lua | 28 ++++++++- src/AuraTable/AuraTable.lua | 18 ++++-- src/Cacheable/Cacheable.lua | 11 ++-- src/ItemBook/ItemBook.lua | 3 + src/Refreshable/Refreshable.lua | 2 +- src/Spell/Spell.lua | 106 +++++++++++++++++++++++++++----- src/SpellBook/SpellBook.lua | 29 +++++++-- src/TimeToDie/TimeToDie.lua | 27 ++++---- src/Unit/Unit.lua | 57 +++++++++-------- src/UnitManager/UnitManager.lua | 13 ++-- src/_bastion.lua | 12 +++- 11 files changed, 227 insertions(+), 79 deletions(-) diff --git a/src/Aura/Aura.lua b/src/Aura/Aura.lua index 2a71e3f..f4ee9a4 100644 --- a/src/Aura/Aura.lua +++ b/src/Aura/Aura.lua @@ -2,8 +2,30 @@ ---@type Tinkr, Bastion local Tinkr, Bastion = ... +---@class AuraAuraData +---@field auraInstanceID? number +---@field name? string +---@field icon? number +---@field count number +---@field dispelType? string +---@field duration number +---@field expirationTime number +---@field source? string +---@field isStealable boolean +---@field nameplateShowPersonal boolean +---@field spellId number +---@field canApplyAura boolean +---@field isBossDebuff boolean +---@field castByPlayer boolean +---@field nameplateShowAll boolean +---@field timeMod number +---@field points table +---@field index? number +---@field type? string + -- Create a new Aura class ---@class Aura +---@field aura AuraAuraData local Aura = {} function Aura:__index(k) @@ -116,8 +138,8 @@ end local foodAndDrinkStrings = { [5] = "Refreshment", [1] = MINIMAP_TRACKING_VENDOR_FOOD, -- Food & Drink - [2] = POWER_TYPE_FOOD, -- Food - [4] = TUTORIAL_TITLE12, -- Drink + [2] = POWER_TYPE_FOOD, -- Food + [4] = TUTORIAL_TITLE12, -- Drink } ---@type { [number]: boolean } @@ -155,7 +177,7 @@ function Aura:CreateFromUnitAuraInfo(unitAuraInfo) maxCharges ]] -- - + --print(unitAuraInfo.name, unitAuraInfo.sourceUnit, unitAuraInfo.expirationTime, unitAuraInfo.duration) self.aura = { auraInstanceID = unitAuraInfo.auraInstanceID, canApplyAura = unitAuraInfo.canApplyAura, diff --git a/src/AuraTable/AuraTable.lua b/src/AuraTable/AuraTable.lua index dec481b..26212f8 100644 --- a/src/AuraTable/AuraTable.lua +++ b/src/AuraTable/AuraTable.lua @@ -1,10 +1,16 @@ ---@type Tinkr, Bastion local Tinkr, Bastion = ... +---@alias auraInstanceId integer + +---@class AuraInstanceTable +---@field [spellId] { [auraInstanceId]: Aura } + -- Create a new AuraTable class ---@class AuraTable ---@field unit Unit ----@field playerAuras table> +---@field playerAuras AuraInstanceTable +---@field auras AuraInstanceTable local AuraTable = {} AuraTable.__index = AuraTable @@ -275,7 +281,7 @@ function AuraTable:Find(spell) local aurasub = auras[spell:GetID()] if not aurasub then - return Bastion.Aura:New() + return self:FindMy(spell) end for k, a in pairs(aurasub) do @@ -321,14 +327,18 @@ end -- Check if the unit has a specific aura ---@param spell Spell ----@param source Unit +---@param source "any" | Unit ---@return Aura function AuraTable:FindFrom(spell, source) + if type(source) == "string" or source == "any" then + return self:FindAny(spell) + end + local auras = self:GetUnitAuras() local aurasub = auras[spell:GetID()] if not aurasub then - return Bastion.Aura:New() + return self:FindMy(spell) end for k, a in pairs(aurasub) do diff --git a/src/Cacheable/Cacheable.lua b/src/Cacheable/Cacheable.lua index 1694e9f..07e6967 100644 --- a/src/Cacheable/Cacheable.lua +++ b/src/Cacheable/Cacheable.lua @@ -1,10 +1,9 @@ ---@type Tinkr, Bastion local Tinkr, Bastion = ... --- Define a Cacheable class ----@class Cacheable: { value: V, cache: Cache, callback?: fun(): V } ----@class Cacheable +---@class Cacheable +---@field cache? Cache local Cacheable = { cache = nil, callback = nil, @@ -33,15 +32,15 @@ function Cacheable:__index(k) end -- When the object is accessed return the value ----@return string function Cacheable:__tostring() return "Bastion.__Cacheable(" .. tostring(self.value) .. ")" end -- Create ----@generic V : Cacheable, V +---@generic V ---@param value V ---@param cb fun(): V +---@return V function Cacheable:New(value, cb) local self = setmetatable({}, Cacheable) @@ -55,7 +54,6 @@ function Cacheable:New(value, cb) end -- Try to update the value ----@return nil function Cacheable:TryUpdate() if self.cache:IsCached("value") then self.value = self.callback() @@ -63,7 +61,6 @@ function Cacheable:TryUpdate() end -- Update the value ----@return nil function Cacheable:Update() self.value = self.callback() end diff --git a/src/ItemBook/ItemBook.lua b/src/ItemBook/ItemBook.lua index c4f43d2..dcb7585 100644 --- a/src/ItemBook/ItemBook.lua +++ b/src/ItemBook/ItemBook.lua @@ -1,8 +1,11 @@ ---@type Tinkr, Bastion local Tinkr, Bastion = ... +---@alias itemId integer + -- Create a new ItemBook class ---@class ItemBook +---@field items table local ItemBook = {} ItemBook.__index = ItemBook diff --git a/src/Refreshable/Refreshable.lua b/src/Refreshable/Refreshable.lua index 8f26844..051791e 100644 --- a/src/Refreshable/Refreshable.lua +++ b/src/Refreshable/Refreshable.lua @@ -31,7 +31,7 @@ end -- Create ---@generic V ---@param value V ----@param cb function +---@param cb fun(): V ---@return V function Refreshable:New(value, cb) local self = setmetatable({}, Refreshable) diff --git a/src/Spell/Spell.lua b/src/Spell/Spell.lua index 7928afc..3407afe 100644 --- a/src/Spell/Spell.lua +++ b/src/Spell/Spell.lua @@ -12,6 +12,8 @@ local Tinkr, Bastion = ... ---@field lastCastAttempt number | false ---@field target Unit | false ---@field damageFormula false | fun(self:Spell):number +---@field overrides { [spellId]: fun(self: Spell): Spell } +---@field auras { [spellId]: { spell: Spell, source?: Unit, target?: Unit, lastApplied: number } } local Spell = { CastableIfFunc = false, PreCastFunc = false, @@ -21,8 +23,17 @@ local Spell = { wasLooking = false, lastCastAt = false, conditions = {}, - buffs = {}, - debuffs = {}, + auras = {}, + overrides = {}, + traits = { + cast = { + moving = true, + dead = false, + global = false, + casting = false, + channeling = false, + }, + }, target = false, release_at = false, damageFormula = false, @@ -88,6 +99,14 @@ 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 @@ -97,10 +116,9 @@ function Spell:PostCast(func) end -- Get the spells name ----@param byId? boolean ---@return string -function Spell:GetName(byId) - return select(1, GetSpellInfo((byId ~= nil and byId) and self:GetID() or self:GetID())) +function Spell:GetName() + return Bastion.Globals.SpellName[self:GetID()] or select(1, GetSpellInfo(self:GetID())) end -- Get the spells icon @@ -113,7 +131,7 @@ end ---@param byId? boolean ---@return number function Spell:GetCooldown(byId) - return select(2, GetSpellCooldown((byId ~= nil and byId) and self:GetID() or self:GetName(byId))) + return select(2, GetSpellCooldown(byId and self:GetID() or self:GetName())) end -- Return the castable function @@ -135,7 +153,7 @@ end ---@param byId? boolean ---@return number function Spell:GetCooldownRemaining(byId) - local start, duration = GetSpellCooldown((byId ~= nil and byId) and self:GetID() or self:GetName()) + local start, duration = GetSpellCooldown(byId and self:GetID() or self:GetName()) if start == 0 then return 0 end @@ -162,6 +180,12 @@ function Spell:ClearCastableFunction() return self end +---@param spell Spell +---@param func fun(self: Spell): boolean +function Spell:AddOverrideSpell(spell, func) + self.overrides[spell:GetID()] = func +end + -- Cast the spell ---@param unit Unit ---@param condition? string|fun(self:Spell):boolean @@ -261,10 +285,9 @@ end ---@param includeOverrides? boolean ---@return boolean 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 + local isKnown = includeOverrides and IsSpellKnownOrOverridesKnown(self:GetID()) or IsSpellKnown(self:GetID()) or + IsPlayerSpell(self:GetID()) + return isKnown end -- Check if the spell is on cooldown @@ -292,13 +315,27 @@ end ---@param override? boolean ---@return boolean function Spell:IsKnownAndUsable(override) - override = override ~= nil and override or false return self:IsKnown(override) and not self:IsOnCooldown() and self:IsUsable(override and false or true) 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()) + ) +end + -- Check if the spell is castable ---@return boolean function Spell:Castable() + if not self:EvaluateTraits() then + return true + end if self:GetCastableFunction() then return self:GetCastableFunction()(self) end @@ -615,10 +652,49 @@ function Spell:Damage() end end ----@param unit Unit +---@param target Unit +---@param source? "any" | Unit +function Spell:GetAura(target, source) + if type(source) == "nil" then + source = Bastion.UnitManager["player"] + end + + return target:GetAuras():FindFrom(self, source) +end + +---@param spell Spell +---@param source? Unit +---@param target? Unit +function Spell:TrackAura(spell, source, target) + self.auras[spell:GetID()] = { + spell = spell, + source = source, + target = target, + lastApplied = 0, + } +end + +---@param aura Spell +---@param source? Unit +---@param target? Unit +function Spell:CheckAuraStatus(aura, source, target) + for id, trackedAura in pairs(self.auras) do + if aura:GetID() == id then + return true + end + end + return false +end + +---@param spell Spell ---@param source? Unit -function Spell:GetAura(unit, source) - return source and unit:GetAuras():FindFrom(self, source) or unit:GetAuras():FindAny(self) +---@param target? Unit +function Spell:UpdateAura(spell, source, target) + if not self.auras[spell:GetID()] then + self:TrackAura(spell, source, target) + end + + self.auras[spell:GetID()].lastApplied = GetTime() end return Spell diff --git a/src/SpellBook/SpellBook.lua b/src/SpellBook/SpellBook.lua index ce70796..70c8cd9 100644 --- a/src/SpellBook/SpellBook.lua +++ b/src/SpellBook/SpellBook.lua @@ -4,6 +4,7 @@ local Tinkr, Bastion = ... -- Create a new SpellBook class ---@class SpellBook ---@field spells table +---@field auras { [spellId]: { [spellId]: { spell: Spell, source?: Unit, target?: Unit, lastApplied: number } } } local SpellBook = {} SpellBook.__index = SpellBook @@ -43,7 +44,7 @@ function SpellBook:GetSpell(id) return self.spells[id] end ----@param ... number[] +---@param ... integer ---@return Spell, ... Spell function SpellBook:GetSpells(...) local spells = {} @@ -54,7 +55,20 @@ function SpellBook:GetSpells(...) return unpack(spells) end ----@param ... number[] +---@param aura Spell +---@return Spell[] +function SpellBook:GetAuraSpells(aura) + local spells = {} + for _, spell in pairs(self.spells) do + if spell:CheckAuraStatus(aura) then + table.insert(spells, spell) + end + end + + return spells +end + +---@param ... integer ---@return List function SpellBook:GetList(...) local spells = {} @@ -73,9 +87,16 @@ function SpellBook:GetSpellByName(name) end ---@param id integer ----@return Spell function SpellBook:GetIfRegistered(id) - return self.spells[id] or self.spells[FindSpellOverrideByID(id)] + if self.spells[id] then + return self.spells[id] + end + + if self.spells[FindSpellOverrideByID(id)] then + return self.spells[FindSpellOverrideByID(id)] + end + + return false end return SpellBook diff --git a/src/TimeToDie/TimeToDie.lua b/src/TimeToDie/TimeToDie.lua index 8de1b8d..e247340 100644 --- a/src/TimeToDie/TimeToDie.lua +++ b/src/TimeToDie/TimeToDie.lua @@ -1,22 +1,27 @@ ---@type Tinkr, Bastion local Tinkr, Bastion = ... -local Cache = Bastion.Caches + local Player = Bastion.UnitManager:Get("player") local Target = Bastion.UnitManager:Get("target") +if not Bastion.Globals.UnitInfo then + Bastion.Globals.UnitInfo = Bastion.Cache:New() +end + +local Cache = Bastion.Globals.UnitInfo --- An attempt to integrate HeroLib TTD timers. ---@class TimeToDie local TimeToDie = { Settings = { -- Refresh time (seconds) : min=0.1, max=2, default = 0.1 - Refresh = 0.1, + Refresh = 0.5, -- History time (seconds) : min=5, max=120, default = 10+0.4 HistoryTime = 10 + 0.4, -- Max history count : min=20, max=500, default = 100 HistoryCount = 100 }, Cache = {}, -- A cache of unused { time, value } tables to reduce garbage due to table creation - ---@type table + ---@type table Units = {}, -- Used to track units, ---@type table ExistingUnits = {}, -- Used to track GUIDs of currently existing units (to be compared with tracked units) @@ -30,12 +35,12 @@ end function TimeToDie:Refresh() local currentTime = GetTime() - local historyCount = self.Settings.HistoryCount - local historyTime = self.Settings.HistoryTime - local ttdCache = self.Cache - local iterableUnits = self:IterableUnits() - local units = self.Units - local existingUnits = self.ExistingUnits + local historyCount = TimeToDie.Settings.HistoryCount + local historyTime = TimeToDie.Settings.HistoryTime + local ttdCache = TimeToDie.Cache + local iterableUnits = TimeToDie:IterableUnits() + local units = TimeToDie.Units + local existingUnits = TimeToDie.ExistingUnits wipe(existingUnits) @@ -72,7 +77,7 @@ function TimeToDie:Refresh() value[1] = time value[2] = healthPercentage end - tableinsert(values, 1, value) + table.insert(values, 1, value) local n = #values -- Delete values that are no longer valid while (n > historyCount) or (time - values[n][1] > historyTime) do @@ -268,7 +273,7 @@ function TimeToDie.FilteredFightRemains(enemies, operator, value, checkIfValid, return false end - return Utils.CompareThis(operator, fightRemains, value) or false + return Bastion.Utils.CompareThis(operator, fightRemains, value) or false end -- Returns if the current boss fight length meets the requirements, 11111 if not a boss fight. diff --git a/src/Unit/Unit.lua b/src/Unit/Unit.lua index 69cf442..55b7943 100644 --- a/src/Unit/Unit.lua +++ b/src/Unit/Unit.lua @@ -518,7 +518,7 @@ local losFlag = bit.bor(0x10) -- Check if the unit can see another unit ---@param targetUnit Unit ---@return boolean -function Unit:CanSee2(targetUnit) +function Unit:CanSee(targetUnit) local npcId = targetUnit:GetID() if npcId and losBlacklist[npcId] then return true @@ -554,22 +554,29 @@ function Unit:GetHitSpherePointFor(destination) return contactPoint end ----@param destinationUnit Unit -function Unit:CanSee(destinationUnit) - local src = self:GetPosition() - local dst = destinationUnit:GetPosition() - if ObjectType(self:GetOMToken()) == 6 then - src.z = src.z + ObjectHeight(self:GetOMToken()) - else - dst = self:GetHitSpherePointFor(destinationUnit) +---@param unit Unit +function Unit:CanSee2(unit) + local ax, ay, az = ObjectPosition(self:GetOMToken()) + local ah = ObjectHeight(self:GetOMToken()) + local attx, atty, attz = GetUnitAttachmentPosition(unit:GetOMToken(), 34) + + if not attx or not ax then + return false end - 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 + if not ah then + return false + end + + if (ax == 0 and ay == 0 and az == 0) or (attx == 0 and atty == 0 and attz == 0) then return true end - local x, y, z = TraceLine(src.x, src.y, src.z, dst.x, dst.y, dst.z, losFlag) + if not attx or not ax then + return false + end + local x, y, z = TraceLine(ax, ay, az + ah, attx, atty, attz, losFlag) if x ~= 0 or y ~= 0 or z ~= 0 then return false else @@ -904,19 +911,20 @@ end ---@return number function Unit:GetMeleeBoost() + local meleeBoost = 0 if IsPlayerSpell(197524) then - local astralNode = C_Traits.GetNodeInfo(C_ClassTalents.GetActiveConfigID() or 0, 82210) + local astralInfluenceNode = C_Traits.GetNodeInfo(C_ClassTalents.GetActiveConfigID() or 0, 82210) local currentSpec = select(1, GetSpecializationInfo(GetSpecialization())) - if astralNode then - local currentRank = astralNode.activeRank + if astralInfluenceNode then + local currentRank = astralInfluenceNode.activeRank if currentRank > 0 then - return ((currentSpec == 103 or currentSpec == 104) and 1 or 3) + (currentRank == 2 and 2 or 0) + meleeBoost = ((currentSpec == 103 or currentSpec == 104) and 1 or 3) + (currentRank == 2 and 2 or 0) end end elseif IsPlayerSpell(196924) then - return 3 + meleeBoost = 3 end - return 0 + return meleeBoost end function Unit:GetModelId() @@ -956,13 +964,12 @@ function Unit:InMelee(unit) end -- Get object id ----@return number function Unit:GetID() if self.id then - return self.id --[[ @as number ]] + return self.id end self.id = ObjectID(self:GetOMToken()) - return self.id --[[ @as number ]] + return self.id or 0 end -- In party @@ -1110,7 +1117,6 @@ end -- Set last combat time ---@param time number ----@return nil function Unit:SetLastCombatTime(time) self.last_combat_time = time end @@ -1524,7 +1530,7 @@ local dummyUnits = { function Unit:IsDummy() local npcId = self:GetID() - return npcId >= 0 and dummyUnits[npcId] == true + return npcId and npcId >= 0 and dummyUnits[npcId] == true or false end ---@param npcId? number @@ -1567,7 +1573,7 @@ end function Unit:TimeToX(percentage, minSamples) if self:IsDummy() then return 6666 end - if self:IsPlayer() and Player:CanAttack(self) then return 25 end + if self:IsPlayer() and Bastion.UnitManager:Get("player"):CanAttack(self) then return 25 end local seconds = 8888 local unitGuid = self:GetGUID() if not unitGuid then @@ -1624,7 +1630,8 @@ function Unit:TimeToDie2(minSamples) minSamples = minSamples or 3 ---@type {TTD: {[number]: number}} - local unitInfo = Cache.UnitInfo:IsCached(unitGuid) and Cache.UnitInfo:Get(unitGuid) or {} + local unitInfo = Bastion.Globals.UnitInfo:IsCached(unitGuid) and + Bastion.Globals.UnitInfo:Get(unitGuid) or {} local ttd = unitInfo.TTD if not ttd then @@ -1635,7 +1642,7 @@ function Unit:TimeToDie2(minSamples) ttd[minSamples] = self:TimeToX(self:SpecialTTDPercentage(self:GetID()), minSamples) end - Bastion.Caches.UnitInfo:Set(unitGuid, unitInfo, .5) + Bastion.Globals.UnitInfo:Set(unitGuid, unitInfo, .5) return ttd[minSamples] end diff --git a/src/UnitManager/UnitManager.lua b/src/UnitManager/UnitManager.lua index e971af0..d0a27df 100644 --- a/src/UnitManager/UnitManager.lua +++ b/src/UnitManager/UnitManager.lua @@ -5,12 +5,14 @@ local ObjectManager = Tinkr.Util.ObjectManager local Unit = Bastion.Unit ----@class CacheableUnit : Cacheable +---@class UnitManager.CustomUnit +---@field unit Cacheable | Unit +---@field cb fun(): Unit -- Create a new UnitManager class ---@class UnitManager : { [UnitId]: Unit } ---@field units table ----@field customUnits table, cb: fun(unit: Unit): Unit }> +---@field customUnits table ---@field objects table ---@field cache Cache local UnitManager = { @@ -107,7 +109,7 @@ function UnitManager:Get(token) end -- Get a unit by guid ----@param guid string +---@param guid string | WowGameObject ---@return Unit function UnitManager:GetObject(guid) return self.objects[guid] @@ -120,13 +122,12 @@ function UnitManager:SetObject(unit) end -- Create a custom unit and cache it for .5 seconds +---@generic V :Unit ---@param token string ----@param cb fun(unit: Unit?): Unit ----@return Unit +---@param cb fun(): Unit function UnitManager:CreateCustomUnit(token, cb) local unit = cb() local cachedUnit = Bastion.Cacheable:New(unit, cb) - if unit == nil then error("UnitManager:CreateCustomUnit - Invalid unit: " .. token) end diff --git a/src/_bastion.lua b/src/_bastion.lua index 2c25b1d..29fd481 100644 --- a/src/_bastion.lua +++ b/src/_bastion.lua @@ -127,9 +127,6 @@ Bastion.Notifications = Bastion.NotificationsList:New() Bastion.Config = Bastion.require("Config") Bastion.TimeToDie = Bastion.require("TimeToDie") -Bastion.Caches = { - UnitInfo = Bastion.Cache:New() -} ---@enum (key) CompareThisTable local compareThisTable = { @@ -183,6 +180,9 @@ end) local pguid = UnitGUID("player") local missed = {} +---@class Bastion.Globals.SpellName : { [spellId]: string } +Bastion.Globals.SpellName = {} + Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", function() local args = { CombatLogGetCurrentEventInfo() } @@ -193,6 +193,12 @@ Bastion.Globals.EventManager:RegisterWoWEvent("COMBAT_LOG_EVENT_UNFILTERED", fun local destGUID = args[8] ---@type number local spellID = args[12] + ---@type string + local spellName = args[13] + + if not Bastion.Globals.SpellName[spellID] or Bastion.Globals.SpellName[spellID] ~= spellName then + Bastion.Globals.SpellName[spellID] = spellName + end -- if sourceGUID == pguid then -- local args = { CombatLogGetCurrentEventInfo() }