From 31706821cd3868e84108c39ab010ccdd5766ec6c Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 31 Aug 2026 19:54:58 +0700 Subject: [PATCH] the combat log's wire completes, and PlayerModel is its own class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - the combat log's last eight wire bodies decode — PARTYKILLLOG, SPELLINSTAKILLLOG, PROCRESIST, SPELLORDAMAGE_IMMUNE, SPELLDISPELLOG, DISPEL_FAILED, ENCHANTMENTLOG, SPELLLOGEXECUTE — each layout read off vmangos's own writer and pinned by a byte-exact golden (DISPEL_FAILED carries no count, the list runs to the end of the body; SPELLLOGEXECUTE's POWER_DRAIN row is (amount, power, multiplier), verified at the client's bytes as well as the server's); with the eleven chat types that were waiting on them, 44 chat types become 55 — 29 sentence families, the death pair and the aura rows riding descriptor diffs because 1.12 has no "unit died" opcode, plus the six trailers ((glancing), (30 blocked), (12 absorbed) and kin) that had been missing from every family; also corrected: the durability-death line was going to the red error frame with hard-coded English, and SMSG_SPELLNONMELEEDAMAGELOG lacked its split-damage branch and its blocked wording - PlayerModel is its own frame kind: SetUnit, RefreshUnit and SetRotation were published on Model, but they belong to the method table (0x84f1fc), and the client's lookup chains derived → base only, so a plain can never reach them — they got there from a strings scan read as ownership, and a strings hit answers whether a name EXISTS, not who owns it (SetUnit's one pooled string is referenced by the PlayerModel and GameTooltip tables and by no Model entry); the new PlayerModel → Model → Frame → Region chain unblocks CreateFrame("PlayerModel"), which is how pfUI builds every unit-frame portrait — and through pfUI, pfQuest and ShaguDPS - a nil where a number goes is four shapes, not one law: a census of all 408 widget-registrar entries found 62 numeric positions that validate-or-raise, 43 that gate then stage an alternate, 64 that silently coerce to 0.0 and 5 that gate then store nothing — so SetMaxLetters(nil) completes and stores 0, "no limit" (the argument COUNT is checked exactly, the type is not), and the backdrop colour setters are asymmetric on purpose: a nil r/g/b channel is 0.0 but a nil alpha is OPAQUE, and reading every nil as 0.0 would have turned those borders transparent; both setters now also quantize ×255 into the packed 0xAARRGGBB byte quad the client actually stores, so 0.09 reads back as 23/255 - two more addon-killers: LIGHTYELLOW_FONT_COLOR_CODE was the one missing line of Fonts.xml's seven-line colour block (aux-addon concatenates it at file scope and died on its own first line), and the guard now enumerates the whole block l.4-20 instead of the names an earlier round had noticed absent; and the unit frames publish the reference's names — 1.12 has no *PowerBar anywhere, every bar is ManaBar and the colour table is ManaBarColor (8 of the top-20 addons read it by name), so ManaBarColor is the published table our own bars index into, with 22 more regions renamed to the reference's own Co-Authored-By: Claude Fable 5 --- .../benilla-app/assets/ui/CharacterFrame.xml | 4 +- crates/benilla-app/assets/ui/Fonts.xml | 5 + crates/benilla-app/assets/ui/PartyFrame.xml | 30 +- crates/benilla-app/assets/ui/UnitFrames.xml | 298 ++++--- crates/benilla-app/src/addon_harness/mod.rs | 1 + crates/benilla-app/src/net/apply.rs | 80 +- .../benilla-app/src/net/apply/combat_chat.rs | 778 ++++++++++++++++-- crates/benilla-app/src/net/apply/death.rs | 24 +- crates/benilla-app/src/ui_action/feed.rs | 50 ++ .../benilla-app/src/ui_action/feed_tests.rs | 4 + crates/benilla-app/src/ui_chat.rs | 13 + crates/benilla-app/src/ui_chat/combat.rs | 714 +++++++++++++++- .../benilla-app/src/ui_chat/combat/tests.rs | 52 +- .../benilla-app/src/ui_chat/combat/watch.rs | 355 ++++++++ crates/benilla-app/src/ui_chat/event.rs | 84 +- crates/benilla-app/src/ui_chat/feed.rs | 25 + crates/benilla-app/src/ui_chat/frames.rs | 19 +- crates/benilla-app/src/ui_chat/input/mod.rs | 138 ++++ crates/benilla-app/src/ui_chat/tests.rs | 48 +- .../src/ui_script/pet_frame_tests.rs | 8 +- .../src/ui_script/shipped_xml_tests.rs | 2 +- .../src/ui_script/tooltip_template_tests.rs | 10 +- .../src/ui_script/uiparent_tests.rs | 59 +- .../src/ui_script/unit_frame_tests.rs | 305 +++++-- crates/benilla-app/src/ui_text/mod.rs | 2 +- crates/benilla-protocol/src/events.rs | 39 +- crates/benilla-protocol/src/events/decode.rs | 8 + .../src/messages/combat_log.rs | 339 ++++++++ crates/benilla-protocol/src/messages/mod.rs | 5 +- .../benilla-protocol/src/messages/opcode.rs | 12 + .../benilla-protocol/src/messages/packet.rs | 58 +- crates/benilla-protocol/src/messages/parse.rs | 25 + crates/benilla-protocol/tests/spells.rs | 194 +++++ crates/benilla-ui/src/script/binding_abi.rs | 19 + .../benilla-ui/src/script/editbox/methods.rs | 46 +- crates/benilla-ui/src/script/editbox/tests.rs | 49 ++ crates/benilla-ui/src/script/mod.rs | 1 + crates/benilla-ui/src/script/modelframe.rs | 199 +++-- crates/benilla-ui/src/script/object.rs | 8 + .../src/script/object/frame_state.rs | 88 +- .../benilla-ui/src/script/tests/backdrop.rs | 66 +- .../benilla-ui/src/script/tests/modelframe.rs | 244 +++++- .../src/script/tests/reference_surface.rs | 9 + crates/benilla-ui/src/widget/kinds/mod.rs | 72 +- crates/benilla-ui/src/widget/mod.rs | 7 +- 45 files changed, 4074 insertions(+), 522 deletions(-) create mode 100644 crates/benilla-app/src/ui_chat/combat/watch.rs diff --git a/crates/benilla-app/assets/ui/CharacterFrame.xml b/crates/benilla-app/assets/ui/CharacterFrame.xml index 4a12958e..b165280c 100644 --- a/crates/benilla-app/assets/ui/CharacterFrame.xml +++ b/crates/benilla-app/assets/ui/CharacterFrame.xml @@ -366,7 +366,7 @@ getglobal("CharacterNameText"):SetText(UnitName("player")) if ShowTextStatusBarText then -- engine tests load file subsets ShowTextStatusBarText(PlayerFrameHealthBar) - ShowTextStatusBarText(PlayerFramePowerBar) + ShowTextStatusBarText(PlayerFrameManaBar) ShowTextStatusBarText(MainMenuExpBar) ShowTextStatusBarText(PetFrameHealthBar) ShowTextStatusBarText(PetFrameManaBar) @@ -394,7 +394,7 @@ PlaySound("igCharacterInfoClose") if HideTextStatusBarText then -- engine tests load file subsets HideTextStatusBarText(PlayerFrameHealthBar) - HideTextStatusBarText(PlayerFramePowerBar) + HideTextStatusBarText(PlayerFrameManaBar) HideTextStatusBarText(MainMenuExpBar) HideTextStatusBarText(PetFrameHealthBar) HideTextStatusBarText(PetFrameManaBar) diff --git a/crates/benilla-app/assets/ui/Fonts.xml b/crates/benilla-app/assets/ui/Fonts.xml index 57337815..cbacae2e 100644 --- a/crates/benilla-app/assets/ui/Fonts.xml +++ b/crates/benilla-app/assets/ui/Fonts.xml @@ -50,6 +50,11 @@ RED_FONT_COLOR_CODE = "|cffff2020" -- l.10 GREEN_FONT_COLOR_CODE = "|cff20ff20" -- l.11 GRAY_FONT_COLOR_CODE = "|cff808080" -- l.12 + -- l.13, and it was the ONE line of this block that was missing — the comments beside it + -- even skipped from `l.12` to `l.14`. `aux-addon.lua:7` concatenates it at file scope + -- (`LIGHTYELLOW_FONT_COLOR_CODE .. "aux: " ..`), so its absence was not a wrong colour, it + -- was the addon dying on its own first line. + LIGHTYELLOW_FONT_COLOR_CODE = "|cffffff9a" -- l.13 FONT_COLOR_CODE_CLOSE = "|r" -- l.14 NORMAL_FONT_COLOR = { r = 1.0, g = 0.82, b = 0 } -- l.15 HIGHLIGHT_FONT_COLOR = { r = 1.0, g = 1.0, b = 1.0 } -- l.16 diff --git a/crates/benilla-app/assets/ui/PartyFrame.xml b/crates/benilla-app/assets/ui/PartyFrame.xml index a327fc8d..1c935b11 100644 --- a/crates/benilla-app/assets/ui/PartyFrame.xml +++ b/crates/benilla-app/assets/ui/PartyFrame.xml @@ -120,15 +120,15 @@ local tf = name .. "TextureFrame" frame.unit = "party" .. id frame.portrait = getglobal(name .. "Portrait") - frame.healthBar = getglobal(name .. "HealthBar") - frame.manaBar = getglobal(name .. "ManaBar") + frame.healthbar = getglobal(name .. "HealthBar") + frame.manabar = getglobal(name .. "ManaBar") -- The recessed-bar look, the reference's own mechanism (ref-TargetFrame.lua l.32-34): -- push the bars one frame level BELOW the art's TextureFrame. Frame level is the only -- key term above the draw layer (decision 0884), so this is the only thing that can hold -- the BACKGROUND party art over the ARTWORK bar fills. See the LAYERING note below. local textureFrameLevel = getglobal(tf):GetFrameLevel() - frame.healthBar:SetFrameLevel(textureFrameLevel - 1) - frame.manaBar:SetFrameLevel(textureFrameLevel - 1) + frame.healthbar:SetFrameLevel(textureFrameLevel - 1) + frame.manabar:SetFrameLevel(textureFrameLevel - 1) frame.nameText = getglobal(tf .. "Name") frame.leaderIcon = getglobal(tf .. "LeaderIcon") frame.masterIcon = getglobal(tf .. "MasterIcon") @@ -164,16 +164,16 @@ local unit = frame.unit if not UnitIsConnected(unit) then -- Disconnected: bar forced full and grey, portrait desaturated, icon on. - frame.healthBar:SetMinMaxValues(0, 1) - frame.healthBar:SetValue(1) - frame.healthBar:SetStatusBarColor(0.5, 0.5, 0.5) + frame.healthbar:SetMinMaxValues(0, 1) + frame.healthbar:SetValue(1) + frame.healthbar:SetStatusBarColor(0.5, 0.5, 0.5) SetDesaturation(frame.portrait, 1) frame.disconnect:Show() return end SetDesaturation(frame.portrait, nil) frame.disconnect:Hide() - frame.healthBar:SetStatusBarColor(0.0, 1.0, 0.0) + frame.healthbar:SetStatusBarColor(0.0, 1.0, 0.0) local hp, hpMax = UnitHealth(unit), UnitHealthMax(unit) if UnitIsDead(unit) then frame.portrait:SetVertexColor(0.35, 0.35, 0.35, 1.0) @@ -196,19 +196,19 @@ frame.nameText:SetText(UnitName(unit) or "Unknown") - frame.healthBar:SetMinMaxValues(0, UnitHealthMax(unit)) - frame.healthBar:SetValue(UnitHealth(unit)) + frame.healthbar:SetMinMaxValues(0, UnitHealthMax(unit)) + frame.healthbar:SetValue(UnitHealth(unit)) local maxPower = UnitManaMax(unit) if maxPower > 0 then - frame.manaBar:Show() + frame.manabar:Show() local _, token = UnitPowerType(unit) local c = BENILLA_POWER_COLORS[token] or BENILLA_POWER_COLORS.MANA - frame.manaBar:SetStatusBarColor(c.r, c.g, c.b) - frame.manaBar:SetMinMaxValues(0, maxPower) - frame.manaBar:SetValue(UnitMana(unit)) + frame.manabar:SetStatusBarColor(c.r, c.g, c.b) + frame.manabar:SetMinMaxValues(0, maxPower) + frame.manabar:SetValue(UnitMana(unit)) else - frame.manaBar:Hide() + frame.manabar:Hide() end -- Leader + master-looter icons (ref: GetPartyLeaderIndex / GetLootMethod vs own id). diff --git a/crates/benilla-app/assets/ui/UnitFrames.xml b/crates/benilla-app/assets/ui/UnitFrames.xml index 92261ae5..5c709785 100644 --- a/crates/benilla-app/assets/ui/UnitFrames.xml +++ b/crates/benilla-app/assets/ui/UnitFrames.xml @@ -14,7 +14,7 @@ The ref gets this with a child `TargetFrameTextureFrame` (ring art) and `SetFrameLevel(bar, textureFrame-1)` to push the bars beneath it, and we do the same, in BenillaUnitFrame_OnLoad: the parent holds trough + portrait (+ target's name plate) in BACKGROUND; the - `$parentHealthBar`/`$parentPowerBar` StatusBars sit at the PARENT's frame level; and + `$parentHealthBar`/`$parentManaBar` StatusBars sit at the PARENT's frame level; and `$parentTextureFrame` (232×100, filling the parent) holds the ring art (BACKGROUND) + name/level/dead text (OVERLAY) one level ABOVE them, so it draws over the bar edges and the portrait. @@ -29,7 +29,8 @@ band with transparent corners (not an opaque surround), so the square is hidden by the portrait itself — set via SetPortraitToTexture, masked to its inscribed circle (the client's portraits are always round: a model render or a circular-cropped icon), so no corner reaches the ring's gap. The - text lives under the TextureFrame, so its globals carry the `…TextureFrame…` name prefix (OnLoad). + text lives under the TextureFrame, but its GLOBAL names are the reference's flat ones + (`PlayerName`, `TargetDeadText`, …), not `$parent`-derived — see OnLoad's `short` prefix. The shared black `…Background` trough sits behind the unfilled bar portion. DATA FLOW UNCHANGED (pin: re-anchor + re-skin, don't rewrite the eventing): BenillaUnitFrame_* @@ -138,13 +139,31 @@ NEWBIE_TOOLTIP_PARTYOPTIONS = NEWBIE_TOOLTIP_PARTYOPTIONS or "If you belong to a party, you can right-click your portrait to bring up the Party Options menu. This menu will allow you to quit the party or see what looting rules the party is using. A party leader can add or remove party members, as well as change the party's looting rules." NEWBIE_TOOLTIP_PLAYEROPTIONS = NEWBIE_TOOLTIP_PLAYEROPTIONS or "Right-click to bring up special commands for interacting with another player. You can inspect their equipment, issue a party invite, initiate a trade, or challenge a player to a duel. A group leader can promote or remove that player from the group." - -- The client's PowerBarColor values, keyed by the UnitPowerType token. + -- ── ManaBarColor (ref UnitFrame.lua l.1-6, verbatim) ──────────────────────────────── + -- The reference's own table: keyed by the NUMERIC power type `UnitPowerType` returns, each + -- row carrying the `prefix` its bar's text is labelled with. **8 addons in the top-20 root + -- read `ManaBarColor` by name**, which is why it is the published one rather than a + -- benilla-private table — we used to keep the same five colours under + -- `BENILLA_POWER_COLORS`, keyed by token string and carrying no prefix, so every one of + -- those reads found nil. + -- + -- One table, not two: the token map below is DERIVED from it rather than restated, so a + -- colour can never differ between what an addon reads and what our own bars paint. + ManaBarColor = {} + ManaBarColor[0] = { r = 0.00, g = 0.00, b = 1.00, prefix = MANA or "Mana" } + ManaBarColor[1] = { r = 1.00, g = 0.00, b = 0.00, prefix = RAGE_POINTS or "Rage" } + ManaBarColor[2] = { r = 1.00, g = 0.50, b = 0.25, prefix = FOCUS_POINTS or "Focus" } + ManaBarColor[3] = { r = 1.00, g = 1.00, b = 0.00, prefix = ENERGY_POINTS or "Energy" } + ManaBarColor[4] = { r = 0.00, g = 1.00, b = 1.00, prefix = HAPPINESS_POINTS or "Happiness" } + + -- Our own bars look colours up by the TOKEN `UnitPowerType` also returns (its 2nd value), + -- so this maps token → the row above. Same objects, so `ManaBarColor` is the only source. BENILLA_POWER_COLORS = { - MANA = { r = 0.0, g = 0.0, b = 1.0 }, - RAGE = { r = 1.0, g = 0.0, b = 0.0 }, - FOCUS = { r = 1.0, g = 0.5, b = 0.25 }, - ENERGY = { r = 1.0, g = 1.0, b = 0.0 }, - HAPPINESS = { r = 0.0, g = 1.0, b = 1.0 }, + MANA = ManaBarColor[0], + RAGE = ManaBarColor[1], + FOCUS = ManaBarColor[2], + ENERGY = ManaBarColor[3], + HAPPINESS = ManaBarColor[4], } -- ── The raid-target mark atlas (ref TargetFrame.lua l.460-463 + l.475-484) ────────────── @@ -202,18 +221,25 @@ function BenillaUnitFrame_OnLoad(frame, unit) frame.unit = unit local name = frame:GetName() - frame.healthBar = getglobal(name .. "HealthBar") - frame.powerBar = getglobal(name .. "PowerBar") - -- The name/level/dead text live inside $parentTextureFrame (so they draw over the ring art - -- that draws over the bars), so their global names carry the TextureFrame prefix. + frame.healthbar = getglobal(name .. "HealthBar") + frame.manabar = getglobal(name .. "ManaBar") + -- TWO prefixes, both the reference's own. The bars are named off the FRAME + -- (`PlayerFrameHealthBar`, ref-PlayerFrame.xml l.288) but the art, text and icons are + -- named off the UNIT (`PlayerName` l.58, `PlayerPortrait` l.35, `TargetDeadText` + -- ref-TargetFrame.xml l.173) — flat globals, which is what an addon reaches for. + -- `short` is the frame name minus its trailing "Frame". + local short = string.sub(name, 1, string.len(name) - 5) + -- Our ring art rides a $parentTextureFrame child (the reference nests two ANONYMOUS + -- frames there instead, ref-PlayerFrame.xml l.49-51), so the frame whose level the bars + -- are pushed under is still named off the frame. local tf = name .. "TextureFrame" -- The recessed-bar look, the reference's own mechanism (ref-TargetFrame.lua l.32-34): -- push the bars one frame level BELOW the ring art's TextureFrame. Frame level is the -- only key term above the draw layer (decision 0884), so this is the only thing that can -- hold a BACKGROUND ring texture over an ARTWORK bar fill. See the LAYERING note up top. local textureFrameLevel = getglobal(tf):GetFrameLevel() - frame.healthBar:SetFrameLevel(textureFrameLevel - 1) - frame.powerBar:SetFrameLevel(textureFrameLevel - 1) + frame.healthbar:SetFrameLevel(textureFrameLevel - 1) + frame.manabar:SetFrameLevel(textureFrameLevel - 1) -- The on-bar numerals (TextStatusBar.xml, decision 1082; the switch that shows them -- is 1140's Status Bar Text). Wiring is unconditional — a frame with no text regions -- gets a no-op, since SetTextStatusBarText drops a nil text and UpdateTextString skips @@ -223,13 +249,13 @@ -- and the XP bar, and on nothing else, which is exactly why Status Bar Text pins your -- own numbers and your pet's but never the target's (decision 1143). if TextStatusBar_Initialize then -- engine tests load file subsets - TextStatusBar_Initialize(frame.healthBar) - TextStatusBar_Initialize(frame.powerBar) - SetTextStatusBarText(frame.healthBar, getglobal(tf .. "HealthBarText")) - SetTextStatusBarText(frame.powerBar, getglobal(tf .. "PowerBarText")) + TextStatusBar_Initialize(frame.healthbar) + TextStatusBar_Initialize(frame.manabar) + SetTextStatusBarText(frame.healthbar, getglobal(name .. "HealthBarText")) + SetTextStatusBarText(frame.manabar, getglobal(name .. "ManaBarText")) if unit == "player" then - frame.healthBar.textLockable = 1 - frame.powerBar.textLockable = 1 + frame.healthbar.textLockable = 1 + frame.manabar.textLockable = 1 end end -- The hover seam (1146): who this bar belongs to, and what its click means. @@ -237,16 +263,16 @@ if unit == "player" then click = PlayerFrame_OnClick end - frame.healthBar.owner = frame - frame.powerBar.owner = frame - frame.healthBar.ownerClick = click - frame.powerBar.ownerClick = click - frame.nameText = getglobal(tf .. "NameText") - frame.levelText = getglobal(tf .. "LevelText") - frame.deadText = getglobal(tf .. "DeadText") + frame.healthbar.owner = frame + frame.manabar.owner = frame + frame.healthbar.ownerClick = click + frame.manabar.ownerClick = click + frame.nameText = getglobal(short .. "Name") + frame.levelText = getglobal(short .. "LevelText") + frame.deadText = getglobal(short .. "DeadText") -- Target-only (the "??" skull on the level slot); getglobal returns nil for the -- player, guarded at use. - frame.highLevel = getglobal(tf .. "HighLevelTexture") + frame.highLevel = getglobal(short .. "HighLevelTexture") -- Target-only (the level/name plate); getglobal returns nil for the player, guarded at use. frame.nameBackground = getglobal(name .. "NameBackground") -- The live model portrait (the real API's global, ref-UnitFrame.lua): bind the portrait @@ -256,11 +282,11 @@ -- model change), which is why the ref's UNIT_PORTRAIT_UPDATE re-call isn't needed here (the -- event joins the surface when addon parity wants it). Until a bake lands (no unit / model -- still loading) the circle is simply empty. - frame.portrait = getglobal(name .. "Portrait") + frame.portrait = getglobal(short .. "Portrait") SetPortraitTexture(frame.portrait, unit) -- The PvP flag icon (decision 0646), under the TextureFrame like the ring art it -- draws over — see BenillaUnitFrame_UpdatePvP. - frame.pvpIcon = getglobal(tf .. "PVPIcon") + frame.pvpIcon = getglobal(short .. "PVPIcon") frame:RegisterEvent("PLAYER_ENTERING_WORLD") frame:RegisterEvent("UNIT_HEALTH") frame:RegisterEvent("UNIT_MAXHEALTH") @@ -283,26 +309,26 @@ if unit == "target" then frame:RegisterEvent("UNIT_AURA") frame:RegisterEvent("UNIT_CLASSIFICATION_CHANGED") - frame.frameTexture = getglobal(tf .. "Texture") + frame.frameTexture = getglobal(name .. "Texture") end -- The portrait hit indicator (decision 0576): only the player frame registers -- UNIT_COMBAT in 1.12 (ref PlayerFrame_OnLoad — the target frame has no feedback -- text). Ref: CombatFeedback_Initialize(PlayerHitIndicator, 30). if unit == "player" then - frame.feedbackText = getglobal(tf .. "HitIndicator") + frame.feedbackText = getglobal(short .. "HitIndicator") frame.feedbackFontHeight = 30 frame:RegisterEvent("UNIT_COMBAT") -- The status flash (ref PlayerFrame_OnLoad, decision 1082): the resting zzz / -- auto-attack swords under the portrait, the gold/red ring, and the pulse -- counters. Handles cached like pvpIcon above; their presence is what gates -- PlayerFrame_UpdateStatus in the shared Update. - frame.statusTexture = getglobal(tf .. "StatusTexture") - frame.attackBackground = getglobal(tf .. "AttackBackground") - frame.restIcon = getglobal(tf .. "RestIcon") - frame.attackIcon = getglobal(tf .. "AttackIcon") - frame.statusGlow = getglobal(name .. "StatusGlow") - frame.restGlow = getglobal(name .. "StatusGlowRestGlow") - frame.attackGlow = getglobal(name .. "StatusGlowAttackGlow") + frame.statusTexture = getglobal(short .. "StatusTexture") + frame.attackBackground = getglobal(short .. "AttackBackground") + frame.restIcon = getglobal(short .. "RestIcon") + frame.attackIcon = getglobal(short .. "AttackIcon") + frame.statusGlow = getglobal(short .. "StatusGlow") + frame.restGlow = getglobal(short .. "RestGlow") + frame.attackGlow = getglobal(short .. "AttackGlow") frame.statusCounter = 0 frame.statusSign = -1 frame.attackBackground:SetVertexColor(0.8, 0.1, 0.1) @@ -320,8 +346,8 @@ -- shared Update exactly like statusTexture above — the target frame declares -- neither texture, so it never runs the law, and the party member frames run -- their own (PartyFrame.xml, a different predicate: an INDEX comparison). - frame.leaderIcon = getglobal(tf .. "LeaderIcon") - frame.masterIcon = getglobal(tf .. "MasterIcon") + frame.leaderIcon = getglobal(short .. "LeaderIcon") + frame.masterIcon = getglobal(short .. "MasterIcon") frame:RegisterEvent("PARTY_MEMBERS_CHANGED") frame:RegisterEvent("PARTY_LEADER_CHANGED") frame:RegisterEvent("PARTY_LOOT_METHOD_CHANGED") @@ -694,7 +720,7 @@ -- plate on (the reference has no such art for the others and leaves an empty rail drawn). -- -- The bars are ARGUMENTS rather than fields: the player and target frames call theirs - -- `powerBar` and the pet frame calls its `manaBar`, following the reference's own two names + -- `manabar` and the pet frame calls its `manabar`, following the reference's own two names -- for one rail, and a helper that reached for a field would have to pick a side. -- -- NO label on the power bar. 1.12 names the resource there (ref UnitFrame_UpdateManaType @@ -703,20 +729,20 @@ -- day: bare numbers read better on these bars (decision 1147). The mechanism stays — -- SetTextStatusBarTextPrefix is live and one call brings any label back — only the calls -- are gone. - function BenillaUnitFrame_PaintBars(healthBar, powerBar, unit) - healthBar:SetMinMaxValues(0, UnitHealthMax(unit)) - healthBar:SetValue(UnitHealth(unit)) + function BenillaUnitFrame_PaintBars(healthbar, manabar, unit) + healthbar:SetMinMaxValues(0, UnitHealthMax(unit)) + healthbar:SetValue(UnitHealth(unit)) local maxPower = UnitManaMax(unit) if maxPower <= 0 then - powerBar:Hide() + manabar:Hide() return false end - powerBar:Show() + manabar:Show() local _, powerToken = UnitPowerType(unit) local swatch = BENILLA_POWER_COLORS[powerToken] or BENILLA_POWER_COLORS.MANA - powerBar:SetStatusBarColor(swatch.r, swatch.g, swatch.b) - powerBar:SetMinMaxValues(0, maxPower) - powerBar:SetValue(UnitMana(unit)) + manabar:SetStatusBarColor(swatch.r, swatch.g, swatch.b) + manabar:SetMinMaxValues(0, maxPower) + manabar:SetValue(UnitMana(unit)) return true end @@ -729,7 +755,7 @@ end frame:Show() - BenillaUnitFrame_PaintBars(frame.healthBar, frame.powerBar, unit) + BenillaUnitFrame_PaintBars(frame.healthbar, frame.manabar, unit) frame.nameText:SetText(UnitName(unit) or "Unknown") @@ -1173,7 +1199,7 @@ frame LEVEL, and nothing else in this layer overlaps the portrait's square, so the two are indistinguishable here — left alone rather than churned inside a bug fix). Texture set in OnLoad via SetPortraitToTexture (circle-masked). --> - + @@ -1202,7 +1228,7 @@ - + @@ -1229,7 +1255,7 @@ - + @@ -1247,35 +1273,43 @@ number rode ON the badge (director report, 2026-08-07); decision 1093. --> - + The NAME is the reference's own, literally — and so is every other + art/text/icon region in the three unit frames. They used to derive + from `$parent`, which published `PlayerFrameTextureFrameNameText` and + left `PlayerName` (PlayerFrame.xml:58), `TargetName` + (TargetFrame.xml:152) and `PetName` (PetFrame.xml:69) — the names an + addon actually reaches for — nil. See BenillaUnitFrame_OnLoad's + `short` prefix for how the shared binder composes them now. + + **This was tried once and REJECTED, and the rejection was right on + its evidence** — the block that stood here argued: (i) not free, since + `UnitFrame_OnLoad` composes these names inside `getglobal`, so a text + search finds nothing and the rename breaks every unit frame (it broke + 40 tests); and (ii) buys nothing, since a "why" read-back over the + 218-addon corpus matched ZERO error rows for `TargetName`, its one + syntactic caller being a `` in an addon that dies + earlier anyway. It closed "revisit if a row ever names it" (1222's + clause: a shadowed name with no observable caller is building against + a guess). + + What changed (1719): the condition it named was met, and its cost + premise expired. A grep of the top-20 root gives `ManaBarColor` 8 + readers, `PlayerFrameManaBar` 3, `PlayerFrameTexture`/ + `TargetFrameTexture`/`PlayerStatusTexture`/`TargetFrameManaBar` 2 + each, and `PlayerName`/`TargetName`/`PetName` 1 each. ShaguTweaks + dies on `TargetFrameManaBar` at `health-numbers.lua:22`. **Fifteen of + the twenty-two renames still have zero measured readers** and were + kept for consistency with the reference and because the cost was + already paid, NOT because demand was shown — say the word and they go + back. --> + - + @@ -1295,12 +1329,12 @@ They draw over the bar FILL because the whole TextureFrame outranks the bars by a frame level. Empty until Status Bar Text is on or (later) the bar is hovered — TextStatusBar.xml owns that decision, not this file. --> - + - + @@ -1311,7 +1345,7 @@ ref-PlayerFrame.xml l.124-135) — ARTWORK over the BACKGROUND ring art, like the ref's. Texture chosen at runtime (decision 0646 §4); the ref's PlayerPVPIconHitArea is deliberately OUT (see the header). --> - - + @@ -1565,7 +1599,7 @@ - + @@ -1577,7 +1611,7 @@ - - + - + @@ -1601,7 +1635,7 @@ - + @@ -1613,7 +1647,7 @@ key never reaches the screen. `hidden` like the ToT twin below, a deliberate divergence: the reference leaves the region shown and relies on CheckDead landing before the frame does. --> -