ca2b78e217
/fui keeps edit mode, reset and list. The thirty diagnostic and tuning functions behind the other subcommands (bar, bags, character, tabs, close, model, minimap, mouse, map, questlog, chat, chatlines, tabard...) go with them, with Panes.Report, the bags read-back and the 205 texts they alone used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
265 lines
9.3 KiB
Lua
265 lines
9.3 KiB
Lua
-- Bottom status bars: experience and reputation.
|
|
-- mainline/StatusTrackingBarTemplate.xml: both bars are the same object (background, fill,
|
|
-- frame over it, centered text), so both are built the same way.
|
|
-- camelot/StatusTrackingBarConstants.lua: the containers are one bar height apart, so the
|
|
-- bars touch; camelot's image is 13 high. The game shows reputation above experience,
|
|
-- although the priorities (Experience 0, Reputation 2) sorted descending suggest the reverse.
|
|
-- Reputation fill color by standing (shared/ReputationBar.lua), same ranges as 3.3.5
|
|
-- FACTION_BAR_COLORS.
|
|
-- The experience bar hides at max level and reputation moves down to its place (camelot
|
|
-- StatusTrackingBarManager fills containers from the bottom). Reputation hides when no
|
|
-- faction is watched.
|
|
-- Both bars span the bottom row (action bar, micro menu, bags) and sit on its top, measured
|
|
-- by BottomBar (ForeverUI.BottomRow). Each stays movable. Both carry camelot's 19 dividers.
|
|
|
|
local HEIGHT = 13 -- camelot image height (1020 x 13)
|
|
|
|
-- Dividers (camelot StatusTrackingBarContainerMixin:UpdateDividers, unchanged in 70450):
|
|
-- STATUS_BAR_NUM_SEGMENTS = 20 segments, so 19 StatusBarDividerTemplate frames (3 x 10,
|
|
-- ui-hud-experiencebar-divider, OVERLAY) at LEFT (width / 20 * i, 0), above the fill.
|
|
-- Rested tick (ExhaustionTick, UI-HUD-ExperienceBar-Frame-Pip, 10 x 14): centered on the right
|
|
-- end of the rested fill, shown while rested experience remains. The rested fill is
|
|
-- Fill-Rested (dark blue); while rested, the earned fill is Fill-Prediction (bright blue) instead
|
|
-- of Fill-Experience (purple). camelot's ExpBarOverrides names them the other way round
|
|
-- (earned Fill-Rested, bonus Fill-Prediction); the game shows them as here (seen in game).
|
|
local SEGMENTS = 20
|
|
local DIVIDER_W, DIVIDER_H = 3, 10
|
|
local L = ForeverUI.L
|
|
|
|
local ATLAS_REPUTATION = {
|
|
"ui-hud-experiencebar-fill-reputation-faction-red-camelot", -- hated
|
|
"ui-hud-experiencebar-fill-reputation-faction-red-camelot", -- hostile
|
|
"ui-hud-experiencebar-fill-reputation-faction-orange-camelot", -- unfriendly
|
|
"ui-hud-experiencebar-fill-reputation-faction-yellow-camelot", -- neutral
|
|
"ui-hud-experiencebar-fill-reputation-faction-green-camelot", -- friendly
|
|
"ui-hud-experiencebar-fill-reputation-faction-green-camelot", -- honored
|
|
"ui-hud-experiencebar-fill-reputation-faction-green-camelot", -- revered
|
|
"ui-hud-experiencebar-fill-reputation-faction-green-camelot", -- exalted
|
|
}
|
|
|
|
-- Crops a fill to the fraction. The image has rounded ends: cutting it on the right gives
|
|
-- the straight edge of a partial fill. width: bar width at 100 %.
|
|
local function populate(texture, atlas, fraction, width)
|
|
local e = atlas and ForeverUI.AtlasEntry(atlas)
|
|
if not e or not fraction or fraction ~= fraction or fraction <= 0 then
|
|
texture:Hide()
|
|
return
|
|
end
|
|
|
|
if fraction > 1 then
|
|
fraction = 1
|
|
end
|
|
|
|
local w = width * fraction
|
|
if w < 1 then
|
|
texture:Hide()
|
|
return
|
|
end
|
|
|
|
texture:SetTexture(e[1])
|
|
texture:SetTexCoord(e[2], e[2] + (e[3] - e[2]) * fraction, e[4], e[5])
|
|
texture:SetWidth(w)
|
|
texture:Show()
|
|
end
|
|
|
|
local function createBar(name)
|
|
local bar = CreateFrame("Frame", name, UIParent)
|
|
bar:SetHeight(HEIGHT)
|
|
bar:EnableMouse(true)
|
|
|
|
local background = bar:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(background, "ui-hud-experiencebar-background-camelot", true)
|
|
background:SetAllPoints(bar)
|
|
|
|
-- Rested part shows behind the earned part: lower layer, same left edge.
|
|
local rested = bar:CreateTexture(nil, "BORDER")
|
|
rested:SetPoint("LEFT", bar, "LEFT", 0, 0)
|
|
rested:SetHeight(HEIGHT)
|
|
rested:Hide()
|
|
|
|
local fill = bar:CreateTexture(nil, "ARTWORK")
|
|
fill:SetPoint("LEFT", bar, "LEFT", 0, 0)
|
|
fill:SetHeight(HEIGHT)
|
|
fill:Hide()
|
|
|
|
local frame = bar:CreateTexture(nil, "OVERLAY")
|
|
ForeverUI.SetAtlas(frame, "ui-hud-experiencebar-frame-camelot", true)
|
|
frame:SetAllPoints(bar)
|
|
|
|
local text = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
|
text:SetPoint("CENTER")
|
|
text:Hide()
|
|
|
|
bar.background, bar.rested, bar.fill, bar.frame, bar.text =
|
|
background, rested, fill, frame, text
|
|
|
|
-- Dividers and rested tick, in a child frame above the fill and the frame art
|
|
local marks = CreateFrame("Frame", nil, bar)
|
|
marks:SetAllPoints(bar)
|
|
marks:SetFrameLevel(bar:GetFrameLevel() + 1)
|
|
bar.dividers = {}
|
|
for i = 1, SEGMENTS - 1 do
|
|
local divider = marks:CreateTexture(nil, "OVERLAY")
|
|
ForeverUI.SetAtlas(divider, "ui-hud-experiencebar-divider", true)
|
|
divider:SetWidth(DIVIDER_W)
|
|
divider:SetHeight(DIVIDER_H)
|
|
bar.dividers[i] = divider
|
|
end
|
|
local tick = marks:CreateTexture(nil, "OVERLAY")
|
|
ForeverUI.SetAtlas(tick, "ui-hud-experiencebar-frame-pip-camelot")
|
|
tick:Hide()
|
|
bar.marks, bar.tick = marks, tick
|
|
|
|
bar:SetScript("OnEnter", function(self)
|
|
self.text:Show()
|
|
end)
|
|
bar:SetScript("OnLeave", function(self)
|
|
self.text:Hide()
|
|
end)
|
|
|
|
return bar
|
|
end
|
|
|
|
-- Places the dividers on the bar's current width (camelot: on each layout change)
|
|
local function placeDividers(bar)
|
|
local width = bar:GetWidth() or 0
|
|
for i, divider in ipairs(bar.dividers) do
|
|
divider:ClearAllPoints()
|
|
divider:SetPoint("LEFT", bar, "LEFT", math.floor(width / SEGMENTS * i + 0.5), 0)
|
|
end
|
|
end
|
|
|
|
local experience = createBar("ForeverUIExperienceBar")
|
|
local reputation = createBar("ForeverUIReputationBar")
|
|
|
|
-- Reputation: just above experience, or in its place when experience is hidden (max level)
|
|
local function placeReputation()
|
|
local rowLine = ForeverUI.BottomRow
|
|
if not rowLine then
|
|
return
|
|
end
|
|
local center = (rowLine.left + rowLine.right) / 2
|
|
local y = experience:IsShown() and rowLine.top + HEIGHT or rowLine.top
|
|
ForeverUI.Layout.SetDefaults("reputationbar", "BOTTOM", "BOTTOM", center, y)
|
|
end
|
|
|
|
-- ---------- Experience
|
|
local function updateExperience()
|
|
local maximum = UnitXPMax("player")
|
|
local level = UnitLevel("player")
|
|
local maxLevel = MAX_PLAYER_LEVEL or 80
|
|
|
|
-- hidden too while a vehicle bar replaces the player's bars (Vehicle.lua)
|
|
if ForeverUI.VehicleArt or not maximum or maximum <= 0 or (level and level >= maxLevel) then
|
|
experience:Hide()
|
|
placeReputation()
|
|
return
|
|
end
|
|
|
|
experience:Show()
|
|
placeReputation()
|
|
|
|
local width = experience:GetWidth()
|
|
local earned = UnitXP("player")
|
|
local rested = (GetXPExhaustion and GetXPExhaustion()) or 0
|
|
local fraction = earned / maximum
|
|
|
|
populate(experience.rested, "ui-hud-experiencebar-fill-rested-camelot",
|
|
(earned + rested) / maximum, width)
|
|
-- rested tick at the end of the rested part
|
|
if rested > 0 then
|
|
local x = math.min(1, (earned + rested) / maximum) * width
|
|
experience.tick:ClearAllPoints()
|
|
experience.tick:SetPoint("CENTER", experience, "LEFT", x, 0)
|
|
experience.tick:Show()
|
|
else
|
|
experience.tick:Hide()
|
|
end
|
|
-- rested (GetRestState 1): the earned part turns bright blue over the darker rested bonus
|
|
local isRested = GetRestState and GetRestState() == 1
|
|
populate(experience.fill, isRested and "ui-hud-experiencebar-fill-prediction-camelot"
|
|
or "ui-hud-experiencebar-fill-experience-camelot", fraction, width)
|
|
|
|
experience.text:SetText(string.format("%d / %d (%d%%)", earned, maximum,
|
|
math.floor(fraction * 100)))
|
|
end
|
|
|
|
-- ---------- Reputation
|
|
local function updateReputation()
|
|
local name, standingId, minimum, maximum, value = GetWatchedFactionInfo()
|
|
if ForeverUI.VehicleArt or not name or not maximum or maximum <= minimum then
|
|
reputation:Hide()
|
|
return
|
|
end
|
|
|
|
reputation:Show()
|
|
|
|
local span = maximum - minimum
|
|
local earned = value - minimum
|
|
local fraction = earned / span
|
|
|
|
populate(reputation.fill, ATLAS_REPUTATION[standingId] or ATLAS_REPUTATION[4],
|
|
fraction, reputation:GetWidth())
|
|
|
|
reputation.text:SetText(string.format("%s %d / %d", name, earned, span))
|
|
end
|
|
|
|
-- ---------- Placement
|
|
-- Both bars span the row (action bar, micro menu, bags) and touch: experience on the row,
|
|
-- reputation just above (or on the row without experience).
|
|
local function place()
|
|
local rowLine = ForeverUI.BottomRow
|
|
if not rowLine then
|
|
return
|
|
end
|
|
|
|
local width = rowLine.right - rowLine.left
|
|
local center = (rowLine.left + rowLine.right) / 2
|
|
|
|
experience:SetWidth(width)
|
|
reputation:SetWidth(width)
|
|
placeDividers(experience)
|
|
placeDividers(reputation)
|
|
|
|
ForeverUI.Layout.SetDefaults("experiencebar", "BOTTOM", "BOTTOM", center, rowLine.top)
|
|
placeReputation()
|
|
end
|
|
|
|
local listener = CreateFrame("Frame")
|
|
listener:RegisterEvent("PLAYER_ENTERING_WORLD")
|
|
listener:RegisterEvent("PLAYER_XP_UPDATE")
|
|
listener:RegisterEvent("PLAYER_LEVEL_UP")
|
|
listener:RegisterEvent("UPDATE_EXHAUSTION")
|
|
listener:RegisterEvent("PLAYER_UPDATE_RESTING")
|
|
listener:RegisterEvent("UPDATE_FACTION")
|
|
listener:SetScript("OnEvent", function(_self, event)
|
|
if event == "PLAYER_ENTERING_WORLD" then
|
|
-- The client's bars: experience, rested tick, and reputation, which re-places itself on
|
|
-- every update.
|
|
ForeverUI.Suppress(MainMenuExpBar)
|
|
ForeverUI.Suppress(ExhaustionTick)
|
|
ForeverUI.Suppress(ReputationWatchBar)
|
|
place()
|
|
end
|
|
updateExperience()
|
|
updateReputation()
|
|
end)
|
|
|
|
ForeverUI.Layout.Register(experience, "experiencebar", L.STATUSBARS_EDIT_LABEL_EXPERIENCE, "BOTTOM", "BOTTOM", 0, 54)
|
|
ForeverUI.Layout.Register(reputation, "reputationbar", L.STATUSBARS_EDIT_LABEL_REPUTATION, "BOTTOM", "BOTTOM", 0, 67)
|
|
place()
|
|
updateExperience()
|
|
updateReputation()
|
|
|
|
ForeverUI.ReputationBar = reputation
|
|
ForeverUI.StatusBarsUpdate = function()
|
|
updateExperience()
|
|
updateReputation()
|
|
end
|
|
-- the row changed width (BottomBar.lua: a micro button was added)
|
|
ForeverUI.StatusBarsLayout = function()
|
|
place()
|
|
updateExperience()
|
|
updateReputation()
|
|
end
|