Type Updates, Spell optimizations, New (HeroLib) TTD calc

main
ck 1 year ago
parent 4d0719b869
commit 09a2fd2e63
  1. 24
      src/Aura/Aura.lua
  2. 18
      src/AuraTable/AuraTable.lua
  3. 11
      src/Cacheable/Cacheable.lua
  4. 3
      src/ItemBook/ItemBook.lua
  5. 2
      src/Refreshable/Refreshable.lua
  6. 106
      src/Spell/Spell.lua
  7. 29
      src/SpellBook/SpellBook.lua
  8. 27
      src/TimeToDie/TimeToDie.lua
  9. 57
      src/Unit/Unit.lua
  10. 13
      src/UnitManager/UnitManager.lua
  11. 12
      src/_bastion.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)
@ -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,

@ -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<number, table<number, Aura>>
---@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

@ -1,10 +1,9 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
-- Define a Cacheable class
---@class Cacheable<V>: { value: V, cache: Cache, callback?: fun(): V }
---@class Cacheable<V>
---@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

@ -1,8 +1,11 @@
---@type Tinkr, Bastion
local Tinkr, Bastion = ...
---@alias itemId integer
-- Create a new ItemBook class
---@class ItemBook
---@field items table<itemId, Item>
local ItemBook = {}
ItemBook.__index = ItemBook

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

@ -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
function Spell:GetAura(unit, source)
return source and unit:GetAuras():FindFrom(self, source) or unit:GetAuras():FindAny(self)
---@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
---@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

@ -4,6 +4,7 @@ local Tinkr, Bastion = ...
-- Create a new SpellBook class
---@class SpellBook
---@field spells table<number, Spell>
---@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

@ -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<string, {[1]: {[1]: number[], [2]: number}, [2]: number }>
---@type table<string, {[1]: {[1]: {[1]: number, [2]: number}, [2]: number}, [2]: number }>
Units = {}, -- Used to track units,
---@type table<string, boolean>
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.

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

@ -5,12 +5,14 @@ local ObjectManager = Tinkr.Util.ObjectManager
local Unit = Bastion.Unit
---@class CacheableUnit<U> : Cacheable<Unit>
---@class UnitManager.CustomUnit
---@field unit Cacheable | Unit
---@field cb fun(): Unit
-- Create a new UnitManager class
---@class UnitManager : { [UnitId]: Unit }
---@field units table<string, Unit>
---@field customUnits table<string, { unit: Cacheable<Unit>, cb: fun(unit: Unit): Unit }>
---@field customUnits table<string, UnitManager.CustomUnit>
---@field objects table<string, Unit>
---@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

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

Loading…
Cancel
Save