Files
benilla-octo/crates/benilla/assets/ui/CombatText.xml
T
SamandClaude Fable 5 a3c5b20698 breath and fatigue bars, combo points, and a stun stops everything
- the breath and fatigue bars are in, driven by the server's timers
- combo points show on the target frame, rogue and druid only, per target, and Overpower lights on them
- a stun freezes turning too, and gm fly over water stays in the air
- /sit and the shipped slash commands work, sheathing sounds like the weapon's material, and the bar never holds an outgrown spell rank

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-02 18:01:15 +07:00

611 lines
33 KiB
XML

<Ui>
<!--
The center-screen scrolling combat text (decision 0578) — the 1.11+ "Floating Combat Text"
option. TRANSCRIBED from the shipped load-on-demand addon `Blizzard_CombatText`
(patch.MPQ `Interface\AddOns\Blizzard_CombatText\`, extracted to
WoW/_extracted_addons/Blizzard_CombatText/): screen-space FontStrings scrolling up from
UIParent-bottom +384 (≈ over the character), driven by the engine events the addon
registers. Deviations from the ref, each named:
- The ref is load-on-demand (`UIParentLoadAddOn` when SHOW_COMBAT_TEXT == "1"); we load
always and let CombatText_UpdateDisplayedMessages gate registration, ref-identically.
- SHOW_COMBAT_TEXT defaults "1" here (ref "0") — the director's ask IS the enabled
experience; every sub-toggle keeps its ref default (UIOptionsFrame.lua l.69-84).
- The ref's virtual FontString template (CombatTextTemplate) expands to 20 identical
strings; region templates are outside the v1 loader's object model (BuffFrame.xml has
the precedent note), so the 20 instances inherit the CombatTextFont Font object and
carry the template's anchor directly.
- UNIT_MANA → UNIT_POWER_UPDATE + the Era Unit* API (our event surface, decision 0068);
same low-mana law. PLAYER_COMBO_POINTS rides the combo wire (0869); its arm reads
`GetComboPoints()`, so it is rogue/druid-only and per-target for free — the binding
gates, not this file (0875).
- Lua 5.0 `for k,v in t` iteration becomes explicit index loops (5.1 VM), backwards
where the body removes.
The wire → messageType mapping feeding COMBAT_TEXT_UPDATE lives app-side
(net/apply/combat_log.rs + ui_unit.rs) and is PROVISIONAL pending the dispatched wow-re
emission pin (decision 0578).
-->
<Script><![CDATA[
NUM_COMBAT_TEXT_LINES = 20
COMBAT_TEXT_SCROLLSPEED = 1.9
COMBAT_TEXT_FADEOUT_TIME = 1.3
COMBAT_TEXT_HEIGHT = 25
COMBAT_TEXT_CRIT_MAXHEIGHT = 60
COMBAT_TEXT_CRIT_MINHEIGHT = 30
COMBAT_TEXT_CRIT_SCALE_TIME = 0.05
COMBAT_TEXT_CRIT_SHRINKTIME = 0.2
COMBAT_TEXT_TO_ANIMATE = {}
COMBAT_TEXT_STAGGER_RANGE = 20
COMBAT_TEXT_SPACING = 10
COMBAT_TEXT_MAX_OFFSET = 130
COMBAT_TEXT_LOW_HEALTH_THRESHOLD = 0.2
COMBAT_TEXT_LOW_MANA_THRESHOLD = 0.2
COMBAT_TEXT_LOCATIONS = {}
-- The option family's saved-variables (ref UIOptionsFrame.lua l.125-152), at the ref
-- defaults except the master (see the header). No options panel binds them yet — flip in
-- Lua when wanted.
SHOW_COMBAT_TEXT = "1"
COMBAT_TEXT_SHOW_LOW_HEALTH_MANA = "1"
COMBAT_TEXT_SHOW_AURAS = "1"
COMBAT_TEXT_SHOW_AURA_FADE = "0"
COMBAT_TEXT_SHOW_COMBAT_STATE = "1"
COMBAT_TEXT_SHOW_DODGE_PARRY_MISS = "0"
COMBAT_TEXT_SHOW_RESISTANCES = "0"
COMBAT_TEXT_SHOW_REPUTATION = "0"
COMBAT_TEXT_SHOW_REACTIVES = "0"
COMBAT_TEXT_SHOW_FRIENDLY_NAMES = "0"
COMBAT_TEXT_SHOW_COMBO_POINTS = "0"
COMBAT_TEXT_SHOW_MANA = "0"
COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
COMBAT_TEXT_FLOAT_MODE = "1"
-- The message words (shipped enUS GlobalStrings values, hardcoded like the rest of our
-- enUS-only data; or-guarded so an earlier definition wins).
MANA = MANA or "Mana"
RAGE = RAGE or "Rage"
FOCUS = FOCUS or "Focus"
ENERGY = ENERGY or "Energy"
HEALTH_LOW = HEALTH_LOW or "Health Low"
MANA_LOW = MANA_LOW or "Mana Low"
ENTERING_COMBAT = ENTERING_COMBAT or "Entering Combat"
LEAVING_COMBAT = LEAVING_COMBAT or "Leaving Combat"
BLOCK = BLOCK or "Block"
ABSORB = ABSORB or "Absorb"
RESIST = RESIST or "Resist"
SPELL_MISSED = SPELL_MISSED or "Miss"
SPELL_DODGED = SPELL_DODGED or "Dodge"
SPELL_PARRIED = SPELL_PARRIED or "Parry"
SPELL_BLOCKED = SPELL_BLOCKED or "Block"
SPELL_EVADED = SPELL_EVADED or "Evade"
SPELL_IMMUNE = SPELL_IMMUNE or "Immune"
SPELL_DEFLECTED = SPELL_DEFLECTED or "Deflect"
SPELL_REFLECTED = SPELL_REFLECTED or "Reflect"
SPELL_RESISTED = SPELL_RESISTED or "Resist"
SPELL_ABSORBED = SPELL_ABSORBED or "Absorb"
AURA_END = AURA_END or "<%s> fades"
BLOCK_TRAILER = BLOCK_TRAILER or " (%d blocked)"
ABSORB_TRAILER = ABSORB_TRAILER or " (%d absorbed)"
RESIST_TRAILER = RESIST_TRAILER or " (%d resisted)"
COMBAT_TEXT_HONOR_GAINED = COMBAT_TEXT_HONOR_GAINED or "Honor %s"
--[[
List of COMBAT_TEXT_TYPE_INFO attributes (ref comment, verbatim)
======================================================
r, g, b = [floats] -- The floating text color
show = [nil, 1] -- Display this message type in the UI
isStaggered = [nil, 1] -- Randomly stagger these messages from left to right
var = [nil, 1] -- This messageType is shown if this variable resolves to "1"
]]
COMBAT_TEXT_TYPE_INFO = {}
COMBAT_TEXT_TYPE_INFO["INTERRUPT"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["DAMAGE_CRIT"] = {r = 1, g = 0.1, b = 0.1, show = 1}
COMBAT_TEXT_TYPE_INFO["DAMAGE"] = {r = 1, g = 0.1, b = 0.1, isStaggered = 1, show = 1}
COMBAT_TEXT_TYPE_INFO["MISS"] = {r = 1, g = 0.1, b = 0.1, isStaggered = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["BLOCK"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["ABSORB"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["RESIST"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["EVADE"] = {r = 1, g = 0.1, b = 0.1, isStaggered = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["DODGE"] = {r = 1, g = 0.1, b = 0.1, isStaggered = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["PARRY"] = {r = 1, g = 0.1, b = 0.1, isStaggered = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["IMMUNE"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["DEFLECT"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["ENCHANTMENT_REMOVED"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["ENCHANTMENT_ADDED"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["PERIODIC_HEAL"] = {r = 0.1, g = 1, b = 0.1, show = 1}
COMBAT_TEXT_TYPE_INFO["MANA"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_MANA"}
COMBAT_TEXT_TYPE_INFO["RAGE"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_MANA"}
COMBAT_TEXT_TYPE_INFO["FOCUS"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_MANA"}
COMBAT_TEXT_TYPE_INFO["ENERGY"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_MANA"}
COMBAT_TEXT_TYPE_INFO["SPELL_ABSORBED"] = {r = 0.79, g = 0.3, b = 0.85, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["SPELL_RESISTED"] = {r = 0.79, g = 0.3, b = 0.85, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["PROC_RESISTED"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["DISPEL_FAILED"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["SPELL_CAST"] = {r = 0.1, g = 1, b = 0.1, show = 1}
COMBAT_TEXT_TYPE_INFO["SPELL_CAST_START"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["AURA_END"] = {r = 0.1, g = 1, b = 0.1, var = "COMBAT_TEXT_SHOW_AURA_FADE"}
COMBAT_TEXT_TYPE_INFO["AURA_END_HARMFUL"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_AURA_FADE"}
COMBAT_TEXT_TYPE_INFO["AURA_START"] = {r = 0.1, g = 1, b = 0.1, var = "COMBAT_TEXT_SHOW_AURAS"}
COMBAT_TEXT_TYPE_INFO["AURA_START_HARMFUL"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_AURAS"}
COMBAT_TEXT_TYPE_INFO["SPELL_DAMAGE"] = {r = 0.79, g = 0.3, b = 0.85, show = 1}
COMBAT_TEXT_TYPE_INFO["SPELL_DODGED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_PARRIED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_BLOCKED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_RESISTANCES"}
COMBAT_TEXT_TYPE_INFO["SPELL_EVADED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_IMMUNE"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_DEFLECTED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_REFLECTED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_MISSED"] = {r = 1, g = 1, b = 1, var = "COMBAT_TEXT_SHOW_DODGE_PARRY_MISS"}
COMBAT_TEXT_TYPE_INFO["SPELL_ACTIVE"] = {r = 1, g = 0.82, b = 0, var = "COMBAT_TEXT_SHOW_REACTIVES"}
COMBAT_TEXT_TYPE_INFO["FACTION"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_REPUTATION"}
COMBAT_TEXT_TYPE_INFO["HEAL_CRIT"] = {r = 0.1, g = 1, b = 0.1, show = 1}
COMBAT_TEXT_TYPE_INFO["HEAL"] = {r = 0.1, g = 1, b = 0.1, show = 1}
COMBAT_TEXT_TYPE_INFO["DAMAGE_SHIELD"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["SPELL_DISPELLED"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["EXTRA_ATTACKS"] = {r = 1, g = 1, b = 1}
COMBAT_TEXT_TYPE_INFO["SPLIT_DAMAGE"] = {r = 1, g = 1, b = 1, show = 1}
COMBAT_TEXT_TYPE_INFO["HONOR_GAINED"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_HONOR_GAINED"}
COMBAT_TEXT_TYPE_INFO["HEALTH_LOW"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_LOW_HEALTH_MANA"}
COMBAT_TEXT_TYPE_INFO["MANA_LOW"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_LOW_HEALTH_MANA"}
COMBAT_TEXT_TYPE_INFO["ENTERING_COMBAT"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_COMBAT_STATE"}
COMBAT_TEXT_TYPE_INFO["LEAVING_COMBAT"] = {r = 1, g = 0.1, b = 0.1, var = "COMBAT_TEXT_SHOW_COMBAT_STATE"}
COMBAT_TEXT_TYPE_INFO["COMBO_POINTS"] = {r = 0.1, g = 0.1, b = 1, var = "COMBAT_TEXT_SHOW_COMBO_POINTS"}
function CombatText_OnLoad()
CombatText_UpdateDisplayedMessages()
CombatText.previousMana = {}
end
function CombatText_OnEvent(event)
-- Set up the messageType
local messageType, message
-- Set the message data
local data = arg2
local displayType
if event == "UNIT_HEALTH" then
if arg1 == "player" then
if UnitHealth("player") / UnitHealthMax("player") <= COMBAT_TEXT_LOW_HEALTH_THRESHOLD then
if not CombatText.lowHealth then
messageType = "HEALTH_LOW"
CombatText.lowHealth = 1
end
else
CombatText.lowHealth = nil
end
end
if not messageType then
return
end
elseif event == "UNIT_POWER_UPDATE" then
-- Ref: UNIT_MANA + UnitMana/UnitManaMax (header deviation — the Era surface).
local mana = UnitPower("player")
local powerType = UnitPowerType("player")
if arg1 == "player" then
if mana / UnitPowerMax("player") <= COMBAT_TEXT_LOW_MANA_THRESHOLD and powerType == 0 then
if not CombatText.lowMana then
messageType = "MANA_LOW"
CombatText.lowMana = 1
end
else
CombatText.lowMana = nil
end
end
if not messageType then
return
end
elseif event == "PLAYER_REGEN_DISABLED" then
messageType = "ENTERING_COMBAT"
elseif event == "PLAYER_REGEN_ENABLED" then
messageType = "LEAVING_COMBAT"
elseif event == "PLAYER_COMBO_POINTS" then
local comboPoints = GetComboPoints()
if comboPoints > 0 then
messageType = "COMBO_POINTS"
data = comboPoints
if MAX_COMBO_POINTS and comboPoints == MAX_COMBO_POINTS then
displayType = "crit"
end
else
return
end
elseif event == "COMBAT_TEXT_UPDATE" then
messageType = arg1
else
messageType = event
end
-- Process the messageType and format the message
local info = COMBAT_TEXT_TYPE_INFO[messageType]
if not info then
info = {r = 1, g = 1, b = 1}
end
-- See if we should display the message or not
if not info.show then
return
end
local isStaggered = info.isStaggered
if messageType == "" then
message = nil
elseif messageType == "DAMAGE_CRIT" then
displayType = "crit"
message = "-" .. data
elseif messageType == "DAMAGE" or messageType == "SPELL_DAMAGE" then
message = "-" .. data
elseif messageType == "SPELL_CAST" or messageType == "AURA_START" or messageType == "AURA_START_HARMFUL" then
message = "<" .. data .. ">"
elseif messageType == "AURA_END" or messageType == "AURA_END_HARMFUL" then
message = format(AURA_END, data)
elseif messageType == "HEAL" or messageType == "PERIODIC_HEAL" then
if COMBAT_TEXT_SHOW_FRIENDLY_NAMES == "1" and messageType == "HEAL" and UnitName("player") ~= data then
message = "+" .. arg3 .. " [" .. data .. "]"
else
message = "+" .. arg3
end
elseif messageType == "HEAL_CRIT" then
displayType = "crit"
if COMBAT_TEXT_SHOW_FRIENDLY_NAMES == "1" and UnitName("player") ~= data then
message = "+" .. arg3 .. " [" .. data .. "]"
else
message = "+" .. arg3
end
elseif messageType == "MANA" then
if tonumber(data) > 0 then
data = "+" .. data
end
message = data .. " " .. MANA
elseif messageType == "RAGE" then
message = "+" .. data .. " " .. RAGE
elseif messageType == "FOCUS" then
message = "+" .. data .. " " .. FOCUS
elseif messageType == "ENERGY" then
message = "+" .. data .. " " .. ENERGY
elseif messageType == "FACTION" then
if tonumber(arg3) > 0 then
arg3 = "+" .. arg3
end
message = "(" .. data .. " " .. arg3 .. ")"
elseif messageType == "BLOCK" then
if arg3 then
message = data .. " " .. format(BLOCK_TRAILER, arg3)
else
message = BLOCK
end
elseif messageType == "ABSORB" then
if arg3 then
message = arg2 .. " " .. format(ABSORB_TRAILER, arg3)
else
message = ABSORB
end
elseif messageType == "RESIST" or messageType == "SPELL_RESISTED" then
if arg3 then
message = data .. " " .. format(RESIST_TRAILER, arg3)
else
message = RESIST
end
elseif messageType == "HONOR_GAINED" then
if tonumber(data) > 0 then
data = "+" .. data
end
message = format(COMBAT_TEXT_HONOR_GAINED, data)
elseif messageType == "SPELL_ACTIVE" then
displayType = "crit"
message = "<" .. data .. ">"
elseif messageType == "COMBO_POINTS" then
message = data .. " Combo Points"
else
message = getglobal(messageType)
if not message then
message = getglobal("COMBAT_TEXT_" .. messageType)
end
end
-- Add the message
if message then
CombatText_AddMessage(message, COMBAT_TEXT_SCROLL_FUNCTION, info.r, info.g, info.b, displayType, isStaggered)
end
end
function CombatText_OnUpdate(elapsed)
-- Backwards, so the removal inside the body can't skip a neighbor (the ref's 5.0
-- `for in` + tremove tolerated it; a 5.1 index loop must not).
for i = getn(COMBAT_TEXT_TO_ANIMATE), 1, -1 do
local value = COMBAT_TEXT_TO_ANIMATE[i]
if value.scrollTime >= COMBAT_TEXT_SCROLLSPEED then
CombatText_RemoveMessage(value)
else
value.scrollTime = value.scrollTime + elapsed
local xPos, yPos = value.scrollFunction(value)
value.yPos = yPos
value:SetPoint("TOP", UIParent, "BOTTOM", xPos, yPos)
if value.scrollTime >= COMBAT_TEXT_FADEOUT_TIME then
local alpha = 1 - ((value.scrollTime - COMBAT_TEXT_FADEOUT_TIME) / (COMBAT_TEXT_SCROLLSPEED - COMBAT_TEXT_FADEOUT_TIME))
value:SetAlpha(max(alpha, 0))
end
-- Handle crit
if value.isCrit then
if value.scrollTime <= COMBAT_TEXT_CRIT_SCALE_TIME then
value:SetTextHeight(floor(COMBAT_TEXT_CRIT_MINHEIGHT + ((COMBAT_TEXT_CRIT_MAXHEIGHT - COMBAT_TEXT_CRIT_MINHEIGHT) * value.scrollTime / COMBAT_TEXT_CRIT_SCALE_TIME)))
elseif value.scrollTime <= COMBAT_TEXT_CRIT_SHRINKTIME then
value:SetTextHeight(floor(COMBAT_TEXT_CRIT_MAXHEIGHT - ((COMBAT_TEXT_CRIT_MAXHEIGHT - COMBAT_TEXT_CRIT_MINHEIGHT) * (value.scrollTime - COMBAT_TEXT_CRIT_SCALE_TIME) / (COMBAT_TEXT_CRIT_SHRINKTIME - COMBAT_TEXT_CRIT_SCALE_TIME))))
else
value.isCrit = nil
end
end
end
end
end
function CombatText_AddMessage(message, scrollFunction, r, g, b, displayType, isStaggered)
local string, noStringsAvailable = CombatText_GetAvailableString()
if noStringsAvailable then
return
end
string:SetText(message)
string:SetTextColor(r, g, b)
string.scrollTime = 0
if displayType == "crit" then
string.scrollFunction = CombatText_StandardScroll
else
string.scrollFunction = scrollFunction
end
-- See which direction the message should flow. (`GetBottom`/`GetTop` are nil before
-- the first layout resolve — the parked seat stands in; identical after frame 1.)
local lowestMessage
if COMBAT_TEXT_LOCATIONS.startY < COMBAT_TEXT_LOCATIONS.endY then
-- Flowing up
lowestMessage = string:GetBottom() or COMBAT_TEXT_LOCATIONS.startY
for i = 1, getn(COMBAT_TEXT_TO_ANIMATE) do
local value = COMBAT_TEXT_TO_ANIMATE[i]
if lowestMessage >= value.yPos - 16 - COMBAT_TEXT_SPACING then
lowestMessage = value.yPos - 16 - COMBAT_TEXT_SPACING
end
end
if lowestMessage < (COMBAT_TEXT_LOCATIONS.startY - COMBAT_TEXT_MAX_OFFSET) then
lowestMessage = string:GetBottom() or COMBAT_TEXT_LOCATIONS.startY
end
else
-- Flowing down
lowestMessage = string:GetTop() or COMBAT_TEXT_LOCATIONS.startY
for i = 1, getn(COMBAT_TEXT_TO_ANIMATE) do
local value = COMBAT_TEXT_TO_ANIMATE[i]
if lowestMessage <= value.yPos + 16 + COMBAT_TEXT_SPACING then
lowestMessage = value.yPos + 16 + COMBAT_TEXT_SPACING
end
end
if lowestMessage > (COMBAT_TEXT_LOCATIONS.startY + COMBAT_TEXT_MAX_OFFSET) then
lowestMessage = string:GetTop() or COMBAT_TEXT_LOCATIONS.startY
end
end
-- Handle crits
if displayType == "crit" then
string.endY = COMBAT_TEXT_LOCATIONS.startY
string.isCrit = 1
string:SetTextHeight(COMBAT_TEXT_CRIT_MINHEIGHT)
elseif displayType == "sticky" then
string.endY = COMBAT_TEXT_LOCATIONS.startY
string:SetTextHeight(COMBAT_TEXT_HEIGHT)
else
string.endY = COMBAT_TEXT_LOCATIONS.endY
string:SetTextHeight(COMBAT_TEXT_HEIGHT)
end
-- Stagger the text if flagged
local staggerAmount = 0
if isStaggered then
staggerAmount = random(0, COMBAT_TEXT_STAGGER_RANGE) - COMBAT_TEXT_STAGGER_RANGE / 2
end
-- Alternate x direction
if not CombatText.xDir or CombatText.xDir < 0 then
CombatText.xDir = 1
else
CombatText.xDir = -1
end
string.xDir = CombatText.xDir
string.startX = COMBAT_TEXT_LOCATIONS.startX + staggerAmount
string.startY = lowestMessage
string.yPos = lowestMessage
string:SetPoint("TOP", UIParent, "BOTTOM", string.startX, lowestMessage)
string:SetAlpha(1)
string:Show()
tinsert(COMBAT_TEXT_TO_ANIMATE, string)
end
function CombatText_RemoveMessage(string)
for i = 1, getn(COMBAT_TEXT_TO_ANIMATE) do
if COMBAT_TEXT_TO_ANIMATE[i] == string then
tremove(COMBAT_TEXT_TO_ANIMATE, i)
string:SetAlpha(0)
string:Hide()
string:SetPoint("TOP", UIParent, "BOTTOM", COMBAT_TEXT_LOCATIONS.startX, COMBAT_TEXT_LOCATIONS.startY)
break
end
end
end
function CombatText_GetAvailableString()
local string
for i = 1, NUM_COMBAT_TEXT_LINES do
string = getglobal("CombatText" .. i)
if not string:IsShown() then
return string
end
end
return CombatText_GetOldestString(), 1
end
function CombatText_GetOldestString()
local oldestString = COMBAT_TEXT_TO_ANIMATE[1]
CombatText_RemoveMessage(oldestString)
return oldestString
end
function CombatText_ClearAnimationList()
COMBAT_TEXT_TO_ANIMATE = {}
for i = 1, NUM_COMBAT_TEXT_LINES do
local string = getglobal("CombatText" .. i)
string:SetAlpha(0)
string:Hide()
string:SetPoint("TOP", UIParent, "BOTTOM", COMBAT_TEXT_LOCATIONS.startX, COMBAT_TEXT_LOCATIONS.startY)
end
end
function CombatText_UpdateDisplayedMessages()
-- Unregister events if combat text is disabled
if SHOW_COMBAT_TEXT == "0" then
CombatText:UnregisterEvent("COMBAT_TEXT_UPDATE")
CombatText:UnregisterEvent("UNIT_HEALTH")
CombatText:UnregisterEvent("UNIT_POWER_UPDATE")
CombatText:UnregisterEvent("PLAYER_COMBO_POINTS")
CombatText:UnregisterEvent("PLAYER_REGEN_DISABLED")
CombatText:UnregisterEvent("PLAYER_REGEN_ENABLED")
return
else
CombatText:RegisterEvent("COMBAT_TEXT_UPDATE")
CombatText:RegisterEvent("UNIT_HEALTH")
CombatText:RegisterEvent("UNIT_POWER_UPDATE")
CombatText:RegisterEvent("PLAYER_COMBO_POINTS")
CombatText:RegisterEvent("PLAYER_REGEN_DISABLED")
CombatText:RegisterEvent("PLAYER_REGEN_ENABLED")
end
-- Update shown messages
for messageType, value in pairs(COMBAT_TEXT_TYPE_INFO) do
if value.var then
if getglobal(value.var) == "1" then
value.show = 1
else
value.show = nil
end
end
end
-- The engine's UI space is the 768-virtual coordinate system itself (decision
-- 0582), so the ref constants stand verbatim (0581's px shim retired). The ref's
-- uiScale-cvar division joins when a cvar system exists.
local uiScale = 1
-- Update scrolldirection
if COMBAT_TEXT_FLOAT_MODE == "1" then
COMBAT_TEXT_SCROLL_FUNCTION = CombatText_StandardScroll
COMBAT_TEXT_LOCATIONS = {
startX = 0,
startY = 384 / uiScale,
endX = 0,
endY = 609 / uiScale
}
elseif COMBAT_TEXT_FLOAT_MODE == "2" then
COMBAT_TEXT_SCROLL_FUNCTION = CombatText_StandardScroll
COMBAT_TEXT_LOCATIONS = {
startX = 0,
startY = 384 / uiScale,
endX = 0,
endY = 159 / uiScale
}
else
COMBAT_TEXT_SCROLL_FUNCTION = CombatText_FountainScroll
COMBAT_TEXT_LOCATIONS = {
startX = 0,
startY = 384 / uiScale,
endX = 0,
endY = 609 / uiScale
}
end
CombatText_ClearAnimationList()
end
function CombatText_StandardScroll(value)
local xPos = value.startX + ((COMBAT_TEXT_LOCATIONS.endX - COMBAT_TEXT_LOCATIONS.startX) * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)
local yPos = value.startY + ((value.endY - COMBAT_TEXT_LOCATIONS.startY) * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)
return xPos, yPos
end
function CombatText_FountainScroll(value)
local radius = 150
local xPos = value.xDir * (value.startX + radius * cos(90 * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)) - value.xDir * radius
local yPos = value.startY + radius * sin(90 * value.scrollTime / COMBAT_TEXT_SCROLLSPEED)
return xPos, yPos
end
]]></Script>
<!-- The 20-string pool (ref CombatText1..20, each an expansion of CombatTextTemplate:
CombatTextFont, hidden, TOP anchored to UIParent BOTTOM (0,384) — re-seated per frame by
CombatText_OnUpdate). -->
<Frame name="CombatText" toplevel="true" parent="UIParent" hidden="false">
<Size><AbsDimension x="10" y="10"/></Size>
<Anchors>
<Anchor point="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<FontString name="CombatText1" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText2" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText3" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText4" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText5" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText6" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText7" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText8" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText9" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText10" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText11" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText12" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText13" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText14" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText15" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText16" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText17" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText18" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText19" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="CombatText20" inherits="CombatTextFont" hidden="true">
<Anchors><Anchor point="TOP" relativeTo="UIParent" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="384"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnLoad>CombatText_OnLoad()</OnLoad>
<OnEvent>CombatText_OnEvent(event)</OnEvent>
<OnUpdate>CombatText_OnUpdate(arg1)</OnUpdate>
</Scripts>
</Frame>
</Ui>