You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

12781 lines
500 KiB

--Plater main software file
--Calls with : are functions imported from the framework
--whenever a variable or function has a --private comment attached to it, means scripts cannot access it (read, write, override), anything else can be overriden with scripts
--with that, you can make your own version of Plater by modifying and overriding functions entirelly using a hooking script, them you can export the script and upload to wago.io (have fun :)
--check the list of available functions and members to override at 'Plater.CanOverride_Functions' and 'Plater.CanOverride_Members'
--Weakauras Scripters: if you need to attach something to Plater nameplates:
-- local namePlate = C_NamePlate.GetNamePlateForUnit (unitID)
-- local unitFrame = namePlate.unitFrame --unitFrame is the main frame where all things is attached, it has SetAllPoints() on the namePlate frame.
-- local healthBar = unitFrame.healthBar
-- local castBar = unitFrame.castBar
-- update localization
-- navigate within the code using search tags: ~color ~border, etc...
if (true) then
--return
--but not today
end
--> details! framework
local DF = _G ["DetailsFramework"]
if (not DF) then
print ("|cFFFFAA00Plater: framework not found, if you just installed or updated the addon, please restart your client.|r")
return
end
--/run UIErrorsFrame:HookScript ("OnEnter", function() UIErrorsFrame:EnableMouse (false);Plater:Msg("UIErrorsFrame had MouseEnabled, its disabled now.") end)
--> some WA or addon are enabling the mouse on the error frame making nameplates unclickable
if (UIErrorsFrame) then
UIErrorsFrame:HookScript ("OnEnter", function()
--safe disable the mouse on error frame avoiding mouse interactions and warn the user
UIErrorsFrame:EnableMouse (false)
Plater:Msg ("something enabled the mouse on UIErrorsFrame, Plater disabled.")
end)
UIErrorsFrame:EnableMouse (false)
end
--> locals
local unpack = unpack
local ipairs = ipairs
local rawset = rawset
--local rawget = rawget --200 locals limit
--local setfenv = setfenv --200 locals limit
local xpcall = xpcall
local InCombatLockdown = InCombatLockdown
local UnitIsPlayer = UnitIsPlayer
local UnitClassification = UnitClassification
local UnitDetailedThreatSituation = UnitDetailedThreatSituation
local UnitCanAttack = UnitCanAttack
--local IsSpellInRange = IsSpellInRange --200 locals limit
local abs = math.abs
local format = string.format
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
local UnitIsUnit = UnitIsUnit
local type = type
local select = select
local UnitGUID = UnitGUID
local strsplit = strsplit
local lower = string.lower
local floor = floor
local max = math.max
local min = math.min
local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
local IS_WOW_PROJECT_CLASSIC_WRATH = IS_WOW_PROJECT_NOT_MAINLINE and ClassicExpansionAtLeast and LE_EXPANSION_WRATH_OF_THE_LICH_KING and ClassicExpansionAtLeast(LE_EXPANSION_WRATH_OF_THE_LICH_KING)
--local IS_WOW_PROJECT_CLASSIC_CATACLYSM = IS_WOW_PROJECT_NOT_MAINLINE and ClassicExpansionAtLeast and LE_EXPANSION_CATACLYSM and ClassicExpansionAtLeast(LE_EXPANSION_CATACLYSM)
local PixelUtil = PixelUtil or DFPixelUtil
local parserFunctions --reference needed
local LibSharedMedia = LibStub:GetLibrary ("LibSharedMedia-3.0") -- https://www.curseforge.com/wow/addons/libsharedmedia-3-0
local LCG = LibStub:GetLibrary("LibCustomGlow-1.0") -- https://github.com/Stanzilla/LibCustomGlow
local LibRangeCheck = LibStub:GetLibrary ("LibRangeCheck-3.0") -- https://github.com/WeakAuras/LibRangeCheck-3.0
local LibTranslit = LibStub:GetLibrary ("LibTranslit-1.0") -- https://github.com/Vardex/LibTranslit
local LDB = LibStub ("LibDataBroker-1.1", true)
local LDBIcon = LDB and LibStub ("LibDBIcon-1.0", true)
local addonId, platerInternal = ...
local _ = nil
--localization
local LOC = DF.Language.GetLanguageTable(addonId)
---@type plater
local Plater = DF:CreateAddOn ("Plater", "PlaterDB", PLATER_DEFAULT_SETTINGS, InterfaceOptionsFrame and { --options table --TODO: DISABLED FOR DRAGONFLIGHT FOR NOW!
name = "Plater Nameplates",
type = "group",
args = {
openOptions = {
name = "Open Plater Options",
desc = "Opens the Plater Options Menu.",
type = "execute",
func = function()
if InterfaceOptionsFrame then
InterfaceOptionsFrame:Hide()
elseif SettingsPanel then
SettingsPanel:Hide()
end
HideUIPanel(GameMenuFrame)
Plater.OpenOptionsPanel()
end,
},
}
})
local GetAddOnMetadata = C_AddOns and C_AddOns.GetAddOnMetadata or GetAddOnMetadata
Plater.versionString = GetAddOnMetadata("Plater", "Version")
Plater.fullVersionInfo = Plater.versionString .. " - DF v" .. select(2,LibStub:GetLibrary("DetailsFramework-1.0")) .. " - " .. GetBuildInfo()
function Plater.GetVersionInfo(printOut)
-- update, just in case...
Plater.versionString = GetAddOnMetadata("Plater", "Version")
Plater.fullVersionInfo = Plater.versionString .. " - DF v" .. select(2,LibStub:GetLibrary("DetailsFramework-1.0")) .. " - " .. GetBuildInfo()
if printOut then print("Plater version info:\n" .. Plater.fullVersionInfo) end
return Plater.fullVersionInfo
end
--> when a hook script is compiled, it increases the build version, so the handler for running scripts will notice in the change and update the script in real time
local PLATER_HOOK_BUILD = 1
function Plater.IncreaseHookBuildID() --private
PLATER_HOOK_BUILD = PLATER_HOOK_BUILD + 1
end
--> if a widget has a RefreshID lower than the addon, it needs to be updated
local PLATER_REFRESH_ID = 1
function Plater.IncreaseRefreshID() --private
PLATER_REFRESH_ID = PLATER_REFRESH_ID + 1
Plater.IncreaseRefreshID_Auras()
end
platerInternal.CreateDataTables(Plater)
Plater.ForceBlizzardNameplateUnits = {
--
}
Plater.AddForceBlizzardNameplateUnits = function(npcID)
if type(npcID) == "number" then
Plater.ForceBlizzardNameplateUnits[npcID] = true
end
end
Plater.RemoveForceBlizzardNameplateUnits = function(npcID)
if type(npcID) == "number" then
Plater.ForceBlizzardNameplateUnits[npcID] = nil
end
end
--store npc names and spell names from the current/latest combat
--used to sort data in the options panel: Spell List, Spell Colors and Npc Colors
Plater.LastCombat = {
npcNames = {},
spellNames = {},
}
Plater.MDTSettings = {
button_width = 18, --button and icon width
button_height = 18,
enemyinfo_button_point = {"topright", "topright", 4.682, -21.361},
spellinfo_button_point = {"bottomright", "bottomright", -12, 2},
icon_texture = [[Interface\Buttons\UI-Panel-BiggerButton-Up]],
icon_coords = {0.2, 0.8, 0.2, 0.8},
alpha = 0.834, --button alpha
}
-- ~hook (hook scripts are cached in the indexed part of these tales, for performance the member ScriptAmount caches the amount of scripts inside the indexed table)
local HOOK_NAMEPLATE_ADDED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_CREATED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_REMOVED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_UPDATED = {ScriptAmount = 0}
local HOOK_TARGET_CHANGED = {ScriptAmount = 0}
local HOOK_CAST_START = {ScriptAmount = 0}
local HOOK_CAST_UPDATE = {ScriptAmount = 0}
local HOOK_CAST_STOP = {ScriptAmount = 0}
local HOOK_RAID_TARGET = {ScriptAmount = 0}
local HOOK_COMBAT_ENTER = {ScriptAmount = 0}
local HOOK_COMBAT_LEAVE = {ScriptAmount = 0}
local HOOK_NAMEPLATE_CONSTRUCTOR = {ScriptAmount = 0}
local HOOK_PLAYER_POWER_UPDATE = {ScriptAmount = 0}
local HOOK_PLAYER_TALENT_UPDATE = {ScriptAmount = 0}
local HOOK_HEALTH_UPDATE = {ScriptAmount = 0}
local HOOK_ZONE_CHANGED = {ScriptAmount = 0}
local HOOK_UNITNAME_UPDATE = {ScriptAmount = 0}
local HOOK_LOAD_SCREEN = {ScriptAmount = 0}
local HOOK_PLAYER_LOGON = {ScriptAmount = 0}
local HOOK_MOD_INITIALIZATION = {ScriptAmount = 0}
local HOOK_MOD_DEINITIALIZATION = {ScriptAmount = 0}
local HOOK_COMM_RECEIVED_MESSAGE = {ScriptAmount = 0}
local HOOK_COMM_SEND_MESSAGE = {ScriptAmount = 0}
local HOOK_OPTION_CHANGED = {ScriptAmount = 0}
local HOOK_MOD_OPTION_CHANGED = {ScriptAmount = 0}
local HOOK_NAMEPLATE_DESTRUCTOR = {ScriptAmount = 0}
platerInternal.HOOK_MOD_OPTION_CHANGED = HOOK_MOD_OPTION_CHANGED --triggered from Plater.ScriptingOptions.lua
local PLATER_GLOBAL_MOD_ENV = {} -- contains modEnv for each mod, identified by "<mod name>"
local PLATER_GLOBAL_SCRIPT_ENV = {} -- contains modEnv for each script, identified by "<script name>"
--> cvars just to make them easier to read
local CVAR_ENABLED = "1"
local CVAR_DISABLED = "0"
--> cache some common used member strings for better reading
local MEMBER_UNITID = "namePlateUnitToken"
local MEMBER_GUID = "namePlateUnitGUID"
local MEMBER_NPCID = "namePlateNpcId"
local MEMBER_QUEST = "namePlateIsQuestObjective"
local MEMBER_REACTION = "namePlateUnitReaction"
local MEMBER_RANGE = "namePlateInRange"
local MEMBER_NOCOMBAT = "namePlateNoCombat"
local MEMBER_NAME = "namePlateUnitName"
local MEMBER_NAMELOWER = "namePlateUnitNameLower"
local MEMBER_TARGET = "namePlateIsTarget"
--> cache nameplate types for better reading the code
local ACTORTYPE_FRIENDLY_PLAYER = "friendlyplayer"
local ACTORTYPE_FRIENDLY_NPC = "friendlynpc"
local ACTORTYPE_ENEMY_PLAYER = "enemyplayer"
local ACTORTYPE_ENEMY_NPC = "enemynpc"
local ACTORTYPE_PLAYER = "player"
local class_specs_coords = {
[577] = {128/512, 192/512, 256/512, 320/512}, --> havoc demon hunter
[581] = {192/512, 256/512, 256/512, 320/512}, --> vengeance demon hunter
[250] = {0, 64/512, 0, 64/512}, --> blood dk
[251] = {64/512, 128/512, 0, 64/512}, --> frost dk
[252] = {128/512, 192/512, 0, 64/512}, --> unholy dk
[102] = {192/512, 256/512, 0, 64/512}, --> druid balance
[103] = {256/512, 320/512, 0, 64/512}, --> druid feral
[104] = {320/512, 384/512, 0, 64/512}, --> druid guardian
[105] = {384/512, 448/512, 0, 64/512}, --> druid resto
[253] = {448/512, 512/512, 0, 64/512}, --> hunter bm
[254] = {0, 64/512, 64/512, 128/512}, --> hunter marks
[255] = {64/512, 128/512, 64/512, 128/512}, --> hunter survivor
[62] = {(128/512) + 0.001953125, 192/512, 64/512, 128/512}, --> mage arcane
[63] = {192/512, 256/512, 64/512, 128/512}, --> mage fire
[64] = {256/512, 320/512, 64/512, 128/512}, --> mage frost
[268] = {320/512, 384/512, 64/512, 128/512}, --> monk bm
[269] = {448/512, 512/512, 64/512, 128/512}, --> monk ww
[270] = {384/512, 448/512, 64/512, 128/512}, --> monk mw
[65] = {0, 64/512, 128/512, 192/512}, --> paladin holy
[66] = {64/512, 128/512, 128/512, 192/512}, --> paladin protect
[70] = {(128/512) + 0.001953125, 192/512, 128/512, 192/512}, --> paladin ret
[256] = {192/512, 256/512, 128/512, 192/512}, --> priest disc
[257] = {256/512, 320/512, 128/512, 192/512}, --> priest holy
[258] = {(320/512) + (0.001953125 * 4), 384/512, 128/512, 192/512}, --> priest shadow
[259] = {64/512, 128/512, 384/512, 448/512}, --> rogue assassination
[260] = {0, 64/512, 384/512, 448/512}, --> rogue outlaw
[261] = {0, 64/512, 192/512, 256/512}, --> rogue sub
[262] = {64/512, 128/512, 192/512, 256/512}, --> shaman elemental
[263] = {128/512, 192/512, 192/512, 256/512}, --> shamel enhancement
[264] = {192/512, 256/512, 192/512, 256/512}, --> shaman resto
[265] = {256/512, 320/512, 192/512, 256/512}, --> warlock aff
[266] = {320/512, 384/512, 192/512, 256/512}, --> warlock demo
[267] = {384/512, 448/512, 192/512, 256/512}, --> warlock destro
[71] = {448/512, 512/512, 192/512, 256/512}, --> warrior arms
[72] = {0, 64/512, 256/512, 320/512}, --> warrior fury
[73] = {64/512, 128/512, 256/512, 320/512}, --> warrior protect
[1467] = {256/512, 320/512, 256/512, 320/512}, --> evoker devastation
[1468] = {320/512, 384/512, 256/512, 320/512}, --> evoker preservation
[1473] = {384/512, 448/512, 256/512, 320/512}, --> evoker augmentation
}
--localization
Plater.AnchorNames = {
LOC["OPTIONS_ANCHOR_TOPLEFT"],
LOC["OPTIONS_ANCHOR_LEFT"],
LOC["OPTIONS_ANCHOR_BOTTOMLEFT"],
LOC["OPTIONS_ANCHOR_BOTTOM"],
LOC["OPTIONS_ANCHOR_BOTTOMRIGHT"],
LOC["OPTIONS_ANCHOR_RIGHT"],
LOC["OPTIONS_ANCHOR_TOPRIGHT"],
LOC["OPTIONS_ANCHOR_TOP"],
LOC["OPTIONS_ANCHOR_CENTER"],
LOC["OPTIONS_ANCHOR_INNERLEFT"],
LOC["OPTIONS_ANCHOR_INNERRIGHT"],
LOC["OPTIONS_ANCHOR_INNERTOP"],
LOC["OPTIONS_ANCHOR_INNERBOTTOM"],
}
Plater.AnchorNamesByPhraseId = {
"OPTIONS_ANCHOR_TOPLEFT",
"OPTIONS_ANCHOR_LEFT",
"OPTIONS_ANCHOR_BOTTOMLEFT",
"OPTIONS_ANCHOR_BOTTOM",
"OPTIONS_ANCHOR_BOTTOMRIGHT",
"OPTIONS_ANCHOR_RIGHT",
"OPTIONS_ANCHOR_TOPRIGHT",
"OPTIONS_ANCHOR_TOP",
"OPTIONS_ANCHOR_CENTER",
"OPTIONS_ANCHOR_INNERLEFT",
"OPTIONS_ANCHOR_INNERRIGHT",
"OPTIONS_ANCHOR_INNERTOP",
"OPTIONS_ANCHOR_INNERBOTTOM",
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> cached value ~cache
--Plater allocate several values in memory to save performance (cpu), this may increase memory usage
--example: intead of querying Plater.db.profile.tank it just hold a pointer to that table in the variable DB_AGGRO_TANK_COLORS, and this pointer is updated when the user changes something in the options panel
local DB_NUMBER_REGION_EAST_ASIA
local DB_TICK_THROTTLE
local DB_LERP_COLOR
local DB_LERP_COLOR_SPEED
local DB_PLATE_CONFIG
local DB_HOVER_HIGHLIGHT
local DB_CLASS_COLORS
--auras
local DB_AURA_ENABLED
local DB_AURA_ALPHA
local DB_AURA_SEPARATE_BUFFS
local DB_USE_UIPARENT
local DB_UNITCOLOR_CACHE = {}
local DB_UNITCOLOR_SCRIPT_CACHE = {}
local IS_USING_DETAILS_INTEGRATION
local DB_TRACK_METHOD
local DB_BORDER_COLOR_R
local DB_BORDER_COLOR_G
local DB_BORDER_COLOR_B
local DB_BORDER_COLOR_A
local DB_BORDER_THICKNESS
local DB_AGGRO_CHANGE_HEALTHBAR_COLOR
local DB_AGGRO_CHANGE_NAME_COLOR
local DB_AGGRO_CHANGE_BORDER_COLOR
local DB_AGGRO_CAN_CHECK_NOTANKAGGRO
local DB_TARGET_SHADY_ENABLED
local DB_TARGET_SHADY_ALPHA
local DB_TARGET_SHADY_COMBATONLY
local DB_NAME_NPCENEMY_ANCHOR
local DB_NAME_NPCFRIENDLY_ANCHOR
local DB_NAME_PLAYERENEMY_ANCHOR
local DB_NAME_PLAYERFRIENDLY_ANCHOR
local DB_DO_ANIMATIONS
local DB_ANIMATION_TIME_DILATATION
local DB_USE_RANGE_CHECK
local DB_USE_NON_TARGETS_ALPHA
local DB_USE_FOCUS_TARGET_ALPHA
local DB_USE_ALPHA_FRIENDLIES
local DB_USE_ALPHA_ENEMIES
local DB_USE_QUICK_HIDE
local DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE
local DB_TEXTURE_CASTBAR
local DB_TEXTURE_CASTBAR_BG
local DB_TEXTURE_HEALTHBAR
local DB_TEXTURE_HEALTHBAR_BG
local DB_CASTBAR_HIDE_ENEMIES
local DB_CASTBAR_HIDE_FRIENDLY
---@type plater_spelldata[]
local DB_CAPTURED_SPELLS = {}
---@type plater_spelldata[]
local DB_CAPTURED_CASTS = {}
--store the aggro color table for tanks and dps
local DB_AGGRO_TANK_COLORS
local DB_AGGRO_DPS_COLORS
--store if the no combat alpha is enabled
local DB_NOT_COMBAT_ALPHA_ENABLED
local DB_USE_HEALTHCUTOFF = false
local DB_HEALTHCUTOFF_AT = 0.2
local DB_HEALTHCUTOFF_AT_UPPER = 0.8
--store translit option
local DB_USE_NAME_TRANSLIT = false
local TRANSLIT_MARK = "*"
--store the npc id cache
local DB_NPCIDS_CACHE = {}
Plater.ScriptAura = {}
local SCRIPT_AURA_TRIGGER_CACHE = Plater.ScriptAura
Plater.ScriptCastBar = {}
local SCRIPT_CASTBAR_TRIGGER_CACHE = Plater.ScriptCastBar
Plater.ScriptUnit = {}
local SCRIPT_UNIT_TRIGGER_CACHE = Plater.ScriptUnit
--spell animations - store a table with information about animation for spells
local SPELL_WITH_ANIMATIONS = {}
--cache this inside plater object to access it from the animation editor
Plater.SPELL_WITH_ANIMATIONS = SPELL_WITH_ANIMATIONS
--store players which have the tank role in the group
local TANK_CACHE = {}
--store pet GUIDs
---@type plater_petinfo[]
local PET_CACHE = {}
--store pets summoned by the player it self
Plater.PlayerPetCache = {}
--store if the player is in combat (not reliable, toggled at regen switch)
local PLAYER_IN_COMBAT
--store if the player is not inside an instance
local IS_IN_OPEN_WORLD = true
--store if the player is inside a instance (raid or dungeon)
local IS_IN_INSTANCE = false
--if true, the animation will update its settings before play
local IS_EDITING_SPELL_ANIMATIONS = false
local HOOKED_BLIZZARD_PLATEFRAMES = {}
local ENABLED_BLIZZARD_PLATEFRAMES = {}
local SUPPORT_BLIZZARD_PLATEFRAMES = false
local NUM_NAMEPLATES_ON_SCREEN = 0
local NAMEPLATES_ON_SCREEN_CACHE = {}
local CLASS_INFO_CACHE = {}
--store a list of friendly players in the player friends list
Plater.FriendsCache = {}
--store quests the player is in
Plater.QuestCache = {}
--store only campaign quests
Plater.QuestCacheCampaign = {}
--cache the profile settings for each actor type on this table, so scripts can have access to profile
Plater.ActorTypeSettingsCache = { --private
RefreshID = -1,
--plate holder tables, they will be overriden when updating the cache
[ACTORTYPE_FRIENDLY_PLAYER] = {},
[ACTORTYPE_FRIENDLY_NPC] = {},
[ACTORTYPE_ENEMY_PLAYER] = {},
[ACTORTYPE_ENEMY_NPC] = {},
[ACTORTYPE_PLAYER] = {},
}
--update the settings cache for scritps
--this is a table with a copy of the settings from the profile so can be safelly accessed by scripts
function Plater.UpdateSettingsCache() --private
if (Plater.ActorTypeSettingsCache.RefreshID >= PLATER_REFRESH_ID) then
return
end
local namePlateConfig = Plater.db.profile.plate_config
Plater.ActorTypeSettingsCache [ACTORTYPE_FRIENDLY_PLAYER] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_FRIENDLY_PLAYER])
Plater.ActorTypeSettingsCache [ACTORTYPE_FRIENDLY_NPC] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_FRIENDLY_NPC])
Plater.ActorTypeSettingsCache [ACTORTYPE_ENEMY_PLAYER] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_ENEMY_PLAYER])
Plater.ActorTypeSettingsCache [ACTORTYPE_ENEMY_NPC] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_ENEMY_NPC])
Plater.ActorTypeSettingsCache [ACTORTYPE_PLAYER] = DF.table.copy ({}, namePlateConfig [ACTORTYPE_PLAYER])
Plater.ActorTypeSettingsCache.RefreshID = PLATER_REFRESH_ID
end
function Plater.InitLDB()
if LDB then
local databroker = LDB:NewDataObject ("Plater", {
type = "data source",
icon = [[Interface\AddOns\Plater\images\cast_bar]],
text = "Plater",
showInCompartment = true,
HotCornerIgnore = true,
OnClick = function (self, button)
if (button == "LeftButton") then
if (PlaterOptionsPanelFrame and PlaterOptionsPanelFrame:IsShown()) then
PlaterOptionsPanelFrame:Hide()
return true
end
Plater.OpenOptionsPanel()
elseif (button == "RightButton") then
GameTooltip:Hide()
local GameCooltip = GameCooltip2
GameCooltip:Reset()
GameCooltip:SetType ("menu")
GameCooltip:SetOption ("ButtonsYMod", -5)
GameCooltip:SetOption ("HeighMod", 5)
GameCooltip:SetOption ("TextSize", 10)
--> disable minimap icon
local toggle_minimap = function()
PlaterDBChr.minimap.hide = not PlaterDBChr.minimap.hide
if (PlaterDBChr.minimap.hide) then
LDBIcon:Hide ("Plater")
else
LDBIcon:Show ("Plater")
end
LDBIcon:Refresh ("Plater", PlaterDBChr.minimap)
end
local toggle_compartment = function()
if LDBIcon:IsButtonInCompartment("Plater") then
LDBIcon:RemoveButtonFromCompartment("Plater")
else
LDBIcon:AddButtonToCompartment("Plater")
end
end
GameCooltip:AddMenu (1, function() Plater.EnableProfiling(true) end, true, nil, nil, "Start profiling", nil, true)
GameCooltip:AddIcon ([[Interface\Addons\Plater\media\sphere_full_64]], 1, 1, 14, 14, 0, 1, 0, 1, "red")
GameCooltip:AddMenu (1, function() Plater.DisableProfiling() end, true, nil, nil, "Stop profiling", nil, true)
GameCooltip:AddIcon ([[Interface\Addons\Plater\media\square_64]], 1, 1, 14, 14, 0, 1, 0, 1, "blue")
GameCooltip:AddMenu (1, function() Plater.ShowPerfData() end, true, nil, nil, "Show profiling data", nil, true)
GameCooltip:AddIcon ([[Interface\Addons\Plater\media\eye_64]], 1, 1, 14, 14, 0, 1, 0, 1, "green")
GameCooltip:AddLine ("$div")
GameCooltip:AddMenu (1, toggle_minimap, true, nil, nil, "Hide/Show Minimap Icon", nil, true)
GameCooltip:AddIcon ([[Interface\Buttons\UI-Panel-HideButton-Disabled]], 1, 1, 14, 14, 7/32, 24/32, 8/32, 24/32, "gray")
GameCooltip:AddMenu (1, toggle_compartment, true, nil, nil, "Hide/Show Compartment Entry", nil, true)
GameCooltip:AddIcon ([[Interface\Buttons\UI-Panel-HideButton-Disabled]], 1, 1, 14, 14, 7/32, 24/32, 8/32, 24/32, "gray")
--GameCooltip:SetBackdrop (1, _detalhes.tooltip_backdrop, nil, _detalhes.tooltip_border_color)
GameCooltip:SetWallpaper (1, [[Interface\SPELLBOOK\Spellbook-Page-1]], {.6, 0.1, 0.64453125, 0}, {.8, .8, .8, 0.2}, true)
GameCooltip:SetOwner (self, "topright", "bottomleft")
GameCooltip:ShowCooltip()
end
end,
OnTooltipShow = function (tooltip)
tooltip:AddLine ("Plater Nameplates", 1, 1, 1)
tooltip:AddLine ("|cFFCFCFCFLeft click|r: Show/Hide Options Window")
tooltip:AddLine ("|cFFCFCFCFRight click|r: Quick Menu")
end,
})
if (databroker and not LDBIcon:IsRegistered ("Plater")) then
PlaterDBChr.minimap = PlaterDBChr.minimap or {}
LDBIcon:Register ("Plater", databroker, PlaterDBChr.minimap)
if not PlaterDBChr.minimap.showInCompartment == true then
--LDBIcon:AddButtonToCompartment("Plater") -- this is opt-in in LDBIcon (for now)
end
end
Plater.databroker = databroker
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> character specific abilities and spells ~spells
-- ~execute
---update if can use execute indicators - this function needs to be updated when a new execute spell is added, removed, modified
---in scripts you can use Plater.SetExecuteRange or override this function completelly
function Plater.GetHealthCutoffValue(getOnly)
Plater.SetExecuteRange (false)
local lowerEnabled, upperEnabled = Plater.db.profile.health_cutoff, Plater.db.profile.health_cutoff_upper
if (not (lowerEnabled or upperEnabled)) then
if not getOnly then
return
end
end
local lowExecute, highExecute = nil, nil
if IS_WOW_PROJECT_MAINLINE then
--retail
--small helper
local isTalentLearned = function(nodeID)
local talentConfig = C_ClassTalents.GetActiveConfigID()
local nodeInfo = talentConfig and nodeID and C_Traits.GetNodeInfo(talentConfig, nodeID)
return nodeInfo and nodeInfo.entryIDsWithCommittedRanks and nodeInfo.entryIDsWithCommittedRanks[1] and true or false
end
local classLoc, class = UnitClass ("player")
local spec = GetSpecialization()
if (spec and class) then
if (class == "PRIEST") then
-- SW:D is available to all priest specs
if IsPlayerSpell(32379) then
lowExecute = 0.20
end
elseif (class == "MAGE") then
if IsPlayerSpell(2948) then -- Scorch
lowExecute = 0.3
if isTalentLearned(449349) then --Sunfury Execution (for Scorch)
lowExecute = 0.35
end
end
if IsPlayerSpell(205026) then --Firestarter
highExecute = 0.9
end
if IsPlayerSpell(384581) then -- Arcane Bombardment
lowExecute = 0.35
end
elseif (class == "WARRIOR") then
-- Execute is baseline
if IsPlayerSpell(163201) then
local using_Massacre = IsPlayerSpell(281001) or IsPlayerSpell(206315)
lowExecute = using_Massacre and 0.35 or 0.2
--local using_Condemn = IsPlayerSpell(317320) -- that's not really used anymore...
--highExecute = using_Condemn and 0.8 or nil
end
elseif (class == "HUNTER") then
if IsPlayerSpell(53351) or IsPlayerSpell(320976) then -- Kill Shot
lowExecute = 0.2
end
if IsPlayerSpell(273887) then --> is using killer instinct?
lowExecute = 0.35
end
if IsPlayerSpell(260228) then --> Careful Aim
highExecute = 0.7
end
elseif (class == "PALADIN") then
-- hammer of wrath
if IsPlayerSpell(24275) then
lowExecute = 0.2
end
elseif (class == "MONK") then
--Touch of Death
if IsPlayerSpell(322113) then
lowExecute = 0.15
end
elseif (class == "WARLOCK") then
if IsPlayerSpell(17877) then --Shadowburn
lowExecute = 0.20
elseif IsSpellKnownOrOverridesKnown(198590) then --Drain Soul
lowExecute = 0.20
end
elseif (class == "ROGUE") then
if IsPlayerSpell(328085) then --Blindside
lowExecute = 0.35
end
elseif (class == "DEATHKNIGHT") then
if IsPlayerSpell(343294) then --Soul Reaper
lowExecute = 0.35
end
end
end
else
-- WotLK and classic
local classLoc, class = UnitClass ("player")
if (class) then
if (class == "WARRIOR") then
-- Execute
if GetSpellInfo(GetSpellInfo(5308)) then
lowExecute = 0.2
end
elseif (class == "PALADIN") then
-- Hammer of Wrath
if GetSpellInfo(GetSpellInfo(24275)) then
lowExecute = 0.2
end
elseif (class == "WARLOCK") then
-- Decimation
if IsPlayerSpell(63156) or IsPlayerSpell(63158) then
lowExecute = 0.25
else
lowExecute = 0.25
end
elseif (class == "HUNTER") then
-- Kill Shot
if GetSpellInfo(GetSpellInfo(53351)) then
lowExecute = 0.2
end
elseif (class == "PRIEST") then
if IS_WOW_PROJECT_CLASSIC_WRATH then -- why wrath again?... can't remember
for i = 1, 6 do
local enabled, _, glyphSpellID = GetGlyphSocketInfo(i)
if enabled and glyphSpellID then
if glyphSpellID == 55682 then --Glyph of Shadow Word: Death
lowExecute = 0.35
break
end
end
end
end
-- SW:D is available to all priest specs
if IsPlayerSpell(32379) then
lowExecute = 0.25
end
end
end
end
if not getOnly then
Plater.SetExecuteRange (true, lowerEnabled and lowExecute or nil, upperEnabled and highExecute or nil)
end
return lowerEnabled and lowExecute or nil, upperEnabled and highExecute or nil
end
---range check ~range
---@param plateFrame plateframe
---@param onAdded boolean
function Plater.CheckRange (plateFrame, onAdded)
Plater.StartLogPerformanceCore("Plater-Core", "Update", "CheckRange")
local profile = Plater.db.profile
local unitFrame = plateFrame.unitFrame
local castBarFade = unitFrame.castBar.fadeOutAnimation:IsPlaying() --and profile.cast_statusbar_use_fade_effects
local nameplateAlpha = 1
local occlusionAlpha = tonumber(GetCVar ("nameplateOccludedAlphaMult")) or 1
if DB_USE_UIPARENT and profile.honor_blizzard_plate_alpha then
--if DB_USE_UIPARENT end
nameplateAlpha = plateFrame:GetAlpha()
end
unitFrame.IsInRange = nil
--if is using the no combat alpha and the unit isn't in combat, ignore the range check, no combat alpha is disabled by default
if (unitFrame.IsSelf) then
unitFrame.IsInRange = true --player plate is always in range
unitFrame:SetAlpha (nameplateAlpha)
unitFrame.healthBar:SetAlpha (1)
if not castBarFade then
unitFrame.castBar:SetAlpha (1)
end
unitFrame.powerBar:SetAlpha (1)
unitFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
unitFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
return
elseif (plateFrame [MEMBER_NOCOMBAT] or unitFrame.isWidgetOnlyMode) then
if unitFrame.isWidgetOnlyMode then
unitFrame:SetAlpha (1)
elseif nameplateAlpha < profile.not_affecting_combat_alpha and nameplateAlpha >= occlusionAlpha then
unitFrame:SetAlpha (nameplateAlpha)
else
unitFrame:SetAlpha (profile.not_affecting_combat_alpha)
end
--unitFrame:SetAlpha (profile.not_affecting_combat_alpha) -- already set if necessary
unitFrame.healthBar:SetAlpha (1)
if not castBarFade then
unitFrame.castBar:SetAlpha (1)
end
unitFrame.powerBar:SetAlpha (1)
unitFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
unitFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
return
--the unit is friendly or not using range check and non targets alpha
elseif ((not DB_USE_ALPHA_FRIENDLIES and plateFrame [MEMBER_REACTION] >= 5) or (not DB_USE_ALPHA_ENEMIES and plateFrame [MEMBER_REACTION] < 5) or (not DB_USE_RANGE_CHECK and not DB_USE_NON_TARGETS_ALPHA)) then
unitFrame:SetAlpha (nameplateAlpha)
unitFrame.healthBar:SetAlpha (1)
if not castBarFade then
unitFrame.castBar:SetAlpha (1)
end
unitFrame.powerBar:SetAlpha (1)
unitFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
unitFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
plateFrame [MEMBER_RANGE] = true
unitFrame [MEMBER_RANGE] = true
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
return
end
--alpha values
local inRangeAlpha
local overallRangeCheckAlpha
local healthBar_rangeCheckAlpha
local castBar_rangeCheckAlpha
local buffFrames_rangeCheckAlpha
local powerBar_rangeCheckAlpha
local rangeChecker
local rangeCheckRange
if plateFrame [MEMBER_REACTION] < 5 then
-- enemy
inRangeAlpha = profile.range_check_in_range_or_target_alpha
overallRangeCheckAlpha = profile.range_check_alpha
healthBar_rangeCheckAlpha = profile.range_check_health_bar_alpha
castBar_rangeCheckAlpha = profile.range_check_cast_bar_alpha
buffFrames_rangeCheckAlpha = profile.range_check_buffs_alpha
powerBar_rangeCheckAlpha = profile.range_check_power_bar_alpha
rangeChecker = Plater.RangeCheckFunctionEnemy or LibRangeCheck:GetHarmMaxChecker(Plater.RangeCheckRangeEnemy or 40, true)
rangeCheckRange = Plater.RangeCheckRangeEnemy
else
-- friendly
inRangeAlpha = profile.range_check_in_range_or_target_alpha_friendlies
overallRangeCheckAlpha = profile.range_check_alpha_friendlies
healthBar_rangeCheckAlpha = profile.range_check_health_bar_alpha_friendlies
castBar_rangeCheckAlpha = profile.range_check_cast_bar_alpha_friendlies
buffFrames_rangeCheckAlpha = profile.range_check_buffs_alpha_friendlies
powerBar_rangeCheckAlpha = profile.range_check_power_bar_alpha_friendlies
rangeChecker = Plater.RangeCheckFunctionFriendly or LibRangeCheck:GetFriendMaxChecker(Plater.RangeCheckRangeFriendly or 40, true)
rangeCheckRange = Plater.RangeCheckRangeFriendly
end
if not rangeChecker then
rangeChecker = function (unit)
local range = (LibRangeCheck:GetRange(unit, nil, true) or 0) <= (rangeCheckRange or 40)
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
return range
end
Plater.GetSpellForRangeCheck()
end
--this unit is target
local unitIsTarget = unitFrame.isSoftInteract -- default to softinteract
local notTheTarget = false
--when the unit is out of range and isnt target, alpha is multiplied by this amount
local alphaMultiplier = 0.70
local healthBar = unitFrame.healthBar
local castBar = unitFrame.castBar
local powerBar = unitFrame.powerBar
local buffFrame1 = unitFrame.BuffFrame
local buffFrame2 = unitFrame.BuffFrame2
--if "units which is not target" is enabled and the player is targetting something else than the player it self
if ((DB_USE_NON_TARGETS_ALPHA and Plater.PlayerHasTargetNonSelf) or (DB_USE_FOCUS_TARGET_ALPHA and Plater.PlayerHasFocusTargetNonSelf)) then
if (plateFrame [MEMBER_TARGET]) then
unitIsTarget = true
elseif (DB_USE_FOCUS_TARGET_ALPHA and unitFrame.IsFocus) then
unitIsTarget = true
else
notTheTarget = true
if (profile.transparency_behavior_use_division) then
alphaMultiplier = 0.5
end
end
end
--is using the range check by ability
if (DB_USE_RANGE_CHECK and rangeChecker) then
--check when the unit just has been added to the screen
local isInRange = rangeChecker (plateFrame [MEMBER_UNITID])
if (isInRange) then
--unit is in rage
unitFrame.IsInRange = true
if (onAdded) then
--plateFrame.FadedIn = true
unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
healthBar:SetAlpha (inRangeAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
if not castBarFade then
castBar:SetAlpha (inRangeAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
end
powerBar:SetAlpha (inRangeAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
buffFrame1:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
buffFrame2:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
plateFrame [MEMBER_RANGE] = true
plateFrame.unitFrame [MEMBER_RANGE] = true
else
local newAlpha = nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1)
if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), newAlpha, 0.01)) then
--play animations (animation aren't while in development)
unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
healthBar:SetAlpha (inRangeAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
if not castBarFade then
castBar:SetAlpha (inRangeAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
end
powerBar:SetAlpha (inRangeAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
buffFrame1:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
buffFrame2:SetAlpha (inRangeAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
end
plateFrame [MEMBER_RANGE] = true
plateFrame.unitFrame [MEMBER_RANGE] = true
end
else
--unit is out of range
unitFrame.IsInRange = false
if (onAdded) then
plateFrame.FadedIn = nil
-- unitFrame:SetAlpha (overallRangeCheckAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
-- healthBar:SetAlpha (healthBar_rangeCheckAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
-- castBar:SetAlpha (castBar_rangeCheckAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
-- powerBar:SetAlpha (powerBar_rangeCheckAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
-- buffFrame1:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
-- buffFrame2:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
unitFrame:SetAlpha ((unitIsTarget and inRangeAlpha or overallRangeCheckAlpha) * nameplateAlpha * (notTheTarget and alphaMultiplier or 1))
healthBar:SetAlpha ((unitIsTarget and inRangeAlpha or healthBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
if not castBarFade then
castBar:SetAlpha ((unitIsTarget and inRangeAlpha or castBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
end
powerBar:SetAlpha ((unitIsTarget and inRangeAlpha or powerBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
buffFrame1:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
buffFrame2:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
plateFrame [MEMBER_RANGE] = false
plateFrame.unitFrame [MEMBER_RANGE] = false
else
local newAlpha = nameplateAlpha * overallRangeCheckAlpha * (notTheTarget and alphaMultiplier or 1)
if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), newAlpha, 0.01)) then
--play animations (animation aren't while in development)
-- unitFrame:SetAlpha (overallRangeCheckAlpha * (notTheTarget and overallRangeCheckAlpha or 1))
-- healthBar:SetAlpha (healthBar_rangeCheckAlpha * (notTheTarget and healthBar_rangeCheckAlpha or 1))
-- castBar:SetAlpha (castBar_rangeCheckAlpha * (notTheTarget and castBar_rangeCheckAlpha or 1))
-- powerBar:SetAlpha (powerBar_rangeCheckAlpha * (notTheTarget and powerBar_rangeCheckAlpha or 1))
-- buffFrame1:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
-- buffFrame2:SetAlpha (buffFrames_rangeCheckAlpha * (notTheTarget and buffFrames_rangeCheckAlpha or 1))
unitFrame:SetAlpha ((unitIsTarget and inRangeAlpha or overallRangeCheckAlpha) * nameplateAlpha * (notTheTarget and alphaMultiplier or 1))
healthBar:SetAlpha ((unitIsTarget and inRangeAlpha or healthBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
if not castBarFade then
castBar:SetAlpha ((unitIsTarget and inRangeAlpha or castBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
end
powerBar:SetAlpha ((unitIsTarget and inRangeAlpha or powerBar_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
buffFrame1:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
buffFrame2:SetAlpha ((unitIsTarget and inRangeAlpha or buffFrames_rangeCheckAlpha) * (notTheTarget and alphaMultiplier or 1))
end
plateFrame [MEMBER_RANGE] = false
plateFrame.unitFrame [MEMBER_RANGE] = false
end
end
--range check isnt enabled, check is no target alpha is
elseif (DB_USE_NON_TARGETS_ALPHA) then
--player has a target other than him self?
if (Plater.PlayerHasTargetNonSelf) then
--is this unit is the current player target?
if (unitIsTarget) then
if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), nameplateAlpha * inRangeAlpha, 0.01)) then
unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
healthBar:SetAlpha (inRangeAlpha)
if not castBarFade then
castBar:SetAlpha (inRangeAlpha)
end
powerBar:SetAlpha (inRangeAlpha)
buffFrame1:SetAlpha (DB_AURA_ALPHA)
buffFrame2:SetAlpha (DB_AURA_ALPHA)
end
plateFrame.FadedIn = true
else
--this unit isnt the current player target
if (not DF:IsNearlyEqual (unitFrame:GetAlpha(), nameplateAlpha * inRangeAlpha * overallRangeCheckAlpha, 0.01)) then
unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha * overallRangeCheckAlpha)
healthBar:SetAlpha (inRangeAlpha * healthBar_rangeCheckAlpha)
if not castBarFade then
castBar:SetAlpha (inRangeAlpha * castBar_rangeCheckAlpha)
end
powerBar:SetAlpha (inRangeAlpha * powerBar_rangeCheckAlpha)
buffFrame1:SetAlpha (inRangeAlpha * buffFrames_rangeCheckAlpha)
buffFrame2:SetAlpha (inRangeAlpha * buffFrames_rangeCheckAlpha)
end
plateFrame.FadedIn = nil
end
else
--player does not have a target, so just set to regular alpha
plateFrame.FadedIn = true
unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
healthBar:SetAlpha (1)
if not castBarFade then
castBar:SetAlpha (1)
end
powerBar:SetAlpha (1)
buffFrame1:SetAlpha (DB_AURA_ALPHA)
buffFrame2:SetAlpha (DB_AURA_ALPHA)
end
else
-- no alpha settings, so just go to default
plateFrame.FadedIn = true
unitFrame:SetAlpha (nameplateAlpha * inRangeAlpha)
healthBar:SetAlpha (1)
if not castBarFade then
castBar:SetAlpha (1)
end
powerBar:SetAlpha (1)
buffFrame1:SetAlpha (DB_AURA_ALPHA)
buffFrame2:SetAlpha (DB_AURA_ALPHA)
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CheckRange")
end
local re_GetSpellForRangeCheck = function()
Plater.GetSpellForRangeCheck()
end
--> execute after player logon or when the player changes its spec
local tryingToUpdateRangeChecker = false
function Plater.GetSpellForRangeCheck()
if tryingToUpdateRangeChecker then return end
Plater.RangeCheckRangeEnemy = nil
Plater.RangeCheckRangeFriendly = nil
Plater.RangeCheckFunctionEnemy = nil
Plater.RangeCheckFunctionFriendly = nil
local specIndex = (IS_WOW_PROJECT_MAINLINE) and GetSpecialization() or 0
if (specIndex) then
local specID = (IS_WOW_PROJECT_MAINLINE) and GetSpecializationInfo (specIndex) or select (3, UnitClass ("player"))
if (specID and specID ~= 0) then
--[[ -- don't do that here, really. it will reset ranges with talent changes, etc. maybe only for current spec?
--range check spells fallback update
local harmCheckers = {}
local maxHarm = 0
for range, func in LibRangeCheck:GetHarmCheckers(true) do
harmCheckers[range] = func
if maxHarm < range then maxHarm = range end
end
local friendCheckers = {}
local maxFriend = 0
for range, func in LibRangeCheck:GetFriendCheckers(true) do
friendCheckers[range] = func
if maxFriend < range then maxFriend = range end
end
if WOW_PROJECT_ID == WOW_PROJECT_MAINLINE then
for specID, _ in pairs (Plater.SpecList [select (2, UnitClass ("player"))]) do
if harmCheckers then
if (PlaterDBChr.spellRangeCheckRangeEnemy [specID] == nil or not harmCheckers[PlaterDBChr.spellRangeCheckRangeEnemy [specID] ]) then
PlaterDBChr.spellRangeCheckRangeEnemy [specID] = maxHarm --Plater.DefaultSpellRangeList [specID]
end
end
if friendCheckers then
if (PlaterDBChr.spellRangeCheckRangeFriendly [specID] == nil or not friendCheckers[PlaterDBChr.spellRangeCheckRangeFriendly [specID] ]) then
PlaterDBChr.spellRangeCheckRangeFriendly [specID] = maxFriend --Plater.DefaultSpellRangeListF [specID]
end
end
end
else
local playerClass = select (3, UnitClass ("player"))
if harmCheckers then
if (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] == nil or not harmCheckers[PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] ]) then
PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] = maxHarm --Plater.DefaultSpellRangeList [playerClass]
end
end
if friendCheckers then
if (PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] == nil or not friendCheckers[PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] ]) then
PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] = maxFriend --Plater.DefaultSpellRangeListF [playerClass]
end
end
end
]]--
--the local character saved variable hold the spell name used for the range check
Plater.RangeCheckRangeFriendly = PlaterDBChr.spellRangeCheckRangeFriendly [specID] or Plater.DefaultSpellRangeListF [specID] or 40
Plater.RangeCheckRangeEnemy = PlaterDBChr.spellRangeCheckRangeEnemy [specID] or Plater.DefaultSpellRangeList [specID] or 40
Plater.RangeCheckFunctionFriendly = LibRangeCheck:GetFriendMaxChecker(Plater.RangeCheckRangeFriendly, true)
Plater.RangeCheckFunctionEnemy = LibRangeCheck:GetHarmMaxChecker(Plater.RangeCheckRangeEnemy, true)
tryingToUpdateRangeChecker = false
else
tryingToUpdateRangeChecker = true
C_Timer.After (1, re_GetSpellForRangeCheck)
end
else
tryingToUpdateRangeChecker = true
C_Timer.After (1, re_GetSpellForRangeCheck)
end
end
-- ~tank --todo: make these functions be inside the Plater object
--true if the 'player' unit is a tank
--parameter "hasTankAura" is used to force aura scan skip for paladins -> UpdatePlayerTankState -> SPELL_AURA_APPLIED/REMOVED (CLASSIC)
local function IsPlayerEffectivelyTank(hasTankAura)
if IS_WOW_PROJECT_MAINLINE then
local assignedRole = UnitGroupRolesAssigned ("player")
if (assignedRole == "NONE") then
local spec = GetSpecialization()
return spec and GetSpecializationRole (spec) == "TANK"
end
return assignedRole == "TANK"
elseif IS_WOW_PROJECT_CLASSIC_WRATH then
local assignedRole = UnitGroupRolesAssigned ("player")
if assignedRole == "NONE" and UnitLevel ("player") >= 10 then
assignedRole = GetTalentGroupRole(GetActiveTalentGroup())
end
local playerIsTank = assignedRole == "TANK"
if not playerIsTank then
playerIsTank = GetPartyAssignment("MAINTANK", "player") or false
end
return playerIsTank
else
local playerIsTank = hasTankAura or false
if not hasTankAura then
local playerClass = Plater.PlayerClass
if playerClass == "WARRIOR" then
local stance = GetShapeshiftFormID() --18 is def, 24 is glad
playerIsTank = stance == 18 or ((not stance == 24) and IsEquippedItemType("Shields")) -- Defensive Stance or shield (and not glad)
elseif playerClass == "DRUID" then
local formId = GetShapeshiftFormID()
playerIsTank = (formId == 5) or (formId == 8) -- Bear Form or Dire Bear Form...
elseif playerClass == "PALADIN" then
for i=1,40 do
local spellId = select(10, UnitBuff("player",i))
if spellId == 25780 or spellId == 407627 then
playerIsTank = true
end
end
elseif playerClass == "ROGUE" then
for i=1,40 do
local spellId = select(10, UnitBuff("player",i))
if spellId == 400015 or spellId == 400016 then
playerIsTank = true
end
end
elseif playerClass == "WARLOCK" then
for i=1,40 do
local spellId = select(10, UnitBuff("player",i))
if spellId == 403789 then
playerIsTank = true
end
end
elseif playerClass == "SHAMAN" then
for i=1,40 do
local spellId = select(10, UnitBuff("player",i))
if spellId == 408680 then
playerIsTank = true
end
end
end
end
-- if the player is assigned as MAINTANK, then treat him as one:
if not playerIsTank then
playerIsTank = GetPartyAssignment("MAINTANK", "player") or false
end
return playerIsTank
end
end
--return true if the unit is in tank role
local function IsUnitEffectivelyTank (unit)
if IS_WOW_PROJECT_MAINLINE then
return UnitGroupRolesAssigned (unit) == "TANK"
elseif IS_WOW_PROJECT_CLASSIC_WRATH then
if IsInRaid() then
return GetPartyAssignment("MAINTANK", unit)
else
return UnitGroupRolesAssigned (unit) == "TANK"
end
else
return GetPartyAssignment("MAINTANK", unit)
end
end
-- toggle Threat Color Mode between tank / dps (CLASSIC)
function Plater.ToggleThreatColorMode()
if IS_WOW_PROJECT_NOT_MAINLINE and not IS_WOW_PROJECT_CLASSIC_WRATH then
Plater.db.profile.tank_threat_colors = not Plater.db.profile.tank_threat_colors
Plater.RefreshTankCache()
if Plater.PlayerIsTank then
print("Plater: Using Tank Threat Colors")
else
print("Plater: Using DPS Threat Colors")
end
end
end
local function UpdatePlayerTankState(hasAura)
if (IsPlayerEffectivelyTank(hasAura)) then
TANK_CACHE [UnitName ("player")] = true
Plater.PlayerIsTank = true
else
TANK_CACHE [UnitName ("player")] = false
if IS_WOW_PROJECT_MAINLINE or IS_WOW_PROJECT_CLASSIC_WRATH then
Plater.PlayerIsTank = false
else
Plater.PlayerIsTank = false or Plater.db.profile.tank_threat_colors
end
end
end
--iterate among group members and store the names of all tanks in the group
--this is called when the player enter, leave or when the group roster is changed
--tank cache is used mostly in the aggro check to know if the player is a tank
function Plater.RefreshTankCache() --private
Plater.PlayerIsTank = false
wipe (TANK_CACHE)
--add the player to the tank pool if the player is a tank
UpdatePlayerTankState()
--search for tanks in the raid
if (IsInRaid()) then
for i = 1, GetNumGroupMembers() do
if (IsUnitEffectivelyTank ("raid" .. i)) then
if (not UnitIsUnit ("raid" .. i, "player")) then
local unitName = UnitName ("raid" .. i)
if unitName ~= UNKNOWN then
TANK_CACHE [unitName] = true
end
end
end
end
--is in group and is inside a dungeon
--there's only one tank on dungeon but dps may see if a unit is not in the tank aggro
elseif (IsInGroup() and Plater.ZoneInstanceType == "party") then
for i = 1, GetNumGroupMembers() -1 do
if (IsUnitEffectivelyTank ("party" .. i)) then
if (not UnitIsUnit ("party" .. i, "player")) then
local unitName = UnitName ("party" .. i)
if unitName ~= UNKNOWN then
TANK_CACHE [unitName] = true
end
end
end
end
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> general unit functions
--> return a table with points on where the unitFrame is attached
--these points are hardcoded in the UpdatePlateSize() function
function Plater.GetPoints (unitFrame)
local points = {
{"topleft", unitFrame.PlateFrame, "topleft", 0, 0},
{"bottomright", unitFrame.PlateFrame, "bottomright", 0, 0},
}
return points
end
--> return an iterator with all namepaltes on the screen
function Plater.GetAllShownPlates() --private
return C_NamePlate.GetNamePlates()
end
--> returns if the unit is tapped (gray health color when another player hit the unit first)
function Plater.IsUnitTapDenied (unitID)
return unitID and not UnitPlayerControlled (unitID) and UnitIsTapDenied (unitID)
end
--> returns what member from the profile need to be used, since there's entries for in combat and out of combat
function Plater.GetHashKey (inCombat) --private
if (inCombat or (inCombat == nil and PLAYER_IN_COMBAT)) then
return "cast_incombat", "health_incombat", "mana_incombat"
else
return "cast", "health", "mana"
end
end
--> return true if the resource bar should shown above the nameplate in the current target nameplate
function Plater.IsShowingResourcesOnTarget() --private
return PlaterDBChr.resources_on_target
end
--> when the player enter in the world, wait a few seconds to get the guild name data
local delayed_guildname_check = function()
Plater.PlayerGuildName = GetGuildInfo ("player")
if (not Plater.PlayerGuildName or Plater.PlayerGuildName == "") then
Plater.PlayerGuildName = "ThePlayerHasNoGuildName/30Char"
end
end
--> run a scheduled update for a nameplate, functions can create schedules when some events are triggered when the client doesn't have the data yet
function Plater.RunScheduledUpdate (timerObject) --private
Plater.StartLogPerformanceCore("Plater-Core", "Update", "RunScheduledUpdate")
local unitId = timerObject.unitId
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit (unitId)
if (plateFrame) then
--save user input data (usualy set from scripts) before call the unit added event
local unitFrame = plateFrame.unitFrame
local customHealthBarWidth = unitFrame.customHealthBarWidth
local customHealthBarHeight = unitFrame.customHealthBarHeight
local customCastBarWidth = unitFrame.customCastBarWidth
local customCastBarHeight = unitFrame.customCastBarHeight
local customPowerBarWidth = unitFrame.customPowerBarWidth
local customPowerBarHeight = unitFrame.customPowerBarHeight
local customBorderColor = unitFrame.customBorderColor
--full refresh the nameplate, this will override user data from scripts
Plater.RunFunctionForEvent ("NAME_PLATE_UNIT_REMOVED", unitId)
Plater.RunFunctionForEvent ("NAME_PLATE_UNIT_ADDED", unitId)
--restore user input data
unitFrame.customHealthBarWidth = customHealthBarWidth
unitFrame.customHealthBarHeight = customHealthBarHeight
unitFrame.customCastBarWidth = customCastBarWidth
unitFrame.customCastBarHeight = customCastBarHeight
unitFrame.customPowerBarWidth = customPowerBarWidth
unitFrame.customPowerBarHeight = customPowerBarHeight
if ( customHealthBarWidth or
customHealthBarHeight or
customCastBarWidth or
customCastBarHeight or
customPowerBarWidth or
customPowerBarHeight
) then
Plater.UpdatePlateSize (plateFrame)
end
unitFrame.customBorderColor = customBorderColor
if (unitFrame.customBorderColor) then
Plater.UpdateBorderColor (plateFrame.unitFrame)
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "RunScheduledUpdate")
end
---run a delayed update on the namepalte, this is used when the client receives an information from the server but does not update the state immediately
---this usualy happens with faction and flag changes
---@param plateFrame plateframe
---@param passedUnitId string|nil
---@param scheduleTime number|nil
function Plater.ScheduleUpdateForNameplate (plateFrame, passedUnitId, scheduleTime) --private
local unitId = passedUnitId or plateFrame [MEMBER_UNITID]
if not unitId and plateFrame.HasUpdateScheduled then -- well... fuck.
plateFrame.HasUpdateScheduled:Cancel()
return
end
--check if there's already an update scheduled for this unit
if (plateFrame.HasUpdateScheduled and not plateFrame.HasUpdateScheduled:IsCancelled()) then
if unitId and (not plateFrame.HasUpdateScheduled.unitId or plateFrame.HasUpdateScheduled.unitId ~= unitId) then
plateFrame.HasUpdateScheduled:Cancel()
else
return
end
end
plateFrame.HasUpdateScheduled = C_Timer.NewTimer (scheduleTime or 0, Plater.RunScheduledUpdate) --scheduleTime or next frame
plateFrame.HasUpdateScheduled.unitId = unitId
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> settings functions
-- ~profile
--refreshes the values for the profile when the profile is loaded or changed
function Plater:RefreshConfig() --private
platerInternal.ScriptTriggers.WipeDeprecatedScriptTriggersFromProfile(Plater.db.profile)
Plater.IncreaseRefreshID()
Plater.RefreshDBUpvalues()
Plater.UpdateAllPlates()
if (PlaterOptionsPanelFrame) then
PlaterOptionsPanelFrame.RefreshOptionsFrame()
end
Plater.UpdateSettingsCache()
end
function Plater:RefreshConfigProfileChanged() --private
Plater:RefreshConfig()
--call the user to /reload his UI
DF:ShowPromptPanel ("Plater profile changed, do you want /reload now (recommended)?", function() ReloadUI() end, function() end, true, 500)
end
--~save ~cvar
local cvars_to_store = {
["NamePlateClassificationScale"] = true,
["NamePlateHorizontalScale"] = true,
["NamePlateVerticalScale"] = true,
["ShowClassColorInNameplate"] = true,
["ShowNamePlateLoseAggroFlash"] = true,
["nameplateGlobalScale"] = true,
["nameplateLargerScale"] = true,
["nameplateLargeTopInset"] = true,
["nameplateLargeBottomInset"] = true,
["nameplateMaxDistance"] = true,
["nameplatePlayerMaxDistance"] = true,
["nameplateMinScale"] = true,
["nameplateMotion"] = true,
["nameplateMotionSpeed"] = true,
["nameplateOccludedAlphaMult"] = true,
["nameplateOtherAtBase"] = true,
["nameplateOtherTopInset"] = true,
["nameplateOtherBottomInset"] = true,
["nameplateOverlapV"] = true,
["nameplateOverlapH"] = true,
["nameplatePersonalHideDelaySeconds"] = true,
["nameplatePersonalShowAlways"] = true,
["nameplatePersonalShowInCombat"] = true,
["nameplatePersonalShowWithTarget"] = true,
["nameplateResourceOnTarget"] = (IS_WOW_PROJECT_MAINLINE),
["nameplateSelectedScale"] = true,
["nameplateSelfAlpha"] = (IS_WOW_PROJECT_MAINLINE),
["nameplateSelfBottomInset"] = (IS_WOW_PROJECT_MAINLINE),
["nameplateSelfScale"] = (IS_WOW_PROJECT_MAINLINE),
["nameplateSelfTopInset"] = (IS_WOW_PROJECT_MAINLINE),
["nameplateShowAll"] = true,
["nameplateShowEnemies"] = true,
["nameplateShowEnemyGuardians"] = true,
["nameplateShowEnemyMinions"] = true,
["nameplateShowEnemyMinus"] = true,
["nameplateShowEnemyPets"] = true,
["nameplateShowEnemyTotems"] = true,
["nameplateShowFriends"] = true,
["nameplateShowFriendlyNPCs"] = true,
["nameplateShowFriendlyMinions"] = true,
["nameplateShowFriendlyPets"] = true,
["nameplateShowFriendlyGuardians"] = true,
["nameplateShowFriendlyTotems"] = true,
["nameplateShowOnlyNames"] = true,
["nameplateShowSelf"] = (IS_WOW_PROJECT_MAINLINE),
["nameplateTargetBehindMaxDistance"] = true,
["clampTargetNameplateToScreen"] = true,
["nameplateTargetRadialPosition"] = true,
["nameplateSelectedAlpha"] = true,
["nameplateNotSelectedAlpha"] = (IS_WOW_PROJECT_NOT_MAINLINE),
["nameplateRemovalAnimation"] = (IS_WOW_PROJECT_NOT_MAINLINE),
["nameplateMinAlpha"] = true,
["nameplateMinAlphaDistance"] = true,
["nameplateShowDebuffsOnFriendly"] = true,
["SoftTargetIconGameObject"] = (IS_WOW_PROJECT_MAINLINE),
["SoftTargetInteract"] = (IS_WOW_PROJECT_MAINLINE),
["SoftTargetNameplateInteract"] = (IS_WOW_PROJECT_MAINLINE),
}
--keep this separate for now, with only stuff that NEEDS restoring in order
local function cvar_restore_order(v1, v2)
local restoreOrder = {
["nameplateShowFriends"] = 1,
["nameplateShowFriendlyNPCs"] = 2,
["nameplateShowFriendlyMinions"] = 3,
["nameplateShowFriendlyPets"] = 4,
["nameplateShowFriendlyGuardians"] = 5,
["nameplateShowFriendlyTotems"] = 6,
["nameplateShowEnemies"] = 7,
["nameplateShowEnemyNPCs"] = 8,
["nameplateShowEnemyMinions"] = 9,
["nameplateShowEnemyPets"] = 10,
["nameplateShowEnemyGuardians"] = 11,
["nameplateShowEnemyTotems"] = 12,
}
local order1, order2 = restoreOrder[v1], restoreOrder[v2]
if order1 and not order2 then
return false
elseif not order1 and order2 then
return true
elseif order1 and order2 then
return order1 < order2
elseif not order1 and not order2 then
return v1 < v2
end
end
function Plater.ParseCVarValue(value)
if value == nil then return nil end
--bool checks
if type(value) == "boolean" then
value = value and 1 or 0 --store as 1/0
elseif value == "true" then
value = 1
elseif value == "false" then
value = 0
end
return tostring(value) --to store string representation
end
local canSaveCVars = false --only allow storing after plater has restored
--on logout or on profile change, or when they are actually set, save some important cvars inside the profile
function Plater.SaveConsoleVariables(cvar, value) --private
if not canSaveCVars then return end
--print("save cvars", cvar, value, debugstack())
local cvarTable = Plater.db.profile.saved_cvars
local cvarLastChangedTable = Plater.db.profile.saved_cvars_last_change
if (not cvarTable) then
--return
Plater.db.profile.saved_cvars = {}
cvarTable = Plater.db.profile.saved_cvars
end
if not cvar then -- store all
for CVarName, enabled in pairs (cvars_to_store) do
if enabled then
cvarTable [CVarName] = Plater.ParseCVarValue(GetCVar (CVarName))
end
end
elseif cvars_to_store [cvar] then
cvarTable [cvar] = Plater.ParseCVarValue(value)
local callstack = debugstack(2) -- starts at "SetCVar" or caller
if callstack then
local caller, line = callstack:match("\"@([^\"]+)\"%]:(%d+)")
if not caller then
caller, line = callstack:match("in function <([^:%[>]+):(%d+)>")
end
--print((caller and caller .. ":" .. line) or callstack)
local isCVarUtil = (caller and caller:lower():find("[\\/]sharedxml[\\/]cvarutil%.lua"))
cvarLastChangedTable [cvar] = not isCVarUtil and (caller and (caller .. ":" .. line)) or callstack or "N/A"
end
end
end
--restore profile cvars
function Plater.RestoreProfileCVars()
if (InCombatLockdown()) then
C_Timer.After (1, function() Plater.RestoreProfileCVars() end)
return
end
--> try to restore cvars from the profile
local savedCVars = Plater.db and Plater.db.profile and Plater.db.profile.saved_cvars
if (savedCVars) then
--pre-sort restore order:
local orderKeys = {}
for k in pairs (cvars_to_store) do
tinsert(orderKeys, k)
end
table.sort(orderKeys, cvar_restore_order)
for _, CVarName in pairs (orderKeys) do
local CVarValue = savedCVars [CVarName]
if CVarValue then --only restore what we want to store/restore!
SetCVar (CVarName, Plater.ParseCVarValue(CVarValue))
end
end
end
canSaveCVars = true --allow storing after restoring the first time
end
function Plater.DebugCVars(cvar)
cvar = cvar and cvar:gsub(" ", "") or nil
if cvar and cvar ~= "" then
if cvars_to_store[cvar] then
print("CVar info:\nName: '" .. cvar .. "'\nCurrent Value: " .. (Plater.ParseCVarValue(GetCVar (cvar)) or "<not set>") .. "\nStored Value: " .. (Plater.db.profile.saved_cvars[cvar] or "<not stored>") .. "\nLast changed by: " .. (Plater.db.profile.saved_cvars_last_change[cvar] and ("\n" .. Plater.db.profile.saved_cvars_last_change[cvar]) or "<no info>"))
else
print("CVar '" .. cvar .. "' is not stored in Plater.")
end
else
print("No CVar name provided. Printing all stored CVar names. Use '/plater cvar <cvar name> for more details.'")
local savedCVars = Plater.db and Plater.db.profile and Plater.db.profile.saved_cvars or {}
for CVarName, CVarValue in pairs (savedCVars) do
print("'" .. CVarName .. "' = " .. (Plater.ParseCVarValue(CVarValue) or "<not set>"))
end
end
end
--refresh call back will run all functions in its table when Plater refreshes the dynamic upvales for the file
Plater.DBRefreshCallback = {}
function Plater.RegisterRefreshDBCallback (func) --private
DF.table.addunique (Plater.DBRefreshCallback, func)
end
function Plater.FireRefreshDBCallback() --private
for _, func in ipairs (Plater.DBRefreshCallback) do
DF:Dispatch (func)
end
end
function Plater.DisableAuraTrackingForAuraTest()
DB_AURA_ENABLED = false
end
--> place most used data into local upvalues to save process time
--> scripts need to call this function if they change something in the profile ~refresh ~db ~upvalues
function Plater.RefreshDBUpvalues()
local profile = Plater.db.profile
DB_AURA_ENABLED = profile.aura_enabled
DB_AURA_ALPHA = profile.aura_alpha
DB_AURA_SEPARATE_BUFFS = profile.buffs_on_aura2
DB_NUMBER_REGION_EAST_ASIA = Plater.db.profile.number_region == "eastasia"
DB_TICK_THROTTLE = profile.update_throttle
DB_LERP_COLOR = profile.use_color_lerp
--class colors
DB_CLASS_COLORS = profile.class_colors
--update colorStr
for className, colorTable in pairs(profile.class_colors) do
colorTable.colorStr = DetailsFramework:FormatColor("hex", colorTable.r, colorTable.g, colorTable.b, 1)
end
DB_LERP_COLOR_SPEED = profile.color_lerp_speed
DB_PLATE_CONFIG = profile.plate_config
DB_TRACK_METHOD = profile.aura_tracker.track_method
DB_DO_ANIMATIONS = profile.use_health_animation
DB_ANIMATION_TIME_DILATATION = profile.health_animation_time_dilatation
DB_HOVER_HIGHLIGHT = profile.hover_highlight
DB_USE_RANGE_CHECK = profile.range_check_enabled
DB_USE_NON_TARGETS_ALPHA = profile.non_targeted_alpha_enabled
DB_USE_FOCUS_TARGET_ALPHA = profile.focus_as_target_alpha
DB_USE_ALPHA_FRIENDLIES = profile.transparency_behavior_on_friendlies
DB_USE_ALPHA_ENEMIES = profile.transparency_behavior_on_enemies
DB_USE_QUICK_HIDE = profile.quick_hide
DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE = profile.show_healthbars_on_not_attackable
DB_NPCIDS_CACHE = Plater.db.profile.npc_cache
DB_USE_UIPARENT = profile.use_ui_parent
DB_BORDER_COLOR_R = profile.border_color [1]
DB_BORDER_COLOR_G = profile.border_color [2]
DB_BORDER_COLOR_B = profile.border_color [3]
DB_BORDER_COLOR_A = profile.border_color [4]
DB_BORDER_THICKNESS = profile.border_thickness
DB_AGGRO_CHANGE_HEALTHBAR_COLOR = profile.aggro_modifies.health_bar_color
DB_AGGRO_CHANGE_BORDER_COLOR = profile.aggro_modifies.border_color
DB_AGGRO_CHANGE_NAME_COLOR = profile.aggro_modifies.actor_name_color
DB_AGGRO_CAN_CHECK_NOTANKAGGRO = profile.aggro_can_check_notank
DB_AGGRO_TANK_COLORS = profile.tank.colors
DB_AGGRO_DPS_COLORS = profile.dps.colors
DB_NOT_COMBAT_ALPHA_ENABLED = profile.not_affecting_combat_enabled
DB_TARGET_SHADY_ENABLED = profile.target_shady_enabled
DB_TARGET_SHADY_ALPHA = profile.target_shady_alpha
DB_TARGET_SHADY_COMBATONLY = profile.target_shady_combat_only
DB_NAME_NPCENEMY_ANCHOR = profile.plate_config.enemynpc.actorname_text_anchor.side
DB_NAME_NPCFRIENDLY_ANCHOR = profile.plate_config.friendlynpc.actorname_text_anchor.side
DB_NAME_PLAYERENEMY_ANCHOR = profile.plate_config.enemyplayer.actorname_text_anchor.side
DB_NAME_PLAYERFRIENDLY_ANCHOR = profile.plate_config.friendlyplayer.actorname_text_anchor.side
DB_TEXTURE_CASTBAR = LibSharedMedia:Fetch ("statusbar", profile.cast_statusbar_texture)
DB_TEXTURE_CASTBAR_BG = LibSharedMedia:Fetch ("statusbar", profile.cast_statusbar_bgtexture)
DB_TEXTURE_HEALTHBAR = LibSharedMedia:Fetch ("statusbar", profile.health_statusbar_texture)
DB_TEXTURE_HEALTHBAR_BG = LibSharedMedia:Fetch ("statusbar", profile.health_statusbar_bgtexture)
DB_CASTBAR_HIDE_ENEMIES = profile.hide_enemy_castbars
DB_CASTBAR_HIDE_FRIENDLY = profile.hide_friendly_castbars
DB_CAPTURED_CASTS = PlaterDB.captured_casts
DB_CAPTURED_SPELLS = PlaterDB.captured_spells
DB_USE_NAME_TRANSLIT = profile.use_name_translit
--refresh lists
Plater.RefreshDBLists()
--refresh auras
Plater.RefreshAuraCache() --on Plater_Auras.lua
Plater.RefreshAuraDBUpvalues() --on Plater_Auras.lua
Plater.UpdateAuraCache() --on Plater_Auras.lua
--refresh resources
Plater.Resources.RefreshResourcesDBUpvalues() --Plater_Resources.lua
end
function Plater.RefreshDBLists()
local profile = Plater.db.profile
wipe (SPELL_WITH_ANIMATIONS)
if (profile.spell_animations) then
for spellId, animations in pairs (profile.spell_animation_list) do
local frameAnimations = {}
local spellName = GetSpellInfo (spellId)
if (spellName) then
for animationIndex, animationOptions in ipairs (animations) do
if (animationOptions.enabled) then
local data = DF.table.deploy ({}, animationOptions)
data.animationCooldown = {} --store nameplate references with [nameplateRef] = GetTime() + cooldown
tinsert (frameAnimations, data)
end
end
SPELL_WITH_ANIMATIONS [spellName] = frameAnimations
end
end
end
IS_USING_DETAILS_INTEGRATION = false
--details integration
if (Details and Details.plater) then
local detailsPlaterConfig = Details.plater
if (detailsPlaterConfig.realtime_dps_enabled) then
IS_USING_DETAILS_INTEGRATION = true
elseif (detailsPlaterConfig.realtime_dps_player_enabled) then
IS_USING_DETAILS_INTEGRATION = true
elseif (detailsPlaterConfig.damage_taken_enabled) then
IS_USING_DETAILS_INTEGRATION = true
end
end
--> build the list of npcs with special colors
wipe (DB_UNITCOLOR_CACHE) --regular color overrides the threat color
wipe (DB_UNITCOLOR_SCRIPT_CACHE) --color only used for scripts, plater does not use them
for npcID, infoTable in pairs (Plater.db.profile.npc_colors) do
local enabled1 = infoTable [1] --this is the overall enabled
local enabled2 = infoTable [2] --if this is true, this color is only used for scripts
local colorID = infoTable [3] --the color
if (enabled1 and not enabled2) then
local r, g, b, a = DF:ParseColors (colorID)
DB_UNITCOLOR_CACHE [npcID] = {r, g, b, a}
elseif (enabled1 and enabled2) then
local r, g, b, a = DF:ParseColors (colorID)
DB_UNITCOLOR_SCRIPT_CACHE [npcID] = {r, g, b, a}
end
end
Plater.IncreaseRefreshID()
Plater.FireRefreshDBCallback()
end
--a patch is a function stored in the Plater_ScriptLibrary file and are executed only once to change a profile setting, remove or add an aura into the tracker or modify a script
--patch versions are stored within the profile, so importing or creating a new profile will apply all patches that wasn't applyed into it yet
function Plater.ApplyPatches() --private ~updates ~scriptupdates ~patch ~patches
if (PlaterPatchLibrary) then
local currentPatch = Plater.db.profile.patch_version
local bSkipNonEssentialPatches = PlaterDB.SkipNonEssentialPatches
for patchId = currentPatch+1, #PlaterPatchLibrary do
local bCanInstallPatch = true
if (bSkipNonEssentialPatches) then
if (PlaterPatchLibrary[patchId].NotEssential) then
print(LOC["OPTIONS_NOESSENTIAL_SKIP_ALERT"], PlaterPatchLibrary[patchId].Notes[1]) --"Skipped non-essential patch:"
bCanInstallPatch = false
end
end
if (bCanInstallPatch) then
local patch = PlaterPatchLibrary [patchId]
Plater:Msg ("Applied Patch #" .. patchId .. ":")
for o = 1, #patch.Notes do
print (patch.Notes [o])
end
DF:Dispatch (patch.Func)
end
Plater.db.profile.patch_version = patchId
end
--do not clear patch library, when creating a new profile it'll need to re-apply patches
--PlaterPatchLibrary = nil
end
end
function Plater.SetNameplateScale(unitFrame, scale)
scale = tonumber(scale)
unitFrame.nameplateScaleAdjust = scale and (scale > 0) and scale or 1
if (DB_USE_UIPARENT) then
Plater.UpdateUIParentScale (unitFrame.PlateFrame)
else
unitFrame:SetScale (unitFrame.nameplateScaleAdjust)
Plater.UpdatePlateSize(unitFrame.PlateFrame)
end
end
---when using UIParent as the parent for the unitFrame, this function is hooked in the plateFrame OnSizeChanged script
---the goal is to adjust the the unitFrame scale when the plateFrame scale changes
---this approach also solves the issue to the unitFrame not playing correctly the animation when the nameplate is removed from the screen
---self is plateFrame, w, h aren't reliable
---@param self plateframe
---@param w any
---@param h any
function Plater.UpdateUIParentScale (self, w, h) --private
local unitFrame = self.unitFrame
if (unitFrame) then
local defaultScale = self:GetEffectiveScale()
--local defaultScale = UIParent:GetEffectiveScale()
if (defaultScale < 0.4) then
--assuming the nameplate is in process of being removed from the screen if the scale if lower than .4
unitFrame:SetScale (defaultScale)
else
--scale (adding a fine tune knob)
local scaleFineTune = max (Plater.db.profile.ui_parent_scale_tune, 0.3)
--@Ariani - March, 9
unitFrame:SetScale (defaultScale * scaleFineTune * (tonumber(unitFrame.nameplateScaleAdjust) or 1))
end
end
end
--this reset the UIParent levels to user default set on the UIParent tab
--there's an api that calls this function called Plater.RefreshNameplateStrata()
function Plater.UpdateUIParentLevels (unitFrame) --private
if (DB_USE_UIPARENT) then
--setup frame strata and levels
local profile = Plater.db.profile
local castBar = unitFrame.castBar
local buffFrame1 = unitFrame.BuffFrame
local buffFrame2 = unitFrame.BuffFrame2
local buffSpecial = unitFrame.ExtraIconFrame
--strata
unitFrame:SetFrameStrata (profile.ui_parent_base_strata)
castBar:SetFrameStrata (profile.ui_parent_cast_strata)
buffFrame1:SetFrameStrata (profile.ui_parent_buff_strata)
buffFrame2:SetFrameStrata (profile.ui_parent_buff2_strata)
buffSpecial:SetFrameStrata (profile.ui_parent_buff_special_strata)
--level
local baseLevel = unitFrame:GetFrameLevel()
local tmplevel = baseLevel + profile.ui_parent_cast_level + 3
castBar:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
tmplevel = baseLevel + profile.ui_parent_buff_level + 3
buffFrame1:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
tmplevel = baseLevel + profile.ui_parent_buff2_level + 10
buffFrame2:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
tmplevel = baseLevel + profile.ui_parent_buff_special_level + 10
buffSpecial:SetFrameLevel ((tmplevel > 0) and tmplevel or 0)
--raid-target marker adjust:
unitFrame.PlaterRaidTargetFrame:SetFrameStrata(unitFrame.healthBar:GetFrameStrata())
unitFrame.PlaterRaidTargetFrame:SetFrameLevel(unitFrame.healthBar:GetFrameLevel() + 25)
end
end
--move the target nameplate to its strata
--also need to move other frame components of this nameplate as well so the entire nameplate is up front
function Plater.UpdateUIParentTargetLevels (unitFrame) --private
if (DB_USE_UIPARENT) then
--move all frames to target strata
local targetStrata = Plater.db.profile.ui_parent_target_strata
unitFrame:SetFrameStrata (targetStrata)
unitFrame.castBar:SetFrameStrata (targetStrata)
unitFrame.BuffFrame:SetFrameStrata (targetStrata)
unitFrame.BuffFrame2:SetFrameStrata (targetStrata)
end
end
--> regional format numbers
do
local eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B
if (GetLocale() == "koKR") then
eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "", "", ""
elseif (GetLocale() == "zhCN") then
eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "", "", "亿"
elseif (GetLocale() == "zhTW") then
eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "", "", ""
else
eastAsiaMyriads_1k, eastAsiaMyriads_10k, eastAsiaMyriads_1B = "", "", ""
end
function Plater.FormatNumber (number)
if (DB_NUMBER_REGION_EAST_ASIA) then
if (number > 99999999) then
return format ("%.2f", number/100000000) .. eastAsiaMyriads_1B
elseif (number > 999999) then
return format ("%.2f", number/10000) .. eastAsiaMyriads_10k
elseif (number > 99999) then
return floor (number/10000) .. eastAsiaMyriads_10k
elseif (number > 9999) then
return format ("%.1f", (number/10000)) .. eastAsiaMyriads_10k
elseif (number > 999) then
return format ("%.1f", (number/1000)) .. eastAsiaMyriads_1k
end
return format ("%.1f", number)
else
if (number > 999999999) then
return format ("%.2fB", number/1000000000)
elseif (number > 999999) then
return format ("%.2fM", number/1000000)
elseif (number > 99999) then
return floor (number/1000) .. "K"
elseif (number > 999) then
return format ("%.1fK", (number/1000))
end
return floor (number)
end
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> event handler
--frame which will receive events
Plater.EventHandlerFrame = CreateFrame ("frame") --private
--schedule zone change
local run_zonechanged_hook = function()
if (HOOK_ZONE_CHANGED.ScriptAmount > 0) then
--for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
local globalScope = _G
for i = 1, 40 do
--run on all nameplates already created
---@type plateframe
local plateFrame = globalScope ["NamePlate" .. i]
if (plateFrame) then
for i = 1, HOOK_ZONE_CHANGED.ScriptAmount do
local globalScriptObject = HOOK_ZONE_CHANGED [i]
local unitFrame = plateFrame.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Zone Changed")
--run
unitFrame:ScriptRunHook (scriptInfo, "Zone Changed")
end
else
break
end
end
end
end
function Plater.ScheduleZoneChangeHook()
if (Plater.ScheduledZoneChangeTriggerHook) then
Plater.ScheduledZoneChangeTriggerHook:Cancel()
end
Plater.ScheduledZoneChangeTriggerHook = C_Timer.NewTimer (2, run_zonechanged_hook)
end
function Plater.RunLoadScreenHook()
for i = 1, HOOK_LOAD_SCREEN.ScriptAmount do
local hookInfo = HOOK_LOAD_SCREEN [i]
Plater.ScriptMetaFunctions.ScriptRunNoAttach (hookInfo, "Load Screen")
end
end
function platerInternal.OnOptionChanged()
for i = 1, HOOK_OPTION_CHANGED.ScriptAmount do
local hookInfo = HOOK_OPTION_CHANGED[i]
Plater.ScriptMetaFunctions.ScriptRunNoAttach(hookInfo, "Option Changed")
end
end
--store all functions for all events that will be registered inside OnInit
local last_GetShapeshiftFormID = GetShapeshiftFormID()
local eventFunctions = {
--when a unit from unatackable change its state, this event triggers several times, a schedule is used to only update once
UNIT_FLAGS = function (_, unit)
--if (unit == "player") then
-- return
--end
if not string.match(unit, "nameplate%d%d?$") then return end
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit (unit, issecure())
if (plateFrame) then
--rules if can schedule an update for unit flag event:
--has the hostility changed?
local reactionChanged = false
local curReaction = plateFrame [MEMBER_REACTION]
local newReaction = UnitReaction(unit, "player")
if not curReaction then -- in case the plater nameplate is not on screen, ensure that it can change
reactionChanged = true
elseif curReaction ~= newReaction then
if curReaction == Plater.UnitReaction.UNITREACTION_NEUTRAL and newReaction ~= curReaction then
reactionChanged = true
elseif curReaction < Plater.UnitReaction.UNITREACTION_NEUTRAL and newReaction >= Plater.UnitReaction.UNITREACTION_NEUTRAL then
reactionChanged = true
elseif curReaction > Plater.UnitReaction.UNITREACTION_NEUTRAL and newReaction <= Plater.UnitReaction.UNITREACTION_NEUTRAL then
reactionChanged = true
end
end
--can the user attack or no longer attack?
local attackableChanged = plateFrame.PlayerCannotAttack ~= not UnitCanAttack ("player", unit)
if (reactionChanged or attackableChanged or not plateFrame.unitFrame.PlaterOnScreen) then
--print ("UNIT_FLAG", plateFrame, issecure(), unit, unit and UnitName (unit))
--Plater.ScheduleUpdateForNameplate (plateFrame, unit)
Plater.RunScheduledUpdate({unitId = unit}) -- do this now
end
end
end,
UNIT_FACTION = function (_, unit)
--if (unit == "player") then
-- return
--end
--fires when somebody changes faction near the player
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit (unit, issecure())
if (plateFrame) then
Plater.ScheduleUpdateForNameplate (plateFrame)
end
end,
ACTIVE_TALENT_GROUP_CHANGED = function()
C_Timer.After (0.5, UpdatePlayerTankState)
C_Timer.After (0.5, Plater.Resources.OnSpecChanged) --~resource
C_Timer.After (2, Plater.GetSpellForRangeCheck)
C_Timer.After (2, Plater.GetHealthCutoffValue)
C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
end,
PLAYER_SPECIALIZATION_CHANGED = function()
C_Timer.After (0.5, Plater.Resources.OnSpecChanged) --~resource
C_Timer.After (2, Plater.GetSpellForRangeCheck)
C_Timer.After (2, Plater.GetHealthCutoffValue)
C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
end,
TRAIT_CONFIG_UPDATED = function()
C_Timer.After (2, Plater.GetSpellForRangeCheck)
C_Timer.After (2, Plater.GetHealthCutoffValue)
C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
end,
PLAYER_TALENT_UPDATE = function()
C_Timer.After (2, Plater.GetSpellForRangeCheck)
C_Timer.After (2, Plater.GetHealthCutoffValue)
C_Timer.After (1, Plater.DispatchTalentUpdateHookEvent)
end,
GROUP_ROSTER_UPDATE = function()
Plater.RefreshTankCache()
end,
UNIT_PET = function(_, unit)
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame and plateFrame.unitFrame.PlaterOnScreen then
if not plateFrame.unitFrame.isPerformanceUnit then
Plater.AddToAuraUpdate(plateFrame.unitFrame.unit) -- force aura update
end
Plater.ScheduleUpdateForNameplate (plateFrame)
end
end
end,
PLAYER_REGEN_DISABLED = function()
PLAYER_IN_COMBAT = true
Plater.RefreshAutoToggle(PLAYER_IN_COMBAT)
Plater.RefreshTankCache()
--Plater.UpdateAuraCache()
Plater.UpdateAllPlates(false, false, true)
--check if can run combat enter hook and schedule it true
if (HOOK_COMBAT_ENTER.ScriptAmount > 0) then
local hookTimer = C_Timer.NewTimer (0.1, Plater.ScheduleHookForCombat)
hookTimer.Event = "Enter Combat"
end
Plater.CombatTime = GetTime()
--store names and casts from 'last' combat, this is used when showing Npcs Colors and Cast Colors to bump up stuff from the last combat
table.wipe(Plater.LastCombat.npcNames)
table.wipe(Plater.LastCombat.spellNames)
--store player and pet guids for friendly affiliation
local unitCachePlayers
local unitCachePets
if (IsInRaid()) then
unitCachePlayers = platerInternal.UnitIdCache.Raid --raid1, raid2, raid3
unitCachePets = platerInternal.UnitIdCache.RaidPet --raidpet1, raidpet2, raidpet3
else
unitCachePlayers = platerInternal.UnitIdCache.Party --player, party1, party2
unitCachePets = platerInternal.UnitIdCache.PartyPet --partypet1, partypet2
end
table.wipe(platerInternal.HasFriendlyAffiliation)
for i = 1, #unitCachePlayers do
local unitGuid = UnitGUID(unitCachePlayers[i])
if (unitGuid) then
platerInternal.HasFriendlyAffiliation[unitGuid] = true
else
break
end
end
for i = 1, #unitCachePets do
local unitGuid = UnitGUID(unitCachePets[i])
if (unitGuid) then
platerInternal.HasFriendlyAffiliation[unitGuid] = true
end
end
end,
PLAYER_REGEN_ENABLED = function()
PLAYER_IN_COMBAT = false
Plater.RefreshAutoToggle(PLAYER_IN_COMBAT, true)
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
plateFrame [MEMBER_NOCOMBAT] = nil
end
--check if can run combat enter hook and schedule it true
if (HOOK_COMBAT_LEAVE.ScriptAmount > 0) then
local hookTimer = C_Timer.NewTimer (0.1, Plater.ScheduleHookForCombat)
hookTimer.Event = "Leave Combat"
end
Plater.RefreshTankCache()
Plater.UpdateAllNameplateColors()
Plater.UpdateAllPlates()
if (platerInternal.OpenOptionspanelAfterCombat) then
local OpenOptionspanelAfterCombat = platerInternal.OpenOptionspanelAfterCombat
platerInternal.OpenOptionspanelAfterCombat = nil
C_Timer.NewTimer (1.5, function() Plater.OpenOptionsPanel(unpack(OpenOptionspanelAfterCombat)) end )
end
end,
FRIENDLIST_UPDATE = function()
wipe (Plater.FriendsCache)
for i = 1, C_FriendList.GetNumFriends() do
local info = C_FriendList.GetFriendInfoByIndex (i)
if (info and info.connected and info.name) then
Plater.FriendsCache [info.name] = true
Plater.FriendsCache [DF:RemoveRealmName (info.name)] = true
end
end
if IS_WOW_PROJECT_MAINLINE then
local _, numBNetOnline = BNGetNumFriends();
for i = 1, numBNetOnline do
local accountInfo = C_BattleNet.GetFriendAccountInfo(i);
if (accountInfo and accountInfo.gameAccountInfo and accountInfo.gameAccountInfo.isOnline and accountInfo.gameAccountInfo.characterName) then
Plater.FriendsCache [accountInfo.gameAccountInfo.characterName] = true
end
end
else
for i = 1, BNGetNumFriends() do
local presenceID, presenceName, battleTag, isBattleTagPresence, toonName, toonID, client, isOnline, lastOnline, isAFK, isDND, messageText, noteText, isRIDFriend, broadcastTime, canSoR = BNGetFriendInfo (i)
if (isOnline and toonName) then
Plater.FriendsCache [toonName] = true
end
end
end
--let's not trigger a full update on all plates because a friend is now online
--Plater.UpdateAllPlates()
end,
RAID_TARGET_UPDATE = function()
Plater.UpdateRaidMarkersOnAllNameplates()
end,
QUEST_REMOVED = function()
Plater.QuestLogUpdated()
end,
QUEST_ACCEPTED = function()
Plater.QuestLogUpdated()
end,
QUEST_ACCEPT_CONFIRM = function()
Plater.QuestLogUpdated()
end,
QUEST_COMPLETE = function()
Plater.QuestLogUpdated()
end,
QUEST_POI_UPDATE = function()
Plater.QuestLogUpdated()
end,
QUEST_QUERY_COMPLETE = function()
Plater.QuestLogUpdated()
end,
QUEST_DETAIL = function()
Plater.QuestLogUpdated()
end,
QUEST_FINISHED = function()
Plater.QuestLogUpdated()
end,
QUEST_GREETING = function()
Plater.QuestLogUpdated()
end,
QUEST_LOG_UPDATE = function()
Plater.QuestLogUpdated()
end,
UNIT_QUEST_LOG_CHANGED = function()
Plater.QuestLogUpdated()
end,
PLAYER_FOCUS_CHANGED = function()
Plater.OnPlayerTargetChanged()
end,
PLAYER_TARGET_CHANGED = function()
Plater.OnPlayerTargetChanged()
end,
PLAYER_UPDATE_RESTING = function()
Plater.RefreshAutoToggle()
end,
--update the unit name, triggered when the client receives the rest of the information about an unit
UNIT_NAME_UPDATE = function (_, unitID)
if (unitID) then
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit (unitID)
if (plateFrame and plateFrame.unitFrame.PlaterOnScreen) then
local unitFrame = plateFrame.unitFrame
local unitName = UnitName (unitID)
local unitNameTranslit = unitName
if DB_USE_NAME_TRANSLIT then
unitNameTranslit = LibTranslit:Transliterate(unitName, TRANSLIT_MARK)
end
plateFrame [MEMBER_NAME] = unitNameTranslit
plateFrame [MEMBER_NAMELOWER] = lower (plateFrame [MEMBER_NAME])
plateFrame.unitNameInternal = unitName
unitFrame [MEMBER_NAME] = plateFrame [MEMBER_NAME]
unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
unitFrame.unitNameInternal = unitName
if (plateFrame.IsSelf) then
--name isn't shown in the personal bar
unitFrame.healthBar.unitName:SetText ("")
return
end
--schedule an name update on this nameplate
unitFrame.ScheduleNameUpdate = true
end
end
end,
ENCOUNTER_END = function()
Plater.CurrentEncounterID = nil
Plater.CurrentEncounterName = nil
Plater.CurrentEncounterDifficultyId = nil
Plater.LatestEncounter = time()
end,
ENCOUNTER_START = function (_, encounterID, encounterName, difficultyID)
Plater.CurrentEncounterID = encounterID
Plater.CurrentEncounterName = encounterName
Plater.CurrentEncounterDifficultyId = difficultyID
local _, zoneType = GetInstanceInfo()
if (zoneType == "raid") then
table.wipe (DB_CAPTURED_SPELLS)
end
end,
CHALLENGE_MODE_START = function()
table.wipe (DB_CAPTURED_SPELLS)
end,
ZONE_CHANGED_NEW_AREA = function()
if (InCombatLockdown()) then
C_Timer.After (1, function() Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA") end)
return
end
Plater.CurrentEncounterID = nil
local pvpType, isFFA, faction = (GetZonePVPInfo or C_PvP.GetZonePVPInfo)()
Plater.ZonePvpType = pvpType
Plater.UpdateBgPlayerRoleCache()
local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
--reset when entering in a battleground
if (instanceType == "pvp") then
table.wipe (DB_CAPTURED_SPELLS)
end
Plater.ZoneInstanceType = instanceType
Plater.ZoneName = name
IS_IN_OPEN_WORLD = Plater.ZoneInstanceType == "none"
IS_IN_INSTANCE = Plater.ZoneInstanceType == "raid" or Plater.ZoneInstanceType == "party"
Plater.UpdateAllPlates()
Plater.RefreshAutoToggle()
--hooks
Plater.ScheduleZoneChangeHook()
if (Plater.PlayerEnteringWorld) then
Plater.PlayerEnteringWorld = false
C_Timer.After (1, Plater.RunLoadScreenHook)
end
end,
ZONE_CHANGED_INDOORS = function()
Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA")
end,
ZONE_CHANGED = function()
Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA")
end,
PLAYER_ENTERING_WORLD = function(_, isInitialLogin, isReloadingUi)
Plater.db.profile.login_counter = Plater.db.profile.login_counter + 1
Plater.ScheduleRunFunctionForEvent (1, "ZONE_CHANGED_NEW_AREA")
Plater.ScheduleRunFunctionForEvent (1, "FRIENDLIST_UPDATE")
Plater.PlayerGuildName = GetGuildInfo ("player")
if (not Plater.PlayerGuildName or Plater.PlayerGuildName == "") then
Plater.PlayerGuildName = "ThePlayerHasNoGuildName/30Char"
--somethimes guild information isn't available at the login
C_Timer.After (10, delayed_guildname_check)
end
local pvpType, isFFA, faction = (GetZonePVPInfo or C_PvP.GetZonePVPInfo)()
Plater.ZonePvpType = pvpType
local name, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo()
Plater.ZoneInstanceType = instanceType
--> ensure resource on target consistency after login:
local resourcesOnTarget = GetCVar ("nameplateResourceOnTarget") == CVAR_ENABLED
if resourcesOnTarget then
PlaterDBChr.resources_on_target = true
if (not InCombatLockdown()) then
SetCVar ("nameplateResourceOnTarget", CVAR_DISABLED) -- reset this to false always, as it conflicts
end
end
-- this seems to be gone as of 18.12.2020
--if Plater.db.profile.plate_config.friendlynpc.quest_enabled and not InCombatLockdown() then
--SetCVar("showQuestTrackingTooltips", 1) -- ensure it is turned on...
--end
--create the frame to hold the plater resoruce bar
Plater.Resources.CreateMainResourceFrame() --~resource
--run hooks on load screen
if (HOOK_LOAD_SCREEN.ScriptAmount > 0) then
Plater.PlayerEnteringWorld = true
end
end,
PLAYER_LOGOUT = function()
--Plater.SaveConsoleVariables()
end,
VARIABLES_LOADED = function()
C_Timer.After (0.1, Plater.ForceCVars)
C_Timer.After (0.2, Plater.RestoreProfileCVars)
C_Timer.After (0.3, Plater.UpdatePlateClickSpace)
C_Timer.After (0.4, function()
Plater.RefreshAutoToggle(InCombatLockdown()) -- refresh this
Plater.UpdateBaseNameplateOptions()
end)
-- hook CVar saving
hooksecurefunc('SetCVar', Plater.SaveConsoleVariables)
if C_CVar and C_CVar.SetCVar then
hooksecurefunc(C_CVar, 'SetCVar', Plater.SaveConsoleVariables)
end
hooksecurefunc('ConsoleExec', function(console)
local par1, par2, par3 = console:match('^(%S+)%s+(%S+)%s*(%S*)')
if par1 then
if par1:lower() == 'set' then -- /console SET cvar value
Plater.SaveConsoleVariables(par2, par3)
else -- /console cvar value
Plater.SaveConsoleVariables(par1, par2)
end
end
end)
end,
--many times at saved variables load the spell database isn't loaded yet
PLAYER_LOGIN = function()
--C_Timer.After (0.1, Plater.GetSpellForRangeCheck)
-- ensure OmniCC settings are up to date
C_Timer.After (1, Plater.RefreshOmniCCGroup)
--wait more time for the talents information be received from the server
C_Timer.After (4, Plater.GetHealthCutoffValue)
C_Timer.After (2, Plater.ScheduleZoneChangeHook)
C_Timer.After (5, function()
local petGUID = UnitGUID ("playerpet")
if (petGUID) then
local entry = {ownerGUID = Plater.PlayerGUID, ownerName = UnitName("player"), petName = UnitName("playerpet"), time = time()}
Plater.PlayerPetCache [petGUID] = entry
end
end)
--if the user just used a /reload to enable ui parenting, auto adjust the fine tune scale
--the uiparent fine tune scale initially: after testing and playing around with it, I think it should be 1 / UIParent:GetEffectiveScale() and scaling should be done by multiplying defaultScale * scaleFineTune
if (Plater.db.profile.use_ui_parent_just_enabled) then
Plater.db.profile.use_ui_parent_just_enabled = false
if (Plater.db.profile.ui_parent_scale_tune == 0) then
--@Ariani - march 9
Plater.db.profile.ui_parent_scale_tune = 1 / UIParent:GetEffectiveScale()
--@Tercio:
--if (UIParent:GetEffectiveScale() < 1) then
-- Plater.db.profile.ui_parent_scale_tune = 1 - UIParent:GetEffectiveScale()
--end
end
end
if (not Plater.db.profile.number_region_first_run) then
if (GetLocale() == "koKR") then
Plater.db.profile.number_region = "eastasia"
elseif (GetLocale() == "zhCN") then
Plater.db.profile.number_region = "eastasia"
elseif (GetLocale() == "zhTW") then
Plater.db.profile.number_region = "eastasia"
else
Plater.db.profile.number_region = "western"
end
Plater.db.profile.number_region_first_run = true
end
if (Plater.db.profile.reopoen_options_panel_on_tab) then
C_Timer.After (2, function()
Plater.OpenOptionsPanel()
PlaterOptionsPanelContainer:SelectTabByIndex (Plater.db.profile.reopoen_options_panel_on_tab)
Plater.db.profile.reopoen_options_panel_on_tab = false
end)
end
--run hooks on player logon
if (HOOK_PLAYER_LOGON.ScriptAmount > 0) then
C_Timer.After (1, function()
for i = 1, HOOK_PLAYER_LOGON.ScriptAmount do
local hookInfo = HOOK_PLAYER_LOGON [i]
Plater.ScriptMetaFunctions.ScriptRunNoAttach (hookInfo, "Player Logon")
end
end)
end
--check addons incompatibility
--> Plater has issues with ElvUI due to be using the same namespace for unitFrame and healthBar
C_Timer.After (15, function()
if ((IsAddOnLoaded or C_AddOns.IsAddOnLoaded) ("ElvUI")) then
if (ElvUI[1] and ElvUI[1].private and ElvUI[1].private.nameplates and ElvUI[1].private.nameplates.enable) then
Plater:Msg ("'ElvUI Nameplates' and 'Plater Nameplates' are enabled and both nameplates won't work together.")
Plater:Msg ("You may disable ElvUI Nameplates at /elvui > Nameplates section or you may disable Plater at the addon control panel.")
end
end
end)
-- ensure resources are up to date
C_Timer.After (3, Plater.Resources.OnSpecChanged)
-- translate NPC_CACHE entries if needed
C_Timer.After (10, Plater.TranslateNPCCache)
end,
DISPLAY_SIZE_CHANGED = function()
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame.PlaterOnScreen then
Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
end
end
Plater.UpdateAllPlates (true)
end,
UI_SCALE_CHANGED = function()
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
end
Plater.UpdateAllPlates (true)
end,
PLAYER_SOFT_INTERACT_CHANGED = function(_, arg1, arg2)
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame.PlaterOnScreen then
if plateFrame [MEMBER_GUID] == arg1 or plateFrame [MEMBER_GUID] == arg2 then
Plater.UpdateSoftInteractTarget(plateFrame, true)
end
end
end
end,
PLAYER_SOFT_FRIEND_CHANGED = function(_, arg1, arg2)
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame.PlaterOnScreen then
if plateFrame [MEMBER_GUID] == arg1 or plateFrame [MEMBER_GUID] == arg2 then
Plater.UpdateSoftInteractTarget(plateFrame, true)
end
end
end
end,
PLAYER_SOFT_ENEMY_CHANGED = function(_, arg1, arg2)
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame.PlaterOnScreen then
if plateFrame [MEMBER_GUID] == arg1 or plateFrame [MEMBER_GUID] == arg2 then
Plater.UpdateSoftInteractTarget(plateFrame, true)
end
end
end
end,
--~created ~events ~oncreated
---@param event string
---@param plateFrame plateframe
NAME_PLATE_CREATED = function (event, plateFrame)
--ViragDevTool_AddData({ctime = GetTime(), unit = plateFrame [MEMBER_UNITID] or "nil", stack = debugstack()}, "NAME_PLATE_CREATED - " .. (plateFrame [MEMBER_UNITID] or "nil"))
--> create the unitframe
local unitFrameOptions = {
ShowPowerBar = false,
ShowBorder = false,
CanModifyHealhBarColor = false,
ShowTargetOverlay = false,
ShowUnitName = false, --let Plater control the unit name
ClearUnitOnHide = false, --let Plater control when the cleanup is execute on the unit frame
}
local healthBarOptions = {
ShowHealingPrediction = false,
ShowShields = false,
}
local castBarOptions = {
FadeInTime = 0.02,
FadeOutTime = 0.66,
SparkHeight = 20,
LazyUpdateCooldown = 0.1,
FillOnInterrupt = false,
HideSparkOnInterrupt = false,
}
local powerBarOptions = {
ShowAlternatePower = false,
}
--community patch by Ariani#0960 (discord)
--make the unitFrame be parented to UIParent allowing frames to be moved between strata levels
--March 3rd, 2019
local newUnitFrame
if (DB_USE_UIPARENT) then
--TODO: why is it not showing properly when V / V hide / show but when leaving / entering scren??? -> plateFrame for now...
--when using UIParent as the unit frame parent, adjust the unitFrame scale to be equal to blizzard plateFrame
newUnitFrame = DF:CreateUnitFrame (UIParent, plateFrame:GetName() .. "PlaterUnitFrame", unitFrameOptions, healthBarOptions, castBarOptions, powerBarOptions)
newUnitFrame:SetAllPoints()
newUnitFrame:SetFrameStrata ("BACKGROUND")
--plateFrame:HookScript("OnSizeChanged", Plater.UpdateUIParentScale)
--create a 33ms show animation played when the nameplate is added in the screen
--nevermind, unitFrame childs are kepping the last alpha value of the animation instead of reseting to their defaults
--i'm considering this a bug in the animation API from the game client
--at the moment this cannot be used
--newUnitFrame.ShowUIParentAnimation = DF:CreateAnimationHub (newUnitFrame, nil, function(self) Plater.UpdateUIParentScale (self:GetParent().PlateFrame) end)
--DF:CreateAnimation (newUnitFrame.ShowUIParentAnimation, "scale", 1, 0.033, .5, .5, 1, 1)
--DF:CreateAnimation (newUnitFrame.ShowUIParentAnimation, "alpha", 1, 0.033, .5, 1)
--end of patch
newUnitFrame.IsUIParent = true --expose to scripts the unitFrame is a UIParent child
else
newUnitFrame = DF:CreateUnitFrame (plateFrame, plateFrame:GetName() .. "PlaterUnitFrame", unitFrameOptions, healthBarOptions, castBarOptions, powerBarOptions)
end
plateFrame.unitFrame = newUnitFrame
--plateFrame.unitFrame:SetPoint("center", plateFrame)
plateFrame.unitFrame:EnableMouse(false)
--mix plater functions (most are for scripting support) into the unit frame
DF:Mixin(newUnitFrame, Plater.ScriptMetaFunctions)
--OnHide handler
newUnitFrame:HookScript("OnHide", newUnitFrame.OnHideWidget)
--OnHealthUpdate
newUnitFrame.healthBar:SetHook("OnHealthChange", Plater.OnHealthChange)
newUnitFrame.healthBar:SetHook("OnHealthMaxChange", Plater.OnHealthMaxChange)
--register details framework hooks
newUnitFrame.castBar:SetHook("OnShow", Plater.CastBarOnShow_Hook)
hooksecurefunc(newUnitFrame.castBar, "OnEvent", Plater.CastBarOnEvent_Hook)
hooksecurefunc(newUnitFrame.castBar, "OnTick", Plater.CastBarOnTick_Hook)
newUnitFrame.HasHooksRegistered = true
--to ensure all applies
newUnitFrame:UpdateTargetOverlay()
--backup the unit frame address so we can restore it in case a script messes up and override the unit frame
plateFrame.unitFramePlater = newUnitFrame
--set proprieties
plateFrame.Plater = true
plateFrame.unitFrame.Plater = true
plateFrame.unitFrame.PlateFrame = plateFrame
plateFrame.isNamePlate = true
plateFrame.unitFrame.isNamePlate = true
plateFrame.unitFrame.IsUnitNameplate = true
plateFrame.NameAnchor = 0
plateFrame.unitFrame.healthBar.isNamePlate = true
plateFrame.unitFrame.RefreshID = 0
-- "PlaterMainAuraIcon"
-- "PlaterSecondaryAuraIcon"
--> buff frames
--main buff frame
plateFrame.unitFrame.BuffFrame = CreateFrame ("frame", plateFrame.unitFrame:GetName() .. "BuffFrame1", plateFrame.unitFrame, BackdropTemplateMixin and "BackdropTemplate")
plateFrame.unitFrame.BuffFrame.amountAurasShown = 0
plateFrame.unitFrame.BuffFrame.PlaterBuffList = {}
plateFrame.unitFrame.BuffFrame.isNameplate = true
plateFrame.unitFrame.BuffFrame.unitFrame = plateFrame.unitFrame --used on resource frame anchor update
plateFrame.unitFrame.BuffFrame.healthBar = plateFrame.unitFrame.healthBar
plateFrame.unitFrame.BuffFrame.AuraCache = {}
--secondary buff frame
plateFrame.unitFrame.BuffFrame2 = CreateFrame ("frame", plateFrame.unitFrame:GetName() .. "BuffFrame2", plateFrame.unitFrame, BackdropTemplateMixin and "BackdropTemplate")
plateFrame.unitFrame.BuffFrame2.amountAurasShown = 0
plateFrame.unitFrame.BuffFrame2.PlaterBuffList = {}
plateFrame.unitFrame.BuffFrame2.isNameplate = true
plateFrame.unitFrame.BuffFrame2.unitFrame = plateFrame.unitFrame
plateFrame.unitFrame.BuffFrame2.healthBar = plateFrame.unitFrame.healthBar
plateFrame.unitFrame.BuffFrame2.AuraCache = {}
--> identify aura containers
plateFrame.unitFrame.BuffFrame.Name = "Main" --aura frame 1
plateFrame.unitFrame.BuffFrame2.Name = "Secondary" --aura frame 2
--> store the secondary anchor inside the regular buff container for speed
plateFrame.unitFrame.BuffFrame.BuffFrame2 = plateFrame.unitFrame.BuffFrame2
plateFrame.unitFrame.BuffFrame2.BuffFrame1 = plateFrame.unitFrame.BuffFrame
--> unit aura cache
plateFrame.unitFrame.AuraCache = {}
plateFrame.unitFrame.GhostAuraCache = {}
plateFrame.unitFrame.ExtraAuraCache = {}
local healthBar = plateFrame.unitFrame.healthBar
--cache the unit frame within the health and cast bars, this avoid GetParent() calls
healthBar.unitFrame = plateFrame.unitFrame
plateFrame.unitFrame.castBar.unitFrame = plateFrame.unitFrame
--> pre create the scale animation used on animations for spell hits
Plater.CreateScaleAnimation (plateFrame)
--> create the animations when the unit goes out of range
Plater.CreateAlphaAnimation (plateFrame)
--store custom indicators
plateFrame.unitFrame.CustomIndicators = {}
--> cliclable area debug
plateFrame.debugAreaTexture = plateFrame:CreateTexture (nil, "background")
plateFrame.debugAreaTexture:SetColorTexture (.1, .1, .1, .834)
plateFrame.debugAreaTexture:SetAllPoints()
plateFrame.debugAreaTexture:Hide()
plateFrame.debugAreaText = plateFrame:CreateFontString (nil, "artwork", "GameFontNormal")
plateFrame.debugAreaText:SetPoint ("bottom", plateFrame.debugAreaTexture, "top", 0, 1)
plateFrame.debugAreaText:SetText ("valid area for clicks")
plateFrame.debugAreaText:SetTextColor (.7, .7, .7)
plateFrame.debugAreaText:Hide()
--> Indicators
--container to store created indicators, they are created at run time
plateFrame.IconIndicators = {}
--> flash aggro
Plater.CreateAggroFlashFrame (plateFrame)
plateFrame.playerHasAggro = false
--> target indicators
--left and right target indicators
plateFrame.unitFrame.TargetTextures2Sides = {}
plateFrame.unitFrame.TargetTextures4Sides = {}
for i = 1, 2 do
local targetTexture = healthBar:CreateTexture (nil, "overlay")
targetTexture:SetDrawLayer ("overlay", 7)
tinsert (plateFrame.unitFrame.TargetTextures2Sides, targetTexture)
end
for i = 1, 4 do
local targetTexture = healthBar:CreateTexture (nil, "overlay")
targetTexture:SetDrawLayer ("overlay", 7)
tinsert (plateFrame.unitFrame.TargetTextures4Sides, targetTexture)
end
--two extra target glow placed outside the healthbar, one above and another below the health bar
local TargetNeonUp = plateFrame.unitFrame:CreateTexture (nil, "overlay")
TargetNeonUp:SetDrawLayer ("overlay", 7)
TargetNeonUp:SetBlendMode ("ADD")
TargetNeonUp:Hide()
plateFrame.TargetNeonUp = TargetNeonUp
plateFrame.unitFrame.TargetNeonUp = TargetNeonUp
local TargetNeonDown = plateFrame.unitFrame:CreateTexture (nil, "overlay")
TargetNeonDown:SetDrawLayer ("overlay", 7)
TargetNeonDown:SetBlendMode ("ADD")
TargetNeonDown:SetTexCoord (0, 1, 1, 0)
TargetNeonDown:Hide()
plateFrame.TargetNeonDown = TargetNeonDown
plateFrame.unitFrame.TargetNeonDown = TargetNeonDown
--> target overlay (the texture added above the nameplate when the unit is selected)
plateFrame.unitFrame.targetOverlayTexture = healthBar:CreateTexture (nil, "artwork")
plateFrame.unitFrame.targetOverlayTexture:SetDrawLayer ("artwork", 2)
plateFrame.unitFrame.targetOverlayTexture:SetBlendMode ("ADD")
plateFrame.unitFrame.targetOverlayTexture:SetAllPoints()
--> create the highlight texture (when the mouse passes over the nameplate and receives a highlight)
Plater.CreateHighlightNameplate (plateFrame)
--> health bar overlay
--> create an overlay frame that sits just above the health bar
--this is ideal for adding borders and other overlays
healthBar.FrameOverlay = CreateFrame ("frame", "$parentOverlayFrame", healthBar, BackdropTemplateMixin and "BackdropTemplate")
healthBar.FrameOverlay:SetAllPoints()
--> execute range textures and animations
--health cutoff texture shown inside the health bar
local healthCutOff = healthBar:CreateTexture (nil, "overlay")
healthCutOff:SetDrawLayer ("overlay", 7)
healthCutOff:SetTexture ([[Interface\AddOns\Plater\images\health_bypass_indicator]])
healthCutOff:SetBlendMode ("ADD")
healthCutOff:Hide()
healthBar.healthCutOff = healthCutOff
healthBar.ExecuteRangeHealthCutOff = healthCutOff --alias for scripting
local cutoffAnimationOnPlay = function()
healthCutOff:Show()
end
local cutoffAnimationOnStop = function()
healthCutOff:SetAlpha (.5)
end
local healthCutOffShowAnimation = DF:CreateAnimationHub (healthCutOff, cutoffAnimationOnPlay, cutoffAnimationOnStop)
DF:CreateAnimation (healthCutOffShowAnimation, "Scale", 1, .2, .3, .3, 1.2, 1.2)
DF:CreateAnimation (healthCutOffShowAnimation, "Scale", 2, .2, 1.2, 1.2, 1, 1)
DF:CreateAnimation (healthCutOffShowAnimation, "Alpha", 1, .2, .2, 1)
DF:CreateAnimation (healthCutOffShowAnimation, "Alpha", 2, .2, 1, .5)
healthCutOff.ShowAnimation = healthCutOffShowAnimation
--shield indicator
local shieldIndicator = healthBar:CreateTexture(nil, "overlay", nil, 7)
shieldIndicator:SetPoint("bottomleft", healthBar, "bottomleft", 0, 0)
shieldIndicator:SetHeight(3)
shieldIndicator:SetTexture([[Interface\AddOns\Plater\images\shieldbar]])
shieldIndicator:SetAlpha(0.85)
shieldIndicator:Hide()
healthBar.shieldIndicator = shieldIndicator
--overlay for the healthbar showing the healthbar of the execute (shown when the unit is on execute range)
local executeRange = healthBar:CreateTexture (nil, "border")
executeRange:SetTexture ([[Interface\AddOns\Plater\images\execute_bar]])
PixelUtil.SetPoint (executeRange, "left", healthBar, "left", 0, 0)
healthBar.executeRange = executeRange
healthBar.ExecuteRangeBar = executeRange --alias for scripting
executeRange:Hide()
--two extra execute glow placed outside the healthbar (disabled by default)
local executeGlowUp = healthBar:CreateTexture (nil, "overlay")
executeGlowUp:SetTexture ([[Interface\AddOns\Plater\images\blue_neon]])
executeGlowUp:SetTexCoord (0, 1, 0, 0.5)
executeGlowUp:SetHeight (32)
executeGlowUp:SetBlendMode ("ADD")
executeGlowUp:Hide()
PixelUtil.SetPoint (executeGlowUp, "bottomright", healthBar, "topright", 0, 0)
PixelUtil.SetPoint (executeGlowUp, "bottomleft", healthBar, "topleft", 0, 0)
healthBar.ExecuteGlowUp = executeGlowUp
local executeGlowDown = healthBar:CreateTexture (nil, "overlay")
executeGlowDown:SetTexture ([[Interface\AddOns\Plater\images\blue_neon]])
executeGlowDown:SetTexCoord (0, 1, 0.5, 1)
executeGlowDown:SetHeight (32)
executeGlowDown:SetBlendMode ("ADD")
executeGlowDown:Hide()
PixelUtil.SetPoint (executeGlowDown, "topright", healthBar, "bottomright", 0, 0)
PixelUtil.SetPoint (executeGlowDown, "topleft", healthBar, "bottomleft", 0, 0)
healthBar.ExecuteGlowDown = executeGlowDown
local executeGlowAnimationOnPlay = function (self)
self:GetParent():Show()
end
local executeGlowAnimationOnStop = function()
end
executeGlowUp.ShowAnimation = DF:CreateAnimationHub (executeGlowUp, executeGlowAnimationOnPlay, executeGlowAnimationOnStop)
DF:CreateAnimation (executeGlowUp.ShowAnimation, "Scale", 1, .2, 1, .1, 1, 1.2, "bottom", 0, 0)
DF:CreateAnimation (executeGlowUp.ShowAnimation, "Scale", 1, .2, 1, 1, 1, 1)
executeGlowDown.ShowAnimation = DF:CreateAnimationHub (executeGlowDown, executeGlowAnimationOnPlay, executeGlowAnimationOnStop)
DF:CreateAnimation (executeGlowDown.ShowAnimation, "Scale", 1, .2, 1, .1, 1, 1.2, "top", 0, 0)
DF:CreateAnimation (executeGlowDown.ShowAnimation, "Scale", 1, .2, 1, 1.1, 1, 1)
--> create the raid target widgets
--raid target inside the health bar
local raidTarget = healthBar:CreateTexture (nil, "overlay")
PixelUtil.SetPoint (raidTarget, "right", raidTarget:GetParent(), "right", -2, 0)
plateFrame.RaidTarget = raidTarget
healthBar.ExtraRaidMark = raidTarget --alias for scripting
--raid target outside the health bar
plateFrame.unitFrame.PlaterRaidTargetFrame = CreateFrame ("frame", nil, plateFrame.unitFrame, BackdropTemplateMixin and "BackdropTemplate")
--plateFrame.unitFrame.PlaterRaidTargetFrame = CreateFrame ("frame", nil, plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
local targetFrame = plateFrame.unitFrame.PlaterRaidTargetFrame
targetFrame:SetSize (22, 22)
PixelUtil.SetPoint (targetFrame, "right", healthBar, "left", -15, 0)
--icon
targetFrame.RaidTargetIcon = targetFrame:CreateTexture (nil, "artwork")
targetFrame.RaidTargetIcon:SetAllPoints()
targetFrame.RaidTargetIcon:SetTexture ([[Interface\TargetingFrame\UI-RaidTargetingIcons]])
--create show animation to play when the icon is set to show
local raidMarkAnimation = DF:CreateAnimationHub (targetFrame.RaidTargetIcon)
DF:CreateAnimation (raidMarkAnimation, "Scale", 1, .075, .1, .1, 1.2, 1.2)
DF:CreateAnimation (raidMarkAnimation, "Scale", 2, .075, 1.2, 1.2, 1, 1)
targetFrame.RaidTargetIcon.ShowAnimation = raidMarkAnimation
--> create details! integration strings
healthBar.DetailsRealTime = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
healthBar.DetailsRealTimeFromPlayer = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
healthBar.DetailsDamageTaken = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
--> tick frames (these frames are used for OnUpdate scripts)
local onTickFrame = CreateFrame ("frame", nil, plateFrame, BackdropTemplateMixin and "BackdropTemplate")
plateFrame.OnTickFrame = onTickFrame
onTickFrame.unit = plateFrame [MEMBER_UNITID]
onTickFrame.HealthBar = healthBar
onTickFrame.PlateFrame = plateFrame
onTickFrame.unitFrame = plateFrame.unitFrame
onTickFrame.BuffFrame = plateFrame.unitFrame.BuffFrame
onTickFrame.BuffFrame2 = plateFrame.unitFrame.BuffFrame2
--> create a second castbar
local castBar2 = DF:CreateCastBar (plateFrame.unitFrame, "$parentCastBar2")
plateFrame.unitFrame.castBar2 = castBar2
castBar2.Icon:ClearAllPoints()
castBar2.Icon:SetPoint("right", castBar2, "left", -1, 0)
castBar2.FrameOverlay = CreateFrame ("frame", "$parentOverlayFrame", castBar2, BackdropTemplateMixin and "BackdropTemplate")
castBar2.FrameOverlay:SetAllPoints()
--pushing the spell name up
castBar2.Text:SetParent (castBar2.FrameOverlay)
--does have a border but its alpha is zero by default
castBar2.FrameOverlay:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
castBar2.FrameOverlay:SetBackdropBorderColor (1, 1, 1, 0)
castBar2:SetPoint("topleft", plateFrame.unitFrame.castBar, "bottomleft", 0, -2)
castBar2:SetPoint("topright", plateFrame.unitFrame.castBar, "bottomright", 0, -2)
--> unit name
--regular name
plateFrame.unitFrame.unitName:SetParent (healthBar) --the name is parented to unitFrame in the framework, parent it to health bar
healthBar.unitName = plateFrame.unitFrame.unitName
healthBar.PlateFrame = plateFrame
plateFrame.unitName = plateFrame.unitFrame.unitName
plateFrame.CurrentUnitNameString = plateFrame.unitFrame.unitName
healthBar.unitName:SetDrawLayer ("overlay", 7)
--special name and title
local ActorNameSpecial = plateFrame.unitFrame:CreateFontString (nil, "artwork", "GameFontNormal")
plateFrame.unitFrame.ActorNameSpecial = ActorNameSpecial --alias for scripts
plateFrame.ActorNameSpecial = ActorNameSpecial
PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame, "center", 0, 0)
plateFrame.ActorNameSpecial:Hide()
local ActorTitleSpecial = plateFrame.unitFrame:CreateFontString (nil, "artwork", "GameFontNormal")
plateFrame.unitFrame.ActorTitleSpecial = ActorTitleSpecial --alias for scripts
plateFrame.ActorTitleSpecial = ActorTitleSpecial
PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", ActorNameSpecial, "bottom", 0, -2)
plateFrame.ActorTitleSpecial:Hide()
--> level text
local actorLevel = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
healthBar.actorLevel = actorLevel
--> life percent text
local lifePercent = healthBar:CreateFontString (nil, "overlay", "GameFontNormal")
lifePercent:SetDrawLayer ("overlay", 5)
healthBar.lifePercent = lifePercent
--> non target occlusion texture (disabled by default, it applies alayer of darkness above nameplates of units that aren't the player target)
local obscuredTexture = healthBar:CreateTexture (nil, "overlay")
obscuredTexture:SetDrawLayer ("overlay", 6)
obscuredTexture:SetAllPoints()
obscuredTexture:SetTexture ("Interface\\Tooltips\\UI-Tooltip-Background")
obscuredTexture:SetVertexColor (0, 0, 0, 1)
plateFrame.Obscured = obscuredTexture
obscuredTexture.Mask = healthBar:CreateMaskTexture(nil, "artwork")
obscuredTexture.Mask:SetAllPoints(obscuredTexture)
obscuredTexture.Mask:SetTexture([[Interface\AddOns\Plater\masks\mask1]])
obscuredTexture.Mask:Hide()
obscuredTexture:AddMaskTexture(obscuredTexture.Mask)
--> create the extra icon frame (used for the special aura)
local options = {
icon_width = 20,
icon_height = 20,
texcoord = {.1, .9, .1, .9},
show_text = true,
}
plateFrame.unitFrame.ExtraIconFrame = DF:CreateIconRow (plateFrame.unitFrame, "$parentExtraIconRow", options)
plateFrame.unitFrame.ExtraIconFrame:ClearIcons()
plateFrame.unitFrame.ExtraIconFrame.RefreshID = 0
plateFrame.unitFrame.ExtraIconFrame.AuraCache = {}
--> cache the extra icon frame inside the buff frame for speed
plateFrame.unitFrame.BuffFrame.ExtraIconFrame = plateFrame.unitFrame.ExtraIconFrame
--> Support for DBM and BigWigs Nameplate Auras
Plater.CreateBossModAuraFrame(plateFrame.unitFrame)
--> 3D model frame
plateFrame.Top3DFrame = CreateFrame ("playermodel", plateFrame:GetName() .. "3DFrame", plateFrame, "ModelWithControlsTemplate")
plateFrame.Top3DFrame:SetPoint ("bottom", plateFrame, "top", 0, -100)
plateFrame.Top3DFrame:SetSize (200, 250)
plateFrame.Top3DFrame:EnableMouse (false)
plateFrame.Top3DFrame:EnableMouseWheel (false)
plateFrame.Top3DFrame:Hide()
plateFrame.unitFrame.Top3DFrame = plateFrame.Top3DFrame
--> castbar // create custom widgets for the cast bar
--[=[
castbar members:
.Text text string to show the spell name
.background default background created in the unit frame
.extraBackground an extra background shown when it has low alpha
.barTexture cast bar texture
.Icon texture to show the spell icon
.Spark texture that follows the cast progress
.percentText text showing that cast progress, usually is the time left to finish the cast
.BorderShield border shown in the icon when the cast isn't interruptible
.FrameOverlay invisible frame shown above the nameplate, useful for creating overlays
.TargetName text showing the name for the target
--]=]
--set a UnitFrame member so scripts can get a quick reference of the unit frame from the castbar without calling for GetParent()
local castBar = plateFrame.unitFrame.castBar
castBar.PlateFrame = plateFrame
castBar.unitFrame = plateFrame.unitFrame
castBar.IsCastBar = true
castBar.isNamePlate = true
castBar.ThrottleUpdate = 0
--check if Masque is enabled on Plater and reskin the cast icon
local castIconFrame = castBar.Icon
if (Plater.Masque and not castIconFrame.Masqued) then
--as masque only skins buttons and not textures alone, work around that with a dummy frame and some meta-table shenannigans to not break anything...
--create the button frame and anchor the original icon within
local dummyMasqueIconButton = CreateFrame ("Button", castBar:GetName() .. "dummyMasqueIconButton", castBar, BackdropTemplateMixin and "BackdropTemplate")
dummyMasqueIconButton:SetSize(castIconFrame:GetSize())
castIconFrame:ClearAllPoints()
castIconFrame:SetParent(dummyMasqueIconButton)
castIconFrame:SetPoint("TOPLEFT")
castIconFrame:SetPoint("BOTTOMRIGHT")
dummyMasqueIconButton:EnableMouse (false)
if dummyMasqueIconButton.EnableMouseMotion then
dummyMasqueIconButton:EnableMouseMotion (false)
end
castIconFrame:Show()
--overwrite original and keep a reference
dummyMasqueIconButton.Icon = castIconFrame
castBar.IconOrig = castIconFrame
castBar.Icon = dummyMasqueIconButton
--now ensure all calls to the icon which are directed towards the original texture are re-routed to the icon texture
local origIndex = getmetatable(dummyMasqueIconButton).__index
local metaTable = {
__index = function (t,k)
--print(k, rawget(dummyMasqueIconButton, k), rawget(origIndex, k), castIconFrame[k])
local v = rawget(dummyMasqueIconButton, k) or rawget(origIndex, k)
if not v then
v = castIconFrame[k]--rawget(castIconFrame, k) or rawget(getmetatable(castIconFrame).__index, k) or rawget(t, k)
if type(v) == "function" then
return function(self, ...) return castIconFrame[k](castIconFrame, ...) end
end
end
return v
end,
__newindex = function (t,k,v)
rawset(t, k, v)
end
}
setmetatable(dummyMasqueIconButton, metaTable)
dummyMasqueIconButton:Show()
-- now skin!
local t = {
Icon = castIconFrame,
}
Plater.Masque.CastIcon:AddButton (dummyMasqueIconButton, t, "Frame", true)
Plater.Masque.CastIcon:ReSkin(dummyMasqueIconButton)
castIconFrame.Masqued = true
dummyMasqueIconButton.Masqued = true
end
--mix the plater functions into the castbar (most of the functions are for scripting support)
DF:Mixin (castBar, Plater.ScriptMetaFunctions)
castBar:HookScript ("OnHide", castBar.OnHideWidget)
--> create an overlay frame that sits just above the castbar
--this is ideal for adding borders and other overlays
castBar.FrameOverlay = CreateFrame ("frame", "$parentOverlayFrame", castBar, BackdropTemplateMixin and "BackdropTemplate")
castBar.FrameOverlay:SetAllPoints()
--create a frame that are always below the castbar, this frame help with extra backdrops
--textures, animations are need to be placed below the cast bar
castBar.FrameDownlayer = CreateFrame ("frame", "$parentDownlayerFrame", castBar, BackdropTemplateMixin and "BackdropTemplate")
castBar.FrameDownlayer:SetFrameLevel(castBar:GetFrameLevel()-1)
castBar.FrameDownlayer:SetAllPoints()
--pushing the spell name and timer up
castBar.Text:SetParent (castBar.FrameOverlay)
castBar.percentText:SetParent (castBar.FrameOverlay)
--does have a border but its alpha is zero by default
castBar.FrameOverlay:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
castBar.FrameOverlay:SetBackdropBorderColor (1, 1, 1, 0)
--creates the target name overlay which shows who the unit is targetting while casting (this is disabled by default)
castBar.FrameOverlay.TargetName = castBar.FrameOverlay:CreateFontString (nil, "overlay", "GameFontNormal")
castBar.TargetName = castBar.FrameOverlay.TargetName --alias for scripts
--> create the spell color texture
castBar.castColorTexture = castBar:CreateTexture("$parentCastColor", "background", nil, -6)
--create custom border frame for modeling
if (Plater.CreateCustomDesignBorder) then
Plater.CreateCustomDesignBorder(castBar)
end
--> border
--create a border using default borders from the retail game
local healthBarBorder = DF:CreateFullBorder(nil, plateFrame.unitFrame.healthBar)
local borderOffset = 0 -- -1 * UIParent:GetEffectiveScale() * (Plater.db.profile.use_ui_parent_just_enabled and Plater.db.profile.ui_parent_scale_tune or 1)
PixelUtil.SetPoint (healthBarBorder, "TOPLEFT", plateFrame.unitFrame.healthBar, "TOPLEFT", -borderOffset, borderOffset)
PixelUtil.SetPoint (healthBarBorder, "TOPRIGHT", plateFrame.unitFrame.healthBar, "TOPRIGHT", borderOffset, borderOffset)
PixelUtil.SetPoint (healthBarBorder, "BOTTOMLEFT", plateFrame.unitFrame.healthBar, "BOTTOMLEFT", -borderOffset, -borderOffset)
PixelUtil.SetPoint (healthBarBorder, "BOTTOMRIGHT", plateFrame.unitFrame.healthBar, "BOTTOMRIGHT", borderOffset, -borderOffset)
healthBarBorder.Left:SetDrawLayer("OVERLAY", 6)
healthBarBorder.Right:SetDrawLayer("OVERLAY", 6)
healthBarBorder.Top:SetDrawLayer("OVERLAY", 6)
healthBarBorder.Bottom:SetDrawLayer("OVERLAY", 6)
plateFrame.unitFrame.healthBar.border = healthBarBorder
local powerBarBorder = DF:CreateFullBorder(nil, plateFrame.unitFrame.powerBar)
PixelUtil.SetPoint (powerBarBorder, "TOPLEFT", plateFrame.unitFrame.powerBar, "TOPLEFT", -borderOffset, borderOffset)
PixelUtil.SetPoint (powerBarBorder, "TOPRIGHT", plateFrame.unitFrame.powerBar, "TOPRIGHT", borderOffset, borderOffset)
PixelUtil.SetPoint (powerBarBorder, "BOTTOMLEFT", plateFrame.unitFrame.powerBar, "BOTTOMLEFT", -borderOffset, -borderOffset)
PixelUtil.SetPoint (powerBarBorder, "BOTTOMRIGHT", plateFrame.unitFrame.powerBar, "BOTTOMRIGHT", borderOffset, -borderOffset)
powerBarBorder.Left:SetDrawLayer("OVERLAY", 6)
powerBarBorder.Right:SetDrawLayer("OVERLAY", 6)
powerBarBorder.Top:SetDrawLayer("OVERLAY", 6)
powerBarBorder.Bottom:SetDrawLayer("OVERLAY", 6)
plateFrame.unitFrame.powerBar.border = powerBarBorder
powerBarBorder:SetVertexColor (0, 0, 0, 1)
--create custom border frame for modeling
if (Plater.CreateCustomDesignBorder) then
Plater.CreateCustomDesignBorder(healthBar)
end
--create custom border frame for modeling
if (Plater.CreateCustomDesignBorder) then
Plater.CreateCustomDesignBorder(plateFrame.unitFrame.powerBar)
end
--> focus indicator
local focusIndicator = healthBar:CreateTexture(nil, "overlay")
focusIndicator:SetDrawLayer("overlay", 2)
PixelUtil.SetPoint(focusIndicator, "topleft", healthBar, "topleft", 0, 0)
PixelUtil.SetPoint(focusIndicator, "bottomright", healthBar, "bottomright", 0, 0)
focusIndicator:Hide()
healthBar.FocusIndicator = focusIndicator
plateFrame.FocusIndicator = focusIndicator
plateFrame.unitFrame.FocusIndicator = focusIndicator
--> low aggro warning
plateFrame.unitFrame.aggroGlowUpper = plateFrame:CreateTexture (nil, "background", nil, -4)
PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowUpper, "bottomleft", plateFrame.unitFrame.healthBar, "topleft", -3, 0)
PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowUpper, "bottomright", plateFrame.unitFrame.healthBar, "topright", 3, 0)
plateFrame.unitFrame.aggroGlowUpper:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
plateFrame.unitFrame.aggroGlowUpper:SetTexCoord (0, 95/128, 0, 9/64)
plateFrame.unitFrame.aggroGlowUpper:SetBlendMode ("ADD")
plateFrame.unitFrame.aggroGlowUpper:SetHeight (4)
plateFrame.unitFrame.aggroGlowUpper:Hide()
plateFrame.unitFrame.aggroGlowLower = plateFrame:CreateTexture (nil, "background", nil, -4)
PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowLower, "topleft", plateFrame.unitFrame.healthBar, "bottomleft", -3, 0)
PixelUtil.SetPoint (plateFrame.unitFrame.aggroGlowLower, "topright", plateFrame.unitFrame.healthBar, "bottomright", 3, 0)
plateFrame.unitFrame.aggroGlowLower:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
plateFrame.unitFrame.aggroGlowLower:SetTexCoord (0, 95/128, 30/64, 38/64)
plateFrame.unitFrame.aggroGlowLower:SetBlendMode ("ADD")
plateFrame.unitFrame.aggroGlowLower:SetHeight (4)
plateFrame.unitFrame.aggroGlowLower:Hide()
--> soft-interact icon
plateFrame.unitFrame.softInteractIconFrame = CreateFrame ("frame",plateFrame.unitFrame:GetName() .. "softInteractIconFrame", plateFrame, BackdropTemplateMixin and "BackdropTemplate")
plateFrame.unitFrame.softInteractIcon = plateFrame.unitFrame.softInteractIconFrame:CreateTexture("$parentIcon", "OVERLAY")
plateFrame.unitFrame.softInteractIcon:SetParent(plateFrame)
plateFrame.unitFrame.softInteractIcon:SetTexture(136243)
plateFrame.unitFrame.softInteractIcon:Show()
plateFrame.unitFrame.softInteractIconFrame:SetFrameLevel(plateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
plateFrame.unitFrame.softInteractIconFrame.Mask = plateFrame.unitFrame.softInteractIconFrame:CreateMaskTexture(nil, "OVERLAY", nil, 1)
plateFrame.unitFrame.softInteractIconFrame.Mask:Show()
plateFrame.unitFrame.softInteractIconFrame.Mask:SetAtlas("CircleMaskScalable", true)
--plateFrame.unitFrame.softInteractIconFrame.Mask:SetScale(1)
plateFrame.unitFrame.softInteractIcon:AddMaskTexture(plateFrame.unitFrame.softInteractIconFrame.Mask)
plateFrame.unitFrame.softInteractIconFrame.Mask:ClearAllPoints()
PixelUtil.SetPoint(plateFrame.unitFrame.softInteractIconFrame.Mask, "CENTER", plateFrame.unitFrame.softInteractIconFrame, "CENTER", 0, 0)
plateFrame.unitFrame.softInteractIconFrame.Mask:SetAllPoints(plateFrame.unitFrame.softInteractIcon)
plateFrame.unitFrame.softInteractIconFrame:Hide()
plateFrame.unitFrame.softInteractIcon.anchor = { side = 8, x = 0, y = 18, }
plateFrame.unitFrame.softInteractIcon.size = 24
--Plater.SetAnchor(plateFrame.unitFrame.softInteractIconFrame, plateFrame.unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, plateFrame.unitFrame.healthBar)
--Plater.SetAnchor(plateFrame.unitFrame.softInteractIconFrame, plateFrame.unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, plateFrame.unitFrame.PlateFrame)
--> name plate created hook
if (HOOK_NAMEPLATE_CREATED.ScriptAmount > 0) then
for i = 1, HOOK_NAMEPLATE_CREATED.ScriptAmount do
local globalScriptObject = HOOK_NAMEPLATE_CREATED [i]
local scriptContainer = plateFrame.unitFrame:ScriptGetContainer()
local scriptInfo = plateFrame.unitFrame:HookGetInfo (globalScriptObject, scriptContainer, "Nameplate Created")
plateFrame.unitFrame:ScriptRunHook (scriptInfo, "Nameplate Created")
end
end
end,
---@param event string
---@param unitBarId string
FORBIDDEN_NAME_PLATE_UNIT_ADDED = function (event, unitBarId)
local unitID = unitBarId
local plateFrame = C_NamePlate.GetNamePlateForUnit (unitID, true)
if (plateFrame) then -- and plateFrame.template == "ForbiddenNamePlateUnitFrameTemplate"
if (not IS_WOW_PROJECT_MAINLINE) then
-- this is for classic cast bars on blizzard default nameplates
if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
else
TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
end
end
end
end,
-- ~added ãdded
---@param event string
---@param unitBarId string
NAME_PLATE_UNIT_ADDED = function (event, unitBarId)
--ViragDevTool_AddData({ctime = GetTime(), unit = unitBarId or "nil", stack = debugstack()}, "NAME_PLATE_UNIT_ADDED - " .. (unitBarId or "nil"))
--debug for hunter faith death
-- if (select (2, UnitClass (unitBarId)) == "HUNTER") then
-- print ("nameplate added", UnitName (unitBarId))
-- end
local unitID = unitBarId
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit (unitID)
if (not plateFrame) then
--try forbidden as well for hiding stuff
plateFrame = C_NamePlate.GetNamePlateForUnit (unitID, true)
if (plateFrame) then
if (not IS_WOW_PROJECT_MAINLINE) then
if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
else
TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
end
end
end
return
end
--> check the unit frame integrity, several times some weakaura or script mess with the unit frame
if (not plateFrame.unitFrame or not plateFrame.unitFrame.SetUnit) then
plateFrame.unitFrame = plateFrame.unitFramePlater
end
--get and format the reaction to always be the value of the constants, then cache the reaction in some widgets for performance
Plater.UpdateSoftInteractTarget(plateFrame)
local reaction = UnitReaction (unitID, "player")
local isSoftInteract = plateFrame.isSoftInteract
local isObject = plateFrame.isObject
local isSoftInteractObject = isObject and isSoftInteract
reaction = reaction or isSoftInteract and Plater.UnitReaction.UNITREACTION_NEUTRAL or Plater.UnitReaction.UNITREACTION_HOSTILE
reaction = reaction <= Plater.UnitReaction.UNITREACTION_HOSTILE and Plater.UnitReaction.UNITREACTION_HOSTILE or reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY and Plater.UnitReaction.UNITREACTION_FRIENDLY or Plater.UnitReaction.UNITREACTION_NEUTRAL
local isWidgetOnlyMode = (IS_WOW_PROJECT_MAINLINE) and UnitNameplateShowsWidgetsOnly (unitID) or false
local isBattlePet = (IS_WOW_PROJECT_MAINLINE) and UnitIsBattlePet(unitID) or false
local isPlayer = UnitIsPlayer (unitID)
local isSelf = UnitIsUnit (unitID, "player")
plateFrame [MEMBER_NPCID] = nil
plateFrame.unitFrame [MEMBER_NPCID] = nil
plateFrame [MEMBER_GUID] = UnitGUID (unitID) or ""
plateFrame.unitFrame [MEMBER_GUID] = plateFrame [MEMBER_GUID]
if (not isPlayer) then
Plater.GetNpcID (plateFrame)
end
local actorType
if (unitID) then
if (isSelf) then
--> personal health bar
actorType = ACTORTYPE_PLAYER
else
--> regular nameplate
if (isPlayer) then
--unit is a player
if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
actorType = ACTORTYPE_FRIENDLY_PLAYER
else
actorType = ACTORTYPE_ENEMY_PLAYER
end
else
--the unit is a npc
if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
actorType = ACTORTYPE_FRIENDLY_NPC
elseif isBattlePet then
actorType = ACTORTYPE_FRIENDLY_NPC
else
--includes neutral npcs
actorType = ACTORTYPE_ENEMY_NPC
end
end
end
end
local isPlateEnabled = not isSoftInteractObject and (DB_PLATE_CONFIG [actorType].module_enabled and not isWidgetOnlyMode) or (not Plater.db.profile.ignore_softinteract_objects and isSoftInteractObject)
isPlateEnabled = (isPlayer or not Plater.ForceBlizzardNameplateUnits[plateFrame [MEMBER_NPCID]]) and isPlateEnabled
local blizzardPlateFrameID = tostring(plateFrame.UnitFrame)
plateFrame.unitFrame.blizzardPlateFrameID = blizzardPlateFrameID
--if (not plateFrame.UnitFrame.HasPlaterHooksRegistered) then
if not HOOKED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] then
--print(HOOKED_BLIZZARD_PLATEFRAMES[tostring(plateFrame.UnitFrame)], tostring(plateFrame.UnitFrame), plateFrame.UnitFrame.HasPlaterHooksRegistered)
--hook the retail nameplate
--plateFrame.UnitFrame:HookScript("OnShow", Plater.OnRetailNamePlateShow)
hooksecurefunc(plateFrame.UnitFrame, "Show", Plater.OnRetailNamePlateShow)
--plateFrame.UnitFrame.HasPlaterHooksRegistered = true
HOOKED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = true
end
-- we should clear stuff here, tbh...
if isPlateEnabled then
ENABLED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = false
else
plateFrame.unitFrame.PlaterOnScreen = false
ENABLED_BLIZZARD_PLATEFRAMES[blizzardPlateFrameID] = true
plateFrame.unitFrame:Hide()
-- this is for classic cast bars on blizzard default nameplates
if (not IS_WOW_PROJECT_MAINLINE) then
if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
else
TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
end
end
return
end
local requiresScheduledUpdate = false
if not NAMEPLATES_ON_SCREEN_CACHE[unitID] then
NAMEPLATES_ON_SCREEN_CACHE[unitID] = true
NUM_NAMEPLATES_ON_SCREEN = NUM_NAMEPLATES_ON_SCREEN + 1
else
requiresScheduledUpdate = true
end
--hide blizzard namepaltes
--plateFrame.UnitFrame:Hide()
Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
--show plater unit frame
plateFrame.unitFrame:Show()
plateFrame.unitFrame.PlaterOnScreen = true
Plater.AddToAuraUpdate(unitID)
-- update DBM and BigWigs nameplate auras
Plater.EnsureUpdateBossModAuras(plateFrame [MEMBER_GUID])
--save the last unit type shown in this plate
plateFrame.PreviousUnitType = plateFrame.actorType
--caching frames
local unitFrame = plateFrame.unitFrame
local castBar = unitFrame.castBar
local healthBar = unitFrame.healthBar
unitFrame.IsNeutralOrHostile = actorType == ACTORTYPE_ENEMY_NPC or actorType == ACTORTYPE_ENEMY_PLAYER
if (unitFrame.ShowUIParentAnimation) then
unitFrame.ShowUIParentAnimation:Play()
end
unitFrame.nameplateScaleAdjust = 1
if (DB_USE_UIPARENT) then
plateFrame:HookScript("OnSizeChanged", Plater.UpdateUIParentScale)
Plater.UpdateUIParentScale(plateFrame)
else
unitFrame:SetScale (1) --reset scale
end
--check if the hide hook is registered on this Blizzard nameplate
if (not unitFrame.HasHideHookRegistered) then
--onHide for unitFrame
plateFrame.unitFrame:HookScript ("OnHide", unitFrame.OnHideWidget)
--onShow for castbar
castBar:SetHook ("OnShow", Plater.CastBarOnShow_Hook)
unitFrame.HasHideHookRegistered = true
end
--powerbar are disabled by default in the settings table, called SetUnit will make the framework hide the power bar
--SetPowerBarSize() will show the power bar or the personal resource bar update also will show it
--ensure castBar is enabled properly when switching actorType or unit (with unit changing, it will be properly enabled)
local castBarWasEnabled = (unitFrame.Settings.ShowCastBar and (plateFrame.PreviousUnitType == actorType)) or (unitFrame.unit ~= unitID)
unitFrame.Settings.ShowCastBar = true -- reset to default, clearing later.
--set the unit
unitFrame:SetUnit (unitID)
--reset performance unit
unitFrame.isPerformanceUnit = nil
unitFrame.healthBar.isPerformanceUnit = nil
if (Plater.PerformanceUnits[plateFrame[MEMBER_NPCID]]) then
--print("perf", plateFrame[MEMBER_NPCID])
unitFrame.castBar:SetUnit(nil) -- no casts
Plater.RemoveFromAuraUpdate (unitID) -- no auras
unitFrame.isPerformanceUnit = true
unitFrame.healthBar.isPerformanceUnit = true
end
--show unit name, the frame work will hide it due to ShowUnitName is set to false
unitFrame.unitName:Show()
--set the unitID in the unitFrame, several script and external addons read this member, adding different variations to be compatible with all
unitFrame.unit = unitID
unitFrame.namePlateUnitToken = unitID
unitFrame.displayedUnit = unitID
unitFrame.DenyColorChange = nil
--was causing taints because MEMBER_UNITID is an actually member from the default blizzard nameplate
--so when this nameplate is reclycled to be in a proteecteed nameplate, it was causing taints
--plateFrame [MEMBER_UNITID] = unitID --causing taints
plateFrame.QuestAmountCurrent = nil
plateFrame.QuestAmountTotal = nil
plateFrame.QuestText = nil
plateFrame.QuestName = nil
plateFrame.QuestIsCampaign = nil
unitFrame.QuestAmountCurrent = nil
unitFrame.QuestAmountTotal = nil
unitFrame.QuestText = nil
unitFrame.QuestName = nil
unitFrame.QuestIsCampaign = nil
--cache the unit target id, so it doesnt need to waste cycles building up on aggro checks
unitFrame.targetUnitID = unitID .. "target"
--clear values
plateFrame.CurrentUnitNameString = plateFrame.unitName
plateFrame.isSelf = nil
plateFrame.IsSelf = nil
unitFrame.IsSelf = nil --value exposed to scripts
castBar.IsSelf = nil --value exposed to scripts
unitFrame.unitName.isRenamed = nil
plateFrame.PlayerCannotAttack = nil
plateFrame.playerGuildName = nil
plateFrame [MEMBER_NOCOMBAT] = nil
plateFrame [MEMBER_TARGET] = nil
unitFrame [MEMBER_TARGET] = nil
--reset custom size set by the user
unitFrame.customHealthBarWidth = nil
unitFrame.customHealthBarHeight = nil
unitFrame.customCastBarWidth = nil
unitFrame.customCastBarHeight = nil
unitFrame.customPowerBarWidth = nil
unitFrame.customPowerBarHeight = nil
--reset border
unitFrame.customBorderColor = nil
Plater.UpdateBorderColor (unitFrame)
--reset custom color flag
unitFrame.UsingCustomColor = nil
unitFrame.InExecuteRange = false
unitFrame.IsInRange = nil
--check if this nameplate has an update scheduled and cancel it in any case
if (plateFrame.HasUpdateScheduled) then
if (not plateFrame.HasUpdateScheduled._cancelled) then
plateFrame.HasUpdateScheduled:Cancel()
end
plateFrame.HasUpdateScheduled = nil
end
if requiresScheduledUpdate then --this COULD counter some rare issues with feign death instant cancel where nameplate hide/show is weird
Plater.ScheduleUpdateForNameplate (plateFrame, unitID, 0.5) --half second enough maybe
end
--cache values
local unitName = UnitName (unitID) or ""
local unitNameTranslit = unitName
if DB_USE_NAME_TRANSLIT then
unitNameTranslit = LibTranslit:Transliterate(unitName, TRANSLIT_MARK)
end
plateFrame [MEMBER_NAME] = unitNameTranslit
plateFrame [MEMBER_NAMELOWER] = lower (plateFrame [MEMBER_NAME])
plateFrame ["namePlateClassification"] = UnitClassification (unitID)
plateFrame.unitNameInternal = unitName
--clear name schedules
unitFrame.ScheduleNameUpdate = nil
unitFrame.InCombat = UnitAffectingCombat (unitID) or (Plater.ForceInCombatUnits[unitFrame [MEMBER_NPCID]] and PLAYER_IN_COMBAT) or false
--cache values into the unitFrame as well to reduce the overhead on scripts and hooks
unitFrame [MEMBER_NAME] = plateFrame [MEMBER_NAME]
unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
unitFrame ["namePlateClassification"] = plateFrame ["namePlateClassification"]
unitFrame.unitNameInternal = unitName
unitFrame [MEMBER_UNITID] = unitID
unitFrame.namePlateThreatPercent = 0
unitFrame.namePlateThreatIsTanking = nil
unitFrame.namePlateThreatStatus = nil
unitFrame.namePlateThreatOffTankIsTanking = false
unitFrame.namePlateThreatOffTankName = nil
plateFrame [MEMBER_REACTION] = reaction
unitFrame [MEMBER_REACTION] = reaction
unitFrame.BuffFrame [MEMBER_REACTION] = reaction
unitFrame.BuffFrame2 [MEMBER_REACTION] = reaction
unitFrame.BuffFrame.unit = unitID
unitFrame.BuffFrame2.unit = unitID
unitFrame.ExtraIconFrame.unit = unitID
plateFrame.isBattlePet = isBattlePet
unitFrame.isBattlePet = isBattlePet
plateFrame.isWidgetOnlyMode = isWidgetOnlyMode
unitFrame.isWidgetOnlyMode = isWidgetOnlyMode
plateFrame.isPlayer = isPlayer
unitFrame.isPlayer = isPlayer
--clear the custom indicators table
wipe (unitFrame.CustomIndicators)
--health amount
Plater.QuickHealthUpdate (unitFrame)
healthBar.IsAnimating = false
--hide execute indicators
healthBar.healthCutOff:Hide()
healthBar.executeRange:Hide()
healthBar.ExecuteGlowUp:Hide()
healthBar.ExecuteGlowDown:Hide()
--reset color values
healthBar.R, healthBar.G, healthBar.B, healthBar.A = nil, nil, nil, nil
--reset the frame level and strata if using UIParent as the parent of the unitFrame
--the function checks if the option is enabled, no need to check here
Plater.UpdateUIParentLevels (unitFrame)
if (unitFrame.unit) then
if (isSelf) then
--> personal health bar
plateFrame.isSelf = true
plateFrame.IsSelf = true
unitFrame.IsSelf = true --this is the value exposed to scripts
castBar.IsSelf = true --this is the value exposed to scripts
plateFrame.NameAnchor = 0
plateFrame.PlayerCannotAttack = true
unitFrame.PlayerCannotAttack = true
--do not allow the framework to show the unit name
unitFrame.Settings.ShowUnitName = false
unitFrame.unitName:Hide()
--setup castbar
unitFrame.Settings.ShowCastBar = DB_PLATE_CONFIG.player.castbar_enabled
if (not DB_PLATE_CONFIG.player.castbar_enabled) then
--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
unitFrame.castBar:SetUnit(nil, nil)
elseif not castBarWasEnabled then
unitFrame.castBar:SetUnit (unitID, unitID)
end
plateFrame.PlateConfig = DB_PLATE_CONFIG.player
Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_PLAYER, nil, true)
Plater.OnUpdateHealth (healthBar)
else
--> regular nameplate
plateFrame.PlayerCannotAttack = not UnitCanAttack ("player", unitID)
unitFrame.PlayerCannotAttack = plateFrame.PlayerCannotAttack --expose to scripts
if (isPlayer) then
--unit is a player
plateFrame.playerGuildName = GetGuildInfo (unitID)
if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
plateFrame.NameAnchor = DB_NAME_PLAYERFRIENDLY_ANCHOR
plateFrame.PlateConfig = DB_PLATE_CONFIG.friendlyplayer
Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_FRIENDLY_PLAYER, nil, true)
unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_FRIENDLY
if (DB_CASTBAR_HIDE_FRIENDLY) then
--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
unitFrame.castBar:SetUnit(nil, nil)
elseif not castBarWasEnabled then
unitFrame.castBar:SetUnit (unitID, unitID)
end
else
plateFrame.NameAnchor = DB_NAME_PLAYERENEMY_ANCHOR
plateFrame.PlateConfig = DB_PLATE_CONFIG.enemyplayer
Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_ENEMY_PLAYER, nil, true)
unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_ENEMIES
if (DB_CASTBAR_HIDE_ENEMIES) then
--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
unitFrame.castBar:SetUnit(nil, nil)
elseif not castBarWasEnabled then
unitFrame.castBar:SetUnit (unitID, unitID)
end
end
else
--the unit is a npc
if (reaction >= Plater.UnitReaction.UNITREACTION_FRIENDLY) then
plateFrame.NameAnchor = DB_NAME_NPCFRIENDLY_ANCHOR
plateFrame.PlateConfig = DB_PLATE_CONFIG.friendlynpc
Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_FRIENDLY_NPC, nil, true)
unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_FRIENDLY
if (DB_CASTBAR_HIDE_FRIENDLY) then
--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
unitFrame.castBar:SetUnit(nil, nil)
elseif not castBarWasEnabled then
unitFrame.castBar:SetUnit (unitID, unitID)
end
elseif isBattlePet then
plateFrame.NameAnchor = DB_NAME_NPCFRIENDLY_ANCHOR
plateFrame.PlateConfig = DB_PLATE_CONFIG.friendlynpc
Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_FRIENDLY_NPC, nil, true)
unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_FRIENDLY
if (DB_CASTBAR_HIDE_FRIENDLY) then
--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
unitFrame.castBar:SetUnit(nil, nil)
elseif not castBarWasEnabled then
unitFrame.castBar:SetUnit (unitID, unitID)
end
else
--includes neutral npcs
--add the npc in the npcid cache
if (Plater.ZoneInstanceType == "raid" or Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "scenario") then
if (plateFrame[MEMBER_NPCID] and plateFrame[MEMBER_NAME] ~= UNKNOWN) then --UNKNOWN is the global string from blizzard
--npcCacheInfo: [1] npc name [2] zone name [3] language
local npcCacheInfo = DB_NPCIDS_CACHE[plateFrame[MEMBER_NPCID]]
if (not npcCacheInfo) then
DB_NPCIDS_CACHE[plateFrame[MEMBER_NPCID]] = {plateFrame[MEMBER_NAME], Plater.ZoneName or "UNKNOWN", Plater.Locale or "enUS"}
else
--the npc is already cached, check if the language is different
if (npcCacheInfo[3] ~= Plater.Locale) then
--the npc is cached but the language is different, update the name
npcCacheInfo[1] = plateFrame[MEMBER_NAME]
npcCacheInfo[2] = Plater.ZoneName or "UNKNOWN"
npcCacheInfo[3] = Plater.Locale
end
end
end
end
plateFrame.NameAnchor = DB_NAME_NPCENEMY_ANCHOR
plateFrame.PlateConfig = DB_PLATE_CONFIG.enemynpc
Plater.UpdatePlateFrame (plateFrame, ACTORTYPE_ENEMY_NPC, nil, true)
unitFrame.Settings.ShowCastBar = not DB_CASTBAR_HIDE_ENEMIES
if (DB_CASTBAR_HIDE_ENEMIES) then
--CastingBarFrame_SetUnit (castBar, nil, nil, nil)
unitFrame.castBar:SetUnit(nil, nil)
elseif not castBarWasEnabled then
unitFrame.castBar:SetUnit (unitID, unitID)
end
--get threat situation to expose it to scripts already in the nameplate added hook
local isTanking, threatStatus, threatpct, threatrawpct, threatValue = UnitDetailedThreatSituation ("player", unitID)
unitFrame.namePlateThreatIsTanking = isTanking
unitFrame.namePlateThreatStatus = threatStatus
unitFrame.namePlateThreatPercent = threatpct or 0
unitFrame.namePlateThreatRawPercent = threatrawpct or 0
unitFrame.namePlateThreatValue = threatValue or 0
Plater.UpdateNameOnRenamedUnit(plateFrame)
end
end
end
end
plateFrame.actorType = actorType
unitFrame.actorType = actorType
unitFrame.ActorType = actorType --exposed to scripts
--sending true to force the color update when the color overrider is enabled
Plater.FindAndSetNameplateColor (unitFrame, true)
--icone da cast bar
Plater.UpdateCastbarIcon(castBar)
--esconde os glow de aggro
unitFrame.aggroGlowUpper:Hide()
unitFrame.aggroGlowLower:Hide()
--widget container update
if IS_WOW_PROJECT_MAINLINE then
plateFrame.unitFrame.WidgetContainer = plateFrame.UnitFrame.WidgetContainer
if plateFrame.unitFrame.WidgetContainer then
plateFrame.unitFrame.WidgetContainer:SetParent(plateFrame.unitFrame)
plateFrame.unitFrame.WidgetContainer:ClearAllPoints()
plateFrame.unitFrame.WidgetContainer:SetIgnoreParentScale(true)
plateFrame.unitFrame.WidgetContainer:SetScale(Plater.db.profile.widget_bar_scale)
Plater.SetAnchor (plateFrame.unitFrame.WidgetContainer, Plater.db.profile.widget_bar_anchor, plateFrame.unitFrame)
end
end
--can check aggro
unitFrame.CanCheckAggro = unitFrame.displayedUnit == unitID and actorType == ACTORTYPE_ENEMY_NPC and not unitFrame.isPerformanceUnit
--tick-setup
plateFrame.OnTickFrame.ThrottleUpdate = DB_TICK_THROTTLE
plateFrame.OnTickFrame.actorType = actorType
plateFrame.OnTickFrame.unit = unitID
plateFrame.OnTickFrame:SetScript ("OnUpdate", Plater.NameplateTick)
--highlight check
if (DB_HOVER_HIGHLIGHT and (not plateFrame.PlayerCannotAttack or (plateFrame.PlayerCannotAttack and DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE)) and (actorType == ACTORTYPE_ENEMY_PLAYER or actorType == ACTORTYPE_ENEMY_NPC)) then
Plater.EnableHighlight (unitFrame)
else
Plater.DisableHighlight (unitFrame)
end
--range
--Plater.CheckRange (plateFrame, true)
--resources - TODO:
Plater.Resources.UpdateResourceFramePosition() --~resource
--hooks
if (HOOK_NAMEPLATE_ADDED.ScriptAmount > 0) then
for i = 1, HOOK_NAMEPLATE_ADDED.ScriptAmount do
local globalScriptObject = HOOK_NAMEPLATE_ADDED [i]
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Nameplate Added")
--run
unitFrame:ScriptRunHook (scriptInfo, "Nameplate Added")
end
end
--tick
Plater.NameplateTick (plateFrame.OnTickFrame, 999)
unitFrame.PlaterOnScreen = true
-- add private aura anchors
Plater.HandlePrivateAuraAnchors(plateFrame.unitFrame) -- requires namePlateUnitToken, PlaterOnScreen and IsSelf to be set
--check if the cast bar test is enabled
if (Plater.IsShowingCastBarTest) then
--start a castbar test for this unit
platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
end
end,
-- ~removed
---@param event string
---@param unitBarId string
NAME_PLATE_UNIT_REMOVED = function (event, unitBarId)
--ViragDevTool_AddData({ctime = GetTime(), unit = unitBarId or "nil", stack = debugstack()}, "NAME_PLATE_UNIT_REMOVED - " .. (unitBarId or "nil"))
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit (unitBarId)
Plater.RemoveFromAuraUpdate (unitBarId) -- ensure no updates
ENABLED_BLIZZARD_PLATEFRAMES[plateFrame.unitFrame.blizzardPlateFrameID] = true -- OnRetailNamePlateShow is called first. ensure the plate might show!
if not plateFrame.unitFrame.PlaterOnScreen then
return
end
NAMEPLATES_ON_SCREEN_CACHE[unitBarId] = false
NUM_NAMEPLATES_ON_SCREEN = NUM_NAMEPLATES_ON_SCREEN - 1
--check if this nameplate has an update scheduled
if (plateFrame.HasUpdateScheduled) then
if (not plateFrame.HasUpdateScheduled._cancelled) then
plateFrame.HasUpdateScheduled:Cancel()
end
plateFrame.HasUpdateScheduled = nil
end
--debug for hunter faith death
--if (select (2, UnitClass (unitBarId)) == "HUNTER") then
-- print ("nameplate removed", UnitName (unitBarId))
--end
--hooks
if (HOOK_NAMEPLATE_REMOVED.ScriptAmount > 0) then
for i = 1, HOOK_NAMEPLATE_REMOVED.ScriptAmount do
local globalScriptObject = HOOK_NAMEPLATE_REMOVED [i]
local unitFrame = plateFrame.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Nameplate Removed")
--run
plateFrame.unitFrame:ScriptRunHook (scriptInfo, "Nameplate Removed")
end
end
plateFrame.OnTickFrame:SetScript ("OnUpdate", nil)
plateFrame.unitFrame.HighlightFrame:SetScript ("OnUpdate", nil)
plateFrame [MEMBER_QUEST] = false
plateFrame.unitFrame [MEMBER_QUEST] = false
plateFrame.QuestInfo = {}
plateFrame.unitFrame.QuestInfo = {}
plateFrame [MEMBER_TARGET] = nil
plateFrame.isObject = nil
plateFrame.unitFrame.isObject = nil
plateFrame.isSoftInteract = nil
plateFrame.unitFrame.isSoftInteract = nil
plateFrame.isSoftInteractObject = nil
plateFrame.unitFrame.isSoftInteractObject = nil
local healthBar = plateFrame.unitFrame.healthBar
if (healthBar.TargetHeight) then
healthBar:SetHeight (healthBar.TargetHeight)
end
healthBar.IsIncreasingHeight = nil
healthBar.IsDecreasingHeight = nil
--hide the highlight
--is mouse over ~highlight ~mouseover
plateFrame.unitFrame.HighlightFrame:Hide()
plateFrame.unitFrame.HighlightFrame.Shown = false
--hide target highlight
plateFrame.TargetNeonUp:Hide()
plateFrame.TargetNeonDown:Hide()
--hide threat highlight
plateFrame.unitFrame.aggroGlowUpper:Hide()
plateFrame.unitFrame.aggroGlowLower:Hide()
--> check if is running any script
plateFrame.unitFrame:OnHideWidget()
plateFrame.unitFrame.castBar:OnHideWidget()
for _, auraIconFrame in ipairs (plateFrame.unitFrame.BuffFrame.PlaterBuffList) do
auraIconFrame:OnHideWidget()
end
for _, auraIconFrame in ipairs (plateFrame.unitFrame.BuffFrame2.PlaterBuffList) do
auraIconFrame:OnHideWidget()
end
--stop animations
if plateFrame.unitFrame.BodyFlashFrame and plateFrame.unitFrame.BodyFlashFrame.animation then
plateFrame.unitFrame.BodyFlashFrame.animation:Stop()
plateFrame.unitFrame.BodyFlashFrame:Hide()
end
if plateFrame.unitFrame.healthBar.HealthFlashFrame and plateFrame.unitFrame.healthBar.HealthFlashFrame.animation then
plateFrame.unitFrame.healthBar.HealthFlashFrame.animation:Stop()
plateFrame.unitFrame.healthBar.HealthFlashFrame:Hide()
end
plateFrame.unitFrame.PlaterOnScreen = nil
--reset auras
Plater.ResetAuraContainer (plateFrame.unitFrame.BuffFrame, true, true)
Plater.HideNonUsedAuraIcons (plateFrame.unitFrame.BuffFrame)
--remove private aura anchors
Plater.HandlePrivateAuraAnchors(plateFrame.unitFrame)
--tell the framework to execute a cleanup on the unit frame, this is required since Plater set .ClearUnitOnHide to false
plateFrame.unitFrame:SetUnit (nil)
-- remove widgets
if IS_WOW_PROJECT_MAINLINE and plateFrame.unitFrame.WidgetContainer then
plateFrame.unitFrame.WidgetContainer:SetIgnoreParentScale(false)
plateFrame.unitFrame.WidgetContainer:SetParent(plateFrame)
plateFrame.unitFrame.WidgetContainer:ClearAllPoints()
plateFrame.unitFrame.WidgetContainer:SetPoint('TOP', plateFrame.castBar, 'BOTTOM')
end
--if plateFrame.UnitFrame and plateFrame.UnitFrame.HealthBarsContainerOrigParent then
-- DevTool:AddData("removing")
-- plateFrame.UnitFrame.HealthBarsContainer:SetParent(plateFrame.UnitFrame.HealthBarsContainerOrigParent)
-- plateFrame.UnitFrame.HealthBarsContainerOrigParent = nil
--end
--community patch by Ariani#0960 (discord)
--make the unitFrame be parented to UIParent allowing frames to be moved between strata levels
--March 3rd, 2019
if (DB_USE_UIPARENT) then
-- need to explicitly hide the frame now, as it is not tethered to the blizz nameplate
plateFrame.unitFrame:Hide()
end
--end of patch
end,
UNIT_INVENTORY_CHANGED = function()
UpdatePlayerTankState()
--Plater.UpdateAllNameplateColors()
--Plater.UpdateAllPlates()
end,
UPDATE_SHAPESHIFT_FORM = function()
local curTime = GetTime()
--this is to work around UPDATE_SHAPESHIFT_FORM firing for all units and not just the player... causing lag...
if last_GetShapeshiftFormID == GetShapeshiftFormID() then
return
end
last_GetShapeshiftFormID = GetShapeshiftFormID()
UpdatePlayerTankState()
Plater.UpdateAllNameplateColors()
Plater.UpdateAllPlates()
Plater.Resources.UpdateResourceFramePosition()
end,
TALENT_GROUP_ROLE_CHANGED = function()
UpdatePlayerTankState()
Plater.UpdateAllNameplateColors()
Plater.UpdateAllPlates()
end,
}
--allow other files of the addon to have access to event functions
platerInternal.Events.GetEventFunction = function(event)
return eventFunctions[event]
end
function Plater.EventHandler (_, event, ...) --private
local func = eventFunctions [event]
if (func) then
Plater.StartLogPerformanceCore("Plater-Core", "Events", event)
func (event, ...)
Plater.EndLogPerformanceCore("Plater-Core", "Events", event)
else
Plater:Msg ("no registered function for event " .. (event or "unknown event"))
end
end
Plater.EventHandlerFrame:SetScript ("OnEvent", Plater.EventHandler)
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_ENTERING_WORLD")
function Plater.RunFunctionForEvent (event, ...) --private
Plater.EventHandler (nil, event, ...)
end
local run_scheduled_event_function = function (timerObject)
local event = timerObject.event
local args = timerObject.args
Plater.RunFunctionForEvent (event, unpack (args))
end
function Plater.ScheduleRunFunctionForEvent (delay, event, ...) --private
local timer = C_Timer.NewTimer (delay, run_scheduled_event_function)
timer.event = event
timer.args = {...}
end
--function for plateFrame.UnitFrame OnShow script
--it'll hide the retail nameplate when it shown
function Plater.OnRetailNamePlateShow (self) --private
if ENABLED_BLIZZARD_PLATEFRAMES[tostring(self)] then
-- do not hide
return
end
--self:Hide()
if self:IsProtected() then
self:ClearAllPoints()
self:SetParent(nil)
if self.HealthBarsContainer then
self.HealthBarsContainerOrigParent = self.HealthBarsContainer:GetParent() or self.HealthBarsContainerOrigParent
self.HealthBarsContainer:ClearAllPoints()
--self.HealthBarsContainer:SetParent(nil)
end
--for _, f in pairs(self:GetChildren() or {}) do
-- --DevTool:AddData(f, "child")
-- if type(f) == "table" and f.IsProtected then
-- local p, ep = f:IsProtected()
-- --DevTool:AddData({p, ep, f}, "protected?")
-- if ep then
-- --DevTool:AddData(f, "protected!")
-- f:ClearAllPoints()
-- f:SetParent(nil)
-- f:Hide()
-- end
-- end
--end
if not self:IsProtected() then
self:Hide()
elseif DevTool then
DevTool:AddData(self, "protected nameplate...")
end
else
self:Hide()
end
if not SUPPORT_BLIZZARD_PLATEFRAMES then
-- should be done if events are not needed
-- CompactUnitFrame_UnregisterEvents only removes event hanlder functions
self:UnregisterAllEvents()
end
if (CompactUnitFrame_UnregisterEvents) then
CompactUnitFrame_UnregisterEvents (self)
end
end
function Plater.SetFontOutlineAndShadow (fontString, outline, shadowColor, shadowXOffSet, shadowYOffSet)
--update the outline
DF:SetFontOutline (fontString, outline)
--update shadow color and shadow offset
if (shadowColor) then
local r, g, b, a = DF:ParseColors (shadowColor)
DF:SetFontShadow (fontString, r, g, b, a, shadowXOffSet, shadowYOffSet)
end
end
---import scripts from the library database, after that apply patches and recompile scripts
function platerInternal.Scripts.UpdateFromLibrary()
Plater.ImportScriptsFromLibrary()
Plater.ApplyPatches()
--and compile all scripts and hooks
Plater.CompileAllScripts("script")
Plater.CompileAllScripts("hook")
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> addon initialization
function Plater.InitializeSavedVariables()
local PlaterDB = _G.PlaterDB
--table to store casts with SPELL_CAST_START
PlaterDB.captured_casts = PlaterDB.captured_casts or {}
--table to store auras and any spell cast
PlaterDB.captured_spells = PlaterDB.captured_spells or {}
--table to store npcIds of performance units
PlaterDB.performance_units = PlaterDB.performance_units or {}
end
function Plater.OnInit() --private --~oninit ~init
LibStub ("AceDBOptions-3.0"):GetOptionsTable (Plater.db, true) -- register this now, to ensure no default "realm", "char - realm" profiles are shown in profiles management
do
local languageCurrentVersion = 1
if (not PlaterLanguage) then
PlaterLanguage = {
language = GetLocale(),
version = languageCurrentVersion,
}
end
if (PlaterLanguage.version < languageCurrentVersion) then
--do stuff in the future
end
DF.Language.SetCurrentLanguage(addonId, PlaterLanguage.language)
end
--PlaterBackup is a table to store data that has been removed by the player might want to restore in another time
PlaterBackup = PlaterBackup or {}
Plater.InitializeSavedVariables()
Plater.RefreshDBUpvalues()
C_Timer.After(0, function()
platerInternal.CreatePerformanceUnits(Plater)
end)
Plater.UpdateBlizzardNameplateFonts()
-- do we need to support blizzard frames?
SUPPORT_BLIZZARD_PLATEFRAMES = (not DB_PLATE_CONFIG [ACTORTYPE_PLAYER].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_ENEMY_PLAYER].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_NPC].module_enabled) or (not DB_PLATE_CONFIG [ACTORTYPE_ENEMY_NPC].module_enabled)
Plater.CombatTime = GetTime()
PLAYER_IN_COMBAT = false
if (InCombatLockdown()) then
PLAYER_IN_COMBAT = true
end
Plater.Locale = GetLocale()
do --log initialization version
pcall(function()
local GetAddOnMetadata = C_AddOns and C_AddOns.GetAddOnMetadata or GetAddOnMetadata
local platerVersion = GetAddOnMetadata("Plater", "Version")
local frameworkVersion = "Framework v" .. select(2,LibStub:GetLibrary("DetailsFramework-1.0"))
local gameVersion = GetBuildInfo()
local gameLocale = Plater.Locale
local charName = UnitName("player")
platerInternal.Logs.Log("INIT | " .. platerVersion .. " | " .. frameworkVersion .. " | " .. gameVersion .. " | " .. gameLocale .. " | " .. charName)
end)
end
PlaterDB.InterruptableSpells = PlaterDB.InterruptableSpells or {}
--check if details is loaded and if the version has support for mythic+ overall event
if (Details and Details.RegistredEvents["COMBAT_MYTHICPLUS_OVERALL_READY"]) then
platerInternal.DetailsEvents = Details:CreateEventListener()
platerInternal.DetailsEvents:RegisterEvent("COMBAT_MYTHICPLUS_OVERALL_READY", function()
local interruptableSpells = {}
local combatObject = Details:GetCurrentCombat()
if (combatObject:GetCombatType() == DETAILS_SEGMENTTYPE_MYTHICDUNGEON_OVERALL) then
local utilityContainer = combatObject:GetContainer(DETAILS_ATTRIBUTE_MISC)
for index, actorObject in utilityContainer:ListActors() do
local interrupttedSpells = actorObject:GetSpellContainer("interruptwhat")
if (interrupttedSpells) then
for spellId in pairs(interrupttedSpells) do
PlaterDB.InterruptableSpells[spellId] = true --~interrupt ~interruptable
end
end
end
end
end)
end
--Plater:BossModsLink()
--character settings
PlaterDBChr = PlaterDBChr or {first_run3 = {}}
PlaterDBChr.first_run3 = PlaterDBChr.first_run3 or {}
PlaterDBChr.debuffsBanned = PlaterDBChr.debuffsBanned or {}
PlaterDBChr.buffsBanned = PlaterDBChr.buffsBanned or {}
PlaterDBChr.spellRangeCheckRangeEnemy = PlaterDBChr.spellRangeCheckRangeEnemy or {}
PlaterDBChr.spellRangeCheckRangeFriendly = PlaterDBChr.spellRangeCheckRangeFriendly or {}
--Register LDB
Plater.InitLDB()
--to fix: attempt to index field 'spellRangeCheck' (a string value)
if (type (PlaterDBChr.spellRangeCheckRangeEnemy) ~= "table") then
PlaterDBChr.spellRangeCheckRangeEnemy = {}
end
if (type (PlaterDBChr.spellRangeCheckRangeFriendly) ~= "table") then
PlaterDBChr.spellRangeCheckRangeFriendly = {}
end
--run once per profile per expansion
platerInternal.ScriptTriggers.WipeDeprecatedScriptTriggersFromProfile(Plater.db.profile)
--ensure global nameplate width/height setting is initialized
Plater.db.profile.plate_config.global_health_width = Plater.db.profile.plate_config.global_health_width or Plater.db.profile.plate_config.enemynpc.health[1]
Plater.db.profile.plate_config.global_health_height = Plater.db.profile.plate_config.global_health_height or Plater.db.profile.plate_config.enemynpc.health[2]
--range check spells
if IS_WOW_PROJECT_MAINLINE then
for specID, _ in pairs (Plater.SpecList [select (2, UnitClass ("player"))]) do
--if (PlaterDBChr.spellRangeCheckRangeEnemy [specID] == nil or not LibRangeCheck:GetHarmMaxChecker (PlaterDBChr.spellRangeCheckRangeEnemy [specID])) then
if (PlaterDBChr.spellRangeCheckRangeEnemy [specID] == nil) then
PlaterDBChr.spellRangeCheckRangeEnemy [specID] = Plater.DefaultSpellRangeList [specID]
end
--if (PlaterDBChr.spellRangeCheckRangeFriendly [specID] == nil or not LibRangeCheck:GetFriendMaxChecker(PlaterDBChr.spellRangeCheckRangeFriendly [specID])) then
if (PlaterDBChr.spellRangeCheckRangeFriendly [specID] == nil) then
PlaterDBChr.spellRangeCheckRangeFriendly [specID] = Plater.DefaultSpellRangeListF [specID]
end
end
else
local playerClass = select (3, UnitClass ("player"))
--if (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] == nil or not LibRangeCheck:GetHarmMaxChecker (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass])) then
if (PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] == nil) then
PlaterDBChr.spellRangeCheckRangeEnemy [playerClass] = Plater.DefaultSpellRangeList [playerClass]
end
--if (PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] == nil or not LibRangeCheck:GetFriendMaxChecker(PlaterDBChr.spellRangeCheckRangeFriendly [playerClass])) then
if (PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] == nil) then
PlaterDBChr.spellRangeCheckRangeFriendly [playerClass] = Plater.DefaultSpellRangeListF [playerClass]
end
end
Plater.RangeCheckRangeEnemy = nil
Plater.RangeCheckRangeFriendly = nil
Plater.RangeCheckFunctionEnemy = nil
Plater.RangeCheckFunctionFriendly = nil
LibRangeCheck.RegisterCallback(Plater, LibRangeCheck.CHECKERS_CHANGED, function() Plater.GetSpellForRangeCheck() end)
--who is the player
Plater.PlayerGUID = UnitGUID ("player")
Plater.PlayerClass = select (2, UnitClass ("player"))
--track player auras
Plater.AddToAuraUpdate("player")
--load scripts from the script library
platerInternal.Scripts.UpdateFromLibrary()
--check if masque is installed and add support for masque addon
local Masque = LibStub ("Masque", true)
if (Masque and Plater.db.profile.enable_masque_support) then
Plater.Masque = {}
Plater.Masque.Callback = function(group, option, value)
group:ReSkin(true)
end
Plater.Masque.AuraFrame1 = Masque:Group ("Plater Nameplates", "Aura Frame 1")
Plater.Masque.AuraFrame1:RegisterCallback(Plater.Masque.Callback)
Plater.Masque.AuraFrame2 = Masque:Group ("Plater Nameplates", "Aura Frame 2")
Plater.Masque.AuraFrame2:RegisterCallback(Plater.Masque.Callback)
Plater.Masque.BuffSpecial = Masque:Group ("Plater Nameplates", "Buff Special")
Plater.Masque.BuffSpecial:RegisterCallback(Plater.Masque.Callback)
Plater.Masque.BossModIconFrame = Masque:Group ("Plater Nameplates", "Boss Mod Icons")
Plater.Masque.BossModIconFrame:RegisterCallback(Plater.Masque.Callback)
Plater.Masque.CastIcon = Masque:Group ("Plater Nameplates", "Cast Bar Icons")
Plater.Masque.CastIcon:RegisterCallback(Plater.Masque.Callback)
end
--set some cvars that we want to set
function Plater.ForceCVars()
if (InCombatLockdown()) then
return C_Timer.After (1, function() Plater.ForceCVars() end)
end
SetCVar ("nameplateMinAlpha", 0.90135484)
SetCVar ("nameplateMinAlphaDistance", -10^5.2)
SetCVar ("nameplateSelectedAlpha", 1)
SetCVar ("nameplateNotSelectedAlpha", 1)
SetCVar ("nameplateRemovalAnimation", DB_USE_QUICK_HIDE and 0 or 1)
SetCVar ("nameplateShowFriendlyBuffs", 0)
SetCVar ("nameplateShowPersonalCooldowns", 0)
if IS_WOW_PROJECT_MAINLINE and not GetCVar("nameplatePlayerMaxDistance") then -- this is 10.1 workaround.
SetCVar ("nameplatePlayerMaxDistance", 60)
end
end
--schedule data update
--C_Timer.After (1, Plater.GetSpellForRangeCheck)
C_Timer.After (4, Plater.GetHealthCutoffValue)
--Mythic Dungeon Tools
platerInternal.InstallMDTHooks()
--hooking scripts has load conditions, here it creates a load filter for plater
--so when a load condition is changed it reload hooks
function Plater.HookLoadCallback (encounterID) --private
Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "HookLoadCallback")
Plater.EncounterID = encounterID
Plater.WipeAndRecompileAllScripts ("hook", true) --sending true to not dispatch a hotReload in the scripts
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "HookLoadCallback")
end
DF:CreateLoadFilterParser (Plater.HookLoadCallback)
--refresh the color overrider
Plater.RefreshColorOverride()
--update the current zone
local _, zoneType = GetInstanceInfo()
Plater.ZoneInstanceType = zoneType
--check if is the first time Plater is running in the account or in the character
local check_first_run = function()
if (not UnitGUID ("player")) then
C_Timer.After (1, Plater.CheckFirstRun)
return
end
if (not Plater.db.profile.first_run3) then
C_Timer.After (15, Plater.SetCVarsOnFirstRun)
--enable UIParent nameplates for new installs of Plater
--this setting is disabled by default and will be enabled for new people
Plater.db.profile.use_ui_parent = true
--adjust the fine tune to player's screen scale
Plater.db.profile.ui_parent_scale_tune = 1 / UIParent:GetEffectiveScale()
elseif (not PlaterDBChr.first_run3 [UnitGUID ("player")]) then
--do not run cvars for individual characters
C_Timer.After (15, Plater.SetCVarsOnFirstRun)
else
Plater.CreatePlaterButtonAtInterfaceOptions()
end
end
function Plater.CheckFirstRun() --private
check_first_run()
end
Plater.CheckFirstRun()
--load a table with a copy of the plateConfigs table to be accessed by scripts
Plater.UpdateSettingsCache()
--events
Plater.EventHandlerFrame:RegisterEvent ("NAME_PLATE_CREATED")
Plater.EventHandlerFrame:RegisterEvent ("NAME_PLATE_UNIT_ADDED")
Plater.EventHandlerFrame:RegisterEvent ("FORBIDDEN_NAME_PLATE_UNIT_ADDED")
Plater.EventHandlerFrame:RegisterEvent ("NAME_PLATE_UNIT_REMOVED")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_TARGET_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_FOCUS_CHANGED")
if IS_WOW_PROJECT_MAINLINE then
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_INTERACT_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_FRIEND_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SOFT_ENEMY_CHANGED")
end
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_REGEN_DISABLED")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_REGEN_ENABLED")
Plater.EventHandlerFrame:RegisterEvent ("ZONE_CHANGED_NEW_AREA")
Plater.EventHandlerFrame:RegisterEvent ("ZONE_CHANGED_INDOORS")
Plater.EventHandlerFrame:RegisterEvent ("ZONE_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("FRIENDLIST_UPDATE")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_LOGOUT")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_UPDATE_RESTING")
Plater.EventHandlerFrame:RegisterEvent ("RAID_TARGET_UPDATE")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_ACCEPTED")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_REMOVED")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_ACCEPT_CONFIRM")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_COMPLETE")
if IS_WOW_PROJECT_MAINLINE then
Plater.EventHandlerFrame:RegisterEvent ("QUEST_POI_UPDATE")
end
Plater.EventHandlerFrame:RegisterEvent ("QUEST_DETAIL")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_FINISHED")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_GREETING")
Plater.EventHandlerFrame:RegisterEvent ("QUEST_LOG_UPDATE")
Plater.EventHandlerFrame:RegisterEvent ("UNIT_QUEST_LOG_CHANGED")
if IS_WOW_PROJECT_MAINLINE then
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_SPECIALIZATION_CHANGED")
Plater.EventHandlerFrame:RegisterEvent (C_Traits and "TRAIT_CONFIG_UPDATED" or "PLAYER_TALENT_UPDATE")
elseif IS_WOW_PROJECT_CLASSIC_WRATH then
Plater.EventHandlerFrame:RegisterEvent ("ACTIVE_TALENT_GROUP_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_TALENT_UPDATE")
end
Plater.EventHandlerFrame:RegisterEvent ("ENCOUNTER_START")
Plater.EventHandlerFrame:RegisterEvent ("ENCOUNTER_END")
if IS_WOW_PROJECT_MAINLINE then
Plater.EventHandlerFrame:RegisterEvent ("CHALLENGE_MODE_START")
end
Plater.EventHandlerFrame:RegisterEvent ("UNIT_NAME_UPDATE")
Plater.EventHandlerFrame:RegisterEvent ("UNIT_FLAGS")
Plater.EventHandlerFrame:RegisterEvent ("UNIT_FACTION")
Plater.EventHandlerFrame:RegisterEvent ("DISPLAY_SIZE_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("UI_SCALE_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("GROUP_ROSTER_UPDATE")
Plater.EventHandlerFrame:RegisterEvent ("UNIT_PET")
if IS_WOW_PROJECT_NOT_MAINLINE then -- tank spec detection
Plater.EventHandlerFrame:RegisterEvent ("UNIT_INVENTORY_CHANGED")
Plater.EventHandlerFrame:RegisterEvent ("UPDATE_SHAPESHIFT_FORM")
if IS_WOW_PROJECT_CLASSIC_WRATH then
Plater.EventHandlerFrame:RegisterEvent ("TALENT_GROUP_ROLE_CHANGED")
end
elseif Plater.PlayerClass == "DRUID" then
Plater.EventHandlerFrame:RegisterEvent ("UPDATE_SHAPESHIFT_FORM")
end
Plater.EventHandlerFrame:RegisterEvent ("PLAYER_LOGIN")
Plater.EventHandlerFrame:RegisterEvent ("VARIABLES_LOADED")
--power update for hooking scripts
local hookPowerEventFrame = CreateFrame ("frame")
--hookPowerEventFrame:RegisterUnitEvent ("UNIT_POWER_UPDATE", "player")
hookPowerEventFrame:RegisterUnitEvent ("UNIT_POWER_FREQUENT", "player")
hookPowerEventFrame:RegisterUnitEvent ("UNIT_MAXPOWER", "player")
--hookPowerEventFrame:RegisterUnitEvent ("UNIT_DISPLAYPOWER", "player")
--hookPowerEventFrame:RegisterUnitEvent ("UNIT_POWER_BAR_HIDE", "player")
hookPowerEventFrame:SetScript ("OnEvent", function(self, event, target, powerType)
-- target is always 'player'
if (HOOK_PLAYER_POWER_UPDATE.ScriptAmount > 0) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if (plateFrame) then
for i = 1, HOOK_PLAYER_POWER_UPDATE.ScriptAmount do
local globalScriptObject = HOOK_PLAYER_POWER_UPDATE [i]
local unitFrame = plateFrame.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Player Power Update")
--run
unitFrame:ScriptRunHook (scriptInfo, "Player Power Update", unitFrame, powerType)
end
end
end
end
end)
--this should pull the resources bar up and down based on if the target has debuffs shown on it or not
function Plater.UpdateResourceFrameAnchor (buffFrame)
if (Plater.CurrentTargetResourceFrame) then
--if this buffFrame anchored in the current target?
if (buffFrame.unitFrame [MEMBER_TARGET]) then
--has any aura shown?
--algumas vezes o amtdebbufs esta retornando 0 por alguma razao...
if (buffFrame.amountAurasShown > 0 or (DB_AURA_SEPARATE_BUFFS and buffFrame.BuffFrame2.amountAurasShown > 0)) then
Plater.CurrentTargetResourceFrame:SetPoint ("bottom", buffFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target + Plater.db.profile.resources.y_offset_target_withauras)
else
Plater.CurrentTargetResourceFrame:SetPoint ("bottom", buffFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target)
end
end
end
end
--this function is declared inside 'NamePlateDriverMixin' at Blizzard_NamePlates.lua
--self if the nameplate driver frame: _G.NamePlateDriverFrame
--at the moment self isn't being used ~personal
function Plater.UpdatePersonalBar (self)
local showSelf = GetCVarBool ("nameplateShowSelf") and Plater.db.profile.plate_config.player.module_enabled
if (not showSelf) then
if PlaterDBChr.resources_on_target then
Plater.UpdateResourceFrame()
end
return
end
--show Plater power bar for the player personal nameplate
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit ("player")
if (plateFrame) then
if (not plateFrame.Plater) then
return
end
if (NamePlateDriverFrame.classNamePlatePowerBar and NamePlateDriverFrame.classNamePlatePowerBar:IsShown()) then
--hide the power bar from default ui
NamePlateDriverFrame.classNamePlatePowerBar:Hide()
NamePlateDriverFrame.classNamePlatePowerBar:UnregisterAllEvents()
end
local unitFrame = plateFrame.unitFrame
--setup the power bar and cast bar from the details! framework unit frame
local powerBar = unitFrame.powerBar
local castBar = unitFrame.castBar
local healthBar = unitFrame.healthBar
if (not DB_PLATE_CONFIG.player.healthbar_enabled) then
--the health bar is set when the nameplate is shown
healthBar:SetUnit (nil)
-- hide target glow
plateFrame.TargetNeonUp:Hide()
plateFrame.TargetNeonDown:Hide()
end
if (DB_PLATE_CONFIG.player.power_enabled) then
powerBar:SetUnit (unitFrame.unit)
--update the power percent text
local plateConfigs = DB_PLATE_CONFIG.player
if (plateConfigs.power_percent_text_enabled) then
unitFrame.powerBar.Settings.ShowPercentText = true
local textString = powerBar.percentText
textString:Show()
DF:SetFontSize (textString, plateConfigs.power_percent_text_size)
DF:SetFontFace (textString, plateConfigs.power_percent_text_font)
DF:SetFontColor (textString, plateConfigs.power_percent_text_color)
Plater.SetAnchor (textString, plateConfigs.power_percent_text_anchor)
textString:SetAlpha (plateConfigs.power_percent_text_alpha)
powerBar.border:SetVertexColor (0, 0, 0, 1) --hardcoded color
Plater.SetFontOutlineAndShadow (textString, plateConfigs.power_percent_text_outline, plateConfigs.power_percent_text_shadow_color, plateConfigs.power_percent_text_shadow_color_offset[1], plateConfigs.power_percent_text_shadow_color_offset[2])
else
unitFrame.powerBar.Settings.ShowPercentText = false
powerBar.percentText:Hide()
end
else
powerBar:SetUnit (nil)
end
--setup the cast bar from details! framework unit frame
if (DB_PLATE_CONFIG.player.castbar_enabled) then
castBar:SetUnit (unitFrame.unit)
castBar.extraBackground:Show()
else
castBar:SetUnit (nil)
end
end
--update resource bar
Plater.UpdateResourceFrame()
end
--can also hook 'ClassNameplateBar:ShowNameplateBar()' which will show and call NamePlateDriverFrame:SetClassNameplateBar(self); which will call SetupClassNameplateBars()
if IS_WOW_PROJECT_MAINLINE then
hooksecurefunc (NamePlateDriverFrame, "SetupClassNameplateBars", function (self)
return Plater.UpdatePersonalBar (self)
end)
--[[ -- fuck things up a bit...
hooksecurefunc (NamePlateBaseMixin, "OnAdded", function(self, namePlateUnitToken, driverFrame)
local plateFrame = C_NamePlate.GetNamePlateForUnit (namePlateUnitToken)
Plater.OnRetailNamePlateShow(plateFrame.UnitFrame)
end)
hooksecurefunc (NamePlateDriverFrame, "OnNamePlateAdded", function(self, namePlateUnitToken)
if not ENABLED_BLIZZARD_PLATEFRAMES[tostring(frame)] then
local plateFrame = C_NamePlate.GetNamePlateForUnit (namePlateUnitToken)
DevTool:AddData(plateFrame, "OnNamePlateAdded")
C_Timer.After(0, function() Plater.OnRetailNamePlateShow(plateFrame.UnitFrame) end)
end
end)
hooksecurefunc ("DefaultCompactNamePlateFrameSetupInternal", function(frame)
DevTool:AddData(frame, "DefaultCompactNamePlateFrameSetupInternal")
if not ENABLED_BLIZZARD_PLATEFRAMES[tostring(frame)] then
--Plater.OnRetailNamePlateShow (frame)
end
end)
--]]
end
--update the resource location and anchor
function Plater.UpdateResourceFrame()
if IS_WOW_PROJECT_NOT_MAINLINE then return end
--this holds a reference of the current resource frame anchored into the 'target' namepate
--it is used when checking if the unit has auras to move the resources up to make room for the auras
Plater.CurrentTargetResourceFrame = nil
local showSelf = GetCVarBool ("nameplateShowSelf") and Plater.db.profile.plate_config.player.module_enabled
local onCurrentTarget = PlaterDBChr.resources_on_target
if (not showSelf) then
if (not onCurrentTarget) then
return
end
end
local resourceFrame = NamePlateDriverFrame.classNamePlateMechanicFrame
if (resourceFrame and not resourceFrame:IsForbidden()) then
if Plater.db.profile.resources_settings.global_settings.show then
resourceFrame:SetAlpha (0)
resourceFrame:Hide()
return
end
--> set scale based on Plater user settings
resourceFrame:SetScale (Plater.db.profile.resources.scale)
resourceFrame:SetAlpha (Plater.db.profile.resources.alpha)
--check if resources are placed on the current target
if (onCurrentTarget) then
--resource bar are placed on the current target nameplate
local targetPlateFrame = C_NamePlate.GetNamePlateForUnit ("target", false) -- don't attach to secure frames to avoid tainting!
if (targetPlateFrame) then
resourceFrame:Show()
resourceFrame:SetParent (targetPlateFrame.unitFrame)
resourceFrame:ClearAllPoints()
resourceFrame:SetPoint ("bottom", targetPlateFrame.unitFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target)
resourceFrame:SetFrameStrata(targetPlateFrame.unitFrame.healthBar:GetFrameStrata())
resourceFrame:SetFrameLevel(targetPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
Plater.CurrentTargetResourceFrame = resourceFrame
Plater.UpdateResourceFrameAnchor (targetPlateFrame.unitFrame.BuffFrame)
else
resourceFrame:Hide()
end
else
--resource bar are placed below the mana bar at the personal bar
local personalPlateFrame = C_NamePlate.GetNamePlateForUnit ("player", issecure())
if (personalPlateFrame) then
resourceFrame:Show()
resourceFrame:SetParent (personalPlateFrame.unitFrame)
resourceFrame:ClearAllPoints()
--> attach to powerbar if shown
if (personalPlateFrame.unitFrame.powerBar:IsShown()) then
resourceFrame:SetPoint ("top", personalPlateFrame.unitFrame.powerBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
else
resourceFrame:SetPoint ("top", personalPlateFrame.unitFrame.healthBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
end
resourceFrame:SetFrameStrata(personalPlateFrame.unitFrame.healthBar:GetFrameStrata())
resourceFrame:SetFrameLevel(personalPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
else
resourceFrame:Hide()
end
end
end
local alternatePowerFrame = NamePlateDriverFrame.classNamePlateAlternatePowerBar
if (alternatePowerFrame and not alternatePowerFrame:IsForbidden()) then
if Plater.db.profile.resources_settings.global_settings.show then
alternatePowerFrame:SetAlpha (0)
alternatePowerFrame:Hide()
return
end
--> set scale based on Plater user settings
alternatePowerFrame:SetScale (Plater.db.profile.resources.scale * (resourceFrame and 2 or 2)) --augvoker and stagger bars, separate handling. same size for now
alternatePowerFrame:SetAlpha (Plater.db.profile.resources.alpha)
--check if resources are placed on the current target
if (onCurrentTarget) then
--resource bar are placed on the current target nameplate
local targetPlateFrame = C_NamePlate.GetNamePlateForUnit ("target", false) -- don't attach to secure frames to avoid tainting!
if (targetPlateFrame) then
alternatePowerFrame:Show()
alternatePowerFrame:SetParent (targetPlateFrame.unitFrame)
alternatePowerFrame:ClearAllPoints()
if resourceFrame then
alternatePowerFrame:SetPoint ("bottom", resourceFrame, "top", 0, 2)
else
alternatePowerFrame:SetPoint ("bottom", targetPlateFrame.unitFrame.healthBar, "top", 0, Plater.db.profile.resources.y_offset_target)
end
alternatePowerFrame:SetFrameStrata(targetPlateFrame.unitFrame.healthBar:GetFrameStrata())
alternatePowerFrame:SetFrameLevel(targetPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
Plater.CurrentTargetResourceFrame = resourceFrame or alternatePowerFrame
Plater.UpdateResourceFrameAnchor (targetPlateFrame.unitFrame.BuffFrame)
else
alternatePowerFrame:Hide()
end
else
--resource bar are placed below the mana bar at the personal bar
local personalPlateFrame = C_NamePlate.GetNamePlateForUnit ("player", issecure())
if (personalPlateFrame) then
alternatePowerFrame:Show()
alternatePowerFrame:SetParent (personalPlateFrame.unitFrame)
alternatePowerFrame:ClearAllPoints()
--> attach to powerbar if shown
if resourceFrame then
alternatePowerFrame:SetPoint ("top", resourceFrame, "bottom", 0, -2)
else
if (personalPlateFrame.unitFrame.powerBar:IsShown()) then
alternatePowerFrame:SetPoint ("top", personalPlateFrame.unitFrame.powerBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
else
alternatePowerFrame:SetPoint ("top", personalPlateFrame.unitFrame.healthBar, "bottom", 0, -3 + Plater.db.profile.resources.y_offset)
end
end
alternatePowerFrame:SetFrameStrata(personalPlateFrame.unitFrame.healthBar:GetFrameStrata())
alternatePowerFrame:SetFrameLevel(personalPlateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
else
alternatePowerFrame:Hide()
end
end
end
end
if IS_WOW_PROJECT_MAINLINE then
--C_CVar.RegisterCVar("nameplateShowOnlyNames") -- ensure this is still available and usable for our purposes, as it was removed with 10.0.5, but re-added with amnesia shortly after. not needed now.
end
-- do this now
Plater.UpdateBaseNameplateOptions()
--this function is declared inside 'NamePlateDriverMixin' at Blizzard_NamePlates.lua
hooksecurefunc (NamePlateDriverFrame, "UpdateNamePlateOptions", function()
Plater.UpdateSelfPlate()
Plater.UpdateBaseNameplateOptions()
Plater.UpdatePlateClickSpace()
end)
--this might come in useful
function Plater.SetNamePlatePreferredClickInsets(nameplateType, left, right, top, bottom)
if not InCombatLockdown() then
if nameplateType == "friendly" then
C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (left or 0, right or 0, top or 0, bottom or 0)
elseif nameplateType == "enemy" then
C_NamePlate.SetNamePlateEnemyPreferredClickInsets (left or 0, right or 0, top or 0, bottom or 0)
elseif nameplateType == "player" then
C_NamePlate.SetNamePlateSelfPreferredClickInsets (left or 0, right or 0, top or 0, bottom or 0)
end
else
C_Timer.After(1, function() Plater.SetNamePlatePreferredClickInsets(nameplateType, left, right, top, bottom) end)
end
end
hooksecurefunc(NamePlateDriverFrame.namePlateSetInsetFunctions, "friendly", function()
--C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (0, 0, 0, 0)
Plater.SetNamePlatePreferredClickInsets("friendly", 0, 0, 0, 0)
end)
hooksecurefunc(NamePlateDriverFrame.namePlateSetInsetFunctions, "enemy", function()
--C_NamePlate.SetNamePlateEnemyPreferredClickInsets (0, 0, 0, 0)
Plater.SetNamePlatePreferredClickInsets("enemy", 0, 0, 0, 0)
end)
if IS_WOW_PROJECT_MAINLINE then
hooksecurefunc(NamePlateDriverFrame.namePlateSetInsetFunctions, "player", function()
--C_NamePlate.SetNamePlateSelfPreferredClickInsets (0, 0, 0, 0)
Plater.SetNamePlatePreferredClickInsets("player", 0, 0, 0, 0)
end)
end
--> cast frame ~castbar
--test castbar ~test
Plater.CastBarTestFrame = CreateFrame ("frame", nil, UIParent, BackdropTemplateMixin and "BackdropTemplate")
function Plater.StartCastBarTest(castNoInterrupt, castTime, isLoop)
castTime = castTime or 3
if (isLoop) then
Plater.CastBarTestFrame.castNoInterrupt = Plater.CastBarTestFrame.castNoInterrupt
Plater.CastBarTestFrame.castTime = Plater.CastBarTestFrame.castTime
else
Plater.CastBarTestFrame.castNoInterrupt = castNoInterrupt
Plater.CastBarTestFrame.castTime = castTime
end
Plater.IsShowingCastBarTest = true
Plater.DoCastBarTest(castNoInterrupt, castTime)
Plater.IsTestRunning = true
end
function platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
local castTime = Plater.CastBarTestFrame.castTime
---@cast plateFrame plateframe
if plateFrame.unitFrame.PlaterOnScreen then
local castBar = plateFrame.unitFrame.castBar
local spellName, _, spellIcon = GetSpellInfo(116)
castBar.Text:SetText(spellName)
castBar.Icon:SetTexture(spellIcon)
castBar.Icon:SetAlpha(1)
castBar.Icon:Show()
castBar.percentText:Show()
castBar:SetMinMaxValues(0, (castTime or 3))
castBar:SetValue(0)
castBar.Spark:Show()
castBar.casting = true
castBar.finished = false
castBar.value = 0
castBar.maxValue = (castTime or 3)
castBar.canInterrupt = castNoInterrupt or math.random (1, 2) == 1
--castBar.canInterrupt = true
--castBar.channeling = true
castBar:UpdateCastColor()
castBar.spellName = spellName
castBar.spellID = 116
castBar.spellTexture = spellIcon
castBar.spellStartTime = GetTime()
castBar.spellEndTime = GetTime() + (castTime or 3)
castBar.SpellStartTime = GetTime()
castBar.SpellEndTime = GetTime() + (castTime or 3)
castBar.playedFinishedTest = nil
castBar.flashTexture:Hide()
castBar:Animation_StopAllAnimations()
if (castBar.channeling) then
Plater.CastBarOnEvent_Hook(castBar, "UNIT_SPELLCAST_CHANNEL_START", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
else
Plater.CastBarOnEvent_Hook(castBar, "UNIT_SPELLCAST_START", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
end
platerInternal.Audio.PlaySoundForCastStart(castBar.spellID)
if (not castBar:IsShown()) then
castBar:Animation_FadeIn()
castBar:Show()
end
Plater.UpdateCastbarTargetText(castBar)
local textString = castBar.FrameOverlay.TargetName
textString:Show()
textString:SetText("Target Name")
end
end
function Plater.DoCastBarTest (castNoInterrupt, castTime)
Plater.CastBarTestFrame.castTime = castTime or 3
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
end
local totalTime = 0
local checkEachSeconds = 0.4 --0.4 default
local forward = true
Plater.CastBarTestFrame:SetScript ("OnUpdate", function (self, deltaTime)
if (totalTime >= checkEachSeconds) then --(Plater.CastBarTestFrame.castTime + 0.1)
totalTime = 0
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame.PlaterOnScreen then
local castBar = plateFrame.unitFrame.castBar
--local textString = castBar.FrameOverlay.TargetName
--textString:Show()
--textString:SetText("Target Name")
if (castBar.finished and not castBar.playedFinishedTest) then
Plater.CastBarOnEvent_Hook (castBar, "UNIT_SPELLCAST_STOP", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
castBar.playedFinishedTest = true
end
end
end
if (Plater.IsShowingCastBarTest) then
--run another cycle
if (not Plater.CastBarTestFrame.ScheduleNewCycle) then
Plater.CastBarTestFrame.ScheduleNewCycle = C_Timer.NewTimer(0.5, function()
if (Plater.IsShowingCastBarTest) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if (not plateFrame.unitFrame.castBar:IsShown()) then
platerInternal.CastBar.StartTestCastBarForNameplate(plateFrame)
end
end
end
Plater.CastBarTestFrame.ScheduleNewCycle = nil
end)
end
else
--don't run another cycle
Plater.CastBarTestFrame:SetScript("OnUpdate", nil)
Plater.IsTestRunning = nil
end
else
totalTime = totalTime + deltaTime
end
end)
end
function Plater.StopCastBarTest()
for _, plateFrame in ipairs(Plater.GetAllShownPlates()) do
local castBar = plateFrame.unitFrame.castBar
if (castBar:IsShown()) then
Plater.CastBarOnEvent_Hook(castBar, "UNIT_SPELLCAST_STOP", plateFrame.unitFrame.unit, plateFrame.unitFrame.unit)
castBar.playedFinishedTest = true
castBar:Hide()
end
end
if (Plater.CastBarTestFrame.ScheduleNewCycle and not Plater.CastBarTestFrame.ScheduleNewCycle:IsCancelled()) then
Plater.CastBarTestFrame.ScheduleNewCycle:Cancel()
Plater.CastBarTestFrame.ScheduleNewCycle = nil
end
Plater.IsTestRunning = nil
Plater.IsShowingCastBarTest = false
Plater.CastBarTestFrame:SetScript("OnUpdate", nil)
end
--> when the option to show the target of the cast is enabled, this function update the text settings but not the target name
function Plater.UpdateCastbarTargetText (castBar)
local profile = Plater.db.profile
if (profile.castbar_target_show) then
local textString = castBar.FrameOverlay.TargetName
textString:Show()
DF:SetFontSize (textString, profile.castbar_target_text_size)
DF:SetFontOutline (textString, profile.castbar_target_shadow)
Plater.SetFontOutlineAndShadow (textString, profile.castbar_target_outline, profile.castbar_target_shadow_color, profile.castbar_target_shadow_color_offset[1], profile.castbar_target_shadow_color_offset[2])
DF:SetFontColor (textString, profile.castbar_target_color)
DF:SetFontFace (textString, profile.castbar_target_font)
Plater.SetAnchor (textString, profile.castbar_target_anchor)
else
castBar.FrameOverlay.TargetName:Hide()
end
end
--> self is the castBar object from the details! framework unit frame widget
--> this hook is set inside the nameplate created event
function Plater.CastBarOnShow_Hook (self, unit) --private
--> this cast bar is a nameplate widget?
if (self.isNamePlate) then
if (self.IsSelf) then
self.extraBackground:Show()
else
--in case the unit is out of range, add some background color for the cast
if (self:GetAlpha() < 0.4) then
self.extraBackground:Show()
else
self.extraBackground:Hide()
end
end
end
end
function Plater.UpdateCastbarIcon(castBar)
local profile = Plater.db.profile
local icon = castBar.Icon
local unitFrame = castBar.unitFrame
local borderShield = castBar.BorderShield
--icon:SetDrawLayer ("OVERLAY", 5)
--borderShield:SetDrawLayer ("OVERLAY", 6)
local castBarHeight = castBar:GetHeight()
if (profile.castbar_icon_customization_enabled) then
if (profile.castbar_icon_show) then
icon:ClearAllPoints()
borderShield:ClearAllPoints()
borderShield:SetTexture ([[Interface\GROUPFRAME\UI-GROUP-MAINTANKICON]])
borderShield:SetTexCoord (0, 1, 0, 1)
borderShield:SetDesaturated (true)
PixelUtil.SetSize (borderShield, castBarHeight * 0.8, castBarHeight)
if (profile.castbar_icon_attach_to_side == "left") then
if (profile.castbar_icon_size == "same as castbar") then
icon:SetPoint("topright", castBar, "topleft", profile.castbar_icon_x_offset, 0)
icon:SetPoint("bottomright", castBar, "bottomleft", profile.castbar_icon_x_offset, 0)
PixelUtil.SetPoint (borderShield, "center", castBar, "left", 0, 0)
elseif (profile.castbar_icon_size == "same as castbar plus healthbar") then
local actorType = unitFrame.actorType
local plateConfigs = DB_PLATE_CONFIG [actorType]
local castBarConfigKey, healthBarConfigKey, manaConfigKey = Plater.GetHashKey (isInCombat)
local healthBarHeight = unitFrame.customHealthBarHeight or (plateConfigs and plateConfigs [healthBarConfigKey][2]) or 0
local castBarOffSetY = plateConfigs and plateConfigs.castbar_offset or 0
if castBarOffSetY > healthBarHeight then
icon:SetPoint("topright", castBar, "topleft", profile.castbar_icon_x_offset, 0)
icon:SetPoint("bottomright", unitFrame.healthBar, "bottomleft", profile.castbar_icon_x_offset, 0)
else
icon:SetPoint("topright", unitFrame.healthBar, "topleft", profile.castbar_icon_x_offset, 0)
icon:SetPoint("bottomright", castBar, "bottomleft", profile.castbar_icon_x_offset, 0)
end
PixelUtil.SetPoint (borderShield, "center", castBar, "left", 0, 0)
end
elseif (profile.castbar_icon_attach_to_side == "right") then
if (profile.castbar_icon_size == "same as castbar") then
icon:SetPoint("topleft", castBar, "topright", profile.castbar_icon_x_offset, 0)
icon:SetPoint("bottomleft", castBar, "bottomright", profile.castbar_icon_x_offset, 0)
PixelUtil.SetPoint (borderShield, "center", castBar, "right", 0, 0)
elseif (profile.castbar_icon_size == "same as castbar plus healthbar") then
local actorType = unitFrame.actorType
local plateConfigs = DB_PLATE_CONFIG [actorType]
local castBarConfigKey, healthBarConfigKey, manaConfigKey = Plater.GetHashKey (isInCombat)
local healthBarHeight = unitFrame.customHealthBarHeight or (plateConfigs and plateConfigs [healthBarConfigKey][2]) or 0
local castBarOffSetY = plateConfigs and plateConfigs.castbar_offset or 0
if castBarOffSetY > healthBarHeight then
icon:SetPoint("topleft", castBar, "topright", profile.castbar_icon_x_offset, 0)
icon:SetPoint("bottomleft", unitFrame.healthBar, "bottomright", profile.castbar_icon_x_offset, 0)
else
icon:SetPoint("topleft", unitFrame.healthBar, "topright", profile.castbar_icon_x_offset, 0)
icon:SetPoint("bottomleft", castBar, "bottomright", profile.castbar_icon_x_offset, 0)
end
PixelUtil.SetPoint (borderShield, "center", castBar, "right", 0, 0)
end
end
icon:SetWidth(icon:GetHeight())
else
icon:Hide()
borderShield:Hide()
end
else
icon:ClearAllPoints()
PixelUtil.SetPoint (icon, "left", castBar, "left", 0, 0)
PixelUtil.SetSize (icon, castBarHeight, castBarHeight)
--setup non interruptible cast shield
borderShield:SetTexture ([[Interface\ACHIEVEMENTFRAME\UI-Achievement-Progressive-IconBorder]])
borderShield:SetTexCoord (5/64, 37/64, 1/64, 36/64)
borderShield:ClearAllPoints()
borderShield:SetPoint ("center", castBar.Icon, "center")
PixelUtil.SetSize (borderShield, castBarHeight * 1.4, castBarHeight * 1.4)
borderShield:SetDesaturated (false)
end
if castBar.Icon.Masqued then
Plater.Masque.CastIcon:ReSkin(castBar.Icon)
end
end
---return if the unit is casting a spell, if the spellId is passed, will return if the unit is casting the spellId, otherwise will return the spellId of the spell being casted
---@param unitId string
---@param spellId number|string|nil
---@return boolean|number
function Plater.UnitIsCasting(unitId, spellId)
if (UnitExists(unitId)) then
---@type plateframe
local plateFrame = C_NamePlate.GetNamePlateForUnit(unitId)
if (plateFrame) then
local castBar = plateFrame.unitFrame.castBar
if (castBar:IsShown()) then
if (spellId) then
return castBar.SpellID == spellId or castBar.SpellName == spellId
else
return castBar.SpellID
end
end
else
return false
end
end
return false
end
---return true if the spell can be interrupted
---the function can only return results for spells that the addon observed being interrupted.
---@param spellId number
---@return boolean|nil
function Plater.IsSpellInterruptable(spellId)
return PlaterDB.InterruptableSpells[spellId]
end
--hook for all castbar events --~cast
function Plater.CastBarOnEvent_Hook (self, event, unit, ...) --private
if (event == "PLAYER_ENTERING_WORLD") then
if (not self.isNamePlate) then
return
end
unit = self.unit
if (self.casting) then
event = "UNIT_SPELLCAST_START"
elseif (self.channeling) then
event = "UNIT_SPELLCAST_CHANNEL_START"
else
return
end
end
if (self.isNamePlate) then
local unitFrame = self.unitFrame
local shouldRunCastStartHook = false
if (event == "UNIT_SPELLCAST_START" or event == "UNIT_SPELLCAST_CHANNEL_START") then
local unitCast = unit
if (unitCast ~= self.unit) then
return
end
-- if we are starting a cast but it is an immediate chained cast, then needs to trigger OnHide and OnShow again afterwards
local globalScriptObject = SCRIPT_CASTBAR_TRIGGER_CACHE[self.SpellName]
if (globalScriptObject and (self.casting or self.channeling) and not self.IsInterrupted) then
self:OnHideWidget()
end
--reset the visibility of the spell name text
self.Text:Show()
local curTime = GetTime()
--local name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellId = UnitCastingInfo (unitCast)
self.SpellName = self.spellName
self.SpellID = self.spellID
self.SpellTexture = self.spellTexture
self.SpellStartTime = self.spellStartTime or curTime
self.SpellEndTime = self.spellEndTime or curTime
local notInterruptible = not self.canInterrupt
self.IsInterrupted = false
self.InterruptSourceName = nil
self.InterruptSourceGUID = nil
self.ReUpdateNextTick = true
self.ThrottleUpdate = -1
if (notInterruptible) then
self.BorderShield:Show()
else
self.BorderShield:Hide()
end
if (notInterruptible) then
self.CanInterrupt = false
else
self.CanInterrupt = true
end
self.FrameOverlay:SetBackdropBorderColor (0, 0, 0, 0)
local profile = Plater.db.profile
local isInCombat = profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat
--reset spark color and size
self.Spark:SetVertexColor(unpack(profile.cast_statusbar_spark_color))
self.Spark:SetAlpha (profile.cast_statusbar_spark_alpha)
PixelUtil.SetSize(self.Spark, profile.cast_statusbar_spark_width, self:GetHeight())
--cut the spell name text to fit within the castbar
Plater.UpdateSpellNameSize (self.Text, unitFrame.ActorType, nil, isInCombat)
Plater.UpdateCastbarTargetText (self)
--castbar icon
Plater.UpdateCastbarIcon(self)
shouldRunCastStartHook = true
--spell color
self.castColorTexture:Hide()
--cast color (from options tab Cast Colors)
local castColors = profile.cast_colors
local customColor = castColors[self.spellID]
if (customColor) then
local isEnabled, color, customSpellName = customColor[1], customColor[2], customColor[3]
if (color and isEnabled) then
local originalCastColor = profile.cast_color_settings.enabled
--set the new cast color
if (color == "white") then
--the color white is used as a default disabled color
originalCastColor = false
else
self:SetColor(color)
end
if (customSpellName and customSpellName ~= "") then
self.Text:SetText(customSpellName)
end
--check if the original cast color is enabled
if (originalCastColor) then
--get the original cast color
local castColor = self:GetCastColor()
self.castColorTexture:Show()
local r, g, b = Plater:ParseColors(castColor)
self.castColorTexture:SetColorTexture(r, g, b)
self.castColorTexture:SetHeight(self:GetHeight() + profile.cast_color_settings.height_offset)
end
end
end
if (self.channeling and (self.SpellStartTime + 0.25 > curTime)) then
platerInternal.Audio.PlaySoundForCastStart(self.spellID) --fallback for edge cases. should not double play
end
-- in some occasions channeled casts don't have a CLEU entry... check this here
if (unitFrame.ActorType == "enemynpc" and event == "UNIT_SPELLCAST_CHANNEL_START" and (not DB_CAPTURED_SPELLS[self.spellID] or DB_CAPTURED_SPELLS[self.spellID].isChanneled == nil)) then
parserFunctions.SPELL_CAST_SUCCESS (nil, "SPELL_CAST_SUCCESS", nil, unitFrame[MEMBER_GUID], unitFrame.unitNameInternal, 0x00000000, nil, nil, nil, nil, nil, self.spellID, nil, nil, nil, nil, nil, nil, nil, nil, nil)
end
elseif (event == "UNIT_SPELLCAST_INTERRUPTED") then
local unitCast = unit
if (unitCast ~= self.unit) then
return
end
--self:Hide()
-- this is called in SPELL_INTERRUPT event
--self:OnHideWidget()
--self.IsInterrupted = true
elseif (event == "UNIT_SPELLCAST_STOP" or event == "UNIT_SPELLCAST_CHANNEL_STOP") then
local unitCast = unit
if (unitCast ~= self.unit) then
return
end
self:OnHideWidget()
if (Plater.db.profile.cast_statusbar_quickhide) then
self:Hide()
end
end
--hooks
if (shouldRunCastStartHook) then
if (HOOK_CAST_START.ScriptAmount > 0) then
for i = 1, HOOK_CAST_START.ScriptAmount do
local globalScriptObject = HOOK_CAST_START [i]
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Cast Start")
--update envTable
local scriptEnv = scriptInfo.Env
scriptEnv._SpellID = self.SpellID
scriptEnv._UnitID = self.unit
scriptEnv._SpellName = self.SpellName
scriptEnv._Texture = self.SpellTexture
scriptEnv._Caster = self.unit
scriptEnv._Duration = self.SpellEndTime - self.SpellStartTime
scriptEnv._StartTime = self.SpellStartTime
scriptEnv._CanInterrupt = self.CanInterrupt
scriptEnv._EndTime = self.SpellEndTime
scriptEnv._RemainingTime = max (self.SpellEndTime - GetTime(), 0)
scriptEnv._CanStealOrPurge = self.CanStealOrPurge
scriptEnv._AuraType = self.AuraType
scriptEnv._CastBarHeight = unitFrame.castBar:GetHeight()
--run
unitFrame:ScriptRunHook (scriptInfo, "Cast Start", self)
end
end
end
end
end
Plater.CastBarOnTick_Hook = function (self, deltaTime) --private
if (self.percentText) then --check if is a plater cast bar
Plater.StartLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick")
self.ThrottleUpdate = self.ThrottleUpdate - deltaTime
if (self.ThrottleUpdate < 0) then
Plater.StartLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick-Full")
self.SpellStartTime = self.spellStartTime or GetTime()
self.SpellEndTime = self.spellEndTime or GetTime()
if (self.ReUpdateNextTick) then
self.ReUpdateNextTick = nil
end
if (self.unit and Plater.db.profile.castbar_target_show and not UnitIsUnit (self.unit, "player")) then
local targetName = UnitName (self.unit .. "target")
if (targetName) then
local canShowTargetName = true
local notInTank = Plater.db.profile.castbar_target_notank
if (notInTank) then
if (Plater.PlayerIsTank and targetName == UnitName("player")) then
canShowTargetName = false
end
end
if (canShowTargetName) then
if DB_USE_NAME_TRANSLIT then
targetName = LibTranslit:Transliterate(targetName, TRANSLIT_MARK)
end
local _, class = UnitClass (self.unit .. "target")
if (class) then
self.FrameOverlay.TargetName:SetText (targetName)
self.FrameOverlay.TargetName:SetTextColor (DF:ParseColors (class))
else
self.FrameOverlay.TargetName:SetText (targetName)
DF:SetFontColor (self.FrameOverlay.TargetName, Plater.db.profile.castbar_target_color)
end
else
self.FrameOverlay.TargetName:SetText ("")
end
else
self.FrameOverlay.TargetName:SetText ("")
end
else
self.FrameOverlay.TargetName:SetText ("")
end
self.ThrottleUpdate = self.unitFrame.PlateFrame.OnTickFrame.ThrottleUpdate + DB_TICK_THROTTLE
--get the script object of the aura which will be showing in this icon frame
local globalScriptObject = SCRIPT_CASTBAR_TRIGGER_CACHE[self.SpellName]
--check if this aura has a custom script
if (globalScriptObject and self.SpellEndTime and GetTime() < self.SpellEndTime and (self.casting or self.channeling) and not self.IsInterrupted) then
--stored information about scripts
local scriptContainer = self:ScriptGetContainer()
--get the info about this particularly script
local scriptInfo = self:ScriptGetInfo (globalScriptObject, scriptContainer)
local scriptEnv = scriptInfo.Env
scriptEnv._SpellID = self.SpellID
scriptEnv._UnitID = self.unit
scriptEnv._SpellName = self.SpellName
scriptEnv._Texture = self.SpellTexture
scriptEnv._Caster = self.unit
scriptEnv._Duration = self.SpellEndTime - self.SpellStartTime
scriptEnv._StartTime = self.SpellStartTime
scriptEnv._CanInterrupt = self.CanInterrupt
scriptEnv._EndTime = self.SpellEndTime
scriptEnv._RemainingTime = max (self.SpellEndTime - GetTime(), 0)
scriptEnv._CanStealOrPurge = self.CanStealOrPurge
scriptEnv._AuraType = self.AuraType
if (self.casting) then
scriptEnv._CastPercent = self.value / self.maxValue * 100
elseif (self.channeling) then
scriptEnv._CastPercent = abs (self.value - self.maxValue) / self.maxValue * 100
end
--run onupdate script
self:ScriptRunOnUpdate (scriptInfo)
end
--hooks
if (HOOK_CAST_UPDATE.ScriptAmount > 0) then
for i = 1, HOOK_CAST_UPDATE.ScriptAmount do
local globalScriptObject = HOOK_CAST_UPDATE [i]
local unitFrame = self.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Cast Update")
--update envTable
local scriptEnv = scriptInfo.Env
scriptEnv._SpellID = self.SpellID
scriptEnv._UnitID = self.unit
scriptEnv._SpellName = self.SpellName
scriptEnv._Texture = self.SpellTexture
scriptEnv._Caster = self.unit
scriptEnv._Duration = self.SpellEndTime - self.SpellStartTime
scriptEnv._StartTime = self.SpellStartTime
scriptEnv._CanInterrupt = self.CanInterrupt
scriptEnv._EndTime = self.SpellEndTime
scriptEnv._RemainingTime = max (self.SpellEndTime - GetTime(), 0)
scriptEnv._CanStealOrPurge = self.CanStealOrPurge
scriptEnv._AuraType = self.AuraType
--run
unitFrame:ScriptRunHook (scriptInfo, "Cast Update", self)
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick-Full")
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "CastBarOnTick")
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> health frame
function Plater.QuickHealthUpdate (unitFrame)
local unitHealth = UnitHealth (unitFrame.unit)
local unitHealthMax = UnitHealthMax (unitFrame.unit)
unitFrame.healthBar:SetMinMaxValues (0, unitHealthMax)
unitFrame.healthBar:SetValue (unitHealth)
unitFrame.healthBar.CurrentHealth = unitHealth
unitFrame.healthBar.CurrentHealthMax = unitHealthMax
unitFrame.healthBar.currentHealth = unitHealth
unitFrame.healthBar.currentHealthMax = unitHealthMax
end
local run_on_health_change_hook = function (unitFrame)
if unitFrame.isPerformanceUnit then return end -- don't run health update hooks on performance units
for i = 1, HOOK_HEALTH_UPDATE.ScriptAmount do
local globalScriptObject = HOOK_HEALTH_UPDATE [i]
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Health Update")
--run
unitFrame:ScriptRunHook (scriptInfo, "Health Update")
end
end
function Plater.OnUpdateHealth (self) --self is unitFrame.healthBar
if (not self.isNamePlate) then
--this is not a nameplate, perhaps another frame from the framework
return
end
Plater.StartLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")
-- update - for whatever weird reason max health event does not give proper values sometimes...
if self.displayedUnit then --failsafe?!
local maxHealth = UnitHealthMax (self.displayedUnit)
self:SetMinMaxValues (0, maxHealth)
self.currentHealthMax = maxHealth
end
---@type plateframe
local plateFrame = self.PlateFrame
local currentHealth = self.currentHealth
local currentHealthMax = self.currentHealthMax
local unitFrame = self.unitFrame
local oldHealth = self.CurrentHealth
--> exposed values to scripts
self.CurrentHealth = currentHealth
self.CurrentHealthMax = currentHealthMax
if (plateFrame.IsSelf) then
self.CurrentHealth = currentHealth
self.CurrentHealthMax = currentHealthMax
--> flash if low health
if (currentHealth / currentHealthMax < 0.27) then
if (not self.PlayHealthFlash) then
Plater.CreateHealthFlashFrame (plateFrame)
end
self.PlayHealthFlash()
else
if (self.PlayHealthFlash) then
if (currentHealth / currentHealthMax > 0.5) then
self.canHealthFlash = true
end
end
end
if (plateFrame.PlateConfig.healthbar_color_by_hp) then
local originalColor = plateFrame.PlateConfig.healthbar_color
local r, g, b = DF:LerpLinearColor (abs (currentHealth / currentHealthMax - 1), 1, originalColor[1], originalColor[2], originalColor[3], 1, .4, 0)
Plater.ChangeHealthBarColor_Internal (self, r, g, b, (originalColor[4] or 1), true)
--else
--Plater.ChangeHealthBarColor_Internal (self, unpack (plateFrame.PlateConfig.healthbar_color))
end
Plater.CheckLifePercentText (unitFrame)
else
--quick hide the nameplate if the unit doesn't exists or if the unit died
if (DB_USE_QUICK_HIDE and (IS_WOW_PROJECT_MAINLINE)) then
if (not UnitExists (unitFrame.unit) or self.CurrentHealth < 1) then
--the unit died!
unitFrame:Hide()
Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")
return
end
end
if (DB_DO_ANIMATIONS) then
--do healthbar animation ~animation ~healthbar
oldHealth = oldHealth or self.CurrentHealth
self.CurrentHealthMax = currentHealthMax
self.AnimationStart = oldHealth
self.AnimationEnd = currentHealth
self:SetValue (oldHealth)
self.IsAnimating = true
if (self.AnimationEnd > self.AnimationStart) then
self.AnimateFunc = Plater.AnimateRightWithAccel
else
self.AnimateFunc = Plater.AnimateLeftWithAccel
end
else
self.CurrentHealth = currentHealth
self.CurrentHealthMax = currentHealthMax
end
if (plateFrame.actorType == ACTORTYPE_FRIENDLY_PLAYER) then
local isWithoutHealthbar = plateFrame.IsFriendlyPlayerWithoutHealthBar
plateFrame.IsNpcWithoutHealthBar = false --ensure this.
Plater.ParseHealthSettingForPlayer (plateFrame)
self.ScheduleNameUpdate = plateFrame.IsFriendlyPlayerWithoutHealthBar ~= isWithoutHealthbar
--Plater.UpdatePlateText (plateFrame, DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER], false)
end
Plater.CheckLifePercentText (unitFrame)
end
Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealth")
end
--self is the healthBar (it's parent is the unitFrame)
function Plater.OnUpdateHealthMax (self)
Plater.StartLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealthMax")
-- ensure updated values...
Plater.QuickHealthUpdate (self.unitFrame)
Plater.CheckLifePercentText (self.unitFrame)
Plater.EndLogPerformanceCore("Plater-Core", "Health", "OnUpdateHealthMax")
end
function Plater.OnHealthChange (self, unitId) --~health
Plater.OnUpdateHealth (self)
--> run on health changed hook
if (HOOK_HEALTH_UPDATE.ScriptAmount > 0) then
return run_on_health_change_hook (self.unitFrame)
end
end
function Plater.OnHealthMaxChange (self, unitId)
Plater.OnUpdateHealthMax (self)
--> run on health changed hook
if (HOOK_HEALTH_UPDATE.ScriptAmount > 0) then
return run_on_health_change_hook (self.unitFrame)
end
end
--> profile changes and refreshes ~db
Plater.db.RegisterCallback (Plater, "OnProfileChanged", "RefreshConfigProfileChanged")
Plater.db.RegisterCallback (Plater, "OnProfileCopied", "RefreshConfig")
Plater.db.RegisterCallback (Plater, "OnProfileReset", "RefreshConfig")
--Plater.db.RegisterCallback (Plater, "OnDatabaseShutdown", "SaveConsoleVariables")
function Plater.OnProfileCreated()
C_Timer.After (.5, function()
Plater:Msg ("new profile created, applying patches and adding default scripts.")
platerInternal.Scripts.UpdateFromLibrary()
--enable UIParent nameplates for new installs of Plater
--this setting is disabled by default and will be enabled for new users and new profiles
Plater.db.profile.use_ui_parent = true
--adjust the fine tune to player's screen scale
Plater.db.profile.ui_parent_scale_tune = 1 / UIParent:GetEffectiveScale()
--call major refresh
Plater:RefreshConfig()
Plater.UpdatePlateClickSpace()
--call the user to /reload UI
DF:ShowPromptPanel ("Plater profile created, do you want /reload now (recommended)?", function() ReloadUI() end, function() end, true, 500)
end)
end
Plater.db.RegisterCallback (Plater, "OnNewProfile", "OnProfileCreated")
Plater.UpdateSelfPlate()
--update quest log after the addon initialization
C_Timer.After (4.1, Plater.QuestLogUpdated)
--update all nameplate after the addon initialized
C_Timer.After (5.1, function()
Plater.IncreaseRefreshID()
Plater.FullRefreshAllPlates()
end)
for i = 1, 3 do
C_Timer.After (i, Plater.RefreshDBUpvalues)
end
if CastingBarFrame then
CastingBarFrame:HookScript ("OnShow", function (self)
if (Plater.db.profile.hide_blizzard_castbar) then
self:Hide()
end
end)
elseif PlayerCastingBarFrame then
PlayerCastingBarFrame:HookScript ("OnShow", function (self)
if (Plater.db.profile.hide_blizzard_castbar) then
self:Hide()
end
end)
end
-- fill class-info cache data
if IS_WOW_PROJECT_MAINLINE then
for classID = 1, MAX_CLASSES do
local _, classFile = GetClassInfo(classID)
CLASS_INFO_CACHE[classFile] = {}
for i = 1, GetNumSpecializationsForClassID(classID) do
local specID, maleName, _, iconID, role = GetSpecializationInfoForClassID(classID, i, 2) -- male
local _, femaleName, _, iconID, role = GetSpecializationInfoForClassID(classID, i, 3) -- female
CLASS_INFO_CACHE[classFile][maleName] = {role = role, specID = specID, iconID = iconID}
CLASS_INFO_CACHE[classFile][femaleName] = CLASS_INFO_CACHE[classFile][maleName]
end
end
end
-- hook to the InterfaceOptionsFrame and VideoOptionsFrame to update the nameplate sizes, as blizzard somehow messes things up there on hide...
if InterfaceOptionsFrame then
InterfaceOptionsFrame:HookScript('OnHide',Plater.UpdatePlateClickSpace)
VideoOptionsFrame:HookScript('OnHide',Plater.UpdatePlateClickSpace)
elseif SettingsPanel then
SettingsPanel:HookScript('OnHide',Plater.UpdatePlateClickSpace)
end
end
function Plater.FormatTime (time)
if (time >= 3600) then
return floor (time / 3600) .. "h"
elseif (time >= 60) then
return floor (time / 60) .. "m"
else
return floor (time)
end
end
function Plater.FormatTimeDecimal (time)
if time < 10 then
return ("%.1f"):format(time)
elseif time < 60 then
return ("%d"):format(time)
elseif time < 3600 then
return ("%d:%02d"):format(time/60%60, time%60)
elseif time < 86400 then
return ("%dh %02dm"):format(time/(3600), time/60%60)
else
return ("%dd %02dh"):format(time/86400, (time/3600) - (floor(time/86400) * 24))
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> color stuff ~color
function Plater.SetQuestColorByReaction (unitFrame)
--unit is a quest mob, reset the color to quest color
if (unitFrame.ActorType and DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enabled) then
if (unitFrame [MEMBER_REACTION] == Plater.UnitReaction.UNITREACTION_NEUTRAL) then
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_neutral))
elseif (unitFrame [MEMBER_REACTION] < Plater.UnitReaction.UNITREACTION_NEUTRAL) then
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enemy))
else
--there's a bug here where quest_color is nil for a friendly npc
--this is happening when an enemy quest npc turns friendly and (probably) the actorType doesn't change
--so in the enemy npc settings table does not have 'quest_color' input
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_PLATE_CONFIG [unitFrame.ActorType].quest_color or {.5, 1, 0, 1}))
end
end
end
--override colors
--this function will set the color of the nameplate by the reaction of the unit shown
--it can only run if color override is enabled and when not in combat or when in combat but color by aggro is disabled
function Plater.ColorOverrider (unitFrame, forceRefresh)
--not in combat or aggro isn't changing the healthbar color
if (forceRefresh or not InCombatLockdown() or not DB_AGGRO_CHANGE_HEALTHBAR_COLOR) then
--isn't a quest
if (not unitFrame [MEMBER_QUEST] or not DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enabled) then
local reaction = unitFrame [MEMBER_REACTION]
--has a valid reaction
if (reaction) then
local r, g, b, a = unpack (Plater.db.profile.color_override_colors [reaction])
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, r, g, b, a, true)
end
else
--unit is a quest mob, reset the color to quest color
Plater.SetQuestColorByReaction (unitFrame)
end
end
end
function Plater.DenyColorChange(unitFrame, state)
unitFrame.DenyColorChange = state
end
--refresh the use of the color overrider
--called from the OnInit and from the options panel when the override color settings is changed
function Plater.RefreshColorOverride() --private
if (Plater.db.profile.color_override) then
Plater.CanOverrideColor = true
else
Plater.CanOverrideColor = false
end
Plater.UpdateAllNameplateColors()
end
--internal function to change the health bar color
function Plater.ChangeHealthBarColor_Internal (healthBar, r, g, b, a, forceNoLerp) --private
a = a or 1
if (r ~= healthBar.R or g ~= healthBar.G or b ~= healthBar.B or a ~= healthBar.A) then
healthBar.R, healthBar.G, healthBar.B, healthBar.A = r, g, b, a
if (not DB_LERP_COLOR or forceNoLerp) then -- ~lerpcolor
healthBar.barTexture:SetVertexColor (r, g, b, a)
end
end
end
--do several checkes to determine which are the color of this nameplate
--if force refresh is true, it'll ignore aggro and incombat checks in the ColorOverrider function
function Plater.FindAndSetNameplateColor (unitFrame, forceRefresh)
local r, g, b, a = 1, 1, 1, 1
local unitID = unitFrame [MEMBER_UNITID]
if (unitFrame.IsSelf or not unitFrame.PlaterOnScreen) then
return
else
--check if is a player
if UnitIsPlayer (unitID) then
if (unitFrame.ActorType == ACTORTYPE_FRIENDLY_PLAYER) then
if (Plater.db.profile.use_playerclass_color) then
local _, class = UnitClass (unitID)
local classColor = DB_CLASS_COLORS [class]
if (classColor) then -- and unitFrame.optionTable.useClassColors
r, g, b, a = classColor.r, classColor.g, classColor.b, classColor.a
end
else
r, g, b, a = unpack(Plater.db.profile.plate_config.friendlyplayer.fixed_class_color)
end
elseif (unitFrame.ActorType == ACTORTYPE_ENEMY_PLAYER) then
if (Plater.db.profile.plate_config.enemyplayer.use_playerclass_color) then
local _, class = UnitClass (unitID)
local classColor = DB_CLASS_COLORS [class]
if (classColor) then -- and unitFrame.optionTable.useClassColors
r, g, b, a = classColor.r, classColor.g, classColor.b, classColor.a
end
else
r, g, b, a = unpack(Plater.db.profile.plate_config.enemyplayer.fixed_class_color)
end
end
--check if is tapped
elseif (Plater.IsUnitTapDenied (unitID)) then
r, g, b, a = unpack (Plater.db.profile.tap_denied_color)
else
if (Plater.CanOverrideColor) then
Plater.ColorOverrider (unitFrame, forceRefresh)
return
end
--check if the mob is a quest mob
if (unitFrame [MEMBER_QUEST] and DB_PLATE_CONFIG [unitFrame.ActorType].quest_color_enabled) then
Plater.SetQuestColorByReaction (unitFrame)
return
end
--get the color from the client
r, g, b, a = UnitSelectionColor (unitID)
end
end
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, r, g, b, a, true)
end
--force an update on all nameplates showin in the screen
--called after a refresh color override (on init and option settings changes)
--called after leaving the combat
function Plater.UpdateAllNameplateColors() --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if (not plateFrame.IsSelf) then
--reset the nameplate color
Plater.FindAndSetNameplateColor (plateFrame.unitFrame)
end
end
end
--get a unit and a text and color the text with the class color of the unit (accepts player GUID as well)
function Plater.SetTextColorByClass (unit, text)
--checking if the unit exists because this can be called from the cleu parser
if (unit) then
local _, class = nil, nil
if (unit:sub(1, #"Player-") == "Player-") then
_, class = GetPlayerInfoByGUID (unit)
else
_, class = UnitClass (unit)
end
if (class) then
local color = DB_CLASS_COLORS [class]
if (color) then
text = "|c" .. color.colorStr .. DF:RemoveRealName (text) .. "|r"
end
else
text = DF:RemoveRealName (text)
end
return text
else
return text
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> update functions ~update
--run the hook "Nameplate Updated", this is called from inside the Tick and UpdateAllPlates()
function Plater.TriggerNameplateUpdatedEvent(unitFrame)
if unitFrame.PlaterOnScreen and (HOOK_NAMEPLATE_UPDATED.ScriptAmount > 0) then
for i = 1, HOOK_NAMEPLATE_UPDATED.ScriptAmount do
local globalScriptObject = HOOK_NAMEPLATE_UPDATED [i]
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Nameplate Updated")
local scriptEnv = scriptInfo.Env
scriptEnv._HealthPercent = unitFrame.healthBar.CurrentHealth / unitFrame.healthBar.CurrentHealthMax * 100
--run
unitFrame:ScriptRunHook (scriptInfo, "Nameplate Updated")
end
end
end
--full refresh calls
function Plater.UpdateAllPlates (forceUpdate, justAdded, regenDisabled) --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
if plateFrame.unitFrame and plateFrame.unitFrame.PlaterOnScreen then
if not plateFrame.unitFrame.isPerformanceUnit then
if not IS_WOW_PROJECT_CLASSIC_ERA or (IS_WOW_PROJECT_CLASSIC_ERA and plateFrame.actorType ~= ACTORTYPE_ENEMY_PLAYER) then -- don't force update in classic
Plater.AddToAuraUpdate(plateFrame.unitFrame.unit) -- force aura update
end
end
Plater.UpdatePlateFrame (plateFrame, nil, forceUpdate, justAdded, regenDisabled)
--trigger a nameplate updated event
Plater.TriggerNameplateUpdatedEvent(plateFrame.unitFrame)
end
end
end
--called from the options panel | this is the same as calling Name_Plate_Unit_Added for each nameplate
function Plater.FullRefreshAllPlates() --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
---@cast plateFrame plateframe
--hack to call the update without overriding user settings from scripts
Plater.RunScheduledUpdate ({unitId = plateFrame [MEMBER_UNITID], GUID = plateFrame [MEMBER_GUID]})
end
end
--update the player bar (personal nameplate)
local re_update_self_plate = function()
Plater.UpdateSelfPlate()
end
function Plater.UpdateSelfPlate() --private
if (InCombatLockdown()) then
return C_Timer.After (.3, re_update_self_plate)
end
C_NamePlate.SetNamePlateSelfClickThrough (DB_PLATE_CONFIG.player.click_through)
--disabled due to modifying the player personal nameplate makes it be a little offset in the Y anchor making it to be in front of the player
-- C_NamePlate.SetNamePlateSelfSize (unpack (DB_PLATE_CONFIG.player.health))
end
-- ~size ~updatesize
---update thee nameplate size including healthbar, castbar, etc
---@param plateFrame plateframe
function Plater.UpdatePlateSize (plateFrame)
if (not plateFrame.actorType) then
return
end
Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdatePlateSize")
local profile = Plater.db.profile
local unitFrame = plateFrame.unitFrame
local healthBar = unitFrame.healthBar
local castBar = unitFrame.castBar
local powerBar = unitFrame.powerBar
local buffFrame1 = unitFrame.BuffFrame
local buffFrame2 = unitFrame.BuffFrame2
local isInCombat = profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat
--use in combat bars when in pvp
if (plateFrame.actorType == ACTORTYPE_ENEMY_PLAYER) then
if ((Plater.ZoneInstanceType == "pvp" or Plater.ZoneInstanceType == "arena") and DB_PLATE_CONFIG.player.pvp_always_incombat) then
isInCombat = true
end
end
local actorType = plateFrame.actorType
--get the config for this actor type
local plateConfigs = DB_PLATE_CONFIG [actorType]
--get the config key based if the player is in combat
local castBarConfigKey, healthBarConfigKey, manaConfigKey = Plater.GetHashKey (isInCombat)
local healthBarWidth, healthBarHeight = unitFrame.customHealthBarWidth or plateConfigs [healthBarConfigKey][1], unitFrame.customHealthBarHeight or plateConfigs [healthBarConfigKey][2]
local castBarWidth, castBarHeight = unitFrame.customCastBarWidth or plateConfigs [castBarConfigKey][1], unitFrame.customCastBarHeight or plateConfigs [castBarConfigKey][2]
local powerBarWidth, powerBarHeight = unitFrame.customPowerBarWidth or plateConfigs [manaConfigKey][1], unitFrame.customPowerBarHeight or plateConfigs [manaConfigKey][2]
local castBarOffSetX = plateConfigs.castbar_offset_x
local castBarOffSetXRel = (healthBarWidth - castBarWidth) / 2
local castBarOffSetY = plateConfigs.castbar_offset
local powerBarOffSetX = (healthBarWidth - powerBarWidth) / 2
local powerBarOffSetY = 0
--calculate the size deviation for pets
local unitType = Plater.GetUnitType (plateFrame)
if (unitType == "pet") then
healthBarHeight = healthBarHeight * Plater.db.profile.pet_height_scale
healthBarWidth = healthBarWidth * Plater.db.profile.pet_width_scale
elseif (unitType == "minus") then
healthBarHeight = healthBarHeight * Plater.db.profile.minor_height_scale
healthBarWidth = healthBarWidth * Plater.db.profile.minor_width_scale
end
--community patch by Ariani#0960 (discord)
--make the unitFrame be parented to UIParent allowing frames to be moved between strata levels
--March 3rd, 2019
if (DB_USE_UIPARENT) then
--unit frame - is set to be the same size as the plateFrame
unitFrame:ClearAllPoints()
unitFrame:SetPoint ("topleft", unitFrame.PlateFrame, "topleft", 0, 0)
unitFrame:SetPoint ("bottomright", unitFrame.PlateFrame, "bottomright", 0, 0)
--health bar
-- ensure that we are using the configured size, as it will be automatically scaled
healthBar:ClearAllPoints()
PixelUtil.SetPoint (healthBar, "center", unitFrame, "center", profile.global_offset_x, profile.global_offset_y)
PixelUtil.SetSize (healthBar, healthBarWidth, healthBarHeight)
--end of patch
--update scale
Plater.UpdateUIParentScale (plateFrame)
else
--unit frame - is set to be the same size as the plateFrame
unitFrame:ClearAllPoints()
--unitFrame:SetAllPoints()
--using the same setpoint pattern on both nameplate parent types to make easy the frameshake to handle the points
unitFrame:SetPoint ("topleft", unitFrame.PlateFrame, "topleft", 0, 0)
unitFrame:SetPoint ("bottomright", unitFrame.PlateFrame, "bottomright", 0, 0)
--health bar
--this calculates the health bar anchor points
--it will always be placed in the center of the nameplate main frame attached with two anchor points
local xOffSet = (plateFrame:GetWidth() - (healthBarWidth * unitFrame.nameplateScaleAdjust)) / 2
local yOffSet = (plateFrame:GetHeight() - (healthBarHeight * unitFrame.nameplateScaleAdjust)) / 2
healthBar:SetScale(1/unitFrame.nameplateScaleAdjust)
healthBar:ClearAllPoints()
PixelUtil.SetPoint (healthBar, "topleft", unitFrame, "topleft", xOffSet + profile.global_offset_x, -yOffSet + profile.global_offset_y)
PixelUtil.SetPoint (healthBar, "bottomright", unitFrame, "bottomright", -xOffSet + profile.global_offset_x, yOffSet + profile.global_offset_y)
end
--execute indicator
healthBar.healthCutOff:SetSize (healthBarHeight, healthBarHeight)
healthBar.executeRange:SetHeight (healthBarHeight)
--cast bar - is set by default below the healthbar
castBar:ClearAllPoints()
PixelUtil.SetPoint (castBar, "topleft", healthBar, "bottomleft", castBarOffSetXRel + castBarOffSetX, castBarOffSetY)
PixelUtil.SetPoint (castBar, "topright", healthBar, "bottomright", -castBarOffSetXRel + castBarOffSetX, castBarOffSetY)
PixelUtil.SetWidth (castBar, castBarWidth)
PixelUtil.SetHeight (castBar, castBarHeight)
--PixelUtil.SetSize (castBar.BorderShield, castBarHeight * 1.4, castBarHeight * 1.4)
PixelUtil.SetSize (castBar.Spark, profile.cast_statusbar_spark_width, castBarHeight)
castBar.Spark:SetAlpha (profile.cast_statusbar_spark_alpha)
Plater.UpdateCastbarIcon(castBar)
castBar._points = {{"topleft", healthBar, "bottomleft", castBarOffSetXRel + castBarOffSetX, castBarOffSetY},
{"topright", healthBar, "bottomright", -castBarOffSetXRel + castBarOffSetX, castBarOffSetY}}
--power bar
powerBar:ClearAllPoints()
PixelUtil.SetPoint (powerBar, "topleft", healthBar, "bottomleft", powerBarOffSetX, powerBarOffSetY)
PixelUtil.SetPoint (powerBar, "topright", healthBar, "bottomright", -powerBarOffSetX, powerBarOffSetY)
PixelUtil.SetSize (powerBar, powerBarWidth, powerBarHeight)
--power bar are hidden by default, show it if there's a custom size for it
if (unitFrame.customPowerBarWidth and unitFrame.customPowerBarHeight) then
powerBar:SetUnit (unitFrame.unit)
end
--aura frame
--plateConfigs.buff_frame_y_offset is the offset from the actor type, e.g. enemy npc
local bf1Anchor = Plater.db.profile.aura_frame1_anchor
Plater.SetAnchor (buffFrame1, {side = bf1Anchor.side, x = bf1Anchor.x, y = bf1Anchor.y + plateConfigs.buff_frame_y_offset}, unitFrame.healthBar, (Plater.db.profile.aura_grow_direction or 2) == 2)
local bf2Anchor = Plater.db.profile.aura_frame2_anchor
Plater.SetAnchor (buffFrame2, {side = bf2Anchor.side, x = bf2Anchor.x, y = bf2Anchor.y + plateConfigs.buff_frame_y_offset}, unitFrame.healthBar, (Plater.db.profile.aura2_grow_direction or 2) == 2)
if (Plater.db.profile.show_health_prediction or Plater.db.profile.show_shield_prediction) and healthBar.displayedUnit then
healthBar:UpdateHealPrediction() -- ensure health prediction is updated properly
end
Plater.UpdateUnitName (plateFrame)
Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateSize")
end
--debug function to print the size of the anchor for each aura container
function Plater.DebugAuraAnchor()
local profile = Plater.db.profile
--get the config for this actor type
local plateConfigs = DB_PLATE_CONFIG ["enemynpc"]
print ("DB_PLATE_CONFIG [enemynpc].buff_frame_y_offset:", plateConfigs.buff_frame_y_offset)
end
--show the background of the clickable aura, this is also shown when changing the clickable area
function Plater.SetPlateBackground (plateFrame)
plateFrame.unitFrame:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
plateFrame.unitFrame:SetBackdropColor (0, 0, 0, 0.5)
plateFrame.unitFrame:SetBackdropBorderColor (0, 0, 0, 1)
end
local shutdown_platesize_debug = function (timer)
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
Plater.HideClickSpace (plateFrame)
if (Plater.db.profile.click_space_always_show) then
Plater.SetPlateBackground (plateFrame)
else
plateFrame.unitFrame:SetBackdrop (nil)
end
end
Plater.PlateSizeDebugTimer = nil
end
local re_UpdatePlateClickSpace = function()
Plater.UpdatePlateClickSpace()
end
--debug to show the clickable area when adjusting the click space
function Plater.ShowClickSpace (plateFrame)
plateFrame.debugAreaText:Show()
plateFrame.debugAreaTexture:Show()
end
function Plater.HideClickSpace (plateFrame)
plateFrame.debugAreaText:Hide()
plateFrame.debugAreaTexture:Hide()
end
-- default blizzard plate shenanigans
function Plater.UpdateBaseNameplateOptions()
if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideHealthbar")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideCastbar")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameBySelection")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameWithExtendedColors")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideHealthbar")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideCastbar")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameBySelection")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameWithExtendedColors")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideHealthbar")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideCastbar")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameBySelection")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameWithExtendedColors")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "showLevel")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "showLevel")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "showLevel")
else
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideHealthbar")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "hideCastbar")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideHealthbar")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "hideCastbar")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideHealthbar")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "hideCastbar")
if not IS_WOW_PROJECT_MAINLINE then
--other defaults
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameBySelection")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "colorNameWithExtendedColors")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameBySelection")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "colorNameWithExtendedColors")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameBySelection")
TextureLoadingGroupMixin.RemoveTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "colorNameWithExtendedColors")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFrameSetUpOptions }, "showLevel")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateFriendlyFrameOptions }, "showLevel")
TextureLoadingGroupMixin.AddTexture({ textures = DefaultCompactNamePlateEnemyFrameOptions }, "showLevel")
end
end
if not IS_WOW_PROJECT_MAINLINE then
for _, plateFrame in pairs(C_NamePlate.GetNamePlates(true)) do
if (plateFrame) then
if GetCVarBool ("nameplateShowOnlyNames") or Plater.db.profile.saved_cvars.nameplateShowOnlyNames == "1" then
TextureLoadingGroupMixin.RemoveTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
else
TextureLoadingGroupMixin.AddTexture({ textures = plateFrame.UnitFrame.CastBar }, "showCastbar")
end
end
end
end
end
-- ~platesize
function Plater.UpdatePlateClickSpace (needReorder, isDebug) --private
if (not Plater.CanChangePlateSize()) then
return C_Timer.After (1, re_UpdatePlateClickSpace)
end
Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdatePlateClickSpace")
local width, height = Plater.db.profile.click_space_friendly[1], Plater.db.profile.click_space_friendly[2]
C_NamePlate.SetNamePlateFriendlySize (width, height) --classic: {132, 32}, retail: {110, 45},
local width, height = Plater.db.profile.click_space[1], Plater.db.profile.click_space[2]
C_NamePlate.SetNamePlateEnemySize (width, height) --classic: {132, 32}, retail: {110, 45},
--C_NamePlate.SetNamePlateSelfPreferredClickInsets (0, 0, 0, 0)
--C_NamePlate.SetNamePlateFriendlyPreferredClickInsets (0, 0, 0, 0)
--C_NamePlate.SetNamePlateEnemyPreferredClickInsets (0, 0, 0, 0)
C_NamePlate.SetNamePlateFriendlyClickThrough (Plater.db.profile.plate_config.friendlyplayer.click_through)
if (isDebug and not Plater.db.profile.click_space_always_show) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if not plateFrame.IsSelf and plateFrame.unitFrame.PlaterOnScreen then
Plater.ShowClickSpace (plateFrame)
if (Plater.PlateSizeDebugTimer and not Plater.PlateSizeDebugTimer._cancelled) then
Plater.PlateSizeDebugTimer:Cancel()
end
end
end
if (not Plater.PlateSizeDebugTimer) then
Plater:Msg ("showing the clickable area for test purposes.")
end
Plater.PlateSizeDebugTimer = C_Timer.NewTimer (3, shutdown_platesize_debug)
end
if (needReorder) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
Plater.UpdatePlateFrame (plateFrame, plateFrame.actorType)
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateClickSpace")
end
function Plater.ForceTickOnAllNameplates() --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if plateFrame.unitFrame.PlaterOnScreen then
Plater.NameplateTick (plateFrame.OnTickFrame, 10) --GetWorldDeltaSeconds()
end
end
end
--FPS meter to spread NameplateTick evenly
Plater.FPSData = {
startTime = GetTime(),
platesUpdatedThisFrame = 0,
platesToUpdatePerFrame = 40,
frames = 1,
curFPS = 1,
}
function Plater.EveryFrameFPSCheck()
-- calculate every .25sec
local curTime = GetTime()
local curFPSData = Plater.FPSData
if (curFPSData.startTime + 0.25) < curTime then
curFPSData.curFPS = max(curFPSData.frames / (curTime - curFPSData.startTime), 1)
curFPSData.platesToUpdatePerFrame = math.ceil(NUM_NAMEPLATES_ON_SCREEN / DB_TICK_THROTTLE / curFPSData.curFPS)
--ViragDevTool_AddData({curFPSData=curFPSData, NUM_NAMEPLATES_ON_SCREEN = NUM_NAMEPLATES_ON_SCREEN}, "Plater_FPS")
curFPSData.frames = 0
curFPSData.startTime = curTime
else
curFPSData.frames = curFPSData.frames + 1
end
--ViragDevTool_AddData(curFPSData.platesUpdatedThisFrame, "platesUpdatedThisFrame")
curFPSData.platesUpdatedThisFrame = 0
C_Timer.After( 0, Plater.EveryFrameFPSCheck )
end
C_Timer.After( 0, Plater.EveryFrameFPSCheck )
-- ~ontick ~onupdate ~tick
function Plater.NameplateTick (tickFrame, deltaTime) --private
Plater.StartLogPerformanceCore("Plater-Core", "Update", "NameplateTick")
tickFrame.ThrottleUpdate = (tickFrame.ThrottleUpdate or 0) - deltaTime
local unitFrame = tickFrame.unitFrame
local healthBar = unitFrame.healthBar
local profile = Plater.db.profile
--throttle updates, things on this block update with the interval set in the advanced tab
local shouldUpdate = tickFrame.ThrottleUpdate < 0
local curFPSData = Plater.FPSData
if shouldUpdate and not ((1.5 * DB_TICK_THROTTLE + tickFrame.ThrottleUpdate) < 0) then --ensure updates are not posponed indefinetely
if curFPSData.platesUpdatedThisFrame >= curFPSData.platesToUpdatePerFrame then
shouldUpdate = false
end
end
if (shouldUpdate) then
Plater.StartLogPerformanceCore("Plater-Core", "Update", "NameplateTick-Full")
curFPSData.platesUpdatedThisFrame = curFPSData.platesUpdatedThisFrame + 1
--make the db path smaller for performance
local actorTypeDBConfig = DB_PLATE_CONFIG [tickFrame.actorType]
--health cutoff (execute range) - don't show if the nameplate is the personal bar
if (DB_USE_HEALTHCUTOFF and not unitFrame.IsSelf and not unitFrame.PlayerCannotAttack) then
local healthPercent = (healthBar.currentHealth or 1) / (healthBar.currentHealthMax or 1)
if (healthPercent < DB_HEALTHCUTOFF_AT) then
if (not healthBar.healthCutOff:IsShown() or healthBar.healthCutOff.isUpper) then
healthBar.healthCutOff.isUpper = false
healthBar.healthCutOff.isLower = true
healthBar.healthCutOff:ClearAllPoints()
healthBar.healthCutOff:SetSize (healthBar:GetHeight(), healthBar:GetHeight())
healthBar.healthCutOff:SetPoint ("center", healthBar, "left", healthBar:GetWidth() * DB_HEALTHCUTOFF_AT, 0)
if (not profile.health_cutoff_hide_divisor) then
healthBar.healthCutOff:Show()
healthBar.healthCutOff.ShowAnimation:Play()
else
healthBar.healthCutOff:Show()
healthBar.healthCutOff:SetAlpha (0)
end
healthBar.executeRange:Show()
healthBar.executeRange:SetTexCoord (0, DB_HEALTHCUTOFF_AT, 0, 1)
healthBar.executeRange:SetAlpha (0.2)
healthBar.executeRange:SetVertexColor (.3, .3, .3)
healthBar.executeRange:SetHeight (healthBar:GetHeight())
healthBar.executeRange:SetPoint ("right", healthBar.healthCutOff, "center")
healthBar.executeRange:SetPoint ("left", healthBar, "left")
if (profile.health_cutoff_extra_glow) then
healthBar.ExecuteGlowUp.ShowAnimation:Play()
healthBar.ExecuteGlowDown.ShowAnimation:Play()
end
end
unitFrame.InExecuteRange = true
elseif (healthPercent > DB_HEALTHCUTOFF_AT_UPPER and healthPercent < 0.999) then
if (not healthBar.healthCutOff:IsShown() or healthBar.healthCutOff.isLower) then
healthBar.healthCutOff.isUpper = true
healthBar.healthCutOff.isLower = false
healthBar.healthCutOff:ClearAllPoints()
healthBar.healthCutOff:SetSize (healthBar:GetHeight(), healthBar:GetHeight())
healthBar.healthCutOff:SetPoint ("center", healthBar, "right", - (healthBar:GetWidth() * (1-DB_HEALTHCUTOFF_AT_UPPER)), 0)
if (not profile.health_cutoff_hide_divisor) then
healthBar.healthCutOff:Show()
healthBar.healthCutOff.ShowAnimation:Play()
else
healthBar.healthCutOff:Show()
healthBar.healthCutOff:SetAlpha (0)
end
healthBar.executeRange:Show()
healthBar.executeRange:SetTexCoord (0, 1-DB_HEALTHCUTOFF_AT_UPPER, 0, 1)
healthBar.executeRange:SetAlpha (0.2)
healthBar.executeRange:SetVertexColor (.3, .3, .3)
healthBar.executeRange:SetHeight (healthBar:GetHeight())
healthBar.executeRange:SetPoint ("left", healthBar.healthCutOff, "center")
healthBar.executeRange:SetPoint ("right", healthBar, "right")
if (profile.health_cutoff_extra_glow) then
healthBar.ExecuteGlowUp.ShowAnimation:Play()
healthBar.ExecuteGlowDown.ShowAnimation:Play()
end
end
unitFrame.InExecuteRange = true
else
healthBar.healthCutOff:Hide()
healthBar.executeRange:Hide()
healthBar.ExecuteGlowUp:Hide()
healthBar.ExecuteGlowDown:Hide()
unitFrame.InExecuteRange = false
end
end
local isSoftInteract = UnitIsUnit(tickFrame.unit, "softinteract")
unitFrame.isSoftInteract = isSoftInteract
unitFrame.PlateFrame.isSoftInteract = isSoftInteract
unitFrame.isSoftInteractObject = isSoftInteract and (unitFrame.PlateFrame.isObject or unitFrame.isObject)
unitFrame.PlateFrame.isSoftInteractObject = isSoftInteract and (unitFrame.PlateFrame.isObject or unitFrame.isObject)
local isLooseTarget = IsTargetLoose()
unitFrame.isLooseTarget = isLooseTarget
unitFrame.PlateFrame.isLooseTarget = isLooseTarget
local isSoftEnemy = UnitIsUnit(tickFrame.unit, "softenemy")
unitFrame.isSoftEnemy = isSoftEnemy
unitFrame.PlateFrame.isSoftEnemy = isSoftEnemy
local isSoftFriend = UnitIsUnit(tickFrame.unit, "softfriend")
unitFrame.isSoftFriend = isSoftFriend
unitFrame.PlateFrame.isSoftFriend = isSoftFriend
local wasCombat = unitFrame.InCombat
unitFrame.InCombat = UnitAffectingCombat (tickFrame.unit) or (Plater.ForceInCombatUnits[unitFrame [MEMBER_NPCID]] and PLAYER_IN_COMBAT) or false
if wasCombat ~= unitFrame.InCombat then
Plater.UpdatePlateSize (tickFrame.PlateFrame)
end
--check aggro if is in combat
if (PLAYER_IN_COMBAT) then
if (unitFrame.CanCheckAggro) then
Plater.UpdateNameplateThread (unitFrame)
end
end
--perform a range check
Plater.CheckRange (tickFrame.PlateFrame, (deltaTime == 999))
--if not in combat, check if can show the percent health out of combat
if (actorTypeDBConfig.percent_text_enabled and (((profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat)) or actorTypeDBConfig.percent_text_ooc)) then
Plater.UpdateLifePercentText (healthBar, unitFrame.unit, actorTypeDBConfig.percent_show_health, actorTypeDBConfig.percent_show_percent, actorTypeDBConfig.percent_text_show_decimals)
healthBar.lifePercent:Show()
else
healthBar.lifePercent:Hide()
end
if (not unitFrame.DenyColorChange) then --tagged from a script
--if the unit tapped? (gray color)
if (Plater.IsUnitTapDenied (tickFrame.unit)) then
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (profile.tap_denied_color))
end
end
--the color overrider for unitIDs goes after the threat check and before the aura, since auras can run scripts and scripts have priority on setting colors
if (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID]]) then
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID]]))
unitFrame.UsingCustomColor = true --exposed to scripts
end
--update buffs and debuffs
if (DB_AURA_ENABLED) then --should update only when the heathbar is shown?
--Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdateAuras")
if (DB_TRACK_METHOD == 0x1) then --automatic
if (tickFrame.actorType == ACTORTYPE_PLAYER) then
--update auras on the personal bar
Plater.UpdateAuras_Self_Automatic (tickFrame.BuffFrame, tickFrame.unit)
else
Plater.UpdateAuras_Automatic (tickFrame.BuffFrame, tickFrame.unit)
end
else
--manual aura track
Plater.UpdateAuras_Manual (tickFrame.BuffFrame, tickFrame.unit, tickFrame.actorType == ACTORTYPE_PLAYER)
end
--update the buff layout and alpha
tickFrame.BuffFrame.unit = tickFrame.unit
--ghost and extra auras only show in the debuff frame
Plater.ShowGhostAuras(tickFrame.BuffFrame)
--check for expired extra auras
platerInternal.ExtraAuras.ClearExpired()
--update extra auras
platerInternal.ExtraAuras.Show(tickFrame.BuffFrame)
--align icons in the aura frame
Plater.AlignAuraFrames (tickFrame.BuffFrame)
--update the alignment on the second aura frame as well if enabled
if (DB_AURA_SEPARATE_BUFFS) then
Plater.AlignAuraFrames (tickFrame.BuffFrame.BuffFrame2)
end
Plater.RunScriptTriggersForAuraIcons (unitFrame)
--tickFrame.BuffFrame:SetAlpha (DB_AURA_ALPHA)
--tickFrame.BuffFrame2:SetAlpha (DB_AURA_ALPHA)
--Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdateAuras")
end
-- update DBM and BigWigs nameplate auras
Plater.UpdateBossModAuras(unitFrame)
--set the delay to perform another update
tickFrame.ThrottleUpdate = DB_TICK_THROTTLE * (unitFrame.isPerformanceUnit and 5 or 1)
--check if the unit name or unit npcID has a script
local globalScriptObject = SCRIPT_UNIT_TRIGGER_CACHE[tickFrame.PlateFrame [MEMBER_NAMELOWER]] or SCRIPT_UNIT_TRIGGER_CACHE[unitFrame [MEMBER_NPCID]]
--check if this aura has a custom script
if (globalScriptObject) then
--stored information about scripts
local scriptContainer = unitFrame:ScriptGetContainer()
--get the info about this particularly script
local scriptInfo = unitFrame:ScriptGetInfo(globalScriptObject, scriptContainer)
local scriptEnv = scriptInfo.Env
scriptEnv._UnitID = tickFrame.PlateFrame [MEMBER_UNITID]
scriptEnv._NpcID = tickFrame.PlateFrame [MEMBER_NPCID]
scriptEnv._UnitName = tickFrame.PlateFrame [MEMBER_NAME]
scriptEnv._UnitGUID = tickFrame.PlateFrame [MEMBER_GUID]
scriptEnv._HealthPercent = healthBar.CurrentHealth / healthBar.CurrentHealthMax * 100
--run onupdate script
unitFrame:ScriptRunOnUpdate(scriptInfo)
end
--scheduled name update
if (unitFrame.ScheduleNameUpdate) then
if (unitFrame.ActorType == ACTORTYPE_FRIENDLY_PLAYER) then
tickFrame.PlateFrame.playerGuildName = GetGuildInfo (tickFrame.unit)
Plater.UpdatePlateText (tickFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], false)
end
Plater.UpdateUnitName (tickFrame.PlateFrame)
unitFrame.ScheduleNameUpdate = nil
--run hook
if (HOOK_UNITNAME_UPDATE.ScriptAmount > 0) then
for i = 1, HOOK_UNITNAME_UPDATE.ScriptAmount do
local globalScriptObject = HOOK_UNITNAME_UPDATE [i]
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Name Updated")
--run
unitFrame:ScriptRunHook (scriptInfo, "Name Updated")
end
end
end
--hooks
Plater.TriggerNameplateUpdatedEvent(unitFrame)
--details! integration
if (IS_USING_DETAILS_INTEGRATION and not tickFrame.PlateFrame.IsSelf and PLAYER_IN_COMBAT and unitFrame.InCombat) then
local detailsPlaterConfig = Details.plater
--> current damage taken from all sources
if (detailsPlaterConfig.realtime_dps_enabled) then
local unitDamageTable = DetailsPlaterFrame.DamageTaken [tickFrame.PlateFrame [MEMBER_GUID]]
if (unitDamageTable) then
local damage = unitDamageTable.CurrentDamage or 0
local textString = healthBar.DetailsRealTime
textString:SetText (Plater.FormatNumber (damage / PLATER_DPS_SAMPLE_SIZE))
else
local textString = healthBar.DetailsRealTime
textString:SetText ("")
end
end
if (detailsPlaterConfig.realtime_dps_player_enabled) then
local unitDamageTable = DetailsPlaterFrame.DamageTaken [tickFrame.PlateFrame [MEMBER_GUID]]
if (unitDamageTable) then
local damage = unitDamageTable.CurrentDamageFromPlayer or 0
local textString = healthBar.DetailsRealTimeFromPlayer
textString:SetText (Plater.FormatNumber (damage / PLATER_DPS_SAMPLE_SIZE))
else
local textString = healthBar.DetailsRealTimeFromPlayer
textString:SetText ("")
end
end
if (detailsPlaterConfig.damage_taken_enabled) then
local unitDamageTable = DetailsPlaterFrame.DamageTaken [tickFrame.PlateFrame [MEMBER_GUID]]
if (unitDamageTable) then
local damage = unitDamageTable.TotalDamageTaken or 0
local textString = healthBar.DetailsDamageTaken
textString:SetText (Plater.FormatNumber (damage))
else
local textString = healthBar.DetailsDamageTaken
textString:SetText ("")
end
end
end
--check shield ~shield
if (IS_WOW_PROJECT_MAINLINE) then
if (profile.indicator_shield) then
local amountAbsorb = UnitGetTotalAbsorbs(tickFrame.PlateFrame[MEMBER_UNITID])
if (amountAbsorb and amountAbsorb > 0) then
--update the total amount on the shield indicator
if (not healthBar.shieldIndicator.shieldTotal) then
healthBar.shieldIndicator.shieldTotal = amountAbsorb
else
if (amountAbsorb > healthBar.shieldIndicator.shieldTotal) then
healthBar.shieldIndicator.shieldTotal = amountAbsorb
end
end
healthBar.shieldIndicator:Show()
local percent = amountAbsorb / healthBar.shieldIndicator.shieldTotal
local width = healthBar:GetWidth() * percent
healthBar.shieldIndicator:SetWidth(width)
else
--hide the shield bar is currently shown
if (healthBar.shieldIndicator:IsShown()) then
healthBar.shieldIndicator:Hide()
healthBar.shieldIndicator.shieldTotal = nil
end
end
end
end
if (unitFrame.castBar:IsShown()) then
Plater.CastBarOnTick_Hook(unitFrame.castBar, 999)
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "NameplateTick-Full")
--end of throttled updates
end
--OnTick updates
--smooth color transition ~lerpcolor
if (DB_LERP_COLOR and not unitFrame.isPerformanceUnit) then
local currentR, currentG, currentB = healthBar.barTexture:GetVertexColor()
local r, g, b = DF:LerpLinearColor (deltaTime, DB_LERP_COLOR_SPEED, currentR, currentG, currentB, healthBar.R or currentR, healthBar.G or currentG, healthBar.B or currentB)
healthBar.barTexture:SetVertexColor (r, g, b)
end
--animate health bar ~animation
if (DB_DO_ANIMATIONS and not unitFrame.isPerformanceUnit) then
if (healthBar.IsAnimating) then
healthBar.AnimateFunc (healthBar, deltaTime)
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "NameplateTick")
end
local set_aggro_color = function (self, r, g, b, a) --self = unitName
if (DB_AGGRO_CHANGE_HEALTHBAR_COLOR) then
if (not self.DenyColorChange) then --tagged from a script
Plater.ChangeHealthBarColor_Internal (self.healthBar, r, g, b, a)
end
end
if (DB_AGGRO_CHANGE_BORDER_COLOR) then
Plater.ForceChangeBorderColor (self, r, g, b)
end
if (DB_AGGRO_CHANGE_NAME_COLOR) then
self.unitName:SetTextColor (r, g, b)
end
end
--aggro threat ~aggro ~threat
function Plater.UpdateNameplateThread (self) --self = unitFrame
--make sure there's a unitID in the unit frame
if (not self.displayedUnit or not self.CanCheckAggro) then
return
end
Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdateNameplateThreat")
local profile = Plater.db.profile
local isTanking, threatStatus, threatpct, threatrawpct, threatValue = UnitDetailedThreatSituation ("player", self.displayedUnit)
--expose all threat situation to scripts
self.namePlateThreatIsTanking = isTanking
self.namePlateThreatStatus = threatStatus
self.namePlateThreatPercent = threatpct or 0
self.namePlateThreatRawPercent = threatrawpct or 0
self.namePlateThreatValue = threatValue or 0
-- (3 = securely tanking, 2 = insecurely tanking, 1 = not tanking but higher threat than tank, 0 = not tanking and lower threat than tank)
self.namePlateThreatOffTankIsTanking = false
self.namePlateThreatOffTankName = nil
self.aggroGlowUpper:Hide()
self.aggroGlowLower:Hide()
--player is a tank
if (Plater.PlayerIsTank) then
--and isn't tanking the unit
if (not isTanking) then
--is the player in combat?
if (self.InCombat) then
--is the player in a raid group?
if (IsInRaid()) then
--check if another tank is effectively tanking
--as the other tank may not be targeted due to spell-casts, we need to check the threat situation for tanks
local unitOffTank = nil
local otherIsTanking, otherThreatStatus, otherThreatpct
--loop on all tanks in the group (tank_cache is updated on entering combat or when group roster is updated)
for tank, _ in pairs(TANK_CACHE) do
if UnitExists(tank) and not UnitIsUnit("player", tank) then
otherIsTanking, otherThreatStatus, otherThreatpct = UnitDetailedThreatSituation (tank, self.displayedUnit)
if otherIsTanking then
unitOffTank = tank
break
end
end
end
--another tank is tanking the unit
if (unitOffTank) then
self.namePlateThreatOffTankIsTanking = true
self.namePlateThreatOffTankName = unitOffTank
--as the unit is being tanked by the off-tank, check if the player it self which is the other tank is about to accidently pull aggro just by hitting the mob
if (threatpct and otherThreatpct) then
--threatpct = player threat on the mob
--otherThreatpct = the aggro on the tank tanking the unit
if ((threatpct + 10) - otherThreatpct > 0) then
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.pulling_from_tank))
else
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.anothertank))
end
else
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.anothertank))
end
else
--no tank is tanking this unit
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.noaggro))
end
if (self.PlateFrame [MEMBER_NOCOMBAT]) then
self.PlateFrame [MEMBER_NOCOMBAT] = nil
Plater.CheckRange (self.PlateFrame, true)
end
else
--player isn't tanking this unit
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.noaggro))
if (self.PlateFrame [MEMBER_NOCOMBAT]) then
self.PlateFrame [MEMBER_NOCOMBAT] = nil
Plater.CheckRange (self.PlateFrame, true)
end
end
else
--if isn't a quest mob
if (not self.PlateFrame [MEMBER_QUEST]) then
--there's no aggro, isn't in combat, isn't a quest mob
if (self [MEMBER_REACTION] == 4) then
--do nothing if the mob is neutral
--set_aggro_color (self, 1, 1, 0) --ticket #185
else
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.nocombat))
end
if (DB_NOT_COMBAT_ALPHA_ENABLED) then
self.PlateFrame [MEMBER_NOCOMBAT] = true
self:SetAlpha (profile.not_affecting_combat_alpha)
end
end
end
else
--The player is tanking and:
if (threatStatus == 3) then --is tanking safely
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.aggro))
elseif (threatStatus == 2) then --is tanking with risk of aggro loss
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.pulling))
if profile.show_aggro_glow then
self.aggroGlowUpper:Show()
self.aggroGlowLower:Show()
end
else --not tanking
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.noaggro))
end
if (self.PlateFrame [MEMBER_NOCOMBAT]) then
self.PlateFrame [MEMBER_NOCOMBAT] = nil
Plater.CheckRange (self.PlateFrame, true)
end
end
else
--dps
if (isTanking) then
--the player is tanking as dps
if profile.dps.use_aggro_solo and not IsInGroup() then
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.solo))
else
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.aggro))
end
if (not self.PlateFrame.playerHasAggro and IS_IN_INSTANCE) then
if profile.show_aggro_flash then
self.PlateFrame.PlayBodyFlash ("-AGGRO-")
end
end
self.PlateFrame.playerHasAggro = true
if (self.PlateFrame [MEMBER_NOCOMBAT]) then
self.PlateFrame [MEMBER_NOCOMBAT] = nil
Plater.CheckRange (self.PlateFrame, true)
end
else
if (threatStatus == nil) then
self.PlateFrame.playerHasAggro = false
--> unit is in combat?
if (self.InCombat) then
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.noaggro))
self.PlateFrame.playerHasAggro = false
if (self.PlateFrame [MEMBER_NOCOMBAT]) then
self.PlateFrame [MEMBER_NOCOMBAT] = nil
Plater.CheckRange (self.PlateFrame, true)
end
else
--if isn't a quest mob
if (not self.PlateFrame [MEMBER_QUEST]) then
--there's no aggro, isn't in combat, isn't a quest mob
if (self [MEMBER_REACTION] == 4) then --ticket #185
--do nothing if the mob is neutral
--set_aggro_color (self, 1, 1, 0)
else
set_aggro_color (self, unpack (DB_AGGRO_TANK_COLORS.nocombat))
end
if (DB_NOT_COMBAT_ALPHA_ENABLED) then --not self.PlateFrame [MEMBER_NOCOMBAT] and
self.PlateFrame [MEMBER_NOCOMBAT] = true
self:SetAlpha (profile.not_affecting_combat_alpha)
end
end
end
else
if (threatStatus == 3) then --player is tanking the mob as dps
if profile.dps.use_aggro_solo and not IsInGroup() then
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.solo))
else
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.aggro))
end
if (not self.PlateFrame.playerHasAggro and IS_IN_INSTANCE) then
if profile.show_aggro_flash then
self.PlateFrame.PlayBodyFlash ("-AGGRO-")
end
end
self.PlateFrame.playerHasAggro = true
elseif (threatStatus == 2) then --player is tanking the mob with low aggro
if profile.dps.use_aggro_solo and not IsInGroup() then
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.solo))
else
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.aggro))
end
self.PlateFrame.playerHasAggro = true
else --the unit isn't attacking the player based on the threat situation
--which color the use in the nameplate based on the threat status
--this color can be overritten by the 'no tank aggro' check
local colorToUse
if (threatStatus == 1) then --player is almost aggroing the mob
--show aggro warning indicators
if profile.show_aggro_glow then
self.aggroGlowUpper:Show()
self.aggroGlowLower:Show()
end
if profile.dps.use_aggro_solo and not IsInGroup() then
colorToUse = DB_AGGRO_DPS_COLORS.solo
else
colorToUse = DB_AGGRO_DPS_COLORS.pulling
end
elseif (threatStatus == 0) then
colorToUse = DB_AGGRO_DPS_COLORS.noaggro
end
if (Plater.ZoneInstanceType == "party" or Plater.ZoneInstanceType == "raid") then
--check if can check for no tank aggro
if (DB_AGGRO_CAN_CHECK_NOTANKAGGRO) then
local unitTarget = UnitName (self.targetUnitID)
--check if the unit isn't attacking a tank comparing the target name with tank names
if (not TANK_CACHE [unitTarget]) then
--check if this isn't a false positive where the mob target another unit to cast a spell
local hasTankAggro = false
for tankName, _ in pairs (TANK_CACHE) do
if UnitExists(tankName) then
local threatStatus = UnitThreatSituation (tankName, self.displayedUnit)
if (threatStatus and threatStatus >= 2) then
--a tank has aggro on this unit, it is a false positive
hasTankAggro = true
break
end
end
end
if (not hasTankAggro) then
--the unit isn't targeting a tank and no tank in the group has threat status of 2 or more, the unit might be attacking a dps or healer
set_aggro_color (self, unpack (DB_AGGRO_DPS_COLORS.notontank))
else
--the unit isn't targeting a tank but a tank in the group has aggro on this unit
set_aggro_color (self, unpack (colorToUse))
end
else
--the unit is targeting a tank
set_aggro_color (self, unpack (colorToUse))
end
else
--isn't checking for 'no tank aggro'
set_aggro_color (self, unpack (colorToUse))
end
else
--player isn't inside a dungeon or raid
set_aggro_color (self, unpack (colorToUse))
end
self.PlateFrame.playerHasAggro = false
end
if (self.PlateFrame [MEMBER_NOCOMBAT]) then
self.PlateFrame [MEMBER_NOCOMBAT] = nil
Plater.CheckRange (self.PlateFrame, true)
end
end
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdateNameplateThreat")
end
function Plater.UpdateSoftInteractTarget(plateFrame, updateText)
local unitFrame = plateFrame.unitFrame
local unitID = plateFrame [MEMBER_UNITID]
local isSoftInteract = UnitIsUnit(unitID, "softinteract")
local reaction = UnitReaction (unitID, "player")
local isObject = (IS_WOW_PROJECT_MAINLINE and UnitIsGameObject(unitID)) or reaction == nil
local isSoftInteractObject = isObject and isSoftInteract
plateFrame.isSoftInteract = isSoftInteract
unitFrame.isSoftInteract = isSoftInteract
plateFrame.isObject = isObject
unitFrame.isObject = isObject
plateFrame.isSoftInteractObject = isSoftInteractObject
unitFrame.isSoftInteractObject = isSoftInteractObject
local isLooseTarget = IsTargetLoose()
unitFrame.isLooseTarget = isLooseTarget
unitFrame.PlateFrame.isLooseTarget = isLooseTarget
local isSoftEnemy = UnitIsUnit(unitID, "softenemy")
unitFrame.isSoftEnemy = isSoftEnemy
unitFrame.PlateFrame.isSoftEnemy = isSoftEnemy
local isSoftFriend = UnitIsUnit(unitID, "softfriend")
unitFrame.isSoftFriend = isSoftFriend
unitFrame.PlateFrame.isSoftFriend = isSoftFriend
if plateFrame.IsNpcWithoutHealthBar and updateText then
Plater.UpdatePlateText (plateFrame, DB_PLATE_CONFIG [plateFrame.unitFrame.ActorType], false)
end
if isSoftInteract and Plater.db.profile.show_softinteract_icons then
--re-anchor
Plater.SetAnchor(unitFrame.softInteractIcon, unitFrame.softInteractIcon.anchor or { side = 8, x = 0, y = 18, }, plateFrame)
local size = unitFrame.softInteractIcon.size or 24
unitFrame.softInteractIconFrame:SetSize(size, size)
unitFrame.softInteractIcon:SetDesaturated(false)
unitFrame.softInteractIcon:SetIgnoreParentAlpha(true)
unitFrame.softInteractIcon:SetSize(size, size)
unitFrame.softInteractIconFrame:Show()
unitFrame.softInteractIcon:Show()
local hasTexture = SetUnitCursorTexture(unitFrame.softInteractIcon, plateFrame [MEMBER_UNITID], nil, true)
if not hasTexture then
unitFrame.softInteractIcon:SetTexture(136243)
end
else
unitFrame.softInteractIconFrame:Hide()
end
end
-- ~target ~selection
function Plater.UpdateTarget (plateFrame) --private
local profile = Plater.db.profile
local unitFrame = plateFrame.unitFrame
if UnitIsUnit (unitFrame [MEMBER_UNITID], "focus") then
if profile.focus_indicator_enabled then
--this is a rare call, no need to cache these values
local texture = LibSharedMedia:Fetch ("statusbar", Plater.db.profile.focus_texture)
plateFrame.FocusIndicator:SetTexture (texture)
plateFrame.FocusIndicator:SetVertexColor (unpack (Plater.db.profile.focus_color))
plateFrame.FocusIndicator:Show()
end
unitFrame.IsFocus = true
else
unitFrame.IsFocus = false
plateFrame.FocusIndicator:Hide()
end
if (UnitIsUnit (unitFrame [MEMBER_UNITID], "target")) then
plateFrame [MEMBER_TARGET] = true
unitFrame [MEMBER_TARGET] = true
--hide obscured texture
plateFrame.Obscured:Hide()
--target indicator
Plater.UpdateTargetIndicator (plateFrame)
--target highlight
if (profile.target_highlight) then
if (plateFrame.actorType ~= ACTORTYPE_FRIENDLY_PLAYER and plateFrame.actorType ~= ACTORTYPE_FRIENDLY_NPC and not plateFrame.PlayerCannotAttack and unitFrame.healthBar:IsShown()) then
plateFrame.TargetNeonUp:Show()
plateFrame.TargetNeonDown:Show()
else
plateFrame.TargetNeonUp:Hide()
plateFrame.TargetNeonDown:Hide()
end
Plater.UpdateTargetHighlight (plateFrame) --neon
else
plateFrame.TargetNeonUp:Hide()
plateFrame.TargetNeonDown:Hide()
end
if (not unitFrame.healthBar:IsShown()) then
unitFrame.targetOverlayTexture:Hide()
else
unitFrame.targetOverlayTexture:Show()
end
if (DB_USE_UIPARENT) then
Plater.UpdateUIParentTargetLevels (unitFrame)
end
Plater.UpdateResourceFrame()
else
plateFrame.TargetNeonUp:Hide()
plateFrame.TargetNeonDown:Hide()
unitFrame.targetOverlayTexture:Hide()
plateFrame [MEMBER_TARGET] = nil
unitFrame [MEMBER_TARGET] = nil
if (unitFrame.IsTarget or unitFrame.TargetTextures2Sides [1]:IsShown() or unitFrame.TargetTextures4Sides [1]:IsShown()) then
for i = 1, 2 do
unitFrame.TargetTextures2Sides [i]:Hide()
end
for i = 1, 4 do
unitFrame.TargetTextures4Sides [i]:Hide()
end
unitFrame.IsTarget = false
end
if (DB_TARGET_SHADY_ENABLED and (not DB_TARGET_SHADY_COMBATONLY or (profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat)) and not plateFrame.IsSelf) then
plateFrame.Obscured:Show()
plateFrame.Obscured:SetAlpha (DB_TARGET_SHADY_ALPHA)
else
plateFrame.Obscured:Hide()
end
if (DB_USE_UIPARENT) then
Plater.UpdateUIParentLevels (unitFrame)
end
end
Plater.CheckRange (plateFrame, true) --disabled on 2018-10-09 | enabled back on 2020-1-16
end
--called when the player targets a new unit, when focus changed or when a unit isn't in the screen any more
function Plater.OnPlayerTargetChanged() --private
Plater.PlayerCurrentTargetGUID = UnitGUID ("target")
Plater.PlayerHasTarget = Plater.PlayerCurrentTargetGUID and true
Plater.PlayerHasTargetNonSelf = Plater.PlayerHasTarget and Plater.PlayerCurrentTargetGUID ~= Plater.PlayerGUID and true
Plater.PlayerCurrentFocusTargetGUID = UnitGUID ("focus")
Plater.PlayerHasFocusTarget = Plater.PlayerCurrentFocusTargetGUID and true
Plater.PlayerHasFocusTargetNonSelf = Plater.PlayerHasFocusTarget and Plater.PlayerCurrentFocusTargetGUID ~= Plater.PlayerGUID and true
for index, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if plateFrame.unitFrame.PlaterOnScreen then
Plater.UpdateTarget (plateFrame)
--hooks
if (HOOK_TARGET_CHANGED.ScriptAmount > 0) then
for i = 1, HOOK_TARGET_CHANGED.ScriptAmount do
local globalScriptObject = HOOK_TARGET_CHANGED [i]
local unitFrame = plateFrame.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Target Changed")
--run
unitFrame:ScriptRunHook (scriptInfo, "Target Changed")
end
end
end
end
Plater.Resources.UpdateResourceFramePosition() --~resource
end
function Plater.UpdateTargetHighlight (plateFrame)
local healthBar = plateFrame.unitFrame.healthBar
local profile = Plater.db.profile
local alpha = profile.target_highlight_alpha
local height = profile.target_highlight_height
local color = profile.target_highlight_color
local texture = profile.target_highlight_texture
plateFrame.TargetNeonUp:SetVertexColor (unpack (color))
plateFrame.TargetNeonUp:SetAlpha (alpha)
plateFrame.TargetNeonUp:SetTexture (texture)
plateFrame.TargetNeonUp:SetHeight (height)
PixelUtil.SetPoint (plateFrame.TargetNeonUp, "bottomleft", healthBar, "topleft", 0, 0)
PixelUtil.SetPoint (plateFrame.TargetNeonUp, "bottomright", healthBar, "topright", 0, 0)
plateFrame.TargetNeonDown:SetVertexColor (unpack (color))
plateFrame.TargetNeonDown:SetAlpha (alpha)
plateFrame.TargetNeonDown:SetTexture (texture)
plateFrame.TargetNeonDown:SetHeight (height)
PixelUtil.SetPoint (plateFrame.TargetNeonDown, "topleft", healthBar, "bottomleft", 0, 0)
PixelUtil.SetPoint (plateFrame.TargetNeonDown, "topright", healthBar, "bottomright", 0, 0)
end
function Plater.UpdateTargetIndicator (plateFrame)
local healthBarHeight = plateFrame.unitFrame.healthBar:GetHeight()
--if the height is lower than 4, just hide all indicators
if (healthBarHeight < 4) then
for i = 1, 2 do
plateFrame.unitFrame.TargetTextures2Sides [i]:Hide()
end
for i = 1, 4 do
plateFrame.unitFrame.TargetTextures4Sides [i]:Hide()
end
return
end
local preset = Plater.TargetIndicators [Plater.db.profile.target_indicator]
if (not preset) then
--use default indicator is the indicator isn't found
preset = Plater.TargetIndicators ["Silver"]
end
local width, height = preset.width, preset.height
local wscale, hscale = preset.wscale or 1, preset.hscale or 1
local x, y = preset.x or 0, preset.y or 0
local desaturated = preset.desaturated
local coords = preset.coords
local path = preset.path
local blend = preset.blend or "BLEND"
local alpha = preset.alpha or 1
local doScale = preset.autoScale
local custScale = preset.scale
local overlayColorR, overlayColorG, overlayColorB = DF:ParseColors (preset.color or "white")
local scale = (not doScale and custScale) or (healthBarHeight / (doScale and height or 10))
--four parts (textures)
if (#coords == 4) then
for i = 1, 4 do
local texture = plateFrame.unitFrame.TargetTextures4Sides [i]
texture:Show()
texture:SetTexture (path)
texture:SetTexCoord (unpack (coords [i]))
texture:SetSize (width * scale * wscale, height * scale * hscale)
texture:SetAlpha (alpha)
texture:SetVertexColor (overlayColorR, overlayColorG, overlayColorB)
texture:SetDesaturated (desaturated)
if (i == 1) then
--PixelUtil.SetPoint (texture, "topleft", plateFrame.unitFrame.healthBar, "topleft", -x * scale, y * scale)
texture:SetPoint ("topleft", plateFrame.unitFrame.healthBar, "topleft", -x * scale, y * scale)
elseif (i == 2) then
--PixelUtil.SetPoint (texture, "bottomleft", plateFrame.unitFrame.healthBar, "bottomleft", -x * scale, -y * scale)
texture:SetPoint ("bottomleft", plateFrame.unitFrame.healthBar, "bottomleft", -x * scale, -y * scale)
elseif (i == 3) then
--PixelUtil.SetPoint (texture, "bottomright", plateFrame.unitFrame.healthBar, "bottomright", x * scale, -y * scale)
texture:SetPoint ("bottomright", plateFrame.unitFrame.healthBar, "bottomright", x * scale, -y * scale)
elseif (i == 4) then
--PixelUtil.SetPoint (texture, "topright", plateFrame.unitFrame.healthBar, "topright", x * scale, y * scale)
texture:SetPoint ("topright", plateFrame.unitFrame.healthBar, "topright", x * scale, y * scale)
end
end
for i = 1, 2 do
plateFrame.unitFrame.TargetTextures2Sides [i]:Hide()
end
--two parts
else
for i = 1, 2 do
local texture = plateFrame.unitFrame.TargetTextures2Sides [i]
texture:Show()
texture:SetTexture (path)
texture:SetBlendMode (blend)
texture:SetTexCoord (unpack (coords [i]))
--PixelUtil.SetSize (texture, width * scale, height * scale)
--PixelUtil.SetSize (texture, width * scale * wscale, height * scale * hscale)
texture:SetSize (width * scale * wscale, height * scale * hscale)
texture:SetDesaturated (desaturated)
texture:SetAlpha (alpha)
texture:SetVertexColor (overlayColorR, overlayColorG, overlayColorB)
if (i == 1) then
--PixelUtil.SetPoint (texture, "left", plateFrame.unitFrame.healthBar, "left", -x * scale, y * scale)
texture:SetPoint ("left", plateFrame.unitFrame.healthBar, "left", -x * scale, y * scale)
elseif (i == 2) then
--PixelUtil.SetPoint (texture, "right", plateFrame.unitFrame.healthBar, "right", x * scale, -y * scale)
texture:SetPoint ("right", plateFrame.unitFrame.healthBar, "right", x * scale, y * scale)
end
end
for i = 1, 4 do
plateFrame.unitFrame.TargetTextures4Sides [i]:Hide()
end
end
plateFrame.unitFrame.IsTarget = true
end
-- ~updatetext ~update - called only from UpdatePlateFrame()
-- update all texts in the nameplate, settings can variate from different unit types
-- needReset is true when the previous unit type shown on this place is different from the current unit
function Plater.UpdatePlateText (plateFrame, plateConfigs, needReset) --private
if plateFrame.unitFrame.isWidgetOnlyMode then
plateFrame.ActorNameSpecial:Hide()
plateFrame.ActorTitleSpecial:Hide()
return
end
-- ensure castBar updates are done, as this needs to be done for all types of plates...
local spellnameString = plateFrame.unitFrame.castBar.Text
local spellPercentString = plateFrame.unitFrame.castBar.percentText
--update spell name text
if (needReset) then
DF:SetFontColor (spellnameString, plateConfigs.spellname_text_color)
--DF:SetFontOutline (spellnameString, plateConfigs.spellname_text_shadow)
Plater.SetFontOutlineAndShadow (spellnameString, plateConfigs.spellname_text_outline, plateConfigs.spellname_text_shadow_color, plateConfigs.spellname_text_shadow_color_offset[1], plateConfigs.spellname_text_shadow_color_offset[2])
DF:SetFontFace (spellnameString, plateConfigs.spellname_text_font)
DF:SetFontSize (spellnameString, plateConfigs.spellname_text_size)
Plater.SetAnchor (spellnameString, plateConfigs.spellname_text_anchor)
end
--update spell cast time
if (plateConfigs.spellpercent_text_enabled) then
spellPercentString:Show()
plateFrame.unitFrame.castBar.Settings.ShowCastTime = true
if (needReset) then
DF:SetFontColor (spellPercentString, plateConfigs.spellpercent_text_color)
DF:SetFontSize (spellPercentString, plateConfigs.spellpercent_text_size)
--DF:SetFontOutline (spellPercentString, plateConfigs.spellpercent_text_shadow)
Plater.SetFontOutlineAndShadow (spellPercentString, plateConfigs.spellpercent_text_outline, plateConfigs.spellpercent_text_shadow_color, plateConfigs.spellpercent_text_shadow_color_offset[1], plateConfigs.spellpercent_text_shadow_color_offset[2])
DF:SetFontFace (spellPercentString, plateConfigs.spellpercent_text_font)
Plater.SetAnchor (spellPercentString, plateConfigs.spellpercent_text_anchor)
end
else
plateFrame.unitFrame.castBar.Settings.ShowCastTime = false
spellPercentString:Hide()
end
-- updates for special frames
if (plateFrame.IsSelf) then
--return
--needReset = true
elseif (plateFrame.IsFriendlyPlayerWithoutHealthBar) then --not critical code
--when the option to show only the player name is enabled
--special string to show the player name
local nameFontString = plateFrame.ActorNameSpecial
nameFontString:Show()
--set the name in the string
plateFrame.CurrentUnitNameString = nameFontString
Plater.UpdateUnitName (plateFrame)
DF:SetFontSize (nameFontString, plateConfigs.actorname_text_size)
DF:SetFontFace (nameFontString, plateConfigs.actorname_text_font)
--DF:SetFontOutline (nameFontString, plateConfigs.actorname_text_shadow)
Plater.SetFontOutlineAndShadow (nameFontString, plateConfigs.actorname_text_outline, plateConfigs.actorname_text_shadow_color, plateConfigs.actorname_text_shadow_color_offset[1], plateConfigs.actorname_text_shadow_color_offset[2])
--check if the player has a guild, this check is done when the nameplate is added
if (plateFrame.playerGuildName) then
if (plateConfigs.show_guild_name) then
Plater.AddGuildNameToPlayerName (plateFrame)
end
end
--set the point of the name and guild texts
nameFontString:ClearAllPoints()
PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame.unitFrame, "center", 0, 10)
--format the color if is the same guild, a friend from friends list or color by player class
if (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_guild_color and plateFrame.playerGuildName == Plater.PlayerGuildName) then
--is a guild friend?
DF:SetFontColor (nameFontString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_guild_color))
plateFrame.isFriend = true
elseif (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_friends_color and Plater.FriendsCache [plateFrame.unitNameInternal]) then
--is regular friend
DF:SetFontColor (nameFontString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_friend_color))
--DF:SetFontOutline (nameFontString, plateConfigs.actorname_text_shadow)
Plater.SetFontOutlineAndShadow (nameFontString, plateConfigs.actorname_text_outline, plateConfigs.actorname_text_shadow_color, plateConfigs.actorname_text_shadow_color_offset[1], plateConfigs.actorname_text_shadow_color_offset[2])
plateFrame.isFriend = true
else
--isn't friend, check if is showing only the name and if is showing class colors
if (plateConfigs.actorname_use_class_color) then
local _, unitClass = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
if (unitClass) then
local color = DB_CLASS_COLORS [unitClass]
DF:SetFontColor (nameFontString, color.r, color.g, color.b)
else
DF:SetFontColor (nameFontString, plateConfigs.actorname_text_color)
end
else
DF:SetFontColor (nameFontString, plateConfigs.actorname_text_color)
end
plateFrame.isFriend = nil
end
return
elseif (plateFrame.IsNpcWithoutHealthBar) then --not critical code
--reset points for special units
plateFrame.ActorNameSpecial:ClearAllPoints()
plateFrame.ActorTitleSpecial:ClearAllPoints()
--hide for good measure as reset
plateFrame.ActorNameSpecial:Hide()
plateFrame.ActorTitleSpecial:Hide()
PixelUtil.SetPoint (plateFrame.ActorNameSpecial, "center", plateFrame.unitFrame, "center", 0, 10)
PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
--there's two ways of showing this for friendly npcs (selected from the options panel): show all names or only npcs with profession names
--enemy npcs always show all
if (plateConfigs.all_names or (plateFrame.isSoftInteract and Plater.db.profile.show_healthbars_on_softinteract)) then
if not plateFrame.isObject or (plateFrame.isObject and not Plater.db.profile.hide_name_on_game_objects) then
plateFrame.ActorNameSpecial:Show()
else
plateFrame.ActorNameSpecial:Hide()
end
plateFrame.CurrentUnitNameString = plateFrame.ActorNameSpecial
Plater.UpdateUnitName (plateFrame)
--if this is an enemy or neutral npc
if (plateFrame [MEMBER_REACTION] <= 4) then
local r, g, b, a
--get the quest color if this npcs is a quest npc
if (plateFrame [MEMBER_QUEST] and DB_PLATE_CONFIG [plateFrame.unitFrame.ActorType].quest_color_enabled) then
if (plateFrame [MEMBER_REACTION] == Plater.UnitReaction.UNITREACTION_NEUTRAL) then
r, g, b, a = unpack (plateConfigs.quest_color_neutral)
else
r, g, b, a = unpack (plateConfigs.quest_color_enemy)
g = g + 0.1
b = b + 0.1
end
else
r, g, b, a = 1, 1, 0, 1 --neutral
if (plateFrame [MEMBER_REACTION] <= 3) then
r, g, b, a = 1, .05, .05, 1
end
end
plateFrame.ActorNameSpecial:SetTextColor (r, g, b, a)
DF:SetFontSize (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_size)
DF:SetFontFace (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_font)
--DF:SetFontOutline (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_shadow)
Plater.SetFontOutlineAndShadow (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_outline, plateConfigs.big_actorname_text_shadow_color, plateConfigs.big_actorname_text_shadow_color_offset[1], plateConfigs.big_actorname_text_shadow_color_offset[2])
--npc title
local subTitle = Plater.GetActorSubName (plateFrame)
if (subTitle and subTitle ~= "") then
plateFrame.ActorTitleSpecial:Show()
--subTitle = DF:RemoveRealmName (subTitle) -- why are removing real names on npc titles? e.g. <T-Shirt Scalper> Skin-Me-Own-Coat-Dibblefur gets broken to <T>.
plateFrame.ActorTitleSpecial:SetText ("<" .. subTitle .. ">")
plateFrame.ActorTitleSpecial:ClearAllPoints()
PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
plateFrame.ActorTitleSpecial:SetTextColor (r, g, b, a)
DF:SetFontSize (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_size)
DF:SetFontFace (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_font)
--DF:SetFontOutline (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_shadow)
Plater.SetFontOutlineAndShadow (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_outline, plateConfigs.big_actortitle_text_shadow_color, plateConfigs.big_actortitle_text_shadow_color_offset[1], plateConfigs.big_actortitle_text_shadow_color_offset[2])
else
plateFrame.ActorTitleSpecial:Hide()
end
else
--it's a friendly npc
plateFrame.ActorNameSpecial:SetTextColor (unpack (plateConfigs.big_actorname_text_color))
DF:SetFontSize (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_size)
DF:SetFontFace (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_font)
--DF:SetFontOutline (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_shadow)
Plater.SetFontOutlineAndShadow (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_outline, plateConfigs.big_actorname_text_shadow_color, plateConfigs.big_actorname_text_shadow_color_offset[1], plateConfigs.big_actorname_text_shadow_color_offset[2])
--profession (title)
local subTitle = Plater.GetActorSubName (plateFrame)
if (subTitle and subTitle ~= "") then
plateFrame.ActorTitleSpecial:Show()
--subTitle = DF:RemoveRealmName (subTitle)
plateFrame.ActorTitleSpecial:SetText ("<" .. subTitle .. ">")
plateFrame.ActorTitleSpecial:ClearAllPoints()
PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
plateFrame.ActorTitleSpecial:SetTextColor (unpack (plateConfigs.big_actortitle_text_color))
DF:SetFontSize (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_size)
DF:SetFontFace (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_font)
--DF:SetFontOutline (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_shadow)
Plater.SetFontOutlineAndShadow (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_outline, plateConfigs.big_actortitle_text_shadow_color, plateConfigs.big_actortitle_text_shadow_color_offset[1], plateConfigs.big_actortitle_text_shadow_color_offset[2])
end
end
else
--scan tooltip to check if there's an title for this npc
local subTitle = Plater.GetActorSubName (plateFrame)
if (subTitle and subTitle ~= "" and not Plater.IsNpcInIgnoreList (plateFrame, true)) then
plateFrame.ActorTitleSpecial:Show()
--subTitle = DF:RemoveRealmName (subTitle)
plateFrame.ActorTitleSpecial:SetText ("<" .. subTitle .. ">")
plateFrame.ActorTitleSpecial:ClearAllPoints()
PixelUtil.SetPoint (plateFrame.ActorTitleSpecial, "top", plateFrame.ActorNameSpecial, "bottom", 0, -2)
plateFrame.ActorTitleSpecial:SetTextColor (unpack (plateConfigs.big_actortitle_text_color))
plateFrame.ActorNameSpecial:SetTextColor (unpack (plateConfigs.big_actorname_text_color))
DF:SetFontSize (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_size)
DF:SetFontFace (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_font)
--DF:SetFontOutline (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_shadow)
Plater.SetFontOutlineAndShadow (plateFrame.ActorTitleSpecial, plateConfigs.big_actortitle_text_outline, plateConfigs.big_actortitle_text_shadow_color, plateConfigs.big_actortitle_text_shadow_color_offset[1], plateConfigs.big_actortitle_text_shadow_color_offset[2])
--npc name
plateFrame.ActorNameSpecial:Show()
plateFrame.CurrentUnitNameString = plateFrame.ActorNameSpecial
Plater.UpdateUnitName (plateFrame)
DF:SetFontSize (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_size)
DF:SetFontFace (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_font)
--DF:SetFontOutline (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_shadow)
Plater.SetFontOutlineAndShadow (plateFrame.ActorNameSpecial, plateConfigs.big_actorname_text_outline, plateConfigs.big_actorname_text_shadow_color, plateConfigs.big_actorname_text_shadow_color_offset[1], plateConfigs.big_actorname_text_shadow_color_offset[2])
end
end
return
end
--critical code
--the nameplate is showing the health bar
--cache the strings for performance
local nameString = plateFrame.unitFrame.healthBar.unitName
local guildString = plateFrame.ActorTitleSpecial
local levelString = plateFrame.unitFrame.healthBar.actorLevel
local lifeString = plateFrame.unitFrame.healthBar.lifePercent
--update the unit name
plateFrame.CurrentUnitNameString = nameString
Plater.UpdateUnitName (plateFrame)
--update unit name text
if (needReset) then
DF:SetFontSize (nameString, plateConfigs.actorname_text_size)
DF:SetFontFace (nameString, plateConfigs.actorname_text_font)
Plater.SetFontOutlineAndShadow (nameString, plateConfigs.actorname_text_outline, plateConfigs.actorname_text_shadow_color, plateConfigs.actorname_text_shadow_color_offset[1], plateConfigs.actorname_text_shadow_color_offset[2])
Plater.SetAnchor (nameString, plateConfigs.actorname_text_anchor)
--PixelUtil.SetHeight (nameString, nameString:GetLineHeight())
end
if (plateFrame.playerGuildName) then
if (plateConfigs.show_guild_name) then
Plater.AddGuildNameToPlayerName (plateFrame)
end
end
if (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_guild_color and plateFrame.playerGuildName == Plater.PlayerGuildName) then
--is a guild friend?
DF:SetFontColor (nameString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_guild_color))
DF:SetFontColor (guildString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_guild_color))
plateFrame.isFriend = true
elseif (Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_friends_color and Plater.FriendsCache [plateFrame.unitNameInternal]) then
--is regular friend
DF:SetFontColor (nameString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_friend_color))
DF:SetFontColor (guildString, unpack(Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_friend_color))
plateFrame.isFriend = true
elseif (plateFrame.actorType == ACTORTYPE_FRIENDLY_PLAYER and Plater.db.profile.plate_config [ACTORTYPE_FRIENDLY_PLAYER].actorname_use_class_color) then
--class colors should be used, if possible, because this is enabled
plateFrame.isFriend = nil
local _, unitClass = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
if (unitClass) then
local color = DB_CLASS_COLORS [unitClass]
DF:SetFontColor (nameString, color.r, color.g, color.b)
DF:SetFontColor (guildString, color.r, color.g, color.b)
else
DF:SetFontColor (nameString, plateConfigs.actorname_text_color)
DF:SetFontColor (guildString, plateConfigs.actorname_text_color)
end
elseif (plateFrame.actorType == ACTORTYPE_ENEMY_PLAYER and Plater.db.profile.plate_config [ACTORTYPE_ENEMY_PLAYER].actorname_use_class_color) then
--class colors should be used, if possible, because this is enabled
plateFrame.isFriend = nil
local _, unitClass = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
if (unitClass) then
local color = DB_CLASS_COLORS [unitClass]
DF:SetFontColor (nameString, color.r, color.g, color.b)
DF:SetFontColor (guildString, color.r, color.g, color.b)
else
DF:SetFontColor (nameString, plateConfigs.actorname_text_color)
DF:SetFontColor (guildString, plateConfigs.actorname_text_color)
end
else
DF:SetFontColor (nameString, plateConfigs.actorname_text_color)
DF:SetFontColor (guildString, plateConfigs.actorname_text_color)
plateFrame.isFriend = nil
end
--update unit level text
if (plateConfigs.level_text_enabled) then
levelString:Show()
if (needReset) then
DF:SetFontSize (levelString, plateConfigs.level_text_size)
DF:SetFontFace (levelString, plateConfigs.level_text_font)
Plater.SetFontOutlineAndShadow (levelString, plateConfigs.level_text_outline, plateConfigs.level_text_shadow_color, plateConfigs.level_text_shadow_color_offset[1], plateConfigs.level_text_shadow_color_offset[2])
Plater.SetAnchor (levelString, plateConfigs.level_text_anchor)
Plater.UpdateLevelTextAndColor (levelString, plateFrame.unitFrame [MEMBER_UNITID])
levelString:SetAlpha (plateConfigs.level_text_alpha)
else
Plater.UpdateLevelTextAndColor (levelString, plateFrame.unitFrame [MEMBER_UNITID])
levelString:SetAlpha (plateConfigs.level_text_alpha)
end
else
levelString:Hide()
end
--update health amount text
if (plateConfigs.percent_text_enabled) then
if (needReset) then
DF:SetFontSize (lifeString, plateConfigs.percent_text_size)
DF:SetFontFace (lifeString, plateConfigs.percent_text_font)
Plater.SetFontOutlineAndShadow (lifeString, plateConfigs.percent_text_outline, plateConfigs.percent_text_shadow_color, plateConfigs.percent_text_shadow_color_offset[1], plateConfigs.percent_text_shadow_color_offset[2])
DF:SetFontColor (lifeString, plateConfigs.percent_text_color)
Plater.SetAnchor (lifeString, plateConfigs.percent_text_anchor)
PixelUtil.SetHeight (lifeString, lifeString:GetLineHeight())
lifeString:SetAlpha (plateConfigs.percent_text_alpha)
end
Plater.UpdateLifePercentText (plateFrame.unitFrame.healthBar, plateFrame.unitFrame [MEMBER_UNITID], plateConfigs.percent_show_health, plateConfigs.percent_show_percent, plateConfigs.percent_text_show_decimals)
else
lifeString:Hide()
end
--name isn't shown in the personal bar
if (plateFrame.IsSelf) then
plateFrame.unitFrame.healthBar.unitName:SetText ("")
end
return true
end
--check if the life percent should be showing for this nameplate
--self is plateFrame
function Plater.UpdateLifePercentVisibility (self)
local plateConfigs = Plater.GetSettings (self)
if (plateConfigs.percent_text_enabled) then
--can show out of combat? or if the player is in combat
if ((Plater.db.profile.use_player_combat_state and PLAYER_IN_COMBAT or self.unitFrame.InCombat) or plateConfigs.percent_text_ooc) then
self.unitFrame.healthBar.lifePercent:Show()
else
self.unitFrame.healthBar.lifePercent:Hide()
end
else
self.unitFrame.healthBar.lifePercent:Hide()
end
end
--return the nameplate config directly from the profile
--this is the actual table from the db, any changes will affect the profile
--self is plateFrame
function Plater.GetSettings (self) --private
return self.PlateConfig
end
function Plater.UpdateLifePercentText (healthBar, unitId, showHealthAmount, showPercentAmount, showDecimals) -- ~health
--get the cached health amount for performance
local currentHealth, maxHealth = healthBar.CurrentHealth, healthBar.CurrentHealthMax
if (showHealthAmount and showPercentAmount) then
local percent = maxHealth == 0 and 100 or (currentHealth / maxHealth * 100)
if (showDecimals) then
if (percent < 10) then
healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. format (" (%.2f%%)", percent))
elseif (percent < 99.9) then
healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. format (" (%.1f%%)", percent))
else
healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. " (100%)")
end
else
healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth) .. format (" (%d%%)", percent))
end
elseif (showHealthAmount) then
healthBar.lifePercent:SetText (Plater.FormatNumber (currentHealth))
elseif (showPercentAmount) then
local percent = maxHealth == 0 and 100 or (currentHealth / maxHealth * 100)
if (showDecimals) then
if (percent < 10) then
healthBar.lifePercent:SetText (format ("%.2f%%", percent))
elseif (percent < 99.9) then
healthBar.lifePercent:SetText (format ("%.1f%%", percent))
else
healthBar.lifePercent:SetText ("100%")
end
else
healthBar.lifePercent:SetText (format ("%d%%", percent))
end
else
healthBar.lifePercent:SetText ("")
end
end
-- this test if the percent life text can updated
function Plater.CheckLifePercentText (unitFrame) --private
if (not unitFrame.actorType) then
return
end
local actorTypeDBConfig = DB_PLATE_CONFIG [unitFrame.actorType]
if (Plater.db.profile.use_player_combat_state and PLAYER_IN_COMBAT or unitFrame.InCombat) then
if (actorTypeDBConfig.percent_text_enabled) then
Plater.UpdateLifePercentText (unitFrame.healthBar, unitFrame.unit, actorTypeDBConfig.percent_show_health, actorTypeDBConfig.percent_show_percent, actorTypeDBConfig.percent_text_show_decimals)
else
unitFrame.healthBar.lifePercent:Hide()
end
else
--if not in combat, check if can show the percent health out of combat
if (actorTypeDBConfig.percent_text_enabled and actorTypeDBConfig.percent_text_ooc) then
Plater.UpdateLifePercentText (unitFrame.healthBar, unitFrame.unit, actorTypeDBConfig.percent_show_health, actorTypeDBConfig.percent_show_percent, actorTypeDBConfig.percent_text_show_decimals)
else
unitFrame.healthBar.lifePercent:Hide()
end
end
end
function Plater.UpdateAllNames() --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if (plateFrame.actorType == ACTORTYPE_PLAYER) then
plateFrame.NameAnchor = 0
elseif (plateFrame.actorType == Plater.UnitReaction.UNITREACTION_FRIENDLY) then
plateFrame.NameAnchor = DB_NAME_PLAYERFRIENDLY_ANCHOR
elseif (plateFrame.actorType == ACTORTYPE_ENEMY_PLAYER) then
plateFrame.NameAnchor = DB_NAME_PLAYERENEMY_ANCHOR
elseif (plateFrame.actorType == ACTORTYPE_FRIENDLY_NPC) then
plateFrame.NameAnchor = DB_NAME_NPCFRIENDLY_ANCHOR
elseif (plateFrame.actorType == ACTORTYPE_ENEMY_NPC) then
plateFrame.NameAnchor = DB_NAME_NPCENEMY_ANCHOR
end
Plater.UpdateUnitName (plateFrame)
end
end
function Plater.UpdateSpellNameSize (nameString, actorType, cutOff, inCombat)
local spellName = nameString:GetText()
if not spellName or spellName == "" then
return
end
local maxLength = Plater.MaxCastBarTextLength or 500
cutOff = cutOff or 40
actorType = actorType or "enemynpc"
if (not Plater.db.profile.no_spellname_length_limit) then
local castKey = Plater.GetHashKey(inCombat)
if Plater.db.profile.plate_config[actorType][castKey] then
maxLength = Plater.db.profile.plate_config[actorType][castKey][1] - cutOff
end
end
while (nameString:GetStringWidth() > maxLength) do
spellName = strsub (spellName, 1, #spellName - 1)
nameString:SetText (spellName)
if (string.len (spellName) <= 1) then
break
end
end
-- cleanup utf8...
spellName = DF:CleanTruncateUTF8String(spellName)
nameString:SetText (spellName)
end
function Plater.AddGuildNameToPlayerName (plateFrame)
local currentText = plateFrame.CurrentUnitNameString:GetText()
if (not currentText:find ("<")) then
plateFrame.CurrentUnitNameString:SetText (currentText .. "\n" .. "<" .. plateFrame.playerGuildName .. ">")
end
end
function Plater.UpdateUnitName (plateFrame)
local nameString = plateFrame.CurrentUnitNameString
if ( not (plateFrame.IsFriendlyPlayerWithoutHealthBar or plateFrame.IsNpcWithoutHealthBar) and plateFrame.NameAnchor >= 9) then
--remove some character from the unit name if the name is placed inside the nameplate
Plater.UpdateUnitNameTextSize (plateFrame, nameString)
else
nameString:SetText (plateFrame [MEMBER_NAME] or plateFrame.unitFrame [MEMBER_NAME] or "")
end
--check if the player has a guild, this check is done when the nameplate is added
if (plateFrame.playerGuildName) then
if (plateFrame.PlateConfig.show_guild_name) then
Plater.AddGuildNameToPlayerName (plateFrame)
end
end
end
function Plater.UpdateUnitNameTextSize (plateFrame, nameString, maxWidth)
local stringSize = maxWidth or max (plateFrame.unitFrame.healthBar:GetWidth() - 6, 44)
local name = plateFrame [MEMBER_NAME] or plateFrame.unitFrame [MEMBER_NAME] or ""
nameString:SetText (name)
if not name or name == "" then
return
end
while (nameString:GetStringWidth() > stringSize) do
name = strsub (name, 1, #name-1)
nameString:SetText (name)
if (string.len (name) <= 1) then
break
end
end
-- cleanup utf8...
name = DF:CleanTruncateUTF8String(name)
nameString:SetText (name)
end
--updates the level text and the color
function Plater.UpdateLevelTextAndColor (levelString, unitId)
--level text
local level = UnitLevel (unitId)
if (not level) then
levelString:SetText ("")
elseif (level == -1) then
levelString:SetText ("??")
else
levelString:SetText (level)
end
--level color
local color = GetRelativeDifficultyColor (UnitLevel ("player") or 120, UnitLevel (unitId) or 120) or Plater.DefaultLevelColor
levelString:SetTextColor (color.r, color.g, color.b, Plater.db.profile.level_text_alpha)
end
--units can be rename on the Npc Colors tab, this function run from nameplate_added and UpdatePlateFrame() (usually from UpdateAllPlates() are called)
function Plater.UpdateNameOnRenamedUnit(plateFrame)
--set the npc name if the unit has a custom name
local newNpcName = Plater.db.profile.npcs_renamed[plateFrame[MEMBER_NPCID]]
local unitFrame = plateFrame.unitFrame
if (newNpcName) then
plateFrame [MEMBER_NAME] = newNpcName
plateFrame [MEMBER_NAMELOWER] = lower (newNpcName)
unitFrame [MEMBER_NAME] = newNpcName
unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
unitFrame.unitName:SetText(newNpcName)
unitFrame.unitName.isRenamed = true
else
if (unitFrame.unitName.isRenamed) then
newNpcName = UnitName(plateFrame[MEMBER_UNITID])
plateFrame [MEMBER_NAME] = newNpcName
plateFrame [MEMBER_NAMELOWER] = lower (newNpcName)
unitFrame [MEMBER_NAME] = newNpcName
unitFrame [MEMBER_NAMELOWER] = plateFrame [MEMBER_NAMELOWER]
unitFrame.unitName:SetText(newNpcName)
unitFrame.unitName.isRenamed = nil
end
end
end
--Blizzard default font settings
function Plater.UpdateBlizzardNameplateFonts()
local profile = Plater.db.profile
if profile.blizzard_nameplate_font_override_enabled then
DF:SetFontFace (_G.SystemFont_NamePlate, profile.blizzard_nameplate_font)
DF:SetFontOutline (_G.SystemFont_NamePlate, profile.blizzard_nameplate_font_outline)
DF:SetFontSize (_G.SystemFont_NamePlate, profile.blizzard_nameplate_font_size)
DF:SetFontFace (_G.SystemFont_NamePlateFixed, profile.blizzard_nameplate_font)
DF:SetFontOutline (_G.SystemFont_NamePlateFixed, profile.blizzard_nameplate_font_outline)
DF:SetFontSize (_G.SystemFont_NamePlateFixed, profile.blizzard_nameplate_font_size)
DF:SetFontFace (_G.SystemFont_LargeNamePlate, profile.blizzard_nameplate_large_font)
DF:SetFontOutline (_G.SystemFont_LargeNamePlate, profile.blizzard_nameplate_large_font_outline)
DF:SetFontSize (_G.SystemFont_LargeNamePlate, profile.blizzard_nameplate_large_font_size)
DF:SetFontFace (_G.SystemFont_LargeNamePlateFixed, profile.blizzard_nameplate_large_font)
DF:SetFontOutline (_G.SystemFont_LargeNamePlateFixed, profile.blizzard_nameplate_large_font_outline)
DF:SetFontSize (_G.SystemFont_LargeNamePlateFixed, profile.blizzard_nameplate_large_font_size)
end
end
-- ~updateplate ~update ~updatenameplate
function Plater.UpdatePlateFrame (plateFrame, actorType, forceUpdate, justAdded, regenDisabled)
Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdatePlateFrame")
actorType = actorType or plateFrame.actorType
if (not actorType or not plateFrame.unitFrame.PlaterOnScreen) then
return
end
local unitFrame = plateFrame.unitFrame
local healthBar = unitFrame.healthBar
local castBar = unitFrame.castBar
local buffFrame = unitFrame.BuffFrame
local buffFrame2 = unitFrame.BuffFrame2
local nameFrame = unitFrame.healthBar.unitName
castBar.Settings.FillOnInterrupt = Plater.db.profile.cast_statusbar_spark_filloninterrupt
castBar.Settings.HideSparkOnInterrupt = Plater.db.profile.cast_statusbar_spark_hideoninterrupt
plateFrame.actorType = actorType
unitFrame.actorType = actorType
unitFrame.ActorType = actorType --exposed to scripts
local shouldForceRefresh = justAdded or forceUpdate
if (plateFrame.IsNpcWithoutHealthBar or plateFrame.IsFriendlyPlayerWithoutHealthBar) then
shouldForceRefresh = true
end
healthBar.BorderIsAggroIndicator = nil
local wasQuestPlate = plateFrame [MEMBER_QUEST]
plateFrame [MEMBER_QUEST] = false
unitFrame [MEMBER_QUEST] = false
plateFrame.QuestInfo = {}
unitFrame.QuestInfo = {}
plateFrame.ActorNameSpecial:Hide()
plateFrame.ActorTitleSpecial:Hide()
plateFrame.Top3DFrame:Hide()
plateFrame.RaidTarget:Hide()
--clear aggro glow
unitFrame.aggroGlowUpper:Hide()
unitFrame.aggroGlowLower:Hide()
--check for quest color
if (IS_IN_OPEN_WORLD and actorType == ACTORTYPE_ENEMY_NPC and DB_PLATE_CONFIG [actorType].quest_enabled) then --actorType == ACTORTYPE_FRIENDLY_NPC or
local isQuestMob = Plater.IsQuestObjective (plateFrame)
if (isQuestMob and DB_PLATE_CONFIG [actorType].quest_color_enabled and not Plater.IsUnitTapDenied (plateFrame.unitFrame.unit)) then
if (plateFrame [MEMBER_REACTION] == Plater.UnitReaction.UNITREACTION_NEUTRAL) then
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].quest_color_neutral))
else
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].quest_color_enemy))
end
else
if (wasQuestPlate) then
Plater.FindAndSetNameplateColor (unitFrame)
end
end
else
if (wasQuestPlate) then
Plater.FindAndSetNameplateColor (unitFrame)
end
end
--if the nameplate is for a friendly npc
if (actorType == ACTORTYPE_FRIENDLY_NPC) then
plateFrame.IsFriendlyPlayerWithoutHealthBar = false
local subTitleExists = false
local subTitle = Plater.GetActorSubName (plateFrame)
if (subTitle and subTitle ~= "" and not Plater.IsNpcInIgnoreList (plateFrame, true)) then
subTitleExists = true
end
Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID])
-- handle own pets separately, including nazjatar guardians
if (Plater.PlayerPetCache [unitFrame [MEMBER_GUID]] and not plateFrame.isBattlePet) then
if (DB_PLATE_CONFIG [actorType].only_names) then
healthBar:Hide()
buffFrame:Hide()
buffFrame2:Hide()
nameFrame:Hide()
plateFrame.IsNpcWithoutHealthBar = true
else
healthBar:Show()
buffFrame:Show()
buffFrame2:Show()
nameFrame:Show()
plateFrame.IsNpcWithoutHealthBar = false
end
elseif (DB_PLATE_CONFIG [actorType].follow_blizzard_npc_option and not (unitFrame.isSoftInteract or plateFrame [MEMBER_TARGET]) and not UnitShouldDisplayName(plateFrame [MEMBER_UNITID])) then
-- hide if following blizzard naming
healthBar:Hide()
buffFrame:Hide()
buffFrame2:Hide()
nameFrame:Hide()
plateFrame.IsNpcWithoutHealthBar = false
elseif (IS_IN_OPEN_WORLD and DB_PLATE_CONFIG [actorType].quest_enabled and Plater.IsQuestObjective (plateFrame)) then
if (DB_PLATE_CONFIG [actorType].quest_color_enabled) then
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].quest_color))
end
healthBar:Show()
buffFrame:Show()
buffFrame2:Show()
nameFrame:Show()
plateFrame.IsNpcWithoutHealthBar = false
--these twoseettings make the healthing dummy show the healthbar
-- Plater.db.profile.plate_config.friendlynpc.only_names = false
-- Plater.db.profile.plate_config.friendlynpc.all_names = false
elseif (DB_PLATE_CONFIG [actorType].only_names) then
--show only the npc name without the health bar
healthBar:Hide()
buffFrame:Hide()
buffFrame2:Hide()
nameFrame:Hide()
plateFrame.IsNpcWithoutHealthBar = true
elseif (not subTitleExists and not DB_PLATE_CONFIG [actorType].all_names) then
-- show only if a title is present
healthBar:Hide()
buffFrame:Hide()
buffFrame2:Hide()
nameFrame:Hide()
plateFrame.IsNpcWithoutHealthBar = true
else
healthBar:Show()
buffFrame:Show()
buffFrame2:Show()
nameFrame:Show()
plateFrame.IsNpcWithoutHealthBar = false
end
elseif (actorType == ACTORTYPE_FRIENDLY_PLAYER) then
plateFrame.IsNpcWithoutHealthBar = false
Plater.ParseHealthSettingForPlayer (plateFrame, justAdded)
--change the player health bar color to either class color or users choice
if (not Plater.db.profile.use_playerclass_color) then
Plater.ChangeHealthBarColor_Internal (healthBar, unpack(DB_PLATE_CONFIG [actorType].fixed_class_color))
else
local _, class = UnitClass (unitFrame [MEMBER_UNITID])
if (class) then
local color = DB_CLASS_COLORS [class]
Plater.ChangeHealthBarColor_Internal (healthBar, color.r, color.g, color.b, color.a)
else
Plater.ChangeHealthBarColor_Internal (healthBar, 1, 1, 1, 1)
end
end
elseif (actorType == ACTORTYPE_ENEMY_PLAYER) then
plateFrame.IsNpcWithoutHealthBar = false
if (plateFrame.PlayerCannotAttack and not DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE) then
healthBar:Hide()
buffFrame:Hide()
buffFrame2:Hide()
nameFrame:Hide()
plateFrame.IsFriendlyPlayerWithoutHealthBar = true
else
healthBar:Show()
buffFrame:Show()
buffFrame2:Show()
nameFrame:Show()
plateFrame.IsFriendlyPlayerWithoutHealthBar = false
if (DB_PLATE_CONFIG [actorType].use_playerclass_color) then
local _, class = UnitClass (unitFrame [MEMBER_UNITID])
if (class) then
local color = DB_CLASS_COLORS [class]
Plater.ChangeHealthBarColor_Internal (healthBar, color.r, color.g, color.b, color.a)
else
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].fixed_class_color))
end
else
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].fixed_class_color))
end
end
else --> ENEMY NPC pass throught here
plateFrame.IsFriendlyPlayerWithoutHealthBar = false
--check if this is an enemy npc but the player cannot attack it
if (plateFrame.PlayerCannotAttack and not DB_SHOW_HEALTHBARS_FOR_NOT_ATTACKABLE and not unitFrame.IsSelf) or unitFrame.isSoftInteractObject then
healthBar:Hide()
buffFrame:Hide()
buffFrame2:Hide()
nameFrame:Hide()
plateFrame.IsNpcWithoutHealthBar = true
else
healthBar:Show()
buffFrame:Show()
buffFrame2:Show()
plateFrame.IsNpcWithoutHealthBar = false
if unitFrame.IsSelf then
--refresh color
if (plateFrame.PlateConfig.healthbar_color_by_hp) then
local currentHealth = healthBar.currentHealth
local currentHealthMax = healthBar.currentHealthMax
local originalColor = plateFrame.PlateConfig.healthbar_color
local r, g, b = DF:LerpLinearColor (abs (currentHealth / currentHealthMax - 1), 1, originalColor[1], originalColor[2], originalColor[3], 1, .4, 0)
Plater.ChangeHealthBarColor_Internal (healthBar, r, g, b, (originalColor[4] or 1), true)
else
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].healthbar_color))
end
else
nameFrame:Show()
-- could be a pet
Plater.ForceFindPetOwner (plateFrame [MEMBER_GUID])
end
end
end
castBar:SetStatusBarTexture (DB_TEXTURE_CASTBAR)
--update all texts in the nameplate
Plater.UpdatePlateText (plateFrame, DB_PLATE_CONFIG [actorType], shouldForceRefresh or plateFrame.PreviousUnitType ~= actorType or unitFrame.RefreshID < PLATER_REFRESH_ID)
if (unitFrame.RefreshID < PLATER_REFRESH_ID or shouldForceRefresh) then
unitFrame.RefreshID = PLATER_REFRESH_ID
local profile = Plater.db.profile
--update highlight texture
unitFrame.HighlightFrame.HighlightTexture:SetTexture (DB_TEXTURE_HEALTHBAR)
unitFrame.HighlightFrame.HighlightTexture:SetBlendMode ("ADD")
unitFrame.HighlightFrame.HighlightTexture:SetAlpha (profile.hover_highlight_alpha)
--click area is shown?
if (profile.click_space_always_show) then
Plater.SetPlateBackground (plateFrame)
else
plateFrame.unitFrame:SetBackdrop (nil)
end
--setup cast bar
castBar.background:SetTexture (DB_TEXTURE_CASTBAR_BG)
castBar.extraBackground:SetTexture (DB_TEXTURE_CASTBAR_BG)
castBar.background:SetVertexColor (unpack (profile.cast_statusbar_bgcolor))
castBar.extraBackground:SetVertexColor (unpack (profile.cast_statusbar_bgcolor))
castBar.flashTexture:SetTexture (DB_TEXTURE_CASTBAR)
castBar.Icon:SetTexCoord (0.078125, 0.921875, 0.078125, 0.921875)
local colors = castBar.Settings.Colors
colors.Casting:SetColor (profile.cast_statusbar_color)
colors.Channeling:SetColor (profile.cast_statusbar_color_channeling)
colors.NonInterruptible:SetColor (profile.cast_statusbar_color_nointerrupt)
colors.Interrupted:SetColor (profile.cast_statusbar_color_interrupted)
colors.Finished:SetColor (profile.cast_statusbar_color_finished)
castBar.Settings.FadeInTime = profile.cast_statusbar_fadein_time
castBar.Settings.FadeOutTime = profile.cast_statusbar_fadeout_time
castBar.fadeOutAnimation.alpha1:SetDuration (castBar.Settings.FadeOutTime)
castBar.fadeInAnimation.alpha1:SetDuration (castBar.Settings.FadeInTime)
castBar.Settings.NoFadeEffects = not profile.cast_statusbar_use_fade_effects
castBar.Settings.SparkTexture = profile.cast_statusbar_spark_texture
castBar.Spark:SetTexture (castBar.Settings.SparkTexture)
castBar.Spark:SetVertexColor (unpack (profile.cast_statusbar_spark_color))
castBar.Spark:SetAlpha (profile.cast_statusbar_spark_alpha)
if (profile.cast_statusbar_spark_half) then
castBar.Spark:SetTexCoord (0, 0.5, 0, 1)
else
castBar.Spark:SetTexCoord (0, 1, 0, 1)
end
castBar.castColorTexture:Hide()
if (profile.cast_color_settings.enabled) then
castBar.castColorTexture:SetWidth(profile.cast_color_settings.width)
castBar.castColorTexture:SetAlpha(profile.cast_color_settings.alpha)
castBar.castColorTexture:SetDrawLayer(profile.cast_color_settings.layer, -6)
Plater.SetAnchor(castBar.castColorTexture, profile.cast_color_settings.anchor)
end
castBar.Settings.SparkOffset = profile.cast_statusbar_spark_offset
--setup power bar
unitFrame.powerBar:SetTexture (DB_TEXTURE_HEALTHBAR)
--setup health bar~
healthBar:SetTexture (DB_TEXTURE_HEALTHBAR)
healthBar.background:SetTexture (DB_TEXTURE_HEALTHBAR_BG)
healthBar.background:SetVertexColor (unpack (profile.health_statusbar_bgcolor))
--update border
Plater.UpdatePlateBorders (plateFrame)
--target overlay texture
local targetedOverlayTexture = LibSharedMedia:Fetch ("statusbar", profile.health_selection_overlay)
unitFrame.targetOverlayTexture:SetTexture (targetedOverlayTexture)
unitFrame.targetOverlayTexture:SetVertexColor (unpack (Plater.db.profile.health_selection_overlay_color))
unitFrame.targetOverlayTexture:SetAlpha (profile.health_selection_overlay_alpha)
--heal prediction
unitFrame.healthBar.Settings.ShowHealingPrediction = Plater.db.profile.show_health_prediction
unitFrame.healthBar.Settings.ShowShields = Plater.db.profile.show_shield_prediction
if (unitFrame.healthBar.unit) then
unitFrame.healthBar:UNIT_HEALTH()
end
if IS_WOW_PROJECT_MAINLINE and unitFrame.WidgetContainer then
Plater.SetAnchor (unitFrame.WidgetContainer, profile.widget_bar_anchor, unitFrame)
plateFrame.unitFrame.WidgetContainer:SetScale(Plater.db.profile.widget_bar_scale)
end
end
--update the plate size for this unit
Plater.UpdatePlateSize (plateFrame)
--raid marker
Plater.UpdatePlateRaidMarker (plateFrame)
--indicators for the unit
Plater.UpdateIndicators (plateFrame, actorType, regenDisabled)
--update the visibility of the health text
Plater.UpdateLifePercentVisibility (plateFrame)
--update the health text
Plater.CheckLifePercentText (unitFrame)
--target indicator
Plater.UpdateTarget (plateFrame)
--personal player bar
if (plateFrame.IsSelf) then
Plater.UpdatePersonalBar (NamePlateDriverFrame)
if (not DB_PLATE_CONFIG [actorType].healthbar_color_by_hp) then
Plater.ChangeHealthBarColor_Internal (healthBar, unpack (DB_PLATE_CONFIG [actorType].healthbar_color))
end
end
Plater.UpdateCustomDesign (unitFrame)
Plater.UpdateNameOnRenamedUnit(plateFrame)
--update options in the extra icons row frame
if (unitFrame.ExtraIconFrame.RefreshID < PLATER_REFRESH_ID) then
Plater.SetAnchor (unitFrame.ExtraIconFrame, Plater.db.profile.extra_icon_anchor)
unitFrame.ExtraIconFrame:SetOption ("anchor", Plater.db.profile.extra_icon_anchor)
unitFrame.ExtraIconFrame:SetOption ("show_text", Plater.db.profile.extra_icon_show_timer)
unitFrame.ExtraIconFrame:SetOption ("text_font", Plater.db.profile.extra_icon_timer_font)
unitFrame.ExtraIconFrame:SetOption ("text_size", Plater.db.profile.extra_icon_timer_size)
unitFrame.ExtraIconFrame:SetOption ("text_outline", Plater.db.profile.extra_icon_timer_outline)
unitFrame.ExtraIconFrame:SetOption ("grow_direction", unitFrame.ExtraIconFrame:GetIconGrowDirection())
unitFrame.ExtraIconFrame:SetOption ("icon_width", Plater.db.profile.extra_icon_width)
unitFrame.ExtraIconFrame:SetOption ("icon_height", Plater.db.profile.extra_icon_height)
unitFrame.ExtraIconFrame:SetOption ("texcoord", Plater.db.profile.extra_icon_wide_icon and Plater.WideIconCoords or Plater.BorderLessIconCoords)
unitFrame.ExtraIconFrame:SetOption ("desc_text", Plater.db.profile.extra_icon_caster_name)
unitFrame.ExtraIconFrame:SetOption ("desc_text_font", Plater.db.profile.extra_icon_caster_font)
unitFrame.ExtraIconFrame:SetOption ("desc_text_size", Plater.db.profile.extra_icon_caster_size)
unitFrame.ExtraIconFrame:SetOption ("desc_text_outline", Plater.db.profile.extra_icon_caster_outline)
unitFrame.ExtraIconFrame:SetOption ("stack_text", Plater.db.profile.extra_icon_show_stacks)
unitFrame.ExtraIconFrame:SetOption ("stack_text_font", Plater.db.profile.extra_icon_stack_font)
unitFrame.ExtraIconFrame:SetOption ("stack_text_size", Plater.db.profile.extra_icon_stack_size)
unitFrame.ExtraIconFrame:SetOption ("stack_text_outline", Plater.db.profile.extra_icon_stack_outline)
unitFrame.ExtraIconFrame:SetOption ("surpress_tulla_omni_cc", Plater.db.profile.disable_omnicc_on_auras)
unitFrame.ExtraIconFrame:SetOption ("surpress_blizzard_cd_timer", true)
unitFrame.ExtraIconFrame:SetOption ("decimal_timer", Plater.db.profile.extra_icon_timer_decimals)
unitFrame.ExtraIconFrame:SetOption ("cooldown_reverse", Plater.db.profile.extra_icon_cooldown_reverse)
unitFrame.ExtraIconFrame:SetOption ("cooldown_swipe_enabled", Plater.db.profile.extra_icon_show_swipe)
unitFrame.ExtraIconFrame:SetOption ("cooldown_edge_texture", Plater.db.profile.extra_icon_cooldown_edge_texture)
--> update refresh ID
unitFrame.ExtraIconFrame.RefreshID = PLATER_REFRESH_ID
end
--update options in the boss mods icons frame
Plater.UpdateBossModAuraFrameSettings(unitFrame, PLATER_REFRESH_ID)
--> details! integration
if (IS_USING_DETAILS_INTEGRATION) then
local detailsPlaterConfig = Details.plater
if (detailsPlaterConfig.realtime_dps_enabled) then
local textString = healthBar.DetailsRealTime
Plater.SetAnchor (textString, detailsPlaterConfig.realtime_dps_anchor)
DF:SetFontSize (textString, detailsPlaterConfig.realtime_dps_size)
DF:SetFontOutline (textString, detailsPlaterConfig.realtime_dps_shadow)
DF:SetFontColor (textString, detailsPlaterConfig.realtime_dps_color)
end
if (detailsPlaterConfig.realtime_dps_player_enabled) then
local textString = healthBar.DetailsRealTimeFromPlayer
Plater.SetAnchor (textString, detailsPlaterConfig.realtime_dps_player_anchor)
DF:SetFontSize (textString, detailsPlaterConfig.realtime_dps_player_size)
DF:SetFontOutline (textString, detailsPlaterConfig.realtime_dps_player_shadow)
DF:SetFontColor (textString, detailsPlaterConfig.realtime_dps_player_color)
end
if (detailsPlaterConfig.damage_taken_enabled) then
local textString = healthBar.DetailsDamageTaken
Plater.SetAnchor (textString, detailsPlaterConfig.damage_taken_anchor)
DF:SetFontSize (textString, detailsPlaterConfig.damage_taken_size)
DF:SetFontOutline (textString, detailsPlaterConfig.damage_taken_shadow)
DF:SetFontColor (textString, detailsPlaterConfig.damage_taken_color)
end
--reset all labels used by details!
healthBar.DetailsRealTime:SetText ("")
healthBar.DetailsRealTimeFromPlayer:SetText ("")
healthBar.DetailsDamageTaken:SetText ("")
end
if (plateFrame.OnTickFrame.actorType == actorType and plateFrame.OnTickFrame.unit == unitFrame [MEMBER_UNITID]) then
Plater.NameplateTick (plateFrame.OnTickFrame, 10)
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdatePlateFrame")
end
-- ~border
--changes the border color, this call is used internally on Plater
--see Plater.SetBorderColor for scripting calls
--currently this is called for threat color changes (if enabled at the options panel)
function Plater.ForceChangeBorderColor (self, r, g, b) --private --self = unitFrame
--this call is from the retail game, file: blizzard_nameplates.lua
if (not self.customBorderColor) then
self.healthBar.border:SetVertexColor (r, g, b)
self.BorderIsAggroIndicator = true
end
end
--> update the border color respecting custom colors set by scripts
function Plater.UpdateBorderColor (self) --self is unitFrame
--set the border color
if (not self.customBorderColor) then
self.healthBar.border:SetVertexColor (DB_BORDER_COLOR_R, DB_BORDER_COLOR_G, DB_BORDER_COLOR_B, DB_BORDER_COLOR_A)
self.powerBar.border:SetVertexColor (DB_BORDER_COLOR_R, DB_BORDER_COLOR_G, DB_BORDER_COLOR_B, DB_BORDER_COLOR_A)
else
self.healthBar.border:SetVertexColor (unpack (self.customBorderColor))
self.powerBar.border:SetVertexColor (unpack (self.customBorderColor))
end
end
function Plater.UpdatePlateBorders (plateFrame) --private
--if didn't pass a plate to update, update all frames
if (not plateFrame) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
Plater.UpdatePlateBorders (plateFrame)
end
return
end
Plater.UpdatePlateBorderThickness (plateFrame)
--ignore if the border is being used internally as threat indicator
if (plateFrame.unitFrame.healthBar.BorderIsAggroIndicator) then
return
end
Plater.UpdateBorderColor (plateFrame.unitFrame)
end
function Plater.UpdatePlateBorderThickness (plateFrame)
if (not plateFrame) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
Plater.UpdatePlateBorderThickness (plateFrame)
end
return
end
plateFrame.unitFrame.healthBar.border:SetBorderSizes (DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS)
plateFrame.unitFrame.healthBar.border:UpdateSizes()
plateFrame.unitFrame.powerBar.border:SetBorderSizes (DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS, DB_BORDER_THICKNESS)
plateFrame.unitFrame.powerBar.border:UpdateSizes()
end
-- ~raidmarker ~raidtarget
function Plater.UpdatePlateRaidMarker (plateFrame)
local index = GetRaidTargetIndex (plateFrame.unitFrame [MEMBER_UNITID])
if (index and not plateFrame.IsSelf) then
local icon = plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon
SetRaidTargetIconTexture (icon, index)
icon:Show()
if (not icon.IsShowning) then
--play animations
icon.IsShowning = true
icon.ShowAnimation:Play()
end
--adjust scale and anchor
plateFrame.unitFrame.PlaterRaidTargetFrame:SetScale (Plater.db.profile.indicator_raidmark_scale)
Plater.SetAnchor (plateFrame.unitFrame.PlaterRaidTargetFrame, Plater.db.profile.indicator_raidmark_anchor)
--adjust frame level:
plateFrame.unitFrame.PlaterRaidTargetFrame:SetFrameStrata(plateFrame.unitFrame.healthBar:GetFrameStrata())
plateFrame.unitFrame.PlaterRaidTargetFrame:SetFrameLevel(plateFrame.unitFrame.healthBar:GetFrameLevel() + 25)
--mini mark inside the nameplate
if (Plater.db.profile.indicator_extra_raidmark) then
plateFrame.RaidTarget:Show()
plateFrame.RaidTarget:SetTexture (icon:GetTexture())
plateFrame.RaidTarget:SetTexCoord (icon:GetTexCoord())
local height = plateFrame.unitFrame.healthBar:GetHeight() - 2
plateFrame.RaidTarget:SetSize (height, height)
plateFrame.RaidTarget:SetAlpha (.4)
end
else
plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon.IsShowning = nil
plateFrame.unitFrame.PlaterRaidTargetFrame.RaidTargetIcon:Hide()
--hide the extra raid target inside the nameplate
plateFrame.RaidTarget:Hide()
end
end
--iterate among all nameplates and update the raid target icon
function Plater.UpdateRaidMarkersOnAllNameplates() --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if plateFrame.unitFrame.PlaterOnScreen then
Plater.UpdatePlateRaidMarker (plateFrame)
--hooks
if (HOOK_RAID_TARGET.ScriptAmount > 0) then
for i = 1, HOOK_RAID_TARGET.ScriptAmount do
local globalScriptObject = HOOK_RAID_TARGET [i]
local unitFrame = plateFrame.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Raid Target")
--run
unitFrame:ScriptRunHook (scriptInfo, "Raid Target")
end
end
end
end
end
-- ~indicators
function Plater.UpdateIndicators (plateFrame, actorType, regenDisabled)
--limpa os indicadores
Plater.ClearIndicators (plateFrame)
local config = Plater.db.profile
if (actorType == ACTORTYPE_ENEMY_PLAYER) then
if (config.indicator_faction) then
--don't show faction icon on arena of battleground, it's kinda useless (terciob july 2022)
if (Plater.ZoneInstanceType ~= "pvp" and Plater.ZoneInstanceType ~= "arena") then
Plater.AddIndicator (plateFrame, UnitFactionGroup (plateFrame.unitFrame [MEMBER_UNITID]))
end
end
if (config.indicator_enemyclass) then
Plater.AddIndicator (plateFrame, "classicon")
end
--don't show spec icon during combat, it occupies a valuable space (terciob july 2022)
if (config.indicator_spec and (config.indicator_spec_always or (not InCombatLockdown() and not regenDisabled))) then
-- use BG info if available
local texture, L, R, T, B = Plater.GetSpecIconForUnitFromBG(plateFrame.unitFrame [MEMBER_UNITID])
if texture then
Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
else
--> check if the user is using details
if (Details and Details.realversion >= 134) then
local spec = Details:GetSpecByGUID (plateFrame [MEMBER_GUID])
if (spec) then
local texture, L, R, T, B = Details:GetSpecIcon (spec)
Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
end
end
end
end
elseif (actorType == ACTORTYPE_FRIENDLY_PLAYER) then
if (config.indicator_friendlyfaction) then
Plater.AddIndicator (plateFrame, UnitFactionGroup (plateFrame.unitFrame [MEMBER_UNITID]))
end
if (config.indicator_friendlyclass) then
Plater.AddIndicator (plateFrame, "classicon")
end
if (config.indicator_friendlyspec) then
-- use BG info if available
local texture, L, R, T, B = Plater.GetSpecIconForUnitFromBG(plateFrame [MEMBER_UNITID])
if texture then
Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
else
--> check if the user is using details
if (Details and Details.realversion >= 134) then
local spec = Details:GetSpecByGUID (plateFrame [MEMBER_GUID])
if (spec) then
local texture, L, R, T, B = Details:GetSpecIcon (spec)
Plater.AddIndicator (plateFrame, "specicon", texture, L, R, T, B)
end
end
end
end
elseif (actorType == ACTORTYPE_ENEMY_NPC) then
--is a pet
if (PET_CACHE [plateFrame [MEMBER_GUID]]) then
if (config.indicator_pet) then
Plater.AddIndicator (plateFrame, "pet")
end
end
--classification
local unitClassification = UnitClassification (plateFrame.unitFrame [MEMBER_UNITID]) --elite minus normal rare rareelite worldboss
if (unitClassification == "worldboss" and config.indicator_worldboss) then
Plater.AddIndicator (plateFrame, "worldboss")
elseif (unitClassification == "rareelite" and (config.indicator_rare or config.indicator_elite)) then
Plater.AddIndicator (plateFrame, "elite")
Plater.AddIndicator (plateFrame, "rare")
else
if (unitClassification == "elite" and config.indicator_elite) then
Plater.AddIndicator (plateFrame, "elite")
end
if (unitClassification == "rare" and config.indicator_rare) then
Plater.AddIndicator (plateFrame, "rare")
end
end
--quest boss
local isQuestBoss = (IS_WOW_PROJECT_MAINLINE) and UnitIsQuestBoss (plateFrame.unitFrame [MEMBER_UNITID]) or false --true false
if (isQuestBoss and config.indicator_quest) then
Plater.AddIndicator (plateFrame, "quest")
end
elseif (actorType == ACTORTYPE_FRIENDLY_NPC and config.indicator_quest) then
if (plateFrame [MEMBER_QUEST]) then
Plater.AddIndicator (plateFrame, "quest")
end
end
--custom indicators from scripts
for i = 1, #plateFrame.unitFrame.CustomIndicators do
Plater.AddIndicator (plateFrame, "custom", unpack (plateFrame.unitFrame.CustomIndicators [i]))
end
end
function Plater.AddIndicator (plateFrame, indicator, ...)
local thisIndicator = plateFrame.IconIndicators [plateFrame.IconIndicators.Next]
if (not thisIndicator) then
local newIndicator = plateFrame.unitFrame.healthBar:CreateTexture (nil, "overlay")
newIndicator:SetDrawLayer("OVERLAY", 7)
newIndicator:SetSize (10, 10)
tinsert (plateFrame.IconIndicators, newIndicator)
thisIndicator = newIndicator
end
thisIndicator:Show()
thisIndicator:SetTexCoord (0, 1, 0, 1)
thisIndicator:SetVertexColor (1, 1, 1)
thisIndicator:SetDesaturated (false)
thisIndicator:SetSize (10, 10)
thisIndicator:SetScale (Plater.db.profile.indicator_scale)
-- ~icons
if (indicator == "pet") then
thisIndicator:SetTexture ([[Interface\AddOns\Plater\images\peticon]])
elseif (indicator == "Horde") then
thisIndicator:SetTexture ([[Interface\PVPFrame\PVP-Currency-Horde]])
thisIndicator:SetSize (12, 12)
elseif (indicator == "Alliance") then
thisIndicator:SetTexture ([[Interface\PVPFrame\PVP-Currency-Alliance]])
thisIndicator:SetTexCoord (4/32, 29/32, 2/32, 30/32)
thisIndicator:SetSize (12, 12)
elseif (indicator == "elite") then
thisIndicator:SetTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
thisIndicator:SetTexCoord (0.75, 1, 0, 1)
thisIndicator:SetVertexColor (1, .8, 0)
thisIndicator:SetSize (12, 12)
elseif (indicator == "rare") then
thisIndicator:SetTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
thisIndicator:SetTexCoord (0.75, 1, 0, 1)
thisIndicator:SetSize (12, 12)
thisIndicator:SetDesaturated (true)
elseif (indicator == "quest") then
thisIndicator:SetTexture ([[Interface\TARGETINGFRAME\PortraitQuestBadge]])
thisIndicator:SetTexCoord (2/32, 26/32, 1/32, 31/32)
elseif (indicator == "classicon") then
local _, class = UnitClass (plateFrame.unitFrame [MEMBER_UNITID])
if (class) then
thisIndicator:SetTexture ([[Interface\GLUES\CHARACTERCREATE\UI-CharacterCreate-Classes]])
thisIndicator:SetTexCoord (unpack (CLASS_ICON_TCOORDS [class]))
end
elseif (indicator == "specicon") then
local texture, L, R, T, B = ...
thisIndicator:SetTexture (texture)
thisIndicator:SetTexCoord (L, R, T, B)
elseif (indicator == "worldboss") then
thisIndicator:SetTexture ([[Interface\Scenarios\ScenarioIcon-Boss]])
elseif (indicator == "custom") then
local texture, width, height, color, L, R, T, B = ...
thisIndicator:SetTexture (texture)
thisIndicator:SetSize (width, height)
thisIndicator:SetTexCoord (L, R, T, B)
local r, g, b = DF:ParseColors (color)
thisIndicator:SetVertexColor (r, g, b)
end
if (plateFrame.IconIndicators.Next == 1) then
Plater.SetAnchor (thisIndicator, Plater.db.profile.indicator_anchor)
else
local attachTo = plateFrame.IconIndicators [plateFrame.IconIndicators.Next - 1]
--se for menor que 4 ele deve crescer para o lado da esquerda, nos outros casos vai para a direita
if (Plater.db.profile.indicator_anchor.side < 4) then
PixelUtil.SetPoint (thisIndicator, "right", attachTo, "left", -2, 0)
else
PixelUtil.SetPoint (thisIndicator, "left", attachTo, "right", 1, 0)
end
end
plateFrame.IconIndicators.Next = plateFrame.IconIndicators.Next + 1
end
function Plater.ClearIndicators (plateFrame)
for _, indicator in ipairs (plateFrame.IconIndicators) do
indicator:Hide()
indicator:ClearAllPoints()
end
plateFrame.IconIndicators.Next = 1
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> misc stuff - general functions ~misc
--auto toggle the show friendly players, and other stuff.
function Plater.RefreshAutoToggle(combat, leavingCombat) --private
if Plater.HasRefreshAutoToggleScheduled and combat == nil then
return
elseif leavingCombat then
if Plater.HasRefreshAutoToggleScheduled then
Plater.HasRefreshAutoToggleScheduled:Cancel()
end
Plater.HasRefreshAutoToggleScheduled = C_Timer.NewTimer (1.5, function()
Plater.HasRefreshAutoToggleScheduled = nil
Plater.RefreshAutoToggle(false)
end) --schedule
return
elseif not leavingCombat and Plater.HasRefreshAutoToggleScheduled then
if combat then
Plater.HasRefreshAutoToggleScheduled:Cancel()
Plater.HasRefreshAutoToggleScheduled = nil
else
return
end
end
if ((combat == nil) and InCombatLockdown()) then
C_Timer.After (0.5, function() Plater.RefreshAutoToggle() end)
return
end
local zoneName, zoneType = GetInstanceInfo()
local profile = Plater.db.profile
-- combat toggle
if (profile.auto_toggle_combat_enabled and (combat ~= nil)) then
local onlyNamesEnabled = GetCVarBool("nameplateShowOnlyNames")
local onlyNamesEnabledRaw = GetCVar("nameplateShowOnlyNames")
--NamePlateDriverFrame:UnregisterEvent("CVAR_UPDATE")
if combat or InCombatLockdown() then -- update this separately and only if needed
if onlyNamesEnabled ~= profile.auto_toggle_combat.blizz_healthbar_ic then
SetCVar("nameplateShowOnlyNames", profile.auto_toggle_combat.blizz_healthbar_ic and CVAR_ENABLED or CVAR_DISABLED)
Plater.UpdateBaseNameplateOptions()
end
else
if onlyNamesEnabled ~= profile.auto_toggle_combat.blizz_healthbar_ooc then
SetCVar("nameplateShowOnlyNames", profile.auto_toggle_combat.blizz_healthbar_ooc and CVAR_ENABLED or CVAR_DISABLED)
--Plater.UpdateBaseNameplateOptions()
end
end
--NamePlateDriverFrame:RegisterEvent("CVAR_UPDATE")
if combat then
SetCVar("nameplateShowFriends", profile.auto_toggle_combat.friendly_ic and CVAR_ENABLED or CVAR_DISABLED)
SetCVar("nameplateShowEnemies", profile.auto_toggle_combat.enemy_ic and CVAR_ENABLED or CVAR_DISABLED)
return
else
SetCVar("nameplateShowFriends", profile.auto_toggle_combat.friendly_ooc and CVAR_ENABLED or CVAR_DISABLED)
SetCVar("nameplateShowEnemies", profile.auto_toggle_combat.enemy_ooc and CVAR_ENABLED or CVAR_DISABLED)
end
end
-- dungeon/raid toggle pets/totems
if not combat then -- just do this out of combat to counter some weird errors
if (profile.auto_inside_raid_dungeon.hide_enemy_player_pets) then
local showEnemyPets = GetCVarBool("nameplateShowEnemyPets")
if (zoneType == "party" or zoneType == "raid") then
if showEnemyPets ~= CVAR_DISABLED then
SetCVar("nameplateShowEnemyPets", CVAR_DISABLED)
end
else
if showEnemyPets ~= CVAR_ENABLED then
SetCVar("nameplateShowEnemyPets", CVAR_ENABLED)
end
end
end
if (profile.auto_inside_raid_dungeon.hide_enemy_player_totems) then
local showEnemyTotems = GetCVarBool("nameplateShowEnemyTotems")
if (zoneType == "party" or zoneType == "raid") then
if showEnemyTotems ~= CVAR_DISABLED then
SetCVar("nameplateShowEnemyTotems", CVAR_DISABLED)
end
else
if showEnemyTotems ~= CVAR_ENABLED then
SetCVar("nameplateShowEnemyTotems", CVAR_ENABLED)
end
end
end
end
--stacking toggle
if (profile.auto_toggle_stacking_enabled and profile.stacking_nameplates_enabled) then
--discover which is the map type the player is in
if (zoneType == "party") then
SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["party"] and CVAR_ENABLED or CVAR_DISABLED)
elseif (zoneType == "raid") then
SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
elseif (zoneType == "arena" or zoneType == "pvp") then
SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
else
--if the player is resting, consider inside a major city
if (IsResting()) then
SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
else
SetCVar ("nameplateMotion", profile.auto_toggle_stacking ["world"] and CVAR_ENABLED or CVAR_DISABLED)
end
end
end
if combat then return end
--friendly nameplate toggle
if (profile.auto_toggle_friendly_enabled) then
--discover which is the map type the player is in
if (zoneType == "party") then
SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["party"] and CVAR_ENABLED or CVAR_DISABLED)
elseif (zoneType == "raid") then
SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
elseif (zoneType == "arena" or zoneType == "pvp") then
SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
else
--if the player is resting, consider inside a major city
if (IsResting()) then
SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
else
SetCVar ("nameplateShowFriends", profile.auto_toggle_friendly ["world"] and CVAR_ENABLED or CVAR_DISABLED)
end
end
end
--enemy nameplate toggle
if (profile.auto_toggle_enemy_enabled) then
--discover which is the map type the player is in
if (zoneType == "party") then
SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["party"] and CVAR_ENABLED or CVAR_DISABLED)
elseif (zoneType == "raid") then
SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["raid"] and CVAR_ENABLED or CVAR_DISABLED)
elseif (zoneType == "arena" or zoneType == "pvp") then
SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["arena"] and CVAR_ENABLED or CVAR_DISABLED)
else
--if the player is resting, consider inside a major city
if (IsResting()) then
SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["cities"] and CVAR_ENABLED or CVAR_DISABLED)
else
SetCVar ("nameplateShowEnemies", profile.auto_toggle_enemy ["world"] and CVAR_ENABLED or CVAR_DISABLED)
end
end
end
end
local anchor_functions = {
function (widget, config, attachTo, centered)--1 topleft
widget:ClearAllPoints()
local widgetRelative = centered and "bottom" or "bottomleft"
PixelUtil.SetPoint (widget, widgetRelative, attachTo, "topleft", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--2 left
widget:ClearAllPoints()
local widgetRelative = centered and "center" or "right"
PixelUtil.SetPoint (widget, widgetRelative, attachTo, "left", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--3 bottomleft
widget:ClearAllPoints()
local widgetRelative = centered and "top" or "topleft"
PixelUtil.SetPoint (widget, widgetRelative, attachTo, "bottomleft", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--4 bottom
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "top", attachTo, "bottom", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--5 bottomright
widget:ClearAllPoints()
local widgetRelative = centered and "top" or "topright"
PixelUtil.SetPoint (widget, widgetRelative, attachTo, "bottomright", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--6 right
widget:ClearAllPoints()
local widgetRelative = centered and "center" or "left"
PixelUtil.SetPoint (widget, widgetRelative, attachTo, "right", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--7 topright
widget:ClearAllPoints()
local widgetRelative = centered and "bottom" or "bottomright"
PixelUtil.SetPoint (widget, widgetRelative, attachTo, "topright", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--8 top
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "bottom", attachTo, "top", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--9 center
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "center", attachTo, "center", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--10 inner left
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "left", attachTo, "left", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--11 inner right
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "right", attachTo, "right", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--12 inner top
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "top", attachTo, "top", config.x, config.y, 0, 0)
end,
function (widget, config, attachTo, centered)--13 inner bottom
widget:ClearAllPoints()
PixelUtil.SetPoint (widget, "bottom", attachTo, "bottom", config.x, config.y, 0, 0)
end
}
--auto set the point based on the table from the config, if attachTo isn't received, it'll use its parent
function Plater.SetAnchor (widget, config, attachTo, centered) --private
attachTo = attachTo or widget:GetParent()
anchor_functions [config.side] (widget, config, attachTo, centered)
end
-- anchor sides as comprehensive table.
Plater.AnchorSides = {
TOP_LEFT = 1,
LEFT = 2,
BOTTOM_LEFT = 3,
BOTTOM = 4,
BOTTOM_RIGHT = 5,
RIGHT = 6,
TOP_RIGHT = 7,
TOP = 8,
CENTER = 9,
INNER_LEFT = 10,
INNER_RIGHT = 11,
INNER_TOP = 12,
INNER_BOTTOM = 13,
}
--check the setting 'only_damaged' and 'only_thename' for player characters. not critical code, can run slow
function Plater.ParseHealthSettingForPlayer (plateFrame, force) --private
local isFriendlyPlayerWithoutHealthBar = plateFrame.IsFriendlyPlayerWithoutHealthBar
if (DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_thename and not DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_damaged) then
if (not isFriendlyPlayerWithoutHealthBar) or force then
Plater.HideHealthBar (plateFrame.unitFrame, true)
end
elseif (DB_PLATE_CONFIG [ACTORTYPE_FRIENDLY_PLAYER].only_damaged) then
local healthBar = plateFrame.unitFrame.healthBar
if ((healthBar.currentHealth or 1) < (healthBar.currentHealthMax or 1)) then
if isFriendlyPlayerWithoutHealthBar or force then
Plater.ShowHealthBar (plateFrame.unitFrame)
end
elseif (not isFriendlyPlayerWithoutHealthBar) or force then
Plater.HideHealthBar (plateFrame.unitFrame, true)
end
elseif isFriendlyPlayerWithoutHealthBar or force then
Plater.ShowHealthBar (plateFrame.unitFrame)
end
end
function Plater.GetPlateAlpha (plateFrame)
return plateFrame and plateFrame.unitFrame and plateFrame.unitFrame:GetAlpha() or -1
end
local widget_set_alpha = function (widget, value)
if (widget.FadeAnimation:IsPlaying()) then
widget.FadeAnimation:Stop()
end
widget.FadeAnimation.Animation:SetFromAlpha (widget:GetAlpha())
widget.FadeAnimation.Animation:SetToAlpha (value)
widget.FadeAnimation:Play()
end
local on_finished_fade_animation = function (fadeAnimation)
local widget = fadeAnimation:GetParent()
widget:SetAlpha(fadeAnimation.Animation:GetToAlpha())
end
Plater.CreateAlphaAnimation = function (plateFrame)
local unitFrame = plateFrame.unitFrame
local healthBar = plateFrame.unitFrame.healthBar
local castBar = plateFrame.unitFrame.castBar
local powerBar = plateFrame.unitFrame.powerBar
local buffFrame = plateFrame.unitFrame.BuffFrame
local buffFrame2 = plateFrame.unitFrame.BuffFrame2
local allWidgets = {
unitFrame, healthBar, castBar, buffFrame, buffFrame2, powerBar
}
for i = 1, #allWidgets do
local widget = allWidgets[i]
widget.FadeAnimation = widget:CreateAnimationGroup()
--widget.FadeAnimation:SetScript ("OnPlay", on_play_fade_animation)
widget.FadeAnimation:SetScript ("OnFinished", on_finished_fade_animation)
widget.FadeAnimation.Animation = widget.FadeAnimation:CreateAnimation ("Alpha")
widget.FadeAnimation.Animation:SetOrder (1)
widget.FadeAnimation.Animation:SetDuration (0.15)
widget.SetAlphaTo = widget_set_alpha
end
end
function Plater.CheckHighlight (self)
if (UnitIsUnit ("mouseover", self.unit)) then
self.HighlightTexture:Show()
else
self.HighlightTexture:Hide()
end
end
--create a new frame for the highlight (when the mouse passes over the nameplate)
function Plater.CreateHighlightNameplate (plateFrame) --private
local highlightOverlay = CreateFrame ("frame", "$parentHighlightOverlay", plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate") --why this was parented to UIParent (question mark)
highlightOverlay:EnableMouse (false)
highlightOverlay:SetAllPoints()
highlightOverlay:SetScript ("OnUpdate", Plater.CheckHighlight)
highlightOverlay:Hide()
--highlightOverlay:SetFrameStrata ("TOOLTIP") --it'll use the same strata as the health bar now
highlightOverlay.HighlightTexture = plateFrame.unitFrame.healthBar:CreateTexture (nil, "overlay")
highlightOverlay.HighlightTexture:SetAllPoints()
highlightOverlay.HighlightTexture:SetColorTexture (1, 1, 1, 1)
highlightOverlay.HighlightTexture:SetAlpha (1)
highlightOverlay.HighlightTexture:Hide()
plateFrame.unitFrame.HighlightFrame = highlightOverlay
end
function Plater.EnableHighlight (unitFrame)
unitFrame.HighlightFrame:Show()
unitFrame.HighlightFrame.HighlightTexture:Show()
unitFrame.HighlightFrame.unit = unitFrame [MEMBER_UNITID]
unitFrame.HighlightFrame:SetScript ("OnUpdate", Plater.CheckHighlight)
end
function Plater.DisableHighlight (unitFrame)
unitFrame.HighlightFrame:SetScript ("OnUpdate", nil)
unitFrame.HighlightFrame:Hide()
unitFrame.HighlightFrame.HighlightTexture:Hide()
end
function Plater.CreateHealthFlashFrame (plateFrame) --private
local f_anim = CreateFrame ("frame", nil, plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
f_anim:SetFrameLevel (plateFrame.unitFrame.healthBar:GetFrameLevel()-1)
f_anim:SetPoint ("topleft", plateFrame.unitFrame.healthBar, "topleft", -2, 2)
f_anim:SetPoint ("bottomright", plateFrame.unitFrame.healthBar, "bottomright", 2, -2)
plateFrame.unitFrame.healthBar.canHealthFlash = true
local t = f_anim:CreateTexture (nil, "artwork")
t:SetColorTexture (1, 1, 1, 1)
t:SetAllPoints()
t:SetBlendMode ("ADD")
f_anim.texture = t
local animation = t:CreateAnimationGroup()
local anim1 = animation:CreateAnimation ("Alpha")
local anim2 = animation:CreateAnimation ("Alpha")
local anim3 = animation:CreateAnimation ("Alpha")
anim1:SetOrder (1)
anim1:SetFromAlpha (0)
anim1:SetToAlpha (1)
anim1:SetDuration (0.1)
anim2:SetOrder (2)
anim2:SetFromAlpha (1)
anim2:SetToAlpha (0)
anim2:SetDuration (0.1)
anim3:SetOrder (3)
anim3:SetFromAlpha (0)
anim3:SetToAlpha (1)
anim3:SetDuration (0.1)
f_anim.animation = animation
animation:SetScript ("OnFinished", function (self)
f_anim:Hide()
end)
animation:SetScript ("OnPlay", function (self)
f_anim:Show()
end)
local do_flash_anim = function (duration)
if (not plateFrame.unitFrame.healthBar.canHealthFlash) then
return
end
plateFrame.unitFrame.healthBar.canHealthFlash = false
duration = duration or 0.1
anim1:SetDuration (duration)
anim2:SetDuration (duration)
anim3:SetDuration (duration)
f_anim:Show()
animation:Play()
end
f_anim:Hide()
plateFrame.unitFrame.healthBar.HealthFlashFrame = f_anim
plateFrame.unitFrame.healthBar.PlayHealthFlash = do_flash_anim
end
function Plater.CreateAggroFlashFrame (plateFrame) --private
local f_anim = CreateFrame ("frame", nil, plateFrame.unitFrame.healthBar, BackdropTemplateMixin and "BackdropTemplate")
--local f_anim = CreateFrame ("frame", nil, plateFrame, BackdropTemplateMixin and "BackdropTemplate")
f_anim:SetFrameLevel (plateFrame.unitFrame.healthBar:GetFrameLevel()+3)
f_anim:SetPoint ("topleft", plateFrame.unitFrame.healthBar, "topleft")
f_anim:SetPoint ("bottomright", plateFrame.unitFrame.healthBar, "bottomright")
local t = f_anim:CreateTexture (nil, "artwork")
--t:SetTexCoord (0, 0.78125, 0, 0.66796875)
--t:SetTexture ([[Interface\AchievementFrame\UI-Achievement-Alert-Glow]])
t:SetColorTexture (1, 1, 1, 1)
t:SetAllPoints()
t:SetBlendMode ("ADD")
f_anim.texture = t
local s = f_anim:CreateFontString (nil, "overlay", "GameFontNormal")
s:SetText ("-AGGRO-")
s:SetTextColor (.70, .70, .70)
s:SetPoint ("center", t, "center")
f_anim.text = s
local animation = t:CreateAnimationGroup()
local anim1 = animation:CreateAnimation ("Alpha")
local anim2 = animation:CreateAnimation ("Alpha")
anim1:SetOrder (1)
anim1:SetFromAlpha (0)
anim1:SetToAlpha (1)
anim1:SetDuration (0.2)
anim2:SetOrder (2)
anim2:SetFromAlpha (1)
anim2:SetToAlpha (0)
anim2:SetDuration (0.2)
f_anim.animation = animation
animation:SetScript ("OnFinished", function (self)
f_anim:Hide()
end)
animation:SetScript ("OnPlay", function (self)
f_anim:Show()
end)
local do_flash_anim = function (text, duration, ignoreCooldown)
if (not ignoreCooldown and Plater.CombatTime+5 > GetTime()) then
return
end
text = text or ""
duration = duration or 0.2
anim1:SetDuration (duration)
anim2:SetDuration (duration)
s:SetText (text)
f_anim:Show()
animation:Play()
end
f_anim:Hide()
plateFrame.unitFrame.BodyFlashFrame = f_anim
plateFrame.PlayBodyFlash = do_flash_anim
end
--> animation with acceleration ~animation ~healthbaranimation
function Plater.AnimateLeftWithAccel (self, deltaTime)
local distance = (self.AnimationStart - self.AnimationEnd) / self.CurrentHealthMax * 100 --scale 1 - 100
local minTravel = min (distance / 10, 3) -- 10 = trigger distance to max speed 3 = speed scale on max travel
local maxTravel = max (minTravel, 0.45) -- 0.45 = min scale speed on low travel speed
local calcAnimationSpeed = (self.CurrentHealthMax * (deltaTime * DB_ANIMATION_TIME_DILATATION)) * maxTravel --re-scale back to unit health, scale with delta time and scale with the travel speed
self.AnimationStart = self.CurrentHealthMax == 0 and 1 or self.AnimationStart - calcAnimationSpeed
self:SetValue (self.AnimationStart)
self.CurrentHealth = self.AnimationStart
if (self.Spark) then
self.Spark:SetPoint ("center", self, "left", self.AnimationStart / self.CurrentHealthMax * self:GetWidth(), 0)
self.Spark:Show()
end
if (self.AnimationStart-1 <= self.AnimationEnd) then
self:SetValue (self.AnimationEnd)
self.CurrentHealth = self.AnimationEnd
self.IsAnimating = false
if (self.Spark) then
self.Spark:Hide()
end
end
end
function Plater.AnimateRightWithAccel (self, deltaTime)
local distance = (self.AnimationEnd - self.AnimationStart) / self.CurrentHealthMax * 100 --scale 1 - 100 basis
local minTravel = min (distance / 10, 3) -- 10 = trigger distance to max speed 3 = speed scale on max travel
local maxTravel = max (minTravel, 0.45) -- 0.45 = min scale speed on low travel speed
local calcAnimationSpeed = (self.CurrentHealthMax * (deltaTime * DB_ANIMATION_TIME_DILATATION)) * maxTravel --re-scale back to unit health, scale with delta time and scale with the travel speed
self.AnimationStart = self.AnimationStart + (calcAnimationSpeed)
self:SetValue (self.AnimationStart)
self.CurrentHealth = self.AnimationStart
if (self.AnimationStart+1 >= self.AnimationEnd) then
self:SetValue (self.AnimationEnd)
self.CurrentHealth = self.AnimationEnd
self.IsAnimating = false
end
end
function Plater.CreateScaleAnimation (plateFrame) --private
--animation table
plateFrame.SpellAnimations = {}
--scale animation
local duration = 0.05
local animationHub = DF:CreateAnimationHub (plateFrame.unitFrame)
animationHub.ScaleUp = DF:CreateAnimation (animationHub, "scale", 1, duration, 1, 1, 1.2, 1.2)
animationHub.ScaleDown = DF:CreateAnimation (animationHub, "scale", 2, duration, 1, 1, 0.8, 0.8)
plateFrame.SpellAnimations ["scale"] = animationHub
end
function Plater.DoNameplateAnimation (plateFrame, frameAnimations, spellName, isCritical) --private
Plater.StartLogPerformanceCore("Plater-Core", "Update", "DoNameplateAnimation")
for animationIndex, animationTable in ipairs (frameAnimations) do
if ((animationTable.animationCooldown [plateFrame] or 0) < GetTime()) then
--animation "scale" is pre constructed when the nameplate frame is created
if (animationTable.animation_type == "scale") then
--get the animation
local animationHub = plateFrame.SpellAnimations ["scale"]
--duration
animationHub.ScaleUp:SetDuration (animationTable.duration)
animationHub.ScaleDown:SetDuration (animationTable.duration)
local scaleUpX, scaleUpY = animationTable.scale_upX, animationTable.scale_upY
local scaleDownX, scaleDownY = animationTable.scale_downX, animationTable.scale_downY;
(animationHub.ScaleUp.SetFromScale or animationHub.ScaleUp.SetScaleFrom) (animationHub.ScaleUp, 1, 1);
(animationHub.ScaleUp.SetToScale or animationHub.ScaleUp.SetScaleTo) (animationHub.ScaleUp, scaleUpX, scaleUpY);
(animationHub.ScaleDown.SetFromScale or animationHub.ScaleDown.SetScaleFrom) (animationHub.ScaleDown, 1, 1);
(animationHub.ScaleDown.SetToScale or animationHub.ScaleDown.SetScaleTo) (animationHub.ScaleDown, scaleDownX, scaleDownY)
--play it
animationHub:Play()
animationTable.animationCooldown [plateFrame] = GetTime() + animationTable.cooldown
elseif (animationTable.animation_type == "frameshake") then
--get the animation
local frameShake = plateFrame.SpellAnimations ["frameshake" .. spellName]
local shakeTargetFrame = plateFrame.unitFrame
if (not frameShake) then
--[=[ 8.2 GetPoint isn't more possible in nameplate childs
for i = 1, shakeTargetFrame:GetNumPoints() do --shakeTargetFrame = unitFrame from Plater
local p1, p2, p3, p4, p5 = shakeTargetFrame:GetPoint (i)
points [#points+1] = {p1, p2, p3, p4, p5}
end
--]=]
local points = Plater.GetPoints (plateFrame.unitFrame)
frameShake = DF:CreateFrameShake (shakeTargetFrame, animationTable.duration, animationTable.amplitude, animationTable.frequency, animationTable.absolute_sineX, animationTable.absolute_sineY, animationTable.scaleX, animationTable.scaleY, animationTable.fade_in, animationTable.fade_out, points)
plateFrame.SpellAnimations ["frameshake" .. spellName] = frameShake
end
local animationScale = Plater.db.profile.spell_animations_scale
if (IS_EDITING_SPELL_ANIMATIONS) then
shakeTargetFrame:SetFrameShakeSettings (frameShake, animationTable.duration, animationTable.amplitude, animationTable.frequency, animationTable.absolute_sineX, animationTable.absolute_sineY, animationTable.scaleX, animationTable.scaleY, animationTable.fade_in, animationTable.fade_out)
end
if (isCritical and animationTable.critical_scale) then
animationScale = animationScale * animationTable.critical_scale
shakeTargetFrame:PlayFrameShake (frameShake, animationScale, animationScale, animationScale, DF:Clamp (0.75, 1.75, animationScale)) --, animationScale
else
--scaleDirection, scaleAmplitude, scaleFrequency, scaleDuration
shakeTargetFrame:PlayFrameShake (frameShake, animationScale, animationScale, animationScale, DF:Clamp (0.75, 1.75, animationScale)) --, animationScale
end
animationTable.animationCooldown [plateFrame] = GetTime() + animationTable.cooldown
end
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "DoNameplateAnimation")
end
function Plater.RefreshIsEditingAnimations (state) --private
IS_EDITING_SPELL_ANIMATIONS = state
end
--black list npcs to not show when showing friendly npcs
local ignored_npcs_when_profession = {
[32751] = true, --warp huntress pet - Dalaran
[110571] = 1, --delas mooonfang - Dalaran
[113199] = true, --delas's pet - Dalaran
[110018] = 1, --gazrix gearlock - Dalaran
[107622] = true, --glutonia - Dalaran
[106263] = 1, --earthen ring shaman - Dalaran
[106262] = 1, --earthen ring shaman - Dalaran
[97141] = true, --koraud - Dalaran
}
function Plater.IsNpcInIgnoreList (plateFrame, onlyProfession) --private
if (onlyProfession) then
local npcId = plateFrame [MEMBER_NPCID]
if (not npcId) then
return
end
if (ignored_npcs_when_profession [npcId]) then
return true
end
end
end
function Plater.UpdateMaxCastbarTextLength(newGlobalSize)
Plater.MaxCastBarTextLength = newGlobalSize
end
function Plater.GetNpcIDFromGUID (guid) --private
local npcID = select (6, strsplit ("-", guid))
return tonumber (npcID or "0") or 0
end
function Plater.GetNpcID (plateFrame) --private
local npcId = plateFrame [MEMBER_GUID]
if (npcId and npcId ~= "") then
npcId = select (6, strsplit ("-", npcId))
if (npcId) then
npcId = tonumber (npcId)
else
return
end
else
return
end
if (not npcId) then
return
end
plateFrame [MEMBER_NPCID] = npcId
plateFrame.unitFrame [MEMBER_NPCID] = npcId
return npcId
end
function Plater.GetUnitType (plateFrame)
if (plateFrame ["namePlateClassification"] == "minus") then
return "minus"
elseif (PET_CACHE [plateFrame [MEMBER_GUID]]) then
return "pet"
end
return "normal"
end
--returns isPet, isPlayerPet, PET_CACHE-entry (if existing)
function Plater.IsUnitPet (unitFrame)
if not unitFrame then return false, false, nil end
local entry = PET_CACHE [unitFrame.PlateFrame [MEMBER_GUID]]
if (entry) then
return true, Plater.PlayerPetCache [unitFrame.PlateFrame [MEMBER_GUID]] and true or false, entry
end
return false, false, nil
end
function Plater.CanChangePlateSize() --private
return not InCombatLockdown()
end
function Plater.RefreshOmniCCGroup (fromInit) --private
if (OmniCC) then
local platerThemeName = "Plater Nameplates Theme"
local platerRuleName = "Plater Nameplates Rule"
--cleanup old data...
OmniCC:RemoveRule("PlaterNameplates Blacklist")
OmniCC:RemoveTheme("PlaterNameplates Blacklist")
--attempt to get the plater theme
local platerTheme = OmniCC:GetTheme(platerThemeName)
--check if the plater theme exists, if it doesn't but the call is from Initialization or Profile Refresh, just quit
if (not platerTheme and fromInit) then
return
end
--if doesn't exists and isn't from init (the call came from the options panel by the user clicking in the checkbox)
if (not platerTheme) then
platerTheme = OmniCC:AddTheme(platerThemeName)
platerTheme.enableText = false
end
local platerRules = OmniCC:AddRule(platerRuleName, platerThemeName)
if not platerRules then
-- rule already exists, get it properly...
for _, rule in OmniCC:GetRulesets() do
if rule.id == platerRuleName then
platerRules = rule
break
end
end
end
DF.table.addunique (platerRules.patterns, "PlaterMainAuraIcon")
DF.table.addunique (platerRules.patterns, "PlaterSecondaryAuraIcon")
DF.table.addunique (platerRules.patterns, "ExtraIconRowIcon")
if (Plater.db.profile.disable_omnicc_on_auras) then
platerTheme.enableText = false
platerRules.enabled = true
else
platerTheme.enableText = true
end
OmniCC:OnProfileChanged()
OmniCC.Cooldown:ForAll("Refresh", true)
end
end
-- tanslate the npc cache entries if needed, do so. can translate names only, but not zones.
function Plater.TranslateNPCCache()
if not Plater.db.profile.auto_translate_npc_names then return end
if Plater.TranslateNPCCacheIsRunning then return end
Plater.TranslateNPCCacheIsRunning = true
local maxPerFrame = 10
local translateTimer = 0.1
local function GetCreatureNameFromID(npcID)
if C_TooltipInfo then
local info = C_TooltipInfo.GetHyperlink(("unit:Creature-0-0-0-0-%d"):format(npcID))
local leftText = info and info.lines and info.lines[1] and info.lines[1].leftText
if leftText and leftText ~= _G.UNKNOWN then
return leftText
end
else
local tooltipFrame = GetCreatureNameFromIDFinderTooltip or CreateFrame ("GameTooltip", "GetCreatureNameFromIDFinderTooltip", nil, "GameTooltipTemplate")
tooltipFrame:SetOwner (WorldFrame, "ANCHOR_NONE")
tooltipFrame:SetHyperlink (("unit:Creature-0-0-0-0-%d"):format(npcID))
local npcNameLine = _G ["GetCreatureNameFromIDFinderTooltipTextLeft1"]
return npcNameLine and npcNameLine:GetText()
end
end
local translate_npc_cache
translate_npc_cache = function()
if not Plater.db.profile.auto_translate_npc_names then return end
if PLAYER_IN_COMBAT then --or not IS_IN_OPEN_WORLD then
C_Timer.After(5, translate_npc_cache)
return
end
local count = 0
local leftOvers = false
for id, entry in pairs(DB_NPCIDS_CACHE) do
if entry[3] ~= Plater.Locale then
local npcName = GetCreatureNameFromID(id)
if npcName then
--DevTool:AddData(npcName, "translated")
entry[1] = npcName
entry[3] = Plater.Locale
count = count + 1
else
--DevTool:AddData(id .. " - " .. entry[1], "not translated")
end
end
if count >= maxPerFrame then
leftOvers = true
break
end
end
if leftOvers and Plater.TranslateNPCCacheIsRunning then
C_Timer.After(translateTimer, translate_npc_cache)
else
Plater.TranslateNPCCacheIsRunning = false
end
end
translate_npc_cache()
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> combat log reader ~combatlog ~cleu
local PlaterCLEUParser = CreateFrame ("frame", "PlaterCLEUParserFrame", UIParent, BackdropTemplateMixin and "BackdropTemplate")
-- defined local above
parserFunctions = {
--todo: if animations are disabled, SPELL_DAMAGE doesn't need to be read
SPELL_DAMAGE = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
if (SPELL_WITH_ANIMATIONS [spellName] and sourceGUID == Plater.PlayerGUID) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if (plateFrame [MEMBER_GUID] == targetGUID and plateFrame.unitFrame.PlaterOnScreen) then
Plater.DoNameplateAnimation (plateFrame, SPELL_WITH_ANIMATIONS [spellName], spellName, isCritical)
end
end
end
end,
--~summon
SPELL_SUMMON = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
--[=[ --some actors are not having the pet flag 0x3000, so we are directly adding all target summons into the cache
print ("Summon", targetFlag, bit.band (targetFlag, 0x00003000) ~= 0)
if (sourceFlag and bit.band (sourceFlag, 0x00003000) ~= 0) then
print ("new pet", sourceName, targetName)
PET_CACHE [sourceGUID] = time
elseif (targetFlag and bit.band (targetFlag, 0x00003000) ~= 0) then
print ("new pet", sourceName, targetName)
PET_CACHE [targetGUID] = time
end
--]=]
---@type plater_petinfo
local entry = {
ownerGUID = sourceGUID,
ownerName = sourceName,
petName = targetName,
time = time
}
PET_CACHE[targetGUID] = entry
if (sourceGUID == Plater.PlayerGUID) then
Plater.PlayerPetCache [targetGUID] = entry
end
--check if the summoner has friendly affiliation, if it is friendly, add it to the friendly affiliation cache
if ((sourceFlag and bit.band(sourceFlag, 0x10) ~= 0) or (targetFlag and bit.band(targetFlag, 0x10) ~= 0)) then --0x10 = affiliation friendly
platerInternal.HasFriendlyAffiliation[targetGUID] = true
end
end,
SPELL_INTERRUPT = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
if (IS_IN_INSTANCE) then
PlaterDB.InterruptableSpells[spellID] = true
end
if (not Plater.db.profile.show_interrupt_author) then
return
end
--~interrupt
local name = sourceName
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
local unitFrame = plateFrame.unitFrame
local castBar = unitFrame.castBar
if (unitFrame.PlaterOnScreen and castBar:IsShown()) then
if (plateFrame [MEMBER_GUID] == targetGUID) then
--get owner name if a pet interrupted
local petOwnerTable = PET_CACHE[sourceGUID]
if (petOwnerTable) then
name = petOwnerTable.ownerName or name
sourceGUID = petOwnerTable.ownerGUID or sourceGUID
end
if DB_USE_NAME_TRANSLIT then
name = LibTranslit:Transliterate(name, TRANSLIT_MARK)
end
castBar.Text:SetText (INTERRUPTED .. " [" .. Plater.SetTextColorByClass (sourceGUID, name) .. "]")
castBar.IsInterrupted = true
castBar.InterruptSourceName = sourceName
castBar.InterruptSourceGUID = sourceGUID
--interrupt animation
if (Plater.db.profile.cast_statusbar_interrupt_anim) then
local interruptAnim = castBar._interruptAnim
if (not interruptAnim) then
--scale animation
local duration = 0.05
local animationHub = DF:CreateAnimationHub(castBar)
animationHub.ScaleUp = DF:CreateAnimation(animationHub, "scale", 1, duration, 1, 1, 1, 1.05)
animationHub.ScaleDown = DF:CreateAnimation(animationHub, "scale", 2, duration, 1, 1.05, 1, 0.95)
--shake animattion
local duration = 0.36
local amplitude = 0.58
local frequency = 3.39
local absolute_sineX = false
local absolute_sineY = false
local scaleX = 0
local scaleY = 1.2
local fade_out = 0.33
local fade_in = 0.001
local cooldown = 0.5
local points = castBar._points
local frameShakeObject = DF:CreateFrameShake(castBar, duration, amplitude, frequency, absolute_sineX, absolute_sineY, scaleX, scaleY, fade_in, fade_out, points)
castBar._interruptAnim = {
Scale = animationHub,
Shake = frameShakeObject
}
interruptAnim = castBar._interruptAnim
end
interruptAnim.Scale:Play()
castBar:PlayFrameShake(interruptAnim.Shake)
end
castBar.FrameOverlay.TargetName:Hide() -- hide the target immediately
--> check and stop the casting script if any
castBar:OnHideWidget()
end
end
end
end,
SPELL_CAST_SUCCESS = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
if ((tonumber(spellID) or 0) > 0 and (not DB_CAPTURED_SPELLS[spellID] or DB_CAPTURED_SPELLS[spellID].isChanneled == nil)) then -- check isChanneled to ensure update of already existing data
if (not platerInternal.HasFriendlyAffiliation[sourceGUID]) then
if (not sourceFlag or bit.band(sourceFlag, 0x60) ~= 0) then --is neutral or hostile
local npcId = Plater:GetNpcIdFromGuid(sourceGUID or "")
local isChanneled = false
if sourceGUID and UnitTokenFromGUID then -- this is the only proper way to check for channeled spells...
local unit = UnitTokenFromGUID(sourceGUID)
if unit and UnitChannelInfo(unit) then
isChanneled = true
end
end
if (npcId and npcId ~= 0) then
---@type plater_spelldata
local spellData = {
event = token,
source = sourceName,
npcID = npcId,
encounterID = Plater.CurrentEncounterID,
encounterName = Plater.CurrentEncounterName,
isChanneled = isChanneled
}
--print("added DB_CAPTURED_SPELLS 1:", sourceName, spellID, spellName)
DB_CAPTURED_SPELLS[spellID] = spellData
if isChanneled and not DB_CAPTURED_CASTS[spellID] then
---@type plater_spelldata
local spellData = {
event = token,
source = sourceName,
npcID = npcId,
encounterID = Plater.CurrentEncounterID,
encounterName = Plater.CurrentEncounterName,
isChanneled = isChanneled
}
DB_CAPTURED_CASTS[spellID] = spellData
end
end
end
end
end
end,
SPELL_CAST_START = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
if (not DB_CAPTURED_CASTS[spellID] and not platerInternal.HasFriendlyAffiliation[sourceGUID]) then
if (not sourceFlag or bit.band(sourceFlag, 0x60) ~= 0) then --is neutral or hostile
local npcId = Plater:GetNpcIdFromGuid(sourceGUID or "")
if (npcId and npcId ~= 0) then
---@type plater_spelldata
local spellData = {
event = token,
source = sourceName,
npcID = npcId,
encounterID = Plater.CurrentEncounterID,
encounterName = Plater.CurrentEncounterName
}
DB_CAPTURED_CASTS[spellID] = spellData
end
end
end
if (spellName) then
Plater.LastCombat.spellNames[spellName] = true
end
platerInternal.Audio.PlaySoundForCastStart(spellID)
end,
SPELL_AURA_APPLIED = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, auraType, overKill, school, resisted, blocked, absorbed, isCritical)
if (not DB_CAPTURED_SPELLS[spellID] and not platerInternal.HasFriendlyAffiliation[sourceGUID]) then
if (not sourceFlag or bit.band(sourceFlag, 0x60) ~= 0) then --is neutral or hostile
local npcId = Plater:GetNpcIdFromGuid(sourceGUID or "")
if (npcId and npcId ~= 0) then
---@type plater_spelldata
local spellData = {
event = token,
source = sourceName,
type = auraType,
npcID = npcId,
encounterID = Plater.CurrentEncounterID,
encounterName = Plater.CurrentEncounterName
}
--print("added DB_CAPTURED_SPELLS 2:", sourceName, spellID, spellName, sourceFlag)
DB_CAPTURED_SPELLS[spellID] = spellData
end
end
end
if IS_WOW_PROJECT_NOT_MAINLINE then
-- paladin tank buff tracking
local playerGUID = Plater.PlayerGUID
if sourceGUID == playerGUID and targetGUID == playerGUID then
spellId = select(7, GetSpellInfo(spellName))
if spellId == 25780 or spellId == 407627 then
UpdatePlayerTankState(true)
--Plater.RefreshTankCache()
end
end
end
end,
UNIT_DIED = function(time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID)
--targetGUID is the GUID of the unit which died
platerInternal.ExtraAuras.RemoveGUIDFromUnitFrameCache(targetGUID)
end,
}
if IS_WOW_PROJECT_NOT_MAINLINE then
tinsert(parserFunctions, {
SPELL_AURA_REMOVED = function (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
-- paladin tank buff tracking
local playerGUID = Plater.PlayerGUID
if sourceGUID == playerGUID and targetGUID == playerGUID then
spellId = select(7, GetSpellInfo(spellName))
if spellId == 25780 or spellId == 407627 then
UpdatePlayerTankState(false)
--Plater.RefreshTankCache()
end
end
end,
})
end
PlaterCLEUParser.Parser = function (self)
local time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical = CombatLogGetCurrentEventInfo()
local func = parserFunctions [token]
if (func) then
Plater.StartLogPerformanceCore("Plater-Core", "Events", token)
func (time, token, hidding, sourceGUID, sourceName, sourceFlag, sourceFlag2, targetGUID, targetName, targetFlag, targetFlag2, spellID, spellName, spellType, amount, overKill, school, resisted, blocked, absorbed, isCritical)
if (targetName) then
Plater.LastCombat.npcNames[targetName] = true
end
Plater.EndLogPerformanceCore("Plater-Core", "Events", token)
end
end
PlaterCLEUParser:SetScript ("OnEvent", PlaterCLEUParser.Parser)
PlaterCLEUParser:RegisterEvent ("COMBAT_LOG_EVENT_UNFILTERED")
C_Timer.NewTicker (600, function()
local now = time()
for guid, entry in pairs (PET_CACHE) do
local time = entry.time
if (time+600 < now) then
PET_CACHE [guid] = nil
end
end
for guid, entry in pairs (Plater.PlayerPetCache) do
local time = entry.time
if (time + 3600 < now) then
Plater.PlayerPetCache [guid] = nil
end
end
end)
Plater.NpcBlackList = {}
function Plater.ForceFindPetOwner (serial) --private
Plater.StartLogPerformanceCore("Plater-Core", "Update", "ForceFindPetOwner")
local petName,text1
local cbMode = tonumber(GetCVar("colorblindMode")) or 0
if IS_WOW_PROJECT_MAINLINE then
local tooltipData = C_TooltipInfo.GetHyperlink ("unit:" .. serial or "")
if tooltipData then
local lines = tooltipData.lines
petName = lines and lines[1] and lines[1].leftText
text1 = lines and lines[2 + cbMode] and lines[2 + cbMode].leftText
end
else
local tooltipFrame = PlaterPetOwnerFinder or CreateFrame ("GameTooltip", "PlaterPetOwnerFinder", nil, "GameTooltipTemplate")
tooltipFrame:SetOwner (WorldFrame, "ANCHOR_NONE")
tooltipFrame:SetHyperlink ("unit:" .. serial or "")
local petNameLine = _G ["PlaterPetOwnerFinderTextLeft1"]
petName = petNameLine and petNameLine:GetText()
local line1 = _G ["PlaterPetOwnerFinderTextLeft" .. (2 + cbMode)]
text1 = line1 and line1:GetText()
end
local isPlayerPet = false
local isOtherPet = false
local ownerName = ""
if (text1 and text1 ~= "") then
local pName = GetUnitName ("player", true)
local playerName = pName:gsub ("%-.*", "") --remove realm name
if (text1:find (playerName)) then
isPlayerPet = true
ownerName = playerName
else
ownerName = (string.match(text1, string.gsub(UNITNAME_TITLE_PET, "%%s", "(%.*)")) or string.match(text1, string.gsub(UNITNAME_TITLE_MINION, "%%s", "(%.*)")) or string.match(text1, string.gsub(UNITNAME_TITLE_GUARDIAN, "%%s", "(%.*)")))
if ownerName then
isOtherPet = true
end
end
end
if (isPlayerPet or isOtherPet) and petName then
local entry = {ownerGUID = UnitGUID(ownerName), ownerName = ownerName, petName = petName, time = time()}
if (isPlayerPet) then
PET_CACHE [serial] = entry
Plater.PlayerPetCache [serial] = entry
elseif (isOtherPet) then
--ViragDevTool_AddData({serial = serial, entry = entry, tooltipFrame = tooltipFrame}, "pet")
PET_CACHE [serial] = entry
end
else
Plater.NpcBlackList [serial] = true
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "ForceFindPetOwner")
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> cvars - ~cvars
function Plater.CreatePlaterButtonAtInterfaceOptions()
if not InterfaceOptionsFrame then return end --DF does not has this anymore TODO
local f = CreateFrame ("frame", nil, InterfaceOptionsNamesPanel, BackdropTemplateMixin and "BackdropTemplate")
f:SetSize (300, 200)
f:SetPoint ("topleft", InterfaceOptionsNamesPanel, "topleft", 10, -440)
local open_options = function()
InterfaceOptionsFrame:Hide()
HideUIPanel(GameMenuFrame)
Plater.OpenOptionsPanel()
end
local Button = DF:CreateButton (f, open_options, 100, 22, "", -1, nil, nil, nil, nil, nil, DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE"))
Button:SetPoint ("topleft", f, "topleft", 10, 0)
Button:SetText ("Advanced Nameplate Options")
Button:SetIcon ([[Interface\BUTTONS\UI-OptionsButton]], 18, 18, "overlay", {0, 1, 0, 1})
end
--elseof
function Plater.SetCVarsOnFirstRun()
if (InCombatLockdown()) then
C_Timer.After (1, function() Plater.SetCVarsOnFirstRun() end)
return
end
canSaveCVars = false -- ensure to not overwrite profile
--> these are the cvars set for each character when they logon
--disabled:
--SetCVar ("nameplateShowSelf", CVAR_DISABLED)
--SetCVar ("nameplateShowFriends", CVAR_ENABLED)
--> location of the personal bar
-- SetCVar ("nameplateSelfBottomInset", 20 / 100)
-- SetCVar ("nameplateSelfTopInset", abs (20 - 99) / 100)
--> set the stacking to true
--SetCVar ("nameplateMotion", CVAR_ENABLED) --March 08, 2019 don't change the stacking type when installing plater
--> distance between each nameplate when using stacking
--SetCVar ("nameplateOverlapV", 1.25)
--> make nameplates always shown and down't show minions
SetCVar ("nameplateShowAll", CVAR_ENABLED)
if IS_WOW_PROJECT_MAINLINE then
SetCVar ("ShowNamePlateLoseAggroFlash", CVAR_ENABLED) --blizzard flash
end
--scale when it is too far away from the camera
SetCVar ("nameplateMinScale", 1)
--scale of the nameplate for important units, default is 1.2 which makes the nameplate be too big with the 1.15 target scale
SetCVar ("nameplateLargerScale", 1.10)
--enable enemy minus nameplates
SetCVar ("nameplateShowEnemyMinions", CVAR_ENABLED)
SetCVar ("nameplateShowEnemyMinus", CVAR_ENABLED)
--don't show friendly npcs
SetCVar ("nameplateShowFriendlyNPCs", 0)
--disable friendly minius nameplates
SetCVar ("nameplateShowFriendlyGuardians", CVAR_DISABLED)
SetCVar ("nameplateShowFriendlyPets", CVAR_DISABLED)
SetCVar ("nameplateShowFriendlyTotems", CVAR_DISABLED)
SetCVar ("nameplateShowFriendlyMinions", CVAR_DISABLED)
--> make it show the class color of players
SetCVar ("ShowClassColorInNameplate", CVAR_ENABLED)
--> lock nameplates to screen
SetCVar ("nameplateOtherTopInset", "0.085")
SetCVar ("nameplateLargeTopInset", "0.085")
SetCVar ("clampTargetNameplateToScreen", "1")
SetCVar ("nameplateTargetRadialPosition", "1")
SetCVar ("nameplateTargetBehindMaxDistance", "30")
--> reset the horizontal and vertical scale
SetCVar ("NamePlateHorizontalScale", CVAR_ENABLED)
SetCVar ("NamePlateVerticalScale", CVAR_ENABLED)
if IS_WOW_PROJECT_MAINLINE then
SetCVar ("NamePlateClassificationScale", CVAR_ENABLED)
end
--> make the selection be a little bigger
SetCVar ("nameplateSelectedScale", "1.15")
--> movement speed of nameplates when using stacking, going above 0.5 this isn't recommended
SetCVar ("nameplateMotionSpeed", "0.025")
--> make the personal bar hide very fast
SetCVar ("nameplatePersonalHideDelaySeconds", 0.2)
--> don't show debuffs on blizzard healthbars
SetCVar ("nameplateShowDebuffsOnFriendly", CVAR_DISABLED)
--> view distance
if IS_WOW_PROJECT_MAINLINE then
SetCVar ("nameplateMaxDistance", 60)
else
SetCVar ("nameplateMaxDistance", 41)
end
--> ensure resource on target consistency:
if IS_WOW_PROJECT_MAINLINE then
PlaterDBChr.resources_on_target = GetCVar ("nameplateResourceOnTarget") == CVAR_ENABLED
SetCVar ("nameplateResourceOnTarget", CVAR_DISABLED)
end
PlaterDBChr.first_run3 [UnitGUID ("player")] = true
Plater.db.profile.first_run3 = true
Plater.RunFunctionForEvent ("ZONE_CHANGED_NEW_AREA")
--Plater:Msg ("Plater has been successfully installed on this character.")
Plater.RestoreProfileCVars() -- restore profile, if existing
canSaveCVars = true -- save cvars again
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> quest log stuff ~quest
function Plater.GetActorSubName (plateFrame) --private
local cbMode = tonumber(GetCVar("colorblindMode")) or 0
local subTitle = ""
if IS_WOW_PROJECT_MAINLINE then
local tooltipData = C_TooltipInfo.GetHyperlink("unit:" .. (plateFrame [MEMBER_GUID] or ""))
if tooltipData then
local line = tooltipData.lines and tooltipData.lines[2 + cbMode]
subTitle = line and line.leftText or ""
end
else
local GameTooltipFrame = PlaterScanTooltip or CreateFrame ("GameTooltip", "PlaterScanTooltip", nil, "GameTooltipTemplate")
GameTooltipFrame:SetOwner (WorldFrame, "ANCHOR_NONE")
GameTooltipFrame:SetHyperlink ("unit:" .. (plateFrame [MEMBER_GUID] or ''))
local GameTooltipFrameTextLeft = _G ["PlaterScanTooltipTextLeft" .. (2 + cbMode)]
subTitle = GameTooltipFrameTextLeft and GameTooltipFrameTextLeft:GetText() or ""
end
if subTitle ~= "" and not subTitle:lower():match (string.gsub(UNIT_LEVEL_TEMPLATE:lower(), "%%d", "(%.*)")) then
return subTitle
else
return nil
end
end
function Plater.IsQuestObjective (plateFrame)
if (not plateFrame [MEMBER_GUID]) then --platerFrame.actorType == "friendlynpc"
return
end
Plater.StartLogPerformanceCore("Plater-Core", "Update", "IsQuestObjective")
-- reset quest amount
plateFrame.QuestAmountCurrent = nil
plateFrame.QuestAmountTotal = nil
plateFrame.QuestText = nil
plateFrame.QuestName = nil
plateFrame.QuestIsCampaign = nil
plateFrame.unitFrame.QuestAmountCurrent = nil
plateFrame.unitFrame.QuestAmountTotal = nil
plateFrame.unitFrame.QuestText = nil
plateFrame.unitFrame.QuestName = nil
plateFrame.unitFrame.QuestIsCampaign = nil
local ScanQuestTextCache = {}
local useQuestie = false
local QuestieTooltips = QuestieLoader and QuestieLoader._modules["QuestieTooltips"]
if QuestieTooltips then
ScanQuestTextCache = QuestieTooltips.GetTooltip("m_"..plateFrame [MEMBER_NPCID])
if not ScanQuestTextCache then
ScanQuestTextCache = {}
end
useQuestie = true
else
if IS_WOW_PROJECT_MAINLINE then
local tooltipData = C_TooltipInfo.GetHyperlink ("unit:" .. plateFrame [MEMBER_GUID])
if tooltipData then
for _, line in ipairs(tooltipData.lines or {}) do
if line.type == Enum.TooltipDataLineType.QuestObjective or line.type == Enum.TooltipDataLineType.QuestTitle or line.type == Enum.TooltipDataLineType.QuestPlayer then
--only add actual quest tooltip lines
ScanQuestTextCache [#ScanQuestTextCache + 1] = line.leftText or ""
end
end
end
else
local GameTooltipScanQuest = PlaterScanQuestTooltip or CreateFrame ("GameTooltip", "PlaterScanQuestTooltip", nil, "GameTooltipTemplate")
GameTooltipScanQuest:SetOwner (WorldFrame, "ANCHOR_NONE")
GameTooltipScanQuest:SetHyperlink ("unit:" .. plateFrame [MEMBER_GUID])
for i = 1, GameTooltipScanQuest:NumLines() do
ScanQuestTextCache [i] = _G ["PlaterScanQuestTooltipTextLeft" .. i]:GetText() or ""
end
end
end
local playerName = UnitName("player")
local isInGroup = IsInGroup()
local unitQuestData = {}
local isQuestUnit = false
local atLeastOneQuestUnfinished = false
for i = 1, #ScanQuestTextCache do
local text = ScanQuestTextCache [i]
if useQuestie then
text = gsub(text,"|c........","") -- remove coloring begin
text = gsub(text,"|r","") -- remove color end
text = gsub(text,"%[.*%] ","") -- remove level text
text = gsub(text," %(%d+%)","") -- remove quest-id
end
if (Plater.QuestCache [text]) then
--unit belongs to a quest
isQuestUnit = true
local isCampaignQuest = Plater.QuestCacheCampaign[text]
local isGroupQuest, yourQuest = nil, nil
---@type questdata
local questData = {
questName = text,
questText = "",
finished = true,
groupQuest = false,
groupFinished = true,
amount = 0,
groupAmount = 0,
total = 0,
yourQuest = false,
isCampaignQuest = isCampaignQuest,
}
local amount1, amount2, questText = nil, nil, nil
local amountSet = false
local j = i
while (ScanQuestTextCache [j+1]) do
--check if the unit objective isn't already done
local nextLineText = ScanQuestTextCache [j+1]
if useQuestie then
nextLineText = gsub(nextLineText,"|c........","") -- remove coloring begin
nextLineText = gsub(nextLineText,"|r","") -- remove color end
end
if (nextLineText) then
if useQuestie then
local isQuestieOwn = nextLineText:match ("%(("..playerName..")%)%s*$") and true or false
local isQuestieGroup = nextLineText:match ("%((%w+)%)%s*$") and isInGroup and true or false
yourQuest = isQuestieOwn or not isQuestieGroup
isGroupQuest = isQuestieGroup
questData.yourQuest = yourQuest
questData.groupQuest = isGroupQuest
end
if not useQuestie and isInGroup and nextLineText == playerName then
yourQuest = true
isGroupQuest = true
questData.yourQuest = true
questData.groupQuest = true
elseif not nextLineText:match(THREAT_TOOLTIP) then
local p1, p2 = nextLineText:match ("(%d+)/(%d+)")
if (not p1) then
-- check for % based quests
p1 = nextLineText:match ("(%d+%%)")
if p1 then
-- remove the % sign for consistency
p1 = string.gsub(p1,"%%", '')
end
end
if (p1 and p2 and not (p1 == p2)) or (p1 and not p2 and not (p1 == "100")) then
-- quest not completed
atLeastOneQuestUnfinished = true
amount1, amount2 = p1, p2
if not amountSet or ((tonumber(p1) or 0) < (tonumber(questData.groupAmount) or 0)) then
questData.groupAmount = amount1
end
questData.total = amount2
if yourQuest ~= false then
yourQuest = false -- already set on data
questData.finished = false
questData.amount = amount1
end
questData.groupFinished = false
questData.questText = nextLineText
questText = nextLineText
amountSet = true
elseif yourQuest and (p1 and p2 and (p1 == p2)) or (p1 and not p2 and (p1 == "100")) then
yourQuest = false -- already set on data
questData.finished = true
questData.amount = p1
end
else
j = 99 --safely break here, as we saw threat% -> quest text is done
end
end
j = j + 1
end
if (amount1 and atLeastOneQuestUnfinished) then
plateFrame.QuestAmountCurrent = questData.groupAmount
plateFrame.QuestAmountTotal = amount2
plateFrame.QuestText = questText
plateFrame.QuestName = text
plateFrame.QuestIsCampaign = isCampaignQuest
--expose to scripts
plateFrame.unitFrame.QuestAmountCurrent = questData.groupAmount
plateFrame.unitFrame.QuestAmountTotal = amount2
plateFrame.unitFrame.QuestText = questText
plateFrame.unitFrame.QuestName = text
plateFrame.unitFrame.QuestIsCampaign = isCampaignQuest
end
if not isGroupQuest then
questData.yourQuest = true
end
tinsert(unitQuestData, questData)
end
end
plateFrame.QuestInfo = unitQuestData
plateFrame.unitFrame.QuestInfo = unitQuestData
local namePlateIsQuestObjective = isQuestUnit and atLeastOneQuestUnfinished
plateFrame [MEMBER_QUEST] = namePlateIsQuestObjective
plateFrame.unitFrame [MEMBER_QUEST] = namePlateIsQuestObjective
Plater.EndLogPerformanceCore("Plater-Core", "Update", "IsQuestObjective")
return namePlateIsQuestObjective
end
local update_quest_cache = function()
--clear the quest cache
wipe (Plater.QuestCache)
wipe (Plater.QuestCacheCampaign)
--do not update if is inside an instance
local isInInstance = IsInInstance()
if (isInInstance) then
return
end
--update the quest cache
local numEntries, numQuests = C_QuestLog.GetNumQuestLogEntries and C_QuestLog.GetNumQuestLogEntries() or GetNumQuestLogEntries()
for questLogId = 1, numEntries do
if IS_WOW_PROJECT_MAINLINE then
local questDetails = C_QuestLog.GetInfo(questLogId)
--any chance to track via quest objective? no unit IDs given there...
--ViragDevTool_AddData({questDetails = questDetails, QuestObjectives = C_QuestLog.GetQuestObjectives(questDetails.questID), Title = C_QuestLog.GetTitleForLogIndex(questLogId)}, "QuestUpdate - " .. questLogId)
if (questDetails and not questDetails.isHeader and questDetails.title and type (questDetails.questID) == "number" and questDetails.questID > 0) then
Plater.QuestCache [questDetails.title] = true
if (questDetails.campaignID) then
Plater.QuestCacheCampaign[questDetails.title] = true
end
end
else
local title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questId, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle (questLogId)
if (type (questId) == "number" and questId > 0) then -- and not isComplete
Plater.QuestCache [title] = true
end
end
end
if IS_WOW_PROJECT_MAINLINE then
local mapId = C_Map.GetBestMapForUnit ("player")
if (mapId) then
local worldQuests = C_TaskQuest.GetQuestsForPlayerByMapID (mapId)
if (type (worldQuests) == "table") then
for i, questTable in ipairs (worldQuests) do
local x, y, floor, numObjectives, questId, inProgress = questTable.x, questTable.y, questTable.floor, questTable.numObjectives, questTable.questId, questTable.inProgress
if (type (questId) == "number" and questId > 0) then
local questName = C_TaskQuest.GetQuestInfoByQuestID (questId)
if (questName) then
Plater.QuestCache [questName] = true
end
end
end
end
end
end
Plater.UpdateAllPlates()
end
function Plater.QuestLogUpdated() --private
if (Plater.UpdateQuestCacheThrottle and not Plater.UpdateQuestCacheThrottle._cancelled) then
Plater.UpdateQuestCacheThrottle:Cancel()
end
Plater.UpdateQuestCacheThrottle = C_Timer.NewTimer(1, update_quest_cache)
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> API ~API
--attempt to get the role of the unit shown in the nameplate
function Plater.GetUnitRole (unitFrame)
if IS_WOW_PROJECT_MAINLINE then
local assignedRole = UnitGroupRolesAssigned (unitFrame.unit)
if (assignedRole and assignedRole ~= "NONE") then
return assignedRole
end
if (Plater.ZoneInstanceType == "arena") then
local oponentes = GetNumArenaOpponentSpecs()
for i = 1, oponentes do
local unitGUID = UnitGUID ("arena" .. i)
if (unitGUID == unitFrame [MEMBER_GUID]) then
local spec = GetArenaOpponentSpec (i)
if (spec) then
local id, name, description, icon, role, class = GetSpecializationInfoByID (spec)
if (role and role ~= "NONE") then
return role
end
end
end
end
elseif (Plater.ZoneInstanceType == "pvp") then
if (Details) then
local actor = Details:GetActor ("current", DETAILS_ATTRIBUTE_DAMAGE, GetUnitName (unitFrame.unit, true))
if (actor) then
local spec = actor.spec
if (spec) then
local id, name, description, icon, role, class = GetSpecializationInfoByID (spec)
if (role and role ~= "NONE") then
return role
end
end
end
end
end
return assignedRole
else
if IS_WOW_PROJECT_CLASSIC_WRATH then
local assignedRole = UnitGroupRolesAssigned (unitFrame.unit)
if (assignedRole and assignedRole ~= "NONE") then
return assignedRole
end
end
if GetPartyAssignment("MAINTANK", unit) then
return "MAINTANK"
elseif GetPartyAssignment("MAINASSIST", unit) then
return "MAINASSIST"
end
end
end
local BG_PLAYER_CACHE = {}
function Plater.UpdateBgPlayerRoleCache()
wipe(BG_PLAYER_CACHE)
if IS_WOW_PROJECT_MAINLINE then
if Plater.ZoneInstanceType == "pvp" then
local curNumScores = GetNumBattlefieldScores()
for i = 1, curNumScores do
local info = C_PvP.GetScoreInfo(i)
if info then
local name, faction, race, class, classToken, talentSpec = info.name, info.faction, info.raceName, info.className, info.classToken, info.talentSpec
if name then
BG_PLAYER_CACHE[name] = {faction = faction, race = race, class = class, classToken = classToken, talentSpec = talentSpec, specID = (CLASS_INFO_CACHE[classToken] and CLASS_INFO_CACHE[classToken][talentSpec] and CLASS_INFO_CACHE[classToken][talentSpec].specID), name = name}
end
end
end
elseif Plater.ZoneInstanceType == "arena" then
local numOpps = GetNumArenaOpponentSpecs();
for i=1, numOpps do
local specID, gender = GetArenaOpponentSpec(i);
if (specID > 0) then
local name = GetUnitName ("arena"..i, true)
if name then
local id, talentSpec, _, _, _, class = GetSpecializationInfoByID(specID, gender);
local class, classToken = UnitClass("arena"..i);
local race = UnitRace("arena"..i);
BG_PLAYER_CACHE[name] = {faction = nil, race = race, class = class, classToken = classToken, talentSpec = talentSpec, specID = specID, name = name}
end
end
end
end
else
--TODO: Does this really work in BG/Arena or is it just score screen?
if Plater.ZoneInstanceType == "pvp" or Plater.ZoneInstanceType == "arena" then
local curNumScores = GetNumBattlefieldScores()
for i = 1, curNumScores do
local name, _, _, _, _, faction, _, race, class, classToken = GetBattlefieldScore(i);
if name then
BG_PLAYER_CACHE[name] = {faction = faction, race = race, class = class, classToken = classToken, talentSpec = "UNKNOWN", specID = nil, name = name}
end
end
end
end
end
function Plater.GetUnitBGInfo(unit)
if (not UnitIsPlayer(unit)) then
return nil
end
if (not Plater.ZoneInstanceType == "pvp" and not Plater.ZoneInstanceType == "arena") then
return nil
end
local name = GetUnitName(unit, true)
if not BG_PLAYER_CACHE[name] then
Plater.UpdateBgPlayerRoleCache()
end
return BG_PLAYER_CACHE[name]
end
function Plater.GetSpecIconForUnitFromBG(unit)
local cache = Plater.GetUnitBGInfo(unit)
if cache and cache.specID then
return Plater.GetSpecIcon(cache.specID)
end
return nil
end
function Plater.GetSpecIcon(spec)
if (spec) then
if (not class_specs_coords[spec]) then -- default to holy paladin if spec not supported
spec = 65
end
if (useAlpha) then
return [[Interface\AddOns\Plater\images\spec_icons_normal_alpha]], unpack (class_specs_coords [spec])
else
return [[Interface\AddOns\Plater\images\spec_icons_normal]], unpack (class_specs_coords [spec])
end
end
end
--similar to Plater.GetSettings, but can be called from scripts
--is is also safe because it passes a read-only table with copied values
function Plater.GetConfig (unitFrame)
return Plater.ActorTypeSettingsCache [unitFrame.ActorType]
end
--return true if the player is in open world (not inside dungeons, etc)
function Plater.IsInOpenWorld()
return IS_IN_OPEN_WORLD
end
--refresh the frame strata and frame level when using UIParent as the parent
function Plater.RefreshNameplateStrata (unitFrame)
return Plater.UpdateUIParentLevels (unitFrame)
end
--add an extra indicator
function Plater.ShowIndicator (unitFrame, texture, width, height, color, L, R, T, B)
tinsert (unitFrame.CustomIndicators, {texture or "", width or 12, height or 12, color or "white", L or 0, R or 1, T or 0, B or 1})
Plater.UpdateIndicators (unitFrame.PlateFrame, unitFrame.ActorType)
end
--allow scripts to perform safe cvars changes with backup of default values
--PostponeCVarChange stores cvars scheduled to be changed after the combat lockdown drops
Plater.PostponeSetCVar = {}
local postpone_set_cvar = function (timerObject)
local variableName, value = timerObject.variableName, timerObject.value
Plater.PostponeSetCVar [variableName] = nil
Plater.SafeSetCVar (variableName, value)
end
function Plater.SafeSetCVar (variableName, value)
--check if is a valid cvar
if (GetCVar (variableName) == nil) then
Plater:Msg ("invalid cvar for Plater.SafeSetCVar()")
return
end
--check if there's a scheduled change for this cvar and cancel it
if (Plater.PostponeSetCVar [variableName]) then
Plater.PostponeSetCVar [variableName]:Cancel()
Plater.PostponeSetCVar [variableName] = nil
end
--check if is in combat, if is, schedule to change this cvar after the lockdown drop
if (InCombatLockdown()) then
local timerObject = C_Timer.NewTimer (0.5, postpone_set_cvar)
timerObject.variableName = variableName
timerObject.value = value
Plater.PostponeSetCVar [variableName] = timerObject
return true
end
--store the default value if there's no default value set yet
local cvarCache = Plater.db.profile.cvar_default_cache
if (cvarCache [variableName] == nil) then
cvarCache [variableName] = GetCVar (variableName)
end
SetCVar (variableName, value)
return true
end
Plater.PostponeRestoreCVar = {}
function Plater.PostponeCVarRestauration (timerObject)
local variableName = timerObject.variableName
Plater.PostponeRestoreCVar [variableName] = nil
Plater.RestoreCVar (variableName)
end
function Plater.RestoreCVar (variableName)
--check if is a valid cvar
if (GetCVar (variableName) == nil) then
Plater:Msg ("invalid cvar for Plater.SafeSetCVar()")
return
end
--check if there's a scheduled change for this cvar and cancel it
if (Plater.PostponeRestoreCVar [variableName]) then
Plater.PostponeRestoreCVar [variableName]:Cancel()
Plater.PostponeRestoreCVar [variableName] = nil
end
--check if is in combat, if is, schedule to change this cvar after the lockdown drop
if (InCombatLockdown()) then
local timerObject = C_Timer.NewTimer (0.5, Plater.PostponeCVarRestauration)
timerObject.variableName = variableName
Plater.PostponeRestoreCVar [variableName] = timerObject
return true
end
--restore the value
local cvarCache = Plater.db.profile.cvar_default_cache
if (cvarCache [variableName]) then
SetCVar (variableName, cvarCache [variableName])
cvarCache [variableName] = nil
return true
end
end
--return if the unit is in the friends list
function Plater.IsUnitInFriendsList (unitFrame)
return Plater.FriendsCache [unitFrame.unitNameInternal]
end
--> api version of the tap denied function
function Plater.IsUnitTapped (unitFrame)
return Plater.IsUnitTapDenied (unitFrame.unit)
end
--set if Plater will check for the execute range and what percent of life is require to enter in the execute range
--healthAmount is a floor com zero to one, example: 25% is 0.25
function Plater.SetExecuteRange (isExecuteEnabled, healthAmountLower, healthAmountUpper)
DB_USE_HEALTHCUTOFF = isExecuteEnabled
DB_HEALTHCUTOFF_AT = tonumber (healthAmountLower) or -0.1
DB_HEALTHCUTOFF_AT_UPPER = tonumber (healthAmountUpper) or 1.1
end
--return the name of the unit guild
function Plater.GetUnitGuildName (unitFrame)
return unitFrame.PlateFrame.playerGuildName
end
--return if the nameplate is showing an aura
function Plater.NameplateHasAura (unitFrame, aura)
return unitFrame.BuffFrame.AuraCache [aura] or unitFrame.BuffFrame2.AuraCache [aura] or unitFrame.ExtraIconFrame.AuraCache [aura]
end
--return if the unit has a specific aura
function Plater.UnitHasAura (unitFrame, aura)
return unitFrame and unitFrame.AuraCache and aura and unitFrame.AuraCache [aura]
end
--return if the unit has an enrage effect
function Plater.UnitHasEnrage (unitFrame)
return unitFrame and unitFrame.AuraCache and unitFrame.AuraCache.hasEnrage
end
--return if the unit has a dispellable effect
function Plater.UnitHasDispellable (unitFrame)
return unitFrame and unitFrame.AuraCache and unitFrame.AuraCache.canStealOrPurge
end
--get npc color set in the colors tab, it is getting the color set by the user with or without the "scripts only checked"
function Plater.GetNpcColor(unitFrame)
local npcId = unitFrame[MEMBER_NPCID]
if (npcId) then
return DB_UNITCOLOR_CACHE[npcId] or DB_UNITCOLOR_SCRIPT_CACHE[npcId]
end
end
--pass some colors and return the first valid color
function Plater.GetColorByPriority(unitFrame, color1, color2, color3)
if (unitFrame) then
--from the Npc Colors and Names
local npcColor = Plater.GetNpcColor(unitFrame)
if (npcColor) then
return npcColor
end
end
if (color1) then
return color1
end
if (color2) then
return color2
end
if (color3) then
return color3
end
end
--return which raid mark the namepalte has
function Plater.GetRaidMark (unitFrame)
if (unitFrame.IsSelf) then
return false
end
return GetRaidTargetIndex (unitFrame.unit)
end
--limit the text size of a font string
function Plater.LimitTextSize (fontString, maxWidth)
if (not fontString) then
Plater:Msg ("Plater.LimitTextSize() with not fontString")
return
end
maxWidth = max (maxWidth or 0, 10)
local text = fontString:GetText()
if not text or text == "" then
return
end
while (fontString:GetStringWidth() > maxWidth) do
text = strsub (text, 1, #text - 1)
fontString:SetText (text)
if (string.len (text) <= 1) then
break
end
end
-- cleanup utf8...
text = DF:CleanTruncateUTF8String(text)
fontString:SetText (text)
end
--create a custom aura checking, this reset the currently shown auras and only check for auras the script passed
--this is only called from scripts
--@buffList: a table with aura names as keys and true as the value, example: ["aura name"] = true
--@debuffList: same as above
--@noSpecialAuras: won't check special auras
function Plater.CheckAuras (self, buffList, debuffList, noSpecialAuras)
local buffFrame = self.BuffFrame
local buffFrame2 = self.BuffFrame2
Plater.ResetAuraContainer (buffFrame)
Plater.TrackSpecificAuras (buffFrame, self.unit, true, buffList, self.IsSelf, noSpecialAuras)
Plater.TrackSpecificAuras (buffFrame, self.unit, false, debuffList, self.IsSelf, noSpecialAuras)
Plater.HideNonUsedAuraIcons (buffFrame)
--update the buff layout and alpha
buffFrame.unit = self.unit
Plater.AlignAuraFrames (buffFrame)
--buffFrame:SetAlpha (DB_AURA_ALPHA)
if (DB_AURA_SEPARATE_BUFFS) then
buffFrame2.unit = self.unit
Plater.AlignAuraFrames (buffFrame2)
--buffFrame2:SetAlpha (DB_AURA_ALPHA)
end
Plater.RunScriptTriggersForAuraIcons (unitFrame)
end
--return the health bar and the unitname text
function Plater.GetHealthBar (unitFrame)
--check if the plateFrame has been passed instead
if (unitFrame.unitFrame) then
unitFrame = unitFrame.unitFrame
end
return unitFrame.healthBar, unitFrame.healthBar.unitName
end
--return the cast bar and the spellname text
function Plater.GetCastBar (unitFrame)
--check if the plateFrame has been passed instead
if (unitFrame.unitFrame) then
unitFrame = unitFrame.unitFrame
end
return unitFrame.castBar, unitFrame.castBar.Text
end
--create a glow around the frame using LibCustomGlow - defaults to "button" glow
--[[ options can be used to create different glow types, see https://www.curseforge.com/wow/addons/libcustomglow
--type "pixel"
options = {
glowType = "pixel",
color = "white", -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
N = 8, -- number of lines. Defaul value is 8;
frequency = 0.25, -- frequency, set to negative to inverse direction of rotation. Default value is 0.25;
length = 4, -- length of lines. Default value depends on region size and number of lines;
th = 2, -- thickness of lines. Default value is 2;
xOffset = 0,
yOffset = 0, -- offset of glow relative to region border;
border = false, -- set to true to create border under lines;
key = "", -- key of glow, allows for multiple glows on one frame;
}
-- type "ants"
options = {
glowType = "ants",
color = "white", -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
N = 4, -- number of particle groups. Each group contains 4 particles. Defaul value is 4;
frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
scale = 1, -- scale of particles
xOffset = 0,
yOffset = 0, -- offset of glow relative to region border;
key = "", -- key of glow, allows for multiple glows on one frame;
}
-- type "button"
options = {
glowType = "button",
color = "white", -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
}
--]]
function Plater.StartGlow(frame, color, options, key)
if not frame then return end
if not color and (options and options.color) then
color = options.color
end
if color then
local r, g, b, a = DF:ParseColors (color)
color = {r, g, b, a}
options.color = color
end
if not options then
options = {
glowType = "button",
color = color,
key = key or "",
}
end
if not options.glowType then
options.glowType = "button"
end
if key then
options.key = key
end
if (not frame.__PlaterGlowFrame) then
frame.__PlaterGlowFrame = CreateFrame("Frame", nil, frame, BackdropTemplateMixin and "BackdropTemplate");
frame.__PlaterGlowFrame:SetAllPoints(frame);
frame.__PlaterGlowFrame:SetSize(frame:GetSize());
end
if options.glowType == "button" then
LCG.ButtonGlow_Start(frame.__PlaterGlowFrame, options.color, options.frequency)
elseif options.glowType == "pixel" then
if not options.border then options.border = false end
LCG.PixelGlow_Start(frame.__PlaterGlowFrame, options.color, options.N, options.frequency, options.length, options.th, options.xOffset, options.yOffset, options.border, options.key or "")
elseif options.glowType == "ants" then
LCG.AutoCastGlow_Start(frame.__PlaterGlowFrame, options.color, options.N, options.frequency, options.scale, options.xOffset, options.yOffset, options.key or "")
end
end
-- creates a button glow effect
function Plater.StartButtonGlow(frame, color, options, key)
-- type "button"
if not options then
options = {
glowType = "button",
color = color, -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
key = key or "", -- key of glow, allows for multiple glows on one frame;
}
else
options.glowType = "button"
end
Plater.StartGlow(frame, color or options.color, options, options.key)
end
-- creates an ants glow effect
function Plater.StartAntsGlow(frame, color, options, key)
-- type "ants"
if not options then
options = {
glowType = "ants",
color = color,
N = 4, -- number of particle groups. Each group contains 4 particles. Defaul value is 4;
frequency = 0.125, -- frequency, set to negative to inverse direction of rotation. Default value is 0.125;
scale = 1, -- scale of particles
xOffset = 0,
yOffset = 0, -- offset of glow relative to region border;
key = key or "", -- key of glow, allows for multiple glows on one frame;
}
else
options.glowType = "ants"
end
Plater.StartGlow(frame, color or options.color, options, options.key)
end
-- creates a pixel glow effect
function Plater.StartPixelGlow(frame, color, options, key)
-- type "pixel"
if not options then
options = {
glowType = "pixel",
color = color, -- all plater color types accepted, from lib: {r,g,b,a}, color of lines and opacity, from 0 to 1. Defaul value is {0.95, 0.95, 0.32, 1}
N = 8, -- number of lines. Defaul value is 8;
frequency = 0.25, -- frequency, set to negative to inverse direction of rotation. Default value is 0.25;
--length = 4, -- length of lines. Default value depends on region size and number of lines;
th = 2, -- thickness of lines. Default value is 2;
xOffset = 0,
yOffset = 0, -- offset of glow relative to region border;
border = false, -- set to true to create border under lines;
key = key or "", -- key of glow, allows for multiple glows on one frame;
}
else
options.glowType = "pixel"
end
Plater.StartGlow(frame, color or options.color, options, options.key)
end
-- stop LibCustomGlow effects on the frame, if existing
-- if glowType (and key) are given, stop one glow. if not, stop all.
function Plater.StopGlow(frame, glowType, key)
if not frame then return end
if not frame.__PlaterGlowFrame then return end
if glowType then
if glowType == "button" then
LCG.ButtonGlow_Stop(frame.__PlaterGlowFrame, key or "")
elseif glowType == "pixel" then
LCG.PixelGlow_Stop(frame.__PlaterGlowFrame, key or "")
elseif glowType == "ants" then
LCG.AutoCastGlow_Stop(frame.__PlaterGlowFrame, key or "")
end
else
LCG.ButtonGlow_Stop(frame.__PlaterGlowFrame, key or "")
LCG.PixelGlow_Stop(frame.__PlaterGlowFrame, key or "")
LCG.AutoCastGlow_Stop(frame.__PlaterGlowFrame, key or "")
end
end
-- stop a button glow
function Plater.StopButtonGlow(frame, key)
Plater.StopGlow(frame, "button", key)
end
-- stop a button glow
function Plater.StopPixelGlow(frame, key)
Plater.StopGlow(frame, "pixel", key)
end
-- stop an ants glow
function Plater.StopAntsGlow(frame, key)
Plater.StopGlow(frame, "ants", key)
end
--create a glow around an icon
function Plater.CreateIconGlow (frame, color, color2, useShowAnimation)
local f = Plater:CreateGlowOverlay (frame, color, color2 or color)
if not useShowAnimation and IS_WOW_PROJECT_MAINLINE then
f:SetScript("OnShow", nil) --reset
local onShow = function(self)
if (self.ProcStartAnim) then
self.ProcStartAnim:Stop()
self.ProcStartFlipbook:Hide()
if (not self.ProcLoop:IsPlaying()) then
self.ProcLoop:Play()
end
end
end
f:SetScript("OnShow", onShow)
end
return f
end
--create a glow around the healthbar or castbar frame
function Plater.CreateNameplateGlow (frame, color, left, right, top, bottom)
local antTable = {
Throttle = 0.025,
AmountParts = 15,
TexturePartsWidth = 167.4,
TexturePartsHeight = 83.6,
TextureWidth = 512,
TextureHeight = 512,
BlendMode = "ADD",
Color = color,
Texture = [[Interface\AddOns\Plater\images\ants_rectangle]],
}
--> ants
local f = DF:CreateAnts (frame, antTable, -27 + (left or 0), 25 + (right or 0), 5 + (top or 0), -7 + (bottom or 0))
f:SetFrameLevel (frame:GetFrameLevel() + 1)
f:SetAlpha (ALPHA_BLEND_AMOUNT - 0.249845)
--> glow
local glow = f:CreateTexture (nil, "background")
glow:SetTexture ([[Interface\AddOns\Plater\images\nameplate_glow]])
PixelUtil.SetPoint (glow, "center", frame, "center", 0, 0)
glow:SetSize (frame:GetWidth() + frame:GetWidth()/2.3, 36)
glow:SetBlendMode ("ADD")
glow:SetVertexColor (DF:ParseColors (color or "white"))
glow:SetAlpha (ALPHA_BLEND_AMOUNT)
glow.GlowTexture = glow
return f
end
function Plater.OnPlayCustomFlashAnimation (animationHub)
animationHub:GetParent():Show()
animationHub.Texture:Show()
--animationHub.Texture:Show()
end
function Plater.OnStopCustomFlashAnimation (animationHub)
animationHub:GetParent():Hide()
animationHub.Texture:Hide()
end
function Plater.UpdateCustomFlashAnimation (animationHub, duration, r, g, b)
for i = 1, #animationHub.AllAnimations do
if (duration) then
animationHub.AllAnimations [i]:SetDuration (duration)
end
if (r) then
r, g, b = DF:ParseColors (r, g, b)
animationHub.Texture:SetColorTexture (r, g, b)
end
end
end
--creates a flash, call returnedValue:Play() to flash
function Plater.CreateFlash (frame, duration, amount, r, g, b, a)
--defaults
duration = duration or 0.25
amount = amount or 1
if (not r) then
r, g, b, a = 1, 1, 1, 1
else
r, g, b, a = DF:ParseColors (r, g, b, a)
end
--create the flash frame
local f = CreateFrame ("frame", "PlaterFlashAnimationFrame".. math.random (1, 100000000), frame, BackdropTemplateMixin and "BackdropTemplate")
f:SetFrameLevel (frame:GetFrameLevel()+1)
f:SetAllPoints()
f:Hide()
--create the flash texture
local t = f:CreateTexture ("PlaterFlashAnimationTexture".. math.random (1, 100000000), "artwork")
t:SetColorTexture (r, g, b, a)
t:SetAllPoints()
t:SetBlendMode ("ADD")
t:Hide()
--create the flash animation
local animationHub = DF:CreateAnimationHub (f, Plater.OnPlayCustomFlashAnimation, Plater.OnStopCustomFlashAnimation)
animationHub.AllAnimations = {}
animationHub.Parent = f
animationHub.Texture = t
animationHub.Amount = amount
animationHub.UpdateDurationAndColor = Plater.UpdateCustomFlashAnimation
for i = 1, amount * 2, 2 do
local fadeIn = DF:CreateAnimation (animationHub, "ALPHA", i, duration, 0, 1)
local fadeOut = DF:CreateAnimation (animationHub, "ALPHA", i + 1, duration, 1, 0)
tinsert (animationHub.AllAnimations, fadeIn)
tinsert (animationHub.AllAnimations, fadeOut)
end
return animationHub
end
--called to undo a color modification
function Plater.RefreshNameplateColor (unitFrame)
if (unitFrame.unit) then
if (Plater.IsUnitTapDenied (unitFrame.unit)) then
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (Plater.db.profile.tap_denied_color))
elseif (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID] or -1]) then
Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, unpack (DB_UNITCOLOR_CACHE [unitFrame [MEMBER_NPCID]]))
unitFrame.UsingCustomColor = true --exposed to scripts
else
if (InCombatLockdown()) then
local unitReaction = unitFrame.PlateFrame [MEMBER_REACTION]
if (unitReaction == 4 and not unitFrame.InCombat) then
Plater.FindAndSetNameplateColor (unitFrame, true)
elseif (DB_AGGRO_CHANGE_HEALTHBAR_COLOR and unitFrame.CanCheckAggro and unitReaction <= 4) then
Plater.UpdateNameplateThread (unitFrame)
else
Plater.FindAndSetNameplateColor (unitFrame)
end
else
Plater.FindAndSetNameplateColor (unitFrame)
end
end
end
end
--modify the color of the health bar
function Plater.SetNameplateColor (unitFrame, r, g, b, a)
if (unitFrame.unit) then
if (not r) then
Plater.RefreshNameplateColor (unitFrame)
else
r, g, b, a = DF:ParseColors (r, g, b, a)
return Plater.ChangeHealthBarColor_Internal (unitFrame.healthBar, r, g, b, a)
end
end
end
--set a temporarly size for the healthbar
--this value is reset when the nameplate is added to the screen
function Plater.SetNameplateSize (unitFrame, width, height)
unitFrame.customHealthBarWidth = width
unitFrame.customHealthBarHeight = height
Plater.UpdatePlateSize (unitFrame.PlateFrame)
end
--modify the color of the cast bar
function Plater.SetCastBarColor (unitFrame, r, g, b)
if (unitFrame.unit) then
if (not r) then
--refresh the castbar color (the framework adjust the cast bar color)
unitFrame.castBar:UpdateCastColor()
else
--framework accepts SetColor, it does parse the color within the call
return unitFrame.castBar:SetColor (r, g, b)
end
end
end
--set a temporarly size for the castbar
--this value is reset when the nameplate is added to the screen
function Plater.SetCastBarSize (unitFrame, width, height)
unitFrame.customCastBarWidth = width
unitFrame.customCastBarHeight = height
Plater.UpdatePlateSize (unitFrame.PlateFrame)
end
--same thing as the two above but for the power bar
function Plater.SetPowerBarSize (unitFrame, width, height)
unitFrame.customPowerBarWidth = width
unitFrame.customPowerBarHeight = height
Plater.UpdatePlateSize (unitFrame.PlateFrame)
end
---changes the border color, this call is for the API, can be called from external sources
---@param self table unitFrame
---@param r any
---@param g number|nil
---@param b number|nil
---@param a number|nil
function Plater.SetBorderColor(self, r, g, b, a)
if (not r) then
self.customBorderColor = nil
Plater.UpdateBorderColor(self)
return
end
r, g, b, a = DF:ParseColors(r, g, b, a)
--UpdateBorderColor will use the value set on customBorderColor member if any
self.customBorderColor = {r, g, b, a}
Plater.UpdateBorderColor(self)
end
---flashes on the health bar border
---@param unitFrame table unitFrame
---@param duration number
function Plater.FlashNameplateBorder(unitFrame, duration)
if (not unitFrame.healthBar.PlayHealthFlash) then
Plater.CreateHealthFlashFrame(unitFrame.PlateFrame)
end
unitFrame.healthBar.canHealthFlash = true
unitFrame.healthBar.PlayHealthFlash(duration)
end
---flashes the unitFrame body showing a text in the middle of the flash texture, by default this call is use to show aggro alerts with the word "-AGGRO-"
---@param unitFrame table unitFrame
---@param text string
---@param duration number
function Plater.FlashNameplateBody(unitFrame, text, duration)
--sending true to ignore cooldown
unitFrame.PlateFrame.PlayBodyFlash(text, duration, true) --weird, there's no reference to the plateFrame
end
---return if the player is in combat
---@return boolean bIsPlayerInCombat
function Plater.IsInCombat()
return InCombatLockdown() or PLAYER_IN_COMBAT
end
---return true if the unit is in the tank role
---@param unitFrame table unitFrame
---@return boolean bIsUnitInTankRole
function Plater.IsUnitTank(unitFrame)
return TANK_CACHE[unitFrame.unitNameInternal]
end
---check the player role and role specialization and return if it is in the tank role
---@return boolean bIsPlayerInTankRole
function Plater.IsPlayerTank()
return IsPlayerEffectivelyTank()
end
---return the table where tanks is stored
---has the unit name as the key and true as value
---@return table<string, boolean> tankList
function Plater.GetTanks()
return TANK_CACHE
end
--change the color of the cast bar
function Plater.SetCastBarBorderColor (castBar, r, g, b, a)
--check if the frame passed was the unitFrame instead of the castbar it self
if (castBar.castBar) then
castBar = castBar.castBar
end
if (not r) then
castBar.FrameOverlay:SetBackdropBorderColor (0, 0, 0, 0)
return
end
r, g, b, a = DF:ParseColors (r, g, b, a)
castBar.FrameOverlay:SetBackdropBorderColor (r, g, b, a)
end
--show the health bar, the health bar is shown by default, use to undo HideHealthBar() call
function Plater.ShowHealthBar (unitFrame)
unitFrame.healthBar:Show()
unitFrame.BuffFrame:Show()
unitFrame.BuffFrame2:Show()
unitFrame.ExtraIconFrame:Show()
unitFrame.healthBar.unitName:Show()
unitFrame.PlateFrame.IsFriendlyPlayerWithoutHealthBar = false
unitFrame.PlateFrame.IsNpcWithoutHealthBar = false
unitFrame.ActorNameSpecial:Hide()
unitFrame.ActorTitleSpecial:Hide()
Plater.UpdatePlateText (unitFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], true)
end
--hide the health bar and show the secondary unit name and title text strings
function Plater.HideHealthBar (unitFrame, showPlayerName, showNameNpc)
unitFrame.healthBar:Hide()
unitFrame.BuffFrame:Hide()
unitFrame.BuffFrame2:Hide()
unitFrame.ExtraIconFrame:Hide()
unitFrame.healthBar.unitName:Hide()
showPlayerName = showPlayerName and (unitFrame.ActorType == ACTORTYPE_FRIENDLY_PLAYER or unitFrame.ActorType == ACTORTYPE_ENEMY_PLAYER)
showNameNpc = showNameNpc and (unitFrame.ActorType == ACTORTYPE_FRIENDLY_NPC or unitFrame.ActorType == ACTORTYPE_ENEMY_NPC)
unitFrame.PlateFrame.IsFriendlyPlayerWithoutHealthBar = showPlayerName
unitFrame.PlateFrame.IsNpcWithoutHealthBar = showNameNpc
if (showPlayerName) then
Plater.UpdatePlateText (unitFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], true)
elseif (showNameNpc) then
Plater.UpdatePlateText (unitFrame.PlateFrame, DB_PLATE_CONFIG [unitFrame.ActorType], true)
end
end
--forces a range check regardless of the user options and only changes the member_range flag, no alpha changes
--if the spell name is passed, it just return the result without modifying the nameplate attributes
function Plater.NameplateInRange (unitFrame, spellName)
if (spellName) then
return IsSpellInRange (spellName, unitFrame [MEMBER_UNITID]) == 1
else
local rangeChecker
if unitFrame [MEMBER_REACTION] < 5 then
rangeChecker = Plater.RangeCheckFunctionEnemy
else
rangeChecker = Plater.RangeCheckFunctionFriendly
end
if (rangeChecker and rangeChecker (unitFrame [MEMBER_UNITID])) then
unitFrame [MEMBER_RANGE] = true
return true
else
unitFrame [MEMBER_RANGE] = false
return false
end
end
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> scripting ~scripting
--the mod/script error handler
local errorContext = {}
local prevErrors = {}
local ErrorHandler = function(errorMessage)
errorContext.message = errorContext.message or "Plater error: "
local msg = errorContext.message .. errorMessage
--throttle error messages a bit...
local lastTime = prevErrors[msg]
local curTime = GetTime()
if lastTime and curTime - lastTime < 1 then
return
end
prevErrors[msg] = curTime
DevTool:AddData(errorContext)
local modscriptInfo = errorContext.modscript and errorContext.modscript.url and ("Mod/Script URL: " .. errorContext.modscript.url .. "\n") or nil
if BugGrabber then
geterrorhandler()(errorContext.message .. "\n" .. (modscriptInfo or "") .. Plater.fullVersionInfo .. "\n" .. errorMessage)
end
Plater:Msg (msg .. (modscriptInfo and ("\n" .. modscriptInfo) or ""))
errorContext = {}
return errorMessage
end
local GetErrorHandler = function(contextMessage, contextModScript)
errorContext.message = contextMessage
errorContext.modscript = contextModScript
return ErrorHandler
end
platerInternal.GetErrorHandler = GetErrorHandler
--scripts mixin - these functions are mixed in with castbar, unitframe and aura icons
Plater.ScriptMetaFunctions = {
--get the table which stores all script information for the widget
--self is the affected widget, e.g. icon frame, unitframe, castbar progressbar
ScriptGetContainer = function(self)
local infoTable = self.ScriptInfoTable
if (not infoTable) then
self.ScriptInfoTable = {}
return self.ScriptInfoTable
else
return infoTable
end
end,
--get the table which stores the information for a single script
--run for hooks only
HookGetInfo = function (self, globalScriptObject, scriptContainer)
scriptContainer = scriptContainer or self:ScriptGetContainer()
--using the memory address of the original scriptObject from db.profile as the map key
local scriptInfo = scriptContainer[globalScriptObject.DBScriptObject.scriptId]
if (
(not scriptInfo) or
(scriptInfo.GlobalScriptObject.NeedHotReload) or
(scriptInfo.GlobalScriptObject.Build and scriptInfo.GlobalScriptObject.Build < PLATER_HOOK_BUILD)
) then
local forceHotReload = scriptInfo and scriptInfo.GlobalScriptObject.NeedHotReload
--keep script info and update as needed
scriptInfo = scriptInfo or {
GlobalScriptObject = globalScriptObject,
HotReload = -1,
Env = {},
IsActive = false
}
scriptInfo.GlobalScriptObject = globalScriptObject
scriptInfo.GlobalScriptObject.Build = PLATER_HOOK_BUILD
scriptInfo.GlobalScriptObject.NeedHotReload = false
if (globalScriptObject.HasConstructor and (not scriptInfo.Initialized or forceHotReload)) then
local modName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Mod-RunHooks", modName, "Constructor")
local okay, errortext = xpcall (globalScriptObject.Constructor, GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r Constructor error: ", globalScriptObject.DBScriptObject), self, self.displayedUnit or self.unit or self:GetParent()[MEMBER_UNITID], self, scriptInfo.Env, PLATER_GLOBAL_MOD_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
Plater.EndLogPerformance("Mod-RunHooks", modName, "Constructor")
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r Constructor error: " .. errortext)
end
scriptInfo.Initialized = true
end
scriptContainer [globalScriptObject.DBScriptObject.scriptId] = scriptInfo
end
--always overwriting the globalScriptObject fixes the issue for not updating the script after saving it but only for OnShow OnUpdate and OnHide
scriptInfo.GlobalScriptObject = globalScriptObject
return scriptInfo
end,
--get the table which stores the information for a single script
--run for scripts only
ScriptGetInfo = function (self, globalScriptObject, scriptContainer)
scriptContainer = scriptContainer or self:ScriptGetContainer()
--using the memory address of the original scriptObject from db.profile as the map key
local scriptInfo = scriptContainer[globalScriptObject.DBScriptObject.scriptId]
local lastUpdateTime = globalScriptObject.LastUpdateTime or 0
if (not scriptInfo or scriptInfo.LastUpdateTime <= lastUpdateTime) then
--create script info
if (not scriptInfo) then
scriptInfo = {
--GlobalScriptObject = globalScriptObject, --is set below
HotReload = -1, --deprecated
Env = {},
IsActive = false
}
end
scriptInfo.LastUpdateTime = GetTime()
--scriptInfo.GlobalScriptObject = globalScriptObject
scriptContainer [globalScriptObject.DBScriptObject.scriptId] = scriptInfo
end
--always overwriting the globalScriptObject fixes the issue for not updating the script after saving it but only for OnShow OnUpdate and OnHide
scriptInfo.GlobalScriptObject = globalScriptObject
return scriptInfo
end,
--if the global script had an update or if the first time running this script on this widget, run the constructor
ScriptHotReload = function (self, scriptInfo)
--dispatch constructor if necessary
if (scriptInfo.HotReload < scriptInfo.GlobalScriptObject.HotReload) then
--update the hotreload state
scriptInfo.HotReload = scriptInfo.GlobalScriptObject.HotReload
--there's some bug with the global env becoming nil after saving a script
--print(scriptInfo.GlobalScriptObject.DBScriptObject.Name, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
--dispatch the constructor
local unitFrame = self.unitFrame or self
local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Scripts", scriptName, "Constructor")
local okay, errortext = xpcall (scriptInfo.GlobalScriptObject ["ConstructorCode"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r Constructor error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
Plater.EndLogPerformance("Scripts", scriptName, "Constructor")
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r Constructor error: " .. errortext)
end
end
end,
--run the update script, called when the castbar updates, from within the tick and from the aura file on the AddAura()
ScriptRunOnUpdate = function (self, scriptInfo)
if (not scriptInfo.IsActive) then
--run constructor
self:ScriptHotReload (scriptInfo)
--run on show
self:ScriptRunOnShow (scriptInfo)
end
--dispatch the runtime script
local unitFrame = self.unitFrame or self
local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Scripts", scriptName, "OnUpdate")
local okay, errortext = xpcall (scriptInfo.GlobalScriptObject ["UpdateCode"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r OnUpdate error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
Plater.EndLogPerformance("Scripts", scriptName, "OnUpdate")
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r OnUpdate error: " .. errortext)
end
end,
--run the OnShow script
ScriptRunOnShow = function(self, scriptInfo)
--dispatch the on show script
local unitFrame = self.unitFrame or self
scriptInfo.Env._DefaultWidth = self:GetWidth()
scriptInfo.Env._DefaultHeight = self:GetHeight()
local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Scripts", scriptName, "OnShow")
local func = scriptInfo.GlobalScriptObject["OnShowCode"]
local okay, errortext = xpcall(func, GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r OnShow error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
Plater.EndLogPerformance("Scripts", scriptName, "OnShow")
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r OnShow error: " .. errortext)
end
scriptInfo.IsActive = true
self.ScriptKey = scriptInfo.GlobalScriptObject.ScriptKey
end,
--run the OnHide script
ScriptRunOnHide = function (self, scriptInfo)
--dispatch the on hide script
local unitFrame = self.unitFrame or self
local scriptName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Scripts", scriptName, "OnHide")
local okay, errortext = xpcall (scriptInfo.GlobalScriptObject ["OnHideCode"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r OnHide error: ", scriptInfo.GlobalScriptObject.DBScriptObject), self, unitFrame.displayedUnit or unitFrame.unit or unitFrame.PlateFrame[MEMBER_UNITID], unitFrame, scriptInfo.Env, PLATER_GLOBAL_SCRIPT_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
Plater.EndLogPerformance("Scripts", scriptName, "OnHide")
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r OnHide error: " .. errortext)
end
scriptInfo.IsActive = false
self.ScriptKey = nil
end,
--run the Initialization script, called during compile time
ScriptRunInitialization = function (globalScriptObject)
--dispatch the init script
local scriptName = globalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Scripts", scriptName, "Initialization")
local okay, errortext = xpcall (globalScriptObject ["Initialization"], GetErrorHandler("Plater Script |cFFAAAA22" .. scriptName .. "|r Initialization error: ", globalScriptObject.DBScriptObject), PLATER_GLOBAL_SCRIPT_ENV [globalScriptObject.DBScriptObject.scriptId])
Plater.EndLogPerformance("Scripts", scriptName, "Initialization")
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Script |cFFAAAA22" .. scriptName .. "|r Initialization error: " .. errortext)
end
end,
ScriptRunCommMessageHook = function(globalScriptObject, hookName, source, ...)
local modName = globalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Mod-RunHooks", modName, hookName)
local okay, errortext = xpcall (globalScriptObject [hookName], GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: ", globalScriptObject.DBScriptObject), PLATER_GLOBAL_MOD_ENV [globalScriptObject.DBScriptObject.scriptId], source, ...)
Plater.EndLogPerformance("Mod-RunHooks", modName, hookName)
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: " .. errortext)
end
end,
ScriptRunHook = function (self, scriptInfo, hookName, frame, ...)
--dispatch a hook for the script
--at the moment, self is always the unit frame
local modName = scriptInfo.GlobalScriptObject.DBScriptObject.Name
Plater.StartLogPerformance("Mod-RunHooks", modName, hookName)
local okay, errortext = xpcall (scriptInfo.GlobalScriptObject [hookName], GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: ", scriptInfo.GlobalScriptObject.DBScriptObject), frame or self, self.displayedUnit, self, scriptInfo.Env, PLATER_GLOBAL_MOD_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId], ...)
Plater.EndLogPerformance("Mod-RunHooks", modName, hookName)
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: " .. errortext)
end
end,
--run only once without attach to the script or hook
ScriptRunNoAttach = function (hookInfo, hookName)
local func = hookInfo [hookName]
local modName = hookInfo.DBScriptObject.Name
Plater.StartLogPerformance("Mod-RunHooks", modName, " -NoAttach- " .. hookName)
local okay, errortext = xpcall (func, GetErrorHandler("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: ", hookInfo.DBScriptObject), PLATER_GLOBAL_MOD_ENV [hookInfo.DBScriptObject.scriptId])
Plater.EndLogPerformance("Mod-RunHooks", modName, " -NoAttach- " .. hookName)
if (not okay) then
--handled via error handler
--Plater:Msg ("Plater Mod |cFFAAAA22" .. modName .. "|r code for |cFFBB8800" .. hookName .. "|r error: " .. errortext)
end
end,
--run when the widget hides
OnHideWidget = function(self)
--check if can quickly quit (if there's no script container for the nameplate)
if (self.ScriptInfoTable) then
local triggerCacheTable
if (self.IsAuraIcon) then
triggerCacheTable = SCRIPT_AURA_TRIGGER_CACHE
elseif (self.IsCastBar) then
triggerCacheTable = SCRIPT_CASTBAR_TRIGGER_CACHE
elseif (self.IsUnitNameplate) then
triggerCacheTable = SCRIPT_UNIT_TRIGGER_CACHE
end
--ScriptKey holds the trigger of the script currently running
local globalScriptObject = triggerCacheTable[self.ScriptKey]
--does the aura has a custom script?
if (globalScriptObject) then
--does the aura icon has a table with script information?
local scriptContainer = self:ScriptGetContainer() --return self.ScriptInfoTable
if (scriptContainer) then
local scriptInfo = self:ScriptGetInfo(globalScriptObject, scriptContainer)
if (scriptInfo and scriptInfo.IsActive) then
self:ScriptRunOnHide(scriptInfo)
end
end
end
end
--> hooks, check which kind of widget this is and then run the appropriate hook
if (self.IsCastBar) then
if (HOOK_CAST_STOP.ScriptAmount > 0) then
for i = 1, HOOK_CAST_STOP.ScriptAmount do
local globalScriptObject = HOOK_CAST_STOP [i]
local unitFrame = self.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Cast Stop")
--run
unitFrame:ScriptRunHook (scriptInfo, "Cast Stop", self)
end
end
end
end,
--stop a running script by the trigger ID
--this is used when deleting a script or disabling it
KillScript = function(self, triggerID)
local triggerCacheTable
if (self.IsAuraIcon) then
triggerCacheTable = SCRIPT_AURA_TRIGGER_CACHE
triggerID = GetSpellInfo(triggerID)
elseif (self.IsCastBar) then
triggerCacheTable = SCRIPT_CASTBAR_TRIGGER_CACHE
triggerID = GetSpellInfo(triggerID)
elseif (self.IsUnitNameplate) then
triggerCacheTable = SCRIPT_UNIT_TRIGGER_CACHE
end
if (self.ScriptKey and self.ScriptKey == triggerID) then
local globalScriptObject = triggerCacheTable[triggerID]
if (globalScriptObject) then
local scriptContainer = self:ScriptGetContainer()
if (scriptContainer) then
local scriptInfo = self:ScriptGetInfo (globalScriptObject, scriptContainer)
if (scriptInfo and scriptInfo.IsActive) then
self:ScriptRunOnHide (scriptInfo)
end
end
end
end
end,
IsProtected = function (self)
-- assume that nameplates are always protected since 8.2
if self then
if self.PlateFrame then
return self.PlateFrame:IsProtected()
end
end
return false
end,
}
function Plater.GetAllScripts (scriptType)
if (scriptType == "script") then
return Plater.db.profile.script_data
elseif (scriptType == "hook") then
return Plater.db.profile.hook_data
end
end
function Plater.GetAllScriptsAsPrioSortedCopy (scriptType)
local scripts
local seeds = {}
local function copyHookTables (t1, t2)
for key, value in pairs (t2) do
if (key ~= "__index") then
if (type (value) == "table") then
t1 [key] = t1 [key] or {}
-- add hashID to the hook-data
t1 [key].scriptId = tostring(value) -- keep this internal hashed
--create UID if it does not exist --TODO maybe not needed in the future
local uID = value.UID
if not uID then
local seed = tonumber(value.Time) or time()
while seeds[seed] do
seed = math.random(value.Time)
end
seeds[seed] = true
uID = Plater.CreateUniqueIdentifier(seed)
--value.UID = uID -- TODO permanently set UID
t1 [key].UID = uID -- TODO temporary volatile for now
end
DF.table.copy (t1 [key], t2 [key])
else
t1 [key] = value
end
end
end
return t1
end
if (scriptType == "script") then
--cleanup first
for scriptId, scriptObject in ipairs (Plater.db.profile.script_data) do
scriptObject.scriptId = nil
end
--copy
scripts = copyHookTables({}, Plater.db.profile.script_data)
elseif (scriptType == "hook") then
--cleanup first
for scriptId, scriptObject in ipairs (Plater.db.profile.hook_data) do
scriptObject.scriptId = nil
end
--copy
scripts = copyHookTables({}, Plater.db.profile.hook_data)
end
local function round(x)
if not x then return nil end
return (x + 0.5 - (x + 0.5) % 1)
end
if scripts then
table.sort(scripts, function(a,b)
if a and not b then
return false
elseif not a and b then
return true
else
return round(a.Prio or 99) > round(b.Prio or 99)
end
return false
end)
end
--table.foreach(scripts, function(index, self) if self then print(self.Prio, self.Name) else print("not self") end end)
return scripts
end
--compile all scripts
function Plater.CompileAllScripts (scriptType, noHotReload)
if (scriptType == "script") then
for scriptId, scriptObject in ipairs (Plater.GetAllScriptsAsPrioSortedCopy ("script")) do
if (scriptObject.Enabled) then
Plater.CompileScript (scriptObject, noHotReload)
end
end
elseif (scriptType == "hook") then
--get all hook scripts from the profile database
for scriptId, scriptObject in ipairs (Plater.GetAllScriptsAsPrioSortedCopy ("hook")) do
Plater.CompileHook (scriptObject, noHotReload)
end
end
end
--recompile a single scriptObject deleting the global environment
function Plater.RecompileScript(scriptObject)
if not scriptObject.scriptId then
scriptObject.scriptId = tostring(scriptObject)
end
local scriptType = Plater.GetScriptType(scriptObject)
if (scriptType == "script") then
Plater.CompileScript(scriptObject)
elseif (scriptType == "hook") then
Plater.CompileHook(scriptObject)
end
end
function Plater.IsModEnabled(modName)
for scriptId, scriptObject in ipairs (Plater.db.profile.hook_data) do
if (scriptObject.Name == modName) then
if (scriptObject.Enabled) then
return true
end
end
end
end
--when a script object get disabled, need to clear all compiled scripts in the cache and recompile than again
--this other scripts that uses the same trigger name get activated
-- ~scripts
Plater.CoreVersion = 1
--internal Plater functions
--in the list/false: can't be overwritten; true->can't be read at all
local privateFunctions = {
["Plater"] = {
["CompileAllScripts"] = true,
["GetAllScripts"] = true,
["GetAllScriptsAsPrioSortedCopy"] = true,
["ScriptMetaFunctions"] = true,
["DecompressData"] = true,
["CompressData"] = true,
["WipeAndRecompileAllScripts"] = true,
["AllHookGlobalContainers"] = true,
["WipeHookContainers"] = true,
["GetContainerForHook"] = true,
["CurrentlyLoadedHooks"] = true,
["RunDestructorForHook"] = true,
["CompileHook"] = true,
["CompileScript"] = true,
["CheckScriptTriggerOverlap"] = true,
["GetScriptObject"] = true,
["GetScriptDB"] = true,
["GetScriptType"] = true,
["GetDecodedScriptType"] = true,
["ImportScriptsFromLibrary"] = true,
["ImportScriptString"] = true,
["AddScript"] = true,
["BuildScriptObjectFromIndexTable"] = true,
["DecodeImportedString"] = true,
["PrepareTableToExport"] = true,
["ScriptReceivedFromGroup"] = true,
["ExportScriptToGroup"] = true,
["ShowImportScriptConfirmation"] = true,
["DispatchTalentUpdateHookEvent "] = true,
["ScheduleHookForCombat"] = true,
["ScheduleRunFunctionForEvent"] = true,
["RunFunctionForEvent"] = true,
["EventHandler"] = true,
["RegisterRefreshDBCallback"] = true,
["FireRefreshDBCallback"] = true,
["RefreshDBUpvalues"] = false,
["RefreshDBLists"] = false,
["UpdateAuraCache"] = false,
["ApplyPatches"] = true,
["RefreshConfig"] = true,
["RefreshConfigProfileChanged"] = true,
["SaveConsoleVariables"] = true,
["GetSettings"] = true,
["CodeTypeNames"] = true,
["HookScripts"] = true,
["HookScriptsDesc"] = true,
["IncreaseHookBuildID"] = true,
["IncreaseRefreshID"] = true,
["IncreaseRefreshID_Auras"] = true,
["SpecList"] = true,
["UpdateSettingsCache"] = true,
["ActorTypeSettingsCache"] = true,
["RunScheduledUpdate"] = true,
["ScheduleUpdateForNameplate"] = true,
["EventHandlerFrame"] = true,
["OnInit"] = true,
["HookLoadCallback"] = true,
["CheckFirstRun"] = true,
["CommHandler"] = true,
["CommReceived"] = true,
["GetAllShownPlates"] = false,
["GetHashKey"] = false,
["IsShowingResourcesOnTarget"] = false,
["OnRetailNamePlateShow"] = true,
["UpdateSelfPlate"] = true,
["CastBarOnShow_Hook"] = true,
["CastBarOnEvent_Hook"] = true,
["CastBarOnTick_Hook"] = true,
["RefreshAuras"] = true,
["CreateAuraIcon"] = true,
["RefreshColorOverride"] = true,
["ChangeHealthBarColor_Internal"] = true,
["UpdateAllPlates"] = true,
["FullRefreshAllPlates"] = true,
["UpdatePlateClickSpace"] = true,
["SetNamePlatePreferredClickInsets"] = true,
["EveryFrameFPSCheck"] = true,
["NameplateTick"] = true,
["OnPlayerTargetChanged"] = true,
["UpdateTarget"] = true,
["UpdateSoftInteractTarget"] = true,
["UpdatePlateText"] = true,
["CheckLifePercentText"] = true,
["UpdateAllNames"] = true,
--["UpdateLevelTextAndColor"] = true,
["UpdatePlateFrame"] = true,
["ForceChangeBorderColor"] = true,
["UpdatePlateBorders"] = true,
["UpdateRaidMarkersOnAllNameplates"] = true,
["RefreshAutoToggle"] = true,
["HasRefreshAutoToggleScheduled"] = true,
["ParseHealthSettingForPlayer"] = true,
["CreateAlphaAnimation"] = true,
["CreateHighlightNameplate"] = true,
["CreateHealthFlashFrame"] = true,
["CreateAggroFlashFrame"] = true,
["CreateScaleAnimation"] = true,
["DoNameplateAnimation"] = true,
["RefreshIsEditingAnimations"] = true,
["IsNpcInIgnoreList"] = true,
["CanChangePlateSize"] = true,
["RefreshOmniCCGroup"] = true,
["CreatePlaterButtonAtInterfaceOptions"] = true,
["SetCVarsOnFirstRun"] = true,
["GetActorSubName"] = false,
["QuestLogUpdated"] = true,
["GetNpcIDFromGUID"] = false,
["GetNpcID"] = false,
["ForceTickOnAllNameplates"] = true,
["UpdateUIParentScale"] = true,
["SetNameplateScale"] = false,
["UpdateUIParentLevels"] = true,
["UpdateUIParentTargetLevels"] = true,
["RefreshTankCache"] = true,
["ToggleThreatColorMode"] = false,
["ForceFindPetOwner"] = true,
["UpdateBgPlayerRoleCache"] = false,
["GetSpecIconForUnitFromBG"] = false,
["GetUnitBGInfo"] = false,
["GetSpecIcon"] = false,
["InitLDB"] = true,
["APIList"] = true,
["FrameworkList"] = true,
["UnitFrameMembers"] = true,
["NameplateComponents"] = true,
["UpdateOptionsTabUpdateState"] = true,
["EnableProfiling"] = false,
["DisableProfiling"] = false,
["StartLogPerformance"] = false,
["EndLogPerformance"] = false,
["StartLogPerformanceCore"] = false,
["StartLogPerformanceCore"] = false,
["EndLogPerformanceCore"] = false,
["DumpPerformance"] = true,
["ShowPerfData"] = true,
["StoreEventLogData"] = true,
["CheckOptionsTab"] = true,
["OpenOptionsPanel"] = true,
["TriggerDefaultMembers"] = true,
["OpenCopyUrlDialog"] = true,
["CreateOptionTableForScriptObject"] = true,
["HasWagoUpdate"] = true,
["GetWagoUpdateDataFromCompanion"] = true,
["CheckWagoUpdates"] = true,
["AddCompanionData"] = true,
["CompanionDataSlugs"] = true,
["GetVersionInfo"] = false,
["versionString"] = false,
["fullVersionInfo"] = false,
["DispatchCommReceivedMessageHookEvent"] = true,
["DispatchCommSendMessageHookEvents"] = true,
["VerifyScriptIdForComm"] = true,
["MessageReceivedFromScript"] = true,
["CreateUniqueIdentifier"] = false,
["GetScriptFromUID"] = true,
["SendCommMessage"] = true,
["CreateCommHeader"] = true,
["SendComm"] = false,
["FPSData"] = {
["startTime"] = true,
["frames"] = true,
["platesUpdatedThisFrame"] = true,
["platesToUpdatePerFrame"] = true,
["curFPS"] = false,
},
["UnitReaction"] = {
["UNITREACTION_HOSTILE"] = false,
["UNITREACTION_NEUTRAL"] = false,
["UNITREACTION_FRIENDLY"] = false,
},
["Export_NpcColors"] = true,
["Export_CastColors"] = true,
["ScriptAura"] = true,
["ScriptCastBar"] = true,
["ScriptUnit"] = true,
["RemoveFromAuraUpdate"] = true,
["AddToAuraUpdate"] = true,
["AnchorSides"] = false,
["SetAnchor"] = false,
["RunScriptTriggersForAuraIcons"] = true,
["AddAura"] = true,
["GetAuraIcon"] = true,
["HideNonUsedAuraIcons"] = true,
["AddExtraIcon"] = true,
["ResetAuraContainer"] = true,
["TrackSpecificAuras"] = true,
["UpdateAuras_Manual"] = true,
["UpdateAuras_Automatic"] = true,
["UpdateAuras_Self_Automatic"] = true,
["GetUnitAuras"] = false,
["GetUnitAurasForUnitID"] = false,
["PerformanceUnits"] = true,
["ForceBlizzardNameplateUnits"] = true,
["COMM_PLATER_PREFIX"] = true,
["COMM_SCRIPT_GROUP_EXPORTED"] = true,
["COMM_SCRIPT_MSG"] = true,
["Resources"] = {
["GetResourceWidgetCreationTable"] = true,
["GetCreateResourceWidgetFunctionForSpecId"] = true,
["RefreshResourcesDBUpvalues"] = true,
["CreateMainResourceFrame"] = true,
["UpdateResourceFrameToUse"] = true,
["GetMainResourceFrame"] = true,
["GetResourceBarInUse"] = true,
["EnableEvents"] = true,
["DisableEvents"] = true,
["HidePlaterResourceFrame"] = true,
["OnSpecChanged"] = true,
["CanUsePlaterResourceFrame"] = true,
["UpdateResourceFramePosition"] = true,
["UpdateMainResourceFrame"] = true,
["UpdateResourceBar"] = true,
["UpdateResourcesFor_HideDeplete"] = true,
["UpdateResourcesFor_ShowDepleted"] = true,
["UpdateResources_NoDepleted"] = true,
["UpdateResources_WithDepleted"] = true,
["GetRuneKeyBySpec"] = true,
["GetCDEdgeBySpec"] = true,
},
["UpdateBaseNameplateOptions"] = true,
["BossModsTimeBarDBM"] = true,
["BossModsTimeBarBW"] = true,
["BigWigs_BarCreated"] = true,
["UpdateBossModAuras"] = true,
["EnsureUpdateBossModAuras"] = true,
["CreateBossModAuraFrame"] = true,
["UpdateBossModAuraFrameSettings"] = true,
["RegisterBossModAuras"] = true,
["GetBossModsEventTimeLeft"] = false,
["GetAltCastBarAltId"] = false,
["ClearAltCastBar"] = false,
["SetAltCastBar"] = false,
["StopAltCastBar"] = false,
["GetBossTimer"] = false,
["RegisterBossModsBars"] = false,
["TranslateNPCCache"] = true,
},
["DetailsFramework"] = {
["SetEnvironment"] = true,
},
["WeakAuras"] = {
["Add"] = true,
["AddMany"] = true,
["Delete"] = true,
["NewAura"] = true,
},
["C_GuildInfo"] = {
["RemoveFromGuild"] = true,
},
--block mail, trades, action house, banks
["C_AuctionHouse"] = true,
["C_Bank"] = true,
["C_GuildBank"] = true,
["SetSendMailMoney"] = true,
["SendMail"] = true,
["SetTradeMoney"] = true,
["AddTradeMoney"] = true,
["PickupTradeMoney"] = true,
["PickupPlayerMoney"] = true,
["AcceptTrade"] = true,
--frames
["BankFrame"] = true,
["TradeFrame"] = true,
["GuildBankFrame"] = true,
["MailFrame"] = true,
["EnumerateFrames"] = true,
--block run code inside code
["RunScript"] = true,
["securecall"] = true,
["getfenv"] = true,
["getfenv"] = true,
["loadstring"] = true,
["pcall"] = true,
["xpcall"] = true,
["getglobal"] = true,
["setmetatable"] = true,
["DevTools_DumpCommand"] = true,
--avoid creating/running macros
["SetBindingMacro"] = true,
["CreateMacro"] = true,
["EditMacro"] = true,
["hash_SlashCmdList"] = true,
["SlashCmdList"] = true,
["MacroEditBox"] = true,
["ChatEdit_SendText"] = true,
["AreDangerousScriptsAllowed"] = true,
--block guild commands
["GuildDisband"] = true,
["GuildUninvite"] = true,
--other things
["C_GMTicketInfo"] = true,
--deny messing addons with script support
["PlaterDB"] = true,
["PlaterDBChr"] = true,
["_detalhes_global"] = true,
["WeakAurasSaved"] = true,
}
local overrideFunctions = {
["CreateFrame"] = function(frameType, name, parent, template, id)
if template then
template = string.gsub(template, "SecureActionButtonTemplate", "")
template = string.gsub(template, "SecureHandlerClickTemplate", "")
end
return CreateFrame(frameType, name, parent, template, id)
end,
}
--this allows full shadowing on 'Plater' global with the filter above
local function buildShadowTable(privateFunctionsTable, tableKey, shadowTable)
if not privateFunctionsTable then return end
shadowTable = shadowTable or {}
--ViragDevTool_AddData({privateFunctionsTable, tableKey, shadowTable}, "buildShadowTable")
local shadowValuesTable = {}
if tableKey then
shadowTable[tableKey] = {}
shadowTable = shadowTable[tableKey]
end
--ViragDevTool_AddData({tableKey, shadowValuesTable}, "buildShadowTable_tables")
for key, value in pairs(privateFunctionsTable) do
--ViragDevTool_AddData({key, value}, "buildShadowTable_ITER")
if type(value) == "table" then
buildShadowTable(value, key, shadowTable)
else
--ViragDevTool_AddData({key, value}, "buildShadowTable_ADD")
shadowValuesTable [key] = value
end
end
--ViragDevTool_AddData({shadowValuesTable, tableKey}, "buildShadowTable_SET")
setmetatable(shadowTable, {
__index = function (env, key)
--ViragDevTool_AddData({env, key, tableKey, tableKey and _G[tableKey] or _G}, "GET")
if key == "_G" then
return env
elseif overrideFunctions [key] then
return overrideFunctions [key]
elseif shadowValuesTable [key] then -- if true, don't return value
return nil
else
return rawget(tableKey and _G[tableKey] or _G, key)
end
end,
__newindex = function (t, k, v)
--ViragDevTool_AddData({t, k, v, tableKey, tableKey and _G[tableKey] or _G}, "SET")
if shadowValuesTable [k] ~= nil then -- if in the list: don't overwrite
error ("'" .. tableKey .. "." .. k .. "' is protected and may not be overwritten.")
else
rawset(tableKey and _G[tableKey] or _G, k, v)
end
end,
})
--ViragDevTool_AddData({shadowTable}, "buildShadowTable_return")
return shadowTable
end
local ShadowTable = nil
local getShadowTable = function()
if not ShadowTable then
ShadowTable = buildShadowTable(privateFunctions)
end
return ShadowTable
end
local platerModEnvironment = {} -- needed for DF:SetEnvironment to have a common mod/script environment in Plater
local platerModEnvironment2 = getShadowTable()
local function SetPlaterEnvironment(func)
setfenv(func, platerModEnvironment2)
end
function Plater.WipeAndRecompileAllScripts (scriptType, noHotReload)
if (scriptType == "script") then
Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - script")
table.wipe(SCRIPT_AURA_TRIGGER_CACHE)
table.wipe(SCRIPT_CASTBAR_TRIGGER_CACHE)
table.wipe(SCRIPT_UNIT_TRIGGER_CACHE)
Plater.CompileAllScripts (scriptType, noHotReload)
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - script")
elseif (scriptType == "hook") then
Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - hook")
Plater.WipeHookContainers (noHotReload)
Plater.CompileAllScripts (scriptType, noHotReload)
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "WipeAndRecompileAllScripts - hook")
end
end
Plater.AllHookGlobalContainers = {
HOOK_NAMEPLATE_CREATED,
HOOK_NAMEPLATE_ADDED,
HOOK_NAMEPLATE_REMOVED,
HOOK_NAMEPLATE_UPDATED,
HOOK_TARGET_CHANGED,
HOOK_CAST_START,
HOOK_CAST_UPDATE,
HOOK_CAST_STOP,
HOOK_RAID_TARGET,
HOOK_COMBAT_ENTER,
HOOK_COMBAT_LEAVE,
HOOK_NAMEPLATE_CONSTRUCTOR,
HOOK_PLAYER_POWER_UPDATE,
HOOK_PLAYER_TALENT_UPDATE,
HOOK_HEALTH_UPDATE,
HOOK_ZONE_CHANGED,
HOOK_UNITNAME_UPDATE,
HOOK_LOAD_SCREEN,
HOOK_PLAYER_LOGON,
HOOK_MOD_INITIALIZATION,
HOOK_COMM_RECEIVED_MESSAGE,
HOOK_COMM_SEND_MESSAGE,
HOOK_OPTION_CHANGED,
HOOK_MOD_OPTION_CHANGED,
HOOK_NAMEPLATE_DESTRUCTOR,
}
function Plater.WipeHookContainers (noHotReload)
Plater.IncreaseHookBuildID()
for _, container in ipairs (Plater.AllHookGlobalContainers) do
if (not noHotReload) then
for _, globalScriptObject in ipairs (container) do
globalScriptObject.NeedHotReload = true
end
end
table.wipe (container)
container.ScriptAmount = 0
end
end
function Plater.GetContainerForHook (hookName)
if (hookName == "Initialization") then
return HOOK_MOD_INITIALIZATION
elseif (hookName == "Deinitialization") then
return HOOK_MOD_DEINITIALIZATION
elseif (hookName == "Constructor") then
return HOOK_NAMEPLATE_CONSTRUCTOR
elseif (hookName == "Nameplate Created") then
return HOOK_NAMEPLATE_CREATED
elseif (hookName == "Nameplate Added") then
return HOOK_NAMEPLATE_ADDED
elseif (hookName == "Nameplate Removed") then
return HOOK_NAMEPLATE_REMOVED
elseif (hookName == "Nameplate Updated") then
return HOOK_NAMEPLATE_UPDATED
elseif (hookName == "Target Changed") then
return HOOK_TARGET_CHANGED
elseif (hookName == "Cast Start") then
return HOOK_CAST_START
elseif (hookName == "Cast Update") then
return HOOK_CAST_UPDATE
elseif (hookName == "Cast Stop") then
return HOOK_CAST_STOP
elseif (hookName == "Raid Target") then
return HOOK_RAID_TARGET
elseif (hookName == "Enter Combat") then
return HOOK_COMBAT_ENTER
elseif (hookName == "Leave Combat") then
return HOOK_COMBAT_LEAVE
elseif (hookName == "Player Power Update") then
return HOOK_PLAYER_POWER_UPDATE
elseif (hookName == "Player Talent Update") then
return HOOK_PLAYER_TALENT_UPDATE
elseif (hookName == "Health Update") then
return HOOK_HEALTH_UPDATE
elseif (hookName == "Zone Changed") then
return HOOK_ZONE_CHANGED
elseif (hookName == "Name Updated") then
return HOOK_UNITNAME_UPDATE
elseif (hookName == "Load Screen") then
return HOOK_LOAD_SCREEN
elseif (hookName == "Player Logon") then
return HOOK_PLAYER_LOGON
elseif (hookName == "Receive Comm Message") then
return HOOK_COMM_RECEIVED_MESSAGE
elseif (hookName == "Send Comm Message") then
return HOOK_COMM_SEND_MESSAGE
elseif (hookName == "Option Changed") then
return HOOK_OPTION_CHANGED
elseif (hookName == "Mod Option Changed") then
return HOOK_MOD_OPTION_CHANGED
elseif (hookName == "Destructor") then
return HOOK_NAMEPLATE_DESTRUCTOR
else
Plater:Msg ("Unknown hook: " .. (hookName or "Invalid Hook Name"))
end
end
--store the names of hooks that passed the filters
Plater.CurrentlyLoadedHooks = {}
function Plater.RunDestructorForHook (scriptObject)
--check if the script has a destructor script
if (scriptObject.Hooks ["Destructor"]) then
--load and compile the destructor code
local code = "return " .. scriptObject.Hooks ["Destructor"]
if IS_WOW_PROJECT_NOT_MAINLINE then
code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
end
local compiledScript, errortext = loadstring (code, "Destructor for " .. scriptObject.Name)
if (not compiledScript) then
Plater:Msg ("failed to compile destructor for script " .. scriptObject.Name .. ": " .. errortext)
else
--store the function to execute
--setfenv (compiledScript, functionFilter)
if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then -- legacy mode
DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
SetPlaterEnvironment(compiledScript)
end
local func = compiledScript()
--iterate among all nameplates
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if (plateFrame) then
--local globalScriptObject = HOOK_NAMEPLATE_DESTRUCTOR [scriptObject.scriptId]
--does not exist when mod is not loaded through load conditions or similar
local globalScriptObject = HOOK_NAMEPLATE_DESTRUCTOR [scriptObject.scriptId] or {
HotReload = -1,
DBScriptObject = scriptObject,
Build = PLATER_HOOK_BUILD,
}
local unitFrame = plateFrame.unitFrame
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Destructor")
local okay, errortext = xpcall (func, GetErrorHandler("Mod: |cFFAAAA22" .. scriptInfo.GlobalScriptObject.DBScriptObject.Name .. "|r " .. scriptObject.Name .. " error: ", scriptObject), unitFrame, unitFrame.displayedUnit, unitFrame, scriptInfo.Env, PLATER_GLOBAL_MOD_ENV [scriptInfo.GlobalScriptObject.DBScriptObject.scriptId])
if (not okay) then
--handled via error handler
--Plater:Msg ("Mod: |cFFAAAA22" .. scriptInfo.GlobalScriptObject.DBScriptObject.Name .. "|r " .. scriptObject.Name .. " error: " .. errortext)
end
end
end
end
end
end
function Plater.RunDeinitializationForHook (scriptObject)
--check if the script has a destructor script
if (scriptObject.Hooks ["Deinitialization"]) then
--load and compile the destructor code
local code = "return " .. scriptObject.Hooks ["Deinitialization"]
if IS_WOW_PROJECT_NOT_MAINLINE then
code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
end
local compiledScript, errortext = loadstring (code, "Deinitialization for " .. scriptObject.Name)
if (not compiledScript) then
Plater:Msg ("failed to compile Deinitialization for script " .. scriptObject.Name .. ": " .. errortext)
else
--store the function to execute
--setfenv (compiledScript, functionFilter)
if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then -- legacy mode
DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
SetPlaterEnvironment(compiledScript)
end
--does not exist when mod is not loaded through load conditions or similar
local globalScriptObject = HOOK_NAMEPLATE_DESTRUCTOR [scriptObject.scriptId] or {
HotReload = -1,
DBScriptObject = scriptObject,
Build = PLATER_HOOK_BUILD,
}
globalScriptObject ["Deinitialization"] = compiledScript()
Plater.ScriptMetaFunctions.ScriptRunNoAttach (globalScriptObject, "Deinitialization")
end
end
end
-- which option types should be copied to modTable.config?
local options_for_config_table = {
[1] = true, -- Color
[2] = true, -- Number
[3] = true, -- Text
[4] = true, -- Toggle
[5] = false, -- Label
[6] = false, -- Blank Line
[7] = true, -- List
[8] = true, -- Audio
}
--compile scripts from the Hooking tab
function Plater.CompileHook (scriptObject, noHotReload)
Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
--check if the script is valid and if is enabled
if (not scriptObject) then
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
if not scriptObject.scriptId then
scriptObject.scriptId = tostring(scriptObject)
end
--check if this hook is currently loaded
if (not scriptObject.Enabled) then
if (Plater.CurrentlyLoadedHooks [scriptObject.scriptId]) then
Plater.CurrentlyLoadedHooks [scriptObject.scriptId] = false
Plater.RunDestructorForHook (scriptObject)
Plater.RunDeinitializationForHook (scriptObject)
end
--clear env when disabling/disabled
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = nil
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
do --check integrity
if (not scriptObject.Name) then
Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
if (not scriptObject.LoadConditions) then
Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
if (
not scriptObject.LoadConditions.class or
not scriptObject.LoadConditions.spec or
not scriptObject.LoadConditions.race or
not scriptObject.LoadConditions.talent or
not scriptObject.LoadConditions.pvptalent or
not scriptObject.LoadConditions.group or
not scriptObject.LoadConditions.role or
not scriptObject.LoadConditions.affix or
not scriptObject.LoadConditions.encounter_ids or
not scriptObject.LoadConditions.map_ids
) then
Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
if (not scriptObject.Hooks) then
Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
end
--check if can load this hook
if (not DF:PassLoadFilters (scriptObject.LoadConditions, Plater.EncounterID)) then
--check if this hook is currently loaded
if (Plater.CurrentlyLoadedHooks [scriptObject.scriptId]) then
Plater.CurrentlyLoadedHooks [scriptObject.scriptId] = false
Plater.RunDestructorForHook (scriptObject)
Plater.RunDeinitializationForHook (scriptObject)
end
if not noHotReload then
--clear env if needed
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = nil
end
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
else
Plater.CurrentlyLoadedHooks [scriptObject.scriptId] = true
end
if not noHotReload then
--clear env if needed
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = nil
end
--store the scripts to be compiled
local scriptCode = {}
--get scripts from the object
for hookName, code in pairs (scriptObject.Hooks) do
scriptCode [hookName] = "return " .. code
end
--get or create the global script object
local globalScriptObject = {
HotReload = -1,
DBScriptObject = scriptObject,
Build = PLATER_HOOK_BUILD,
scriptId = scriptObject.scriptId,
}
--init modEnv if necessary
local needsInitCall = false
if (not PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId]) then
needsInitCall = true
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId] = {
config = {}
}
end
--copy options to global env
-- ensure options are valid:
Plater.CreateOptionTableForScriptObject(scriptObject)
local scriptOptions = scriptObject.Options
local scriptOptionsValues = scriptObject.OptionsValues
for i = 1, #scriptOptions do
local thisOption = scriptOptions[i]
if (options_for_config_table[thisOption.Type]) then
if (type(scriptOptionsValues[thisOption.Key]) == "boolean") then
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key]
elseif (thisOption.Type == 7) then --list type
--check if the options is a list
--build default values if needed
if not scriptOptionsValues[thisOption.Key] then
scriptOptionsValues[thisOption.Key] = DF.table.copy({}, thisOption.Value)
end
--build a hash table with the entries in the list
local hashTable = {}
for index, entryTable in ipairs(scriptOptionsValues[thisOption.Key]) do
local key = entryTable[1]
local value = entryTable[2]
hashTable[key] = value
end
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId].config[thisOption.Key] = hashTable
else
PLATER_GLOBAL_MOD_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key] or thisOption.Value
end
end
end
--compile
for hookName, code in pairs (scriptCode) do
local globalScriptContainer = Plater.GetContainerForHook (hookName)
if (type (code) ~= "string") then
Plater:Msg ("fail to load mod: " .. (scriptObject.Name or "") .. ".")
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
return
end
if IS_WOW_PROJECT_NOT_MAINLINE then
code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
end
--find occurences of Plater.SendComm(arg1, arg2, arg3, ...) and replace with Plater.SendComm_Internal(uniqueIdentifier, arg1, arg2, arg3, ...) or fail to compile for all than "Send Comm Message" and don't replace (use empty dummy)
if hookName == "Send Comm Message" then
code = string.gsub(code, "Plater.SendComm%s*%(", "Plater.SendComm(" .. globalScriptContainer.ScriptAmount + 1 .. ", \"" .. scriptObject.scriptId .. "\", \"" .. scriptObject.UID .. "\", ")
else
local foundSendComm = string.find(code, "Plater.SendComm")
if foundSendComm then
Plater:Msg ("failed to compile " .. hookName .. " for script " .. scriptObject.Name .. ": " .. "Usage of 'Plater.SendComm' is only allowed in 'Send Comm Message' hook.")
end
end
local compiledScript, errortext = loadstring (code, "" .. hookName .. " for " .. scriptObject.Name)
if (not compiledScript) then
Plater:Msg ("failed to compile " .. hookName .. " for script " .. scriptObject.Name .. ": " .. errortext)
else
--setfenv (compiledScript, functionFilter)
if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then -- legacy mode
DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
SetPlaterEnvironment(compiledScript)
end
--store the function to execute inside the global script object
globalScriptObject [hookName] = compiledScript()
--insert the script in the global script container, remove existing, as option changes re-compile only single mod without wipe
local isReplace = false
for i, curScriptObject in ipairs(globalScriptContainer) do
if scriptObject.scriptId == curScriptObject.scriptId then
tremove(globalScriptContainer, i)
isReplace = true
end
end
tinsert (globalScriptContainer, globalScriptObject)
globalScriptContainer.ScriptAmount = globalScriptContainer.ScriptAmount + (isReplace and 0 or 1)
if (hookName == "Constructor") then
globalScriptObject.HasConstructor = true
elseif (hookName == "Initialization") and needsInitCall then
Plater.ScriptMetaFunctions.ScriptRunNoAttach (globalScriptObject, "Initialization")
end
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileHook")
end
--compile scripts from the Scripting tab
function Plater.CompileScript(scriptObject, noHotReload, ...)
Plater.StartLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
--check if the script is valid and if is enabled
if (not scriptObject) then
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
return
elseif (not scriptObject.Enabled) then
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
return
end
if (not scriptObject.scriptId) then
scriptObject.scriptId = tostring(scriptObject)
end
--clear env on re-compilation if necessary
if (not noHotReload) then
PLATER_GLOBAL_SCRIPT_ENV[scriptObject.scriptId] = nil
end
--store the scripts to be compiled
local scriptCode, scriptFunctions = {}, {}
--get scripts passed
for i = 1, select ("#",...) do
scriptCode[Plater.CodeTypeNames [i]] = "return " .. select(i, ...)
end
--get scripts which wasn't passed
for i = 1, #Plater.CodeTypeNames do
local scriptType = Plater.CodeTypeNames[i]
-- ensure init is filled always, 5 is the index where the init code is
if (not scriptObject[scriptType] and i == 5) then
scriptObject[scriptType] = [=[
function (scriptTable)
--insert code here
end
]=]
end
if (not scriptCode[scriptType]) then
scriptCode[scriptType] = "return " .. scriptObject[scriptType]
end
end
--init modEnv if necessary
local needsInitCall = false
if (not PLATER_GLOBAL_SCRIPT_ENV[scriptObject.scriptId]) then
needsInitCall = true
PLATER_GLOBAL_SCRIPT_ENV[scriptObject.scriptId] = {
config = {}
}
end
--copy options to global env and ensure options are valid
Plater.CreateOptionTableForScriptObject(scriptObject)
local scriptOptions = scriptObject.Options
local scriptOptionsValues = scriptObject.OptionsValues
for i = 1, #scriptOptions do
local thisOption = scriptOptions[i]
if (options_for_config_table[thisOption.Type]) then
if (type(scriptOptionsValues[thisOption.Key]) == "boolean") then
PLATER_GLOBAL_SCRIPT_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key]
elseif (thisOption.Type == 7) then --check if the options is a list
--build default values if needed
if not scriptOptionsValues[thisOption.Key] then
scriptOptionsValues[thisOption.Key] = DF.table.copy({}, thisOption.Value)
end
--build a hash table with the entries in the list
local hashTable = {}
for index, entryTable in ipairs(scriptOptionsValues[thisOption.Key]) do
local key = entryTable[1]
local value = entryTable[2]
hashTable[key] = value
end
PLATER_GLOBAL_SCRIPT_ENV [scriptObject.scriptId].config[thisOption.Key] = hashTable
else
PLATER_GLOBAL_SCRIPT_ENV [scriptObject.scriptId].config[thisOption.Key] = scriptOptionsValues[thisOption.Key] or thisOption.Value
end
end
end
--compile
for scriptType, code in pairs(scriptCode) do
if (IS_WOW_PROJECT_NOT_MAINLINE) then
code = string.gsub(code, "\"NamePlateFullBorderTemplate\"", "\"PlaterNamePlateFullBorderTemplate\"")
end
local compiledScript, errortext = loadstring(code, "" .. scriptType .. " for " .. scriptObject.Name)
if (not compiledScript) then
Plater:Msg ("failed to compile " .. scriptType .. " for script " .. scriptObject.Name .. ": " .. errortext)
else
--get the function to execute
--setfenv(compiledScript, functionFilter) deprecated
if (Plater.db.profile.shadowMode and Plater.db.profile.shadowMode == 0) then --legacy mode
DF:SetEnvironment(compiledScript, nil, platerModEnvironment)
elseif (not Plater.db.profile.shadowMode or Plater.db.profile.shadowMode == 1) then
SetPlaterEnvironment(compiledScript)
end
--extract the function
scriptFunctions[scriptType] = compiledScript()
end
end
--trigger container is the table with spellIds for auras and/or spellcast
--triggerId is the spellId converted to spellName or the unitName in case of a Unit name
local triggerContainer, triggerId
if (scriptObject.ScriptType == 1 or scriptObject.ScriptType == 2) then --aura or castbar
triggerContainer = "SpellIds"
elseif (scriptObject.ScriptType == 3) then --unit name
triggerContainer = "NpcNames"
end
for i = 1, #scriptObject[triggerContainer] do
local triggerId = scriptObject[triggerContainer][i]
--if the trigger is using spellId, check if the spell exists
if (scriptObject.ScriptType == 1 or scriptObject.ScriptType == 2) then
if (type(triggerId) == "number") then
triggerId = GetSpellInfo(triggerId)
if (not triggerId) then
if IS_WOW_PROJECT_MAINLINE then -- disable this in classic for now... too spammy
--just ignore, the spell on trigger list will be blank with a remove trigger button
--Plater:Msg ("failed to get the spell name for spellId: " .. (scriptObject [triggerContainer] [i] or "invalid spellId") .. " for script '" .. scriptObject.Name .. "', the spell has been removed from the triggers.")
end
end
end
elseif (scriptObject.ScriptType == 3) then --unit names
--cast the string to number to see if it's a npcId
triggerId = tonumber(triggerId) or triggerId
--if is a unit name, make it be in lower case
if (type(triggerId) == "string") then
triggerId = triggerId:lower()
end
end
if (triggerId) then
--get the global script object table
local triggerCacheTable
if (scriptObject.ScriptType == 1) then
triggerCacheTable = SCRIPT_AURA_TRIGGER_CACHE
elseif (scriptObject.ScriptType == 2) then
triggerCacheTable = SCRIPT_CASTBAR_TRIGGER_CACHE
elseif (scriptObject.ScriptType == 3) then
triggerCacheTable = SCRIPT_UNIT_TRIGGER_CACHE
end
local globalScriptObject = triggerCacheTable[triggerId]
if (not globalScriptObject) then
--first time compiled, create the global script object
--this table controls the hot reload state, holds the original object from the database and has the compiled functions
globalScriptObject = {
DBScriptObject = scriptObject,
--whenever the script is applied or saved the hot reload increases making it run again when the script is triggered
HotReload = 1,
--script key is set in the widget so it can lookup for a script using the key when the widget is hidding
ScriptKey = triggerId,
}
--insert the table just created inthe the triggerCacheTable
triggerCacheTable[triggerId] = globalScriptObject
else --hot reload and update
globalScriptObject.HotReload = globalScriptObject.HotReload + 1
globalScriptObject.DBScriptObject = scriptObject
end
globalScriptObject.LastUpdateTime = GetTime()-0.05
--add the script functions to the global object table
for scriptType, func in pairs(scriptFunctions) do
globalScriptObject[scriptType] = func
end
--run initialization (once)
if (needsInitCall) then
Plater.ScriptMetaFunctions.ScriptRunInitialization(globalScriptObject)
needsInitCall = false
end
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Mod/Script", "CompileScript")
end
--check all triggers of all scripts for overlaps
--where a same spellId, npcName or npcId is being used by two or more scripts
--return a table with the triggerId with a index table of all scripts using that trigger
function Plater.CheckScriptTriggerOverlap()
--store all triggers of all scripts in the format [triggerId] = {scripts using this trigger}
local allTriggers = {
Auras = {},
Casts = {},
Npcs = {},
}
--build the table containinf all scripts and what scripts they trigger
for index, scriptObject in ipairs (Plater.GetAllScripts ("script")) do
if (scriptObject.Enabled) then
for _, spellId in ipairs (scriptObject.SpellIds) do
if (scriptObject.ScriptType == 1) then
--> triggers auras
local triggerTable = allTriggers.Auras [spellId]
if (not triggerTable) then
allTriggers.Auras [spellId] = {scriptObject}
else
tinsert (triggerTable, scriptObject)
end
elseif (scriptObject.ScriptType == 2) then
--> triggers cast
local triggerTable = allTriggers.Casts [spellId]
if (not triggerTable) then
allTriggers.Casts [spellId] = {scriptObject}
else
tinsert (triggerTable, scriptObject)
end
end
end
for _, NpcId in ipairs (scriptObject.NpcNames) do
local triggerTable = allTriggers.Npcs [NpcId]
if (not triggerTable) then
allTriggers.Npcs [NpcId] = {scriptObject}
else
tinsert (triggerTable, scriptObject)
end
end
end
end
--> store scripts with overlap
local scriptsWithOverlap = {
Auras = {},
Casts = {},
Npcs = {},
}
local amount = 0
--> check if there's more than 1 script for each trigger
for triggerId, scriptsTable in pairs (allTriggers.Auras) do
if (#scriptsTable > 1) then
--overlap found
scriptsWithOverlap.Auras [triggerId] = scriptsTable
amount = amount + 1
end
end
for triggerId, scriptsTable in pairs (allTriggers.Casts) do
if (#scriptsTable > 1) then
--overlap found
scriptsWithOverlap.Casts [triggerId] = scriptsTable
amount = amount + 1
end
end
for triggerId, scriptsTable in pairs (allTriggers.Npcs) do
if (#scriptsTable > 1) then
--overlap found
scriptsWithOverlap.Npcs [triggerId] = scriptsTable
amount = amount + 1
end
end
return scriptsWithOverlap, amount
end
---add a trigger to a script
---@param triggerId number|string triggerId can be a npcId, npcName for NPCs or a spellId or spellName for auras and casts
---@param triggerType string|number there's 3 types of triggers: Auras, Casts and Npcs. Auras and Casts uses 'scriptObject.SpellIds' to store the triggerId and Npcs uses 'scriptObject.NpcNames'
---what define the type of trigger is the scriptObject.ScriptType, in other places of this project, triggerType can also be called scriptType
---triggerType expects: aura = 1, cast = 2, npc = 3
---@param scriptName string
---@return boolean 'true' if the trigger was added to the script, false if something went wrong
---@return string|nil message of error if the trigger wasn't added to the script
function Plater.AddTriggerToScript(triggerId, triggerType, scriptName)
--attempt to get the scriptObject for the passed scriptName
local scriptObject = Plater.GetScriptObject(scriptName, "script")
if (not scriptObject) then
return false, "script not found"
end
--remove the trigger from any script to avoid overlaps (a trigger can only exists in one script at time)
platerInternal.Scripts.RemoveTriggerFromAnyScript(triggerId)
--check the triggerType to know in what table the script will store the triggerId
if (triggerType == 1 or triggerType == 2 or triggerType == "aura" or triggerType == "cast") then
--aura or cast
DF.table.addunique(scriptObject.SpellIds, triggerId)
elseif (triggerType == 3 or triggerType == "npc") then
--npc
DF.table.addunique(scriptObject.NpcNames, triggerId)
else
return false, "invalid triggerType"
end
Plater.WipeAndRecompileAllScripts("script")
return true
end
function platerInternal.Scripts.RemoveTriggerFromAnyScript(triggerId)
local scriptObject = platerInternal.Scripts.IsTriggerOnAnyScript(triggerId)
if (scriptObject) then
platerInternal.Scripts.RemoveTriggerFromScript(scriptObject, triggerId)
end
end
function platerInternal.Scripts.IsTriggerOnAnyScript(triggerId)
local allScripts = Plater.db.profile.script_data
for i = 1, #allScripts do
local scriptObject = allScripts[i]
if (platerInternal.Scripts.DoesScriptHasTrigger(scriptObject, triggerId)) then
return scriptObject
end
end
end
function platerInternal.Scripts.GetScriptObjectByName(scriptName)
local allScripts = Plater.db.profile.script_data
for i = 1, #allScripts do
local scriptObject = allScripts[i]
if (scriptObject.Name == scriptName) then
return scriptObject
end
end
end
--add or remove a trigger without the need to pass through the scripting panel
function platerInternal.Scripts.AddSpellToScriptTriggers(scriptObject, spellId)
DF.table.addunique(scriptObject.SpellIds, spellId)
Plater.WipeAndRecompileAllScripts("script")
end
function platerInternal.Scripts.RemoveSpellFromScriptTriggers(scriptObject, spellId, noRecompile)
local index = DF.table.find(scriptObject.SpellIds, spellId)
if (index) then
tremove(scriptObject.SpellIds, index)
if (not noRecompile) then
Plater.WipeAndRecompileAllScripts("script")
end
end
end
function platerInternal.Scripts.DoesScriptHasTrigger(scriptObject, trigger)
local index = DF.table.find(scriptObject.SpellIds, trigger)
if (index) then
return true
end
local index = DF.table.find(scriptObject.NpcNames, trigger)
if (index) then
return true
end
end
function platerInternal.Scripts.RemoveTriggerFromScript(scriptObject, triggerId)
local index = DF.table.find(scriptObject.SpellIds, triggerId)
if (index) then
tremove(scriptObject.SpellIds, index)
Plater.WipeAndRecompileAllScripts("script")
end
local index = DF.table.find(scriptObject.NpcNames, triggerId)
if (index) then
tremove(scriptObject.NpcNames, index)
Plater.WipeAndRecompileAllScripts("script")
end
end
function platerInternal.Scripts.AddNpcToScriptTriggers(scriptObject, npcId)
DF.table.addunique(scriptObject.NpcNames, npcId)
Plater.WipeAndRecompileAllScripts("script")
end
function platerInternal.Scripts.RemoveNpcFromScriptTriggers(scriptObject, npcId)
local index = DF.table.find(scriptObject.NpcNames, npcId)
if (index) then
tremove(scriptObject.NpcNames, index)
Plater.WipeAndRecompileAllScripts("script")
end
end
---retrive the script object for a selected scriptId
---@param scriptID number|string if number scriptId is the index of the script in the db table, this index can change when a script is removed
---@param scriptType string is always "script" or "hook", hooks scripts are stored in a different table, ingame they are called "Mods"
function Plater.GetScriptObject (scriptID, scriptType)
if (type(scriptID) == "string" and scriptType == "script") then
return platerInternal.Scripts.GetScriptObjectByName(scriptID)
end
if (scriptType == "script") then
local script = Plater.db.profile.script_data [scriptID]
if (script) then
return script
end
elseif (scriptType == "hook") then
local script = Plater.db.profile.hook_data [scriptID]
if (script) then
return script
end
end
end
--return the main db table for the script type
function Plater.GetScriptDB (scriptType)
if (scriptType == "script") then
return Plater.db.profile.script_data
elseif (scriptType == "hook") then
return Plater.db.profile.hook_data
end
end
--if the type of a scriptObject is unknown
function Plater.GetScriptType (scriptObject)
if (scriptObject.Hooks) then
return "hook"
elseif (scriptObject.SpellIds) then
return "script"
end
end
function Plater.DispatchCommReceivedMessageHookEvent(scriptUID, source, ...)
if (HOOK_COMM_RECEIVED_MESSAGE.ScriptAmount > 0) then
for i = 1, HOOK_COMM_RECEIVED_MESSAGE.ScriptAmount do
local globalScriptObject = HOOK_COMM_RECEIVED_MESSAGE[i]
if (globalScriptObject.DBScriptObject.UID == scriptUID) then
--run
Plater.ScriptMetaFunctions.ScriptRunCommMessageHook(globalScriptObject, "Receive Comm Message", source, ...)
end
end
end
end
function Plater.DispatchCommSendMessageHookEvents()
if (HOOK_COMM_SEND_MESSAGE.ScriptAmount > 0) then
for i = 1, HOOK_COMM_SEND_MESSAGE.ScriptAmount do
local globalScriptObject = HOOK_COMM_SEND_MESSAGE[i]
--run
Plater.ScriptMetaFunctions.ScriptRunCommMessageHook(globalScriptObject, "Send Comm Message")
end
end
end
function Plater.VerifyScriptIdForComm(scriptIndex, scriptId, uniqueId)
if not scriptIndex or not scriptId or not uniqueId then return end
local globalScriptObject = HOOK_COMM_SEND_MESSAGE[scriptIndex]
if globalScriptObject and globalScriptObject.DBScriptObject and globalScriptObject.DBScriptObject.scriptId and globalScriptObject.DBScriptObject.scriptId == scriptId and globalScriptObject.DBScriptObject.UID and globalScriptObject.DBScriptObject.UID == uniqueId then
return true
end
return false
end
function Plater.DispatchTalentUpdateHookEvent()
if (HOOK_PLAYER_TALENT_UPDATE.ScriptAmount > 0) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if (plateFrame and plateFrame.unitFrame.PlaterOnScreen) then
for i = 1, HOOK_PLAYER_TALENT_UPDATE.ScriptAmount do
local globalScriptObject = HOOK_PLAYER_TALENT_UPDATE [i]
local unitFrame = plateFrame.unitFrame
if not plateFrame.unitFrame.PlaterOnScreen then
return
end
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Player Talent Update")
--run
unitFrame:ScriptRunHook (scriptInfo, "Player Talent Update")
end
end
end
end
end
function Plater.ScheduleHookForCombat (timerObject)
if (timerObject.Event == "Enter Combat") then
if (HOOK_COMBAT_ENTER.ScriptAmount > 0) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
for i = 1, HOOK_COMBAT_ENTER.ScriptAmount do
local globalScriptObject = HOOK_COMBAT_ENTER [i]
local unitFrame = plateFrame.unitFrame
if not plateFrame.unitFrame.PlaterOnScreen then
return
end
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Enter Combat")
--run
unitFrame:ScriptRunHook (scriptInfo, "Enter Combat")
end
end
end
elseif (timerObject.Event == "Leave Combat") then
if (HOOK_COMBAT_LEAVE.ScriptAmount > 0) then
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
for i = 1, HOOK_COMBAT_LEAVE.ScriptAmount do
local globalScriptObject = HOOK_COMBAT_LEAVE [i]
local unitFrame = plateFrame.unitFrame
if not plateFrame.unitFrame.PlaterOnScreen then
return
end
local scriptContainer = unitFrame:ScriptGetContainer()
local scriptInfo = unitFrame:HookGetInfo(globalScriptObject, scriptContainer, "Leave Combat")
--run
unitFrame:ScriptRunHook (scriptInfo, "Leave Combat")
end
end
end
end
end