mirror of
https://github.com/brues-code/pfUI.git
synced 2026-09-22 15:46:56 +00:00
2ef9c5ee1e
Problem:
Since a recent Turtle-WoW update, nameplates would show the same castbar on all mobs sharing a name (e.g. multiple "Defias Rogue Wizard"). Additionally, a targeted mob that was not casting would incorrectly display the castbar of a nearby mob with the same name.
Changes:
**`modules/nameplates.lua`**
- Removed `hasNampower` version-sniffing entirely. Nampower is a hard requirement for pfUI; all related guards have been removed and the code runs unconditionally.
- Castbar lookup now always uses `nameplate.cachedGuid` directly — never `GetUnitGUID("target")` or a unit name string.
- Removed dead fallback block using `UnitCastingInfo("target")`.
- Restored fallback via `pfGetCastInfo(cachedGuid)` / `pfGetChannelInfo(cachedGuid)` (see libcast changes below) for casts not tracked by libdebuff (e.g. already-in-progress casts on login).
**`libs/libcast.lua`**
- Renamed `UnitCastingInfo` → `pfGetCastInfo` and `UnitChannelInfo` → `pfGetChannelInfo` to avoid confusion with the Blizzard API of the same name, which does not exist in Vanilla.
- Name-based fallback lookup (`libcast.db[unitName]`) is now skipped when a GUID is available. This prevents cast bleed between mobs that share a name.
**`libs/libpredict.lua`, `modules/castbar.lua`, `modules/afkcam.lua`**
- Updated all call sites to use the renamed `pfGetCastInfo` / `pfGetChannelInfo`.
578 lines
21 KiB
Lua
578 lines
21 KiB
Lua
-- load pfUI environment
|
|
setfenv(1, pfUI:GetEnvironment())
|
|
|
|
--[[ libcast ]]--
|
|
-- A pfUI library that detects and saves all ongoing castbars of players, NPCs and enemies.
|
|
-- The library also includes spells that usually don't have a castbar like Multi-Shot and Aimed Shot.
|
|
-- This is exclusivly used for vanilla in order to provide pfGetChannelInfo and pfGetCastInfo functions.
|
|
--
|
|
-- External functions:
|
|
-- pfGetChannelInfo(unit)
|
|
-- Returns information on the spell currently cast by the specified unit.
|
|
-- Returns nil if no spell is being cast.
|
|
--
|
|
-- cast[String] - The name of the spell, or nil if no spell is being cast.
|
|
-- nameSubtext[String] - (DUMMY) The string describing the rank of the spell, e.g. "Rank 1".
|
|
-- text[String] - The name to be displayed.
|
|
-- texture[String] - The texture path associated with the spell.
|
|
-- startTime[Number] - Specifies when casting has begun, in milliseconds.
|
|
-- endTime[Number] - Specifies when casting will end, in milliseconds.
|
|
-- isTradeSkill[Boolean] - (DUMMY) Specifies if the cast is a tradeskill
|
|
--
|
|
-- pfGetCastInfo(unit)
|
|
-- Returns information on the spell currently channeled by the specified unit.
|
|
-- Returns nil if no spell is being channeled.
|
|
--
|
|
-- cast[String] - The name of the spell, or nil if no spell is being cast.
|
|
-- nameSubtext[String] - (DUMMY) The string describing the rank of the spell, e.g. "Rank 1".
|
|
-- text[String] - The name to be displayed.
|
|
-- texture[String] - The texture path associated with the spell.
|
|
-- startTime[Number] - Specifies when casting has begun, in milliseconds.
|
|
-- endTime[Number] - Specifies when casting will end, in milliseconds.
|
|
-- isTradeSkill[Boolean] - (DUMMY) Specifies if the cast is a tradeskill
|
|
--
|
|
-- Internal functions:
|
|
-- libcast:AddAction(mob, spell, channel)
|
|
-- Adds a spell to the database by using pfUI's spell database
|
|
-- to obtain durations and icons
|
|
--
|
|
-- libcast:RemoveAction(mob, spell)
|
|
-- Removes the castbar of a given mob, if `spell` is an interrupt.
|
|
-- spell can be set to "INTERRUPT" to force remove an action.
|
|
--
|
|
|
|
-- return instantly if we're not on a vanilla client
|
|
if pfUI.client > 11200 then return end
|
|
|
|
-- return instantly when another libcast is already active
|
|
if pfUI.api.libcast then return end
|
|
|
|
local lastcasttex, lastrank, _
|
|
local scanner = libtipscan:GetScanner("libcast")
|
|
|
|
local libcast = CreateFrame("Frame", "pfEnemyCast")
|
|
local player = UnitName("player")
|
|
|
|
pfGetChannelInfo = function(unit)
|
|
-- convert to name if unitstring was given
|
|
local unitName = pfValidUnits[unit] and UnitName(unit) or unit
|
|
|
|
-- Get GUID if Nampower is available
|
|
local guid = nil
|
|
|
|
-- Check if unit itself is a GUID (starts with "0x")
|
|
if type(unit) == "string" and string.sub(unit, 1, 2) == "0x" then
|
|
guid = unit -- unit IS the GUID
|
|
elseif pfValidUnits[unit] and UnitExists then
|
|
-- unit is a token like "target" - get GUID from it
|
|
local unitGuid = GetUnitGUID(unit)
|
|
guid = unitGuid
|
|
end
|
|
|
|
-- For player: ALWAYS use libcast.db because it handles channel updates correctly
|
|
local isPlayer = unit == "player" or unitName == player
|
|
|
|
if isPlayer then
|
|
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
local db = libcast.db[player]
|
|
|
|
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
|
if not db.channel then return end
|
|
cast = db.cast
|
|
nameSubtext = db.rank
|
|
text = ""
|
|
texture = db.icon
|
|
startTime = db.start * 1000
|
|
endTime = startTime + db.casttime
|
|
isTradeSkill = nil
|
|
elseif db then
|
|
db.cast = nil
|
|
db.rank = nil
|
|
db.start = nil
|
|
db.casttime = nil
|
|
db.icon = nil
|
|
db.channel = nil
|
|
end
|
|
|
|
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
end
|
|
|
|
-- For non-player units: use libdebuff GUID-based tracking or libcast.db
|
|
-- Try GUID-based lookup first (from libdebuff's SPELL_START tracking)
|
|
local db = nil
|
|
if guid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[guid] then
|
|
-- Use libdebuff's cast tracking (from SPELL_START_OTHER events)
|
|
local castData = pfUI.libdebuff_casts[guid]
|
|
if castData.event == "START" and castData.endTime and castData.endTime > GetTime() then
|
|
-- Convert libdebuff format to libcast format
|
|
db = {
|
|
cast = castData.spellName,
|
|
rank = nil,
|
|
start = castData.startTime,
|
|
casttime = castData.duration * 1000, -- Convert back to ms
|
|
icon = castData.icon,
|
|
channel = nil -- TODO: libdebuff should distinguish channel vs cast
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Fallback to name-based lookup (CHAT_MSG castbars)
|
|
-- Skip when GUID is available to avoid same-name mob bleed
|
|
if not db and not guid and libcast.db[unitName] then
|
|
db = libcast.db[unitName]
|
|
end
|
|
|
|
-- Fallback to libcast.db for non-player units
|
|
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
|
|
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
|
if not db.channel then return end
|
|
cast = db.cast
|
|
nameSubtext = db.rank
|
|
text = ""
|
|
texture = db.icon
|
|
startTime = db.start * 1000
|
|
endTime = startTime + db.casttime
|
|
isTradeSkill = nil
|
|
elseif db then
|
|
db.cast = nil
|
|
db.rank = nil
|
|
db.start = nil
|
|
db.casttime = nil
|
|
db.icon = nil
|
|
db.channel = nil
|
|
end
|
|
|
|
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
end
|
|
|
|
pfGetCastInfo = function(unit)
|
|
-- convert to name if unitstring was given
|
|
local unitName = pfValidUnits[unit] and UnitName(unit) or unit
|
|
|
|
-- Get GUID if Nampower is available
|
|
local guid = nil
|
|
|
|
-- Check if unit itself is a GUID (starts with "0x")
|
|
if type(unit) == "string" and string.sub(unit, 1, 2) == "0x" then
|
|
guid = unit -- unit IS the GUID
|
|
elseif pfValidUnits[unit] and UnitExists then
|
|
-- unit is a token like "target" - get GUID from it
|
|
local unitGuid = GetUnitGUID(unit)
|
|
guid = unitGuid
|
|
end
|
|
|
|
-- For player: ALWAYS use libcast.db because it handles pushback correctly
|
|
local isPlayer = unit == "player" or unitName == player
|
|
|
|
if isPlayer then
|
|
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
local db = libcast.db[player]
|
|
|
|
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
|
if db.channel then return end
|
|
cast = db.cast
|
|
nameSubtext = db.rank or ""
|
|
text = ""
|
|
texture = db.icon
|
|
startTime = db.start * 1000
|
|
endTime = startTime + db.casttime
|
|
isTradeSkill = nil
|
|
elseif db then
|
|
db.cast = nil
|
|
db.rank = nil
|
|
db.start = nil
|
|
db.casttime = nil
|
|
db.icon = nil
|
|
db.channel = nil
|
|
end
|
|
|
|
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
end
|
|
|
|
-- For non-player units: use libdebuff GUID-based tracking or libcast.db
|
|
-- Try GUID-based lookup first (from libdebuff's SPELL_START tracking)
|
|
local db = nil
|
|
if guid and pfUI.libdebuff_casts and pfUI.libdebuff_casts[guid] then
|
|
-- Use libdebuff's cast tracking (from SPELL_START_OTHER events)
|
|
local castData = pfUI.libdebuff_casts[guid]
|
|
if castData.event == "START" and castData.endTime and castData.endTime > GetTime() then
|
|
-- Convert libdebuff format to libcast format
|
|
db = {
|
|
cast = castData.spellName,
|
|
rank = nil,
|
|
start = castData.startTime,
|
|
casttime = castData.duration * 1000, -- Convert back to ms
|
|
icon = castData.icon,
|
|
channel = nil -- TODO: libdebuff should distinguish channel vs cast
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Fallback to name-based lookup (CHAT_MSG castbars)
|
|
-- Skip when GUID is available to avoid same-name mob bleed
|
|
if not db and not guid and libcast.db[unitName] then
|
|
db = libcast.db[unitName]
|
|
end
|
|
|
|
-- Fallback to libcast.db for non-player units
|
|
local cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
|
|
if db and db.cast and db.start + db.casttime / 1000 > GetTime() then
|
|
if db.channel then return end
|
|
cast = db.cast
|
|
nameSubtext = db.rank or ""
|
|
text = ""
|
|
texture = db.icon
|
|
startTime = db.start * 1000
|
|
endTime = startTime + db.casttime
|
|
isTradeSkill = nil
|
|
elseif db then
|
|
db.cast = nil
|
|
db.rank = nil
|
|
db.start = nil
|
|
db.casttime = nil
|
|
db.icon = nil
|
|
db.channel = nil
|
|
end
|
|
|
|
return cast, nameSubtext, text, texture, startTime, endTime, isTradeSkill
|
|
end
|
|
|
|
function libcast:AddAction(mob, spell, channel)
|
|
if not mob or not spell then return nil end
|
|
|
|
if L["spells"][spell] ~= nil then
|
|
local casttime = L["spells"][spell].t
|
|
local icon = L["spells"][spell].icon and string.format("%s%s", "Interface\\Icons\\", L["spells"][spell].icon) or nil
|
|
|
|
-- add cast action to the database
|
|
if not self.db[mob] then self.db[mob] = {} end
|
|
self.db[mob].cast = spell
|
|
self.db[mob].rank = nil
|
|
self.db[mob].start = GetTime()
|
|
self.db[mob].casttime = casttime
|
|
self.db[mob].icon = icon
|
|
self.db[mob].channel = channel
|
|
|
|
return true
|
|
end
|
|
|
|
return nil
|
|
end
|
|
|
|
function libcast:RemoveAction(mob, spell)
|
|
if self.db[mob] and ( L["interrupts"][spell] ~= nil or spell == "INTERRUPT" ) then
|
|
|
|
-- remove cast action to the database
|
|
self.db[mob].cast = nil
|
|
self.db[mob].rank = nil
|
|
self.db[mob].start = nil
|
|
self.db[mob].casttime = nil
|
|
self.db[mob].icon = nil
|
|
self.db[mob].channel = nil
|
|
end
|
|
end
|
|
|
|
-- main data
|
|
libcast.db = { [player] = {} }
|
|
|
|
-- environmental casts
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_HOSTILEPLAYER_BUFF")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_FRIENDLYPLAYER_BUFF")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_BUFFS")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_BUFFS")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_HOSTILEPLAYER_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_FRIENDLYPLAYER_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_SELF_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PARTY_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PARTY_BUFF")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_PARTY_BUFFS")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_PERIODIC_CREATURE_BUFFS")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_DAMAGE")
|
|
libcast:RegisterEvent("CHAT_MSG_SPELL_CREATURE_VS_CREATURE_BUFF")
|
|
|
|
-- player spells
|
|
libcast:RegisterEvent("SPELLCAST_START")
|
|
libcast:RegisterEvent("SPELLCAST_STOP")
|
|
libcast:RegisterEvent("SPELLCAST_FAILED")
|
|
libcast:RegisterEvent("SPELLCAST_INTERRUPTED")
|
|
libcast:RegisterEvent("SPELLCAST_DELAYED")
|
|
libcast:RegisterEvent("SPELLCAST_CHANNEL_START")
|
|
libcast:RegisterEvent("SPELLCAST_CHANNEL_STOP")
|
|
libcast:RegisterEvent("SPELLCAST_CHANNEL_UPDATE")
|
|
|
|
local mob, spell, icon, _
|
|
local lastSpellId = nil -- spellId cached from SPELL_START_SELF (Nampower)
|
|
|
|
libcast:SetScript("OnEvent", function()
|
|
-- Fill database with player casts
|
|
if event == "SPELLCAST_START" then
|
|
-- Get icon from Nampower using spellId cached from SPELL_START_SELF
|
|
icon = nil
|
|
if lastSpellId and GetSpellRecField and GetSpellIconTexture then
|
|
local iconId = GetSpellRecField(lastSpellId, "spellIconID")
|
|
if iconId then
|
|
icon = GetSpellIconTexture(iconId)
|
|
if icon and not string.find(icon, "\\") then
|
|
icon = "Interface\\Icons\\" .. icon
|
|
end
|
|
end
|
|
end
|
|
-- fallback to L["spells"] / lastcasttex if Nampower didn't provide icon
|
|
if not icon then
|
|
icon = L["spells"][arg1] and L["spells"][arg1].icon and string.format("%s%s", "Interface\\Icons\\", L["spells"][arg1].icon) or lastcasttex
|
|
end
|
|
lastSpellId = nil
|
|
|
|
this.db[player].cast = arg1
|
|
this.db[player].rank = lastrank
|
|
this.db[player].start = GetTime()
|
|
this.db[player].casttime = arg2
|
|
this.db[player].icon = icon
|
|
this.db[player].channel = nil
|
|
|
|
if not L["spells"][arg1] or not L["spells"][arg1].icon or not L["spells"][arg1].t then
|
|
L["spells"][arg1] = L["spells"][arg1] or { }
|
|
L["spells"][arg1].icon = L["spells"][arg1].icon or icon
|
|
L["spells"][arg1].t = L["spells"][arg1].t or arg2
|
|
end
|
|
lastcasttex, lastrank = nil, nil
|
|
elseif event == "SPELLCAST_STOP" or event == "SPELLCAST_FAILED" or event == "SPELLCAST_INTERRUPTED" then
|
|
lastSpellId = nil
|
|
if this.db[player] and not this.db[player].channel then
|
|
-- remove cast action to the database
|
|
this.db[player].cast = nil
|
|
this.db[player].rank = nil
|
|
this.db[player].rank = nil
|
|
this.db[player].start = nil
|
|
this.db[player].casttime = nil
|
|
this.db[player].icon = nil
|
|
this.db[player].channel = nil
|
|
else
|
|
lastcasttex, lastrank = nil, nil
|
|
end
|
|
elseif event == "SPELLCAST_DELAYED" then
|
|
if this.db[player].cast then
|
|
-- Pushback: increase casttime instead of shifting start
|
|
-- arg1 is the delay amount in milliseconds
|
|
this.db[player].casttime = this.db[player].casttime + arg1
|
|
end
|
|
elseif event == "SPELLCAST_CHANNEL_START" then
|
|
-- add cast action to the database
|
|
this.db[player].cast = arg2
|
|
this.db[player].rank = lastrank
|
|
this.db[player].start = GetTime()
|
|
this.db[player].casttime = arg1
|
|
this.db[player].icon = L["spells"][arg2] and L["spells"][arg2].icon and string.format("%s%s", "Interface\\Icons\\", L["spells"][arg2].icon) or lastcasttex
|
|
this.db[player].channel = true
|
|
|
|
lastcasttex, lastrank = nil, nil
|
|
elseif event == "SPELLCAST_CHANNEL_STOP" then
|
|
if this.db[player] and this.db[player].channel then
|
|
-- remove cast action to the database
|
|
this.db[player].cast = nil
|
|
this.db[player].rank = nil
|
|
this.db[player].start = nil
|
|
this.db[player].casttime = nil
|
|
this.db[player].icon = nil
|
|
this.db[player].channel = nil
|
|
end
|
|
elseif event == "SPELLCAST_CHANNEL_UPDATE" then
|
|
if this.db[player].cast then
|
|
this.db[player].start = -this.db[player].casttime/1000 + GetTime() + arg1/1000
|
|
end
|
|
-- Fill database with environmental casts
|
|
elseif arg1 then
|
|
-- (.+) begins to cast (.+).
|
|
mob, spell = cmatch(arg1, SPELLCASTOTHERSTART)
|
|
if libcast:AddAction(mob, spell) then return end
|
|
|
|
-- (.+) begins to perform (.+).
|
|
mob, spell = cmatch(arg1, SPELLPERFORMOTHERSTART)
|
|
if libcast:AddAction(mob, spell) then return end
|
|
|
|
-- (.+) gains (.+).
|
|
mob, spell = cmatch(arg1, AURAADDEDOTHERHELPFUL)
|
|
if libcast:RemoveAction(mob, spell) then return end
|
|
|
|
-- (.+) is afflicted by (.+).
|
|
mob, spell = cmatch(arg1, AURAADDEDOTHERHARMFUL)
|
|
if libcast:RemoveAction(mob, spell) then return end
|
|
|
|
-- Your (.+) hits (.+) for (%d+).
|
|
spell, mob = cmatch(arg1, SPELLLOGSELFOTHER)
|
|
if libcast:RemoveAction(mob, spell) then return end
|
|
|
|
-- Your (.+) crits (.+) for (%d+).
|
|
spell, mob = cmatch(arg1, SPELLLOGCRITSELFOTHER)
|
|
if libcast:RemoveAction(mob, spell) then return end
|
|
|
|
-- (.+)'s (.+) %a hits (.+) for (%d+).
|
|
_, spell, mob = cmatch(arg1, SPELLLOGOTHEROTHER)
|
|
if libcast:RemoveAction(mob, spell) then return end
|
|
|
|
-- (.+)'s (.+) %a crits (.+) for (%d+).
|
|
_, spell, mob = cmatch(arg1, SPELLLOGCRITOTHEROTHER)
|
|
if libcast:RemoveAction(mob, spell) then return end
|
|
|
|
-- You interrupt (.+)'s (.+).
|
|
mob, _ = cmatch(arg1, SPELLINTERRUPTSELFOTHER)
|
|
if libcast:RemoveAction(mob, "INTERRUPT") then return end
|
|
|
|
-- (.+) interrupts (.+)'s (.+).
|
|
_, mob, _ = cmatch(arg1, SPELLINTERRUPTOTHEROTHER)
|
|
if libcast:RemoveAction(mob, "INTERRUPT") then return end
|
|
end
|
|
end)
|
|
|
|
--[[ Custom Casts
|
|
Enable Castbars for spells that don't have a castbar by default
|
|
(e.g Multi-Shot and Aimed Shot)
|
|
]]--
|
|
local aimedshot = L["customcast"]["AIMEDSHOT"]
|
|
local multishot = L["customcast"]["MULTISHOT"]
|
|
|
|
libcast.customcast = {}
|
|
libcast.customcast[strlower(aimedshot)] = function(begin, duration)
|
|
if begin then
|
|
local duration = duration or 3000
|
|
|
|
for i=1,32 do
|
|
if UnitBuff("player", i) == "Interface\\Icons\\Racial_Troll_Berserk" then
|
|
local berserk = 0.3
|
|
if((UnitHealth("player")/UnitHealthMax("player")) >= 0.40) then
|
|
berserk = (1.30 - (UnitHealth("player") / UnitHealthMax("player"))) / 3
|
|
end
|
|
duration = duration / (1 + berserk)
|
|
elseif UnitBuff("player", i) == "Interface\\Icons\\Ability_Hunter_RunningShot" then
|
|
duration = duration / 1.4
|
|
elseif UnitBuff("player", i) == "Interface\\Icons\\Ability_Warrior_InnerRage" then
|
|
duration = duration / 1.3
|
|
elseif UnitBuff("player", i) == "Interface\\Icons\\Inv_Trinket_Naxxramas04" then
|
|
duration = duration / 1.2
|
|
end
|
|
end
|
|
|
|
local _,_, lag = GetNetStats()
|
|
local start = GetTime() + lag/1000
|
|
|
|
-- add cast action to the database
|
|
libcast.db[player].cast = aimedshot
|
|
libcast.db[player].rank = lastrank
|
|
libcast.db[player].start = start
|
|
libcast.db[player].casttime = duration
|
|
libcast.db[player].icon = "Interface\\Icons\\Inv_spear_07"
|
|
libcast.db[player].channel = nil
|
|
else
|
|
-- remove cast action to the database
|
|
libcast.db[player].cast = nil
|
|
libcast.db[player].rank = nil
|
|
libcast.db[player].start = nil
|
|
libcast.db[player].casttime = nil
|
|
libcast.db[player].icon = nil
|
|
libcast.db[player].channel = nil
|
|
end
|
|
end
|
|
|
|
libcast.customcast[strlower(multishot)] = function(begin, duration)
|
|
if begin then
|
|
local duration = duration or 500
|
|
|
|
for i=1,32 do
|
|
if UnitBuff("player", i) == "Interface\\Icons\\Racial_Troll_Berserk" then
|
|
local berserk = 0.3
|
|
if((UnitHealth("player")/UnitHealthMax("player")) >= 0.40) then
|
|
berserk = (1.30 - (UnitHealth("player") / UnitHealthMax("player"))) / 3
|
|
end
|
|
duration = duration / (1 + berserk)
|
|
elseif UnitBuff("player", i) == "Interface\\Icons\\Ability_Hunter_RunningShot" then
|
|
duration = duration / 1.4
|
|
elseif UnitBuff("player", i) == "Interface\\Icons\\Ability_Warrior_InnerRage" then
|
|
duration = duration / 1.3
|
|
elseif UnitBuff("player", i) == "Interface\\Icons\\Inv_Trinket_Naxxramas04" then
|
|
duration = duration / 1.2
|
|
end
|
|
end
|
|
|
|
local _,_, lag = GetNetStats()
|
|
local start = GetTime() + lag/1000
|
|
|
|
-- add cast action to the database
|
|
libcast.db[player].cast = multishot
|
|
libcast.db[player].rank = lastrank
|
|
libcast.db[player].start = start
|
|
libcast.db[player].casttime = duration
|
|
libcast.db[player].icon = "Interface\\Icons\\Ability_upgrademoonglaive"
|
|
libcast.db[player].channel = nil
|
|
else
|
|
-- remove cast action to the database
|
|
libcast.db[player].cast = nil
|
|
libcast.db[player].rank = nil
|
|
libcast.db[player].start = nil
|
|
libcast.db[player].casttime = nil
|
|
libcast.db[player].icon = nil
|
|
libcast.db[player].channel = nil
|
|
end
|
|
end
|
|
|
|
local function CastCustom(id, bookType, rawSpellName, rank, texture, castingTime)
|
|
if not id or not rawSpellName then return end -- ignore if the spell is not found
|
|
if not castingTime or castingTime == 0 then
|
|
-- instant-cast: clear lastcasttex so next cast doesn't inherit this icon
|
|
lastcasttex, lastrank = nil, nil
|
|
return
|
|
end
|
|
|
|
lastrank = rank
|
|
lastcasttex = texture
|
|
|
|
local func = libcast.customcast[strlower(rawSpellName)]
|
|
if not func then return end
|
|
|
|
if GetSpellCooldown(id, bookType) == 0 or pfGetCastInfo(player) then return end -- detect casting
|
|
|
|
func(true)
|
|
end
|
|
|
|
hooksecurefunc("UseContainerItem", function(id, index)
|
|
lastcasttex = GetContainerItemInfo(id, index)
|
|
end)
|
|
|
|
hooksecurefunc("CastSpell", function(id, bookType)
|
|
local cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime, _, _, cachedSpellId, cachedBookType = libspell.GetSpellInfo(id, bookType)
|
|
|
|
CastCustom(cachedSpellId, cachedBookType, cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime)
|
|
end)
|
|
|
|
hooksecurefunc("CastSpellByName", function(spellCasted, target)
|
|
local cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime, _, _, cachedSpellId, cachedBookType = libspell.GetSpellInfo(spellCasted)
|
|
|
|
CastCustom(cachedSpellId, cachedBookType, cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime)
|
|
end)
|
|
|
|
hooksecurefunc("UseAction", function(slot, target, button)
|
|
if GetActionText(slot) or not IsCurrentAction(slot) then return end
|
|
|
|
scanner:SetAction(slot)
|
|
local rawSpellName, rank = scanner:Line(1)
|
|
if not rawSpellName then return end -- ignore if the spell is not found
|
|
|
|
local cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime, _, _, cachedSpellId, cachedBookType = libspell.GetSpellInfo(rawSpellName .. (rank and ("(" .. rank .. ")") or ""))
|
|
|
|
CastCustom(cachedSpellId, cachedBookType, cachedRawSpellName, cachedRank, cachedTexture, cachedCastingTime)
|
|
end)
|
|
|
|
-- Cache spellId from SPELL_START_SELF so SPELLCAST_START can use it for icon lookup
|
|
pfUI.libdebuff_spell_start_self_hooks = pfUI.libdebuff_spell_start_self_hooks or {}
|
|
pfUI.libdebuff_spell_start_self_hooks["libcast_icon"] = function(spellId)
|
|
lastSpellId = spellId
|
|
end
|
|
|
|
-- add libcast to pfUI API
|
|
pfUI.api.libcast = libcast |