- forty files that carried the reference's literal Lua are rewritten as our own expression of the behaviour — worst identical run 61 lines down to 7 — and the README now says exactly that - the overlap gate measures the code itself across all four reference corpora, and ratchets: the number may only go down - a lost session ends at the login screen instead of silently re-authenticating and stealing the account back - a building's skybox is a real model: Caverns of Time's white ceiling becomes its painted nebula and additive stars Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1983 lines
134 KiB
XML
1983 lines
134 KiB
XML
<Ui>
|
||
<!--
|
||
benilla's character window (decision 0208, phase 1a display + phase 1b equip interactions
|
||
— the C-key window, paper doll first):
|
||
the round player portrait + name + level line + close button + the five-tab row, dressed
|
||
from the real 1.12 `CharacterFrame.xml`/`.lua` + `PaperDollFrame.xml`/`.lua` +
|
||
`CharacterFrameTemplates.xml` + the `Model_*` rotate helpers (`UIParent.lua:1421-1462`) —
|
||
extracted from the MPQ chain to session scratch this session (director-local reference
|
||
artifacts, never committed — the 0068/0138 pattern; line cites below are into that
|
||
extraction's own `ref-CharacterFrame.xml`/`.lua`, `ref-PaperDollFrame.xml`/`.lua`,
|
||
`ref-UIParent.lua`, `ref-GlobalStrings.lua`). `CharacterFrameTabButtonTemplate` is ALREADY
|
||
defined in `UiPanels.xml:507-563` (battle-tested by Merchant/QuestLog, 0138) — reused
|
||
here, not redefined.
|
||
|
||
Docks the left UIPanel slot exactly like Gossip/Merchant/Quest/QuestLog (`UiPanels.xml`
|
||
`UIPanelWindows["CharacterFrame"] = { area = "left", pushable = 0 }`, mirroring the
|
||
ref's own `CharacterFrame` row, `UIParent.lua` pin cited in `UiPanels.xml`'s header) — so it
|
||
competes for/replaces those windows, never stacks with them. `ToggleCharacter(tab)` (ref
|
||
`CharacterFrame.lua:3-23`) is the show/hide/tab-switch entry point;
|
||
the app's 'C' binding calls it (`ui_script/mod.rs`).
|
||
|
||
Every frame this file defines wears the REFERENCE's name (`CharacterFrame`,
|
||
`PaperDollFrame`, …) — including the strings `ToggleCharacter`/`CharacterFrame_ShowSubFrame`
|
||
pass around internally (`BENILLA_CHARACTERFRAME_SUBFRAMES` lists `"PaperDollFrame"`). The
|
||
`Benilla` prefix these carried until the reference-naming pass is gone: addons index these
|
||
globals by the reference's name, and a prefixed frame is simply the wrong name for a frame
|
||
that IS the reference's.
|
||
|
||
Phase 1a shipped the window, display-only. Phase 1b (THIS pass) adds the equip
|
||
interactions over the SAME display — `PickupInventoryItem`/`UseInventoryItem`/
|
||
`AutoEquipCursorItem`/`CursorCanGoInSlot`/`IsInventoryItemLocked`, joining the paper doll
|
||
onto the ONE cursor-payload space via its sentinel `EQUIPMENT_BAG` bag id (decision 0216
|
||
§1, extended by decision 0208's own phasing, §"Phasing"/"1a"/"1b", and 0216/0218's
|
||
cursor-arc design). Tabs 2-5 (Pet/Reputation/Skills/Honor) were all OMITTED at 1b, the 0138
|
||
merchant-tab precedent ("tabs arrive with their pages") — `BENILLA_CHARACTERFRAME_SUBFRAMES`
|
||
only lists the pages that exist. Skills arrived with decision 0437 phase 4
|
||
(`SkillFrame.xml`), **Pet with decision 1057** (`PetPaperDollFrame.xml`), which takes the
|
||
real client's own slot 2, and **Reputation** (`ReputationFrame.xml`), which takes its own
|
||
slot 3 and puts Skills back on the reference's 4 — so the first four tabs are the
|
||
reference's, in its order, at its numbers, and only Honor (its tab 5) is still out. Three things here are shared
|
||
WITH the pet page rather than owned by this one, and each is flagged at its definition: the
|
||
`(unit, prefix)` stat setters (including `_SetDefense`, which exists for the pet row alone),
|
||
the `BenillaPaperDollModel_*` pane quartet (driven through each pane's own `frame.setFacing`),
|
||
and the tab row itself, whose tab 2 the pet page raises and hides.
|
||
|
||
Named deferrals/OMITs (each re-cited at its own site below):
|
||
- Guild line (`CharacterGuildText`) and the PVP rank name (`UnitPVPName` — plain `UnitName`
|
||
ships instead): both decision 0208's own named deferrals (guild needs `CMSG_GUILD_QUERY`;
|
||
rank needs the honor arc). `CharacterTitleText` (also PVP-rank-driven) is dropped with it.
|
||
- Shift-click chat-link insert (decision 1059) and ctrl-click `DressUpItemLink` (the
|
||
dressing room, decision 1060) both LANDED — the left-click fork below is now the
|
||
reference's own three arms (ctrl → preview, shift → post the link, else pick up).
|
||
They were named deferrals when this file was written; the note stays because the fork
|
||
below reads as a transcription of that history.
|
||
- The ammo slot (id 0) LOADS ammo by entry (`CMSG_SET_AMMO`) — it does NOT swap into an
|
||
equip slot (decision 0526; wow-re `cursor-dragdrop-slots.md`: the client's auto-equip
|
||
sender forks ammo-class → SET_AMMO). So it can't share the 19 named slots'
|
||
`PickupInventoryItem` path — its click/drop route to `BenillaCharacterAmmoSlot_OnClick`
|
||
→ `AutoEquipCursorItem` (the app's autoequip drain resolves the held entry and forks to
|
||
SET_AMMO), gated by `CursorCanGoInSlot(0)` so only an ammo payload fits. It carries no
|
||
ring, so the `CURSOR_UPDATE` highlight / `ITEM_LOCK` dim legs no-op, and there is no
|
||
drag-OUT of loaded ammo; it keeps sharing `BenillaPaperDollSlot_OnLoad`/`_Update`/
|
||
`_OnEvent` for its DISPLAY. (The four equipped-bag icons, ids 20-23, joined the
|
||
`EQUIPMENT_BAG` payload space in decision 0216/0218 — no longer out of the drag space.)
|
||
- Cooldowns (`GetInventoryItemCooldown`/`CooldownFrameTemplate`), the broken-red tint
|
||
(`GetInventoryItemBroken`), and the shift-compare tooltip (`SHOW_COMPARE_TOOLTIP`):
|
||
none of their backing feeds/engine affordances exist yet — the item stats feed has no
|
||
durability/cooldown channel (the same named gap as 0138's damaged-sell-price).
|
||
- The `CURSOR_UPDATE` slot highlight is an EMULATION, not the reference's true
|
||
`LockHighlight`/`UnlockHighlight` (grepped `benilla-ui/src/script/button.rs` this
|
||
session — no such Button methods exist; a Button's highlight texture only ever shows
|
||
on REAL mouse hover, `ButtonState::region_visible`'s hardwired `enabled && hovered`, no
|
||
forced-show path). A real forced-highlight affordance is a `benilla-ui` follow-up, not
|
||
blocking this slice — `BenillaPaperDollSlot_LockHighlight`/`_UnlockHighlight` below
|
||
tint the slot's Quickslot2 ring instead, flagged at their own definition.
|
||
- The relic slot: `UnitHasRelicSlot` is vanilla-always-false (there is no relic slot in
|
||
1.12), so the ref's `CharacterAmmoSlot:Hide()`/relic-texture branches are DEAD CODE even
|
||
in the real client — omitted outright rather than transcribing unreachable branches; the
|
||
ammo slot always shows.
|
||
- Class-specific stat-tooltip flavor text (`WARRIOR_STRENGTH_TOOLTIP` and its ~20 siblings):
|
||
only the 5 `DEFAULT_*_TOOLTIP` fallbacks ship. `PaperDollStatTooltip` still looks
|
||
the class-specific global up FIRST exactly like the ref, so dropping in the per-class
|
||
table later is pure string data, no logic change — the numeric stat lines (the
|
||
load-bearing content) are complete either way.
|
||
- The model pane ships as a booth-texture stand-in, not a live `<Model>`/`<PlayerModel>`
|
||
widget (decision 0208 §5): our engine's `FrameKind::Model` parses but has no
|
||
`SetUnit`/`RefreshUnit` surface (verified this session — grepped `benilla-ui/src/script`),
|
||
so `CharacterModelFrame` materializes here as a plain `<Frame>` sampling the paper-doll
|
||
booth bake via `BenillaSetBoothTexture(region, "paperdoll")` (the `SetPortraitTexture`
|
||
plumbing's sibling, per this session's parallel-build contract) — `DISPLAY_SIZE_CHANGED`/
|
||
`RefreshUnit` don't apply and are dropped. `OnMouseUp`-to-equip DOES apply, though (1b):
|
||
it needs no live 3D — just `EnableMouse(true)` + the two script hooks, added below. The
|
||
held-button continuous spin (`Model_OnUpdate`, ref `UIParent.lua:1444-1462`) needs
|
||
`GetButtonState()=="PUSHED"` — grepped, no such method exists on our `Button` — so this
|
||
ships CLICK-ONLY (±0.03 rad/click); the held-spin is a follow-up once/if `GetButtonState`
|
||
lands.
|
||
- The ammo slot's data path (verified this session, not the speculative framing in decision
|
||
0208 §4): the ref's `CharacterAmmoSlot` is NOT driven by a separate `PLAYER_AMMO_ID`
|
||
Lua global — it shares the exact same `PaperDollItemSlotButton_OnLoad`/`_Update` functions
|
||
the 19 named slots use (ref `ref-PaperDollFrame.xml:999-1028`, byte-identical to the
|
||
shared template's own Scripts block at l.8-37), just keyed off `GetInventorySlotInfo(
|
||
"AmmoSlot")` instead of e.g. `"HeadSlot"`. Whatever `PLAYER_AMMO_ID`/bag-summing happens
|
||
for that particular slot id is the parallel binding's own implementation detail, invisible
|
||
to this Lua — re-expressed here exactly as the ref wires it.
|
||
- `$parentStock` (the ammo slot's yellow "in bags" count, ref `ref-PaperDollFrame.xml:
|
||
970-978`): no Lua in this session's extraction ever populates it (likely an ammo-picker-
|
||
addon-only overlay outside this pull) — dropped rather than guessed at.
|
||
|
||
Engine-gap notes (transcribed anyway per this file's own instructions, or worked around) —
|
||
the first two are CLOSED, kept only because the workarounds below still read as if they held:
|
||
- `HitRectInsets` (both `CharacterFrame` and `PaperDollFrame`, ref's identical
|
||
0/30/0/45 on both) was a loader no-op when this file was written; it landed with the
|
||
micro menu (decision 0655) and now really insets the mouse rect.
|
||
- `enableMouse`/`setAllPoints` (frame-level) were no-ops on `<Frame>` too; both are wired
|
||
now (`loader/geometry.rs`'s `apply_attrs`/`apply_anchors`, and
|
||
`loader/tests.rs::frame_set_all_points_pins_to_parent`). `PaperDollFrame` still
|
||
carries an explicit `Size`+`TOPLEFT` anchor rather than the ref's `setAllPoints="true"`
|
||
— exactly equivalent for a child sized to fill its parent, so it is left alone rather
|
||
than churned.
|
||
- A plain `<Frame>` isn't mouse-enabled by construction in this engine (only Button/
|
||
CheckButton/EditBox/ScrollingMessageFrame are — `widget/mod.rs`'s ctor comment, already
|
||
the reason `UnitFrames.xml` calls `EnableMouse(true)` on its own root frame) — every
|
||
hover-tooltip `<Frame>` below (the 5 attribute rows, armor/attack/damage/ranged rows, the
|
||
5 resistance frames — none of them Buttons) gets its own `self:EnableMouse(true)` in
|
||
`OnLoad` for this reason; the 19 item slots are Buttons and need no such call.
|
||
- `TEXT(x)` (the ref's localization-tooling identity macro) has no engine equivalent and
|
||
needs none — it's a build-time no-op wrapper in the real client, so every `TEXT(FOO)` call
|
||
below is transcribed as the bare `FOO` global.
|
||
- The font color codes (`HIGHLIGHT_FONT_COLOR_CODE`/`GREEN_FONT_COLOR_CODE`/
|
||
`RED_FONT_COLOR_CODE`/`FONT_COLOR_CODE_CLOSE`, `NORMAL_FONT_COLOR`/`HIGHLIGHT_FONT_COLOR`)
|
||
are defined by the real `Fonts.xml`'s own <Script> block (ref l.8-19 — extracted and
|
||
verified this session), so ours live in our `Fonts.xml` (loaded first), not here.
|
||
|
||
Sounds (decision 0090): `igCharacterInfoOpen`/`Close` on Show/Hide (ref `CharacterFrame.lua:
|
||
83/96`), `igCharacterInfoTab` on the tab click (ref `CharacterFrameTab_OnClick`, l.47),
|
||
`igInventoryRotateCharacter` on each rotate click (ref `UIParent.lua:1432/1441`).
|
||
-->
|
||
<Script><![CDATA[
|
||
-- ── GlobalStrings values this window uses (ref-GlobalStrings.lua, cited per line — the
|
||
-- `X = X or "..."` idiom matches CastingBar.xml's own convention: a real GlobalStrings.lua,
|
||
-- if one ever loads, wins over this fallback) ──────────────────────────────────────────────
|
||
CHARACTER = CHARACTER or "Character" -- :481
|
||
SKILLS = SKILLS or "Skills" -- :3477 (decision 0437 phase 4, the Skills tab label)
|
||
PET = PET or "Pet" -- :3010 (the Pet tab label, decision 1057)
|
||
REPUTATION = REPUTATION or "Reputation" -- :3328 (the Reputation tab label)
|
||
PLAYER_LEVEL = PLAYER_LEVEL or "Level %d %s %s" -- :3075
|
||
ARMOR = ARMOR or "Armor" -- :62
|
||
ARMOR_COLON = ARMOR_COLON or "Armor:" -- :63
|
||
DEFENSE_COLON = DEFENSE_COLON or "Defense:" -- :917 (the shared
|
||
-- _SetDefense's label; only
|
||
-- the pet page has the row)
|
||
ARMOR_TOOLTIP = ARMOR_TOOLTIP or
|
||
"Decreases the amount of damage taken from physical attacks. The amount of reduction is influenced by the level of the attacker.\nDamage reduction against a level %d attacker: %.1f%%" -- :65
|
||
ATTACK_POWER_COLON = ATTACK_POWER_COLON or "Power:" -- :71
|
||
ATTACK_SPEED_COLON = ATTACK_SPEED_COLON or "Attack Speed (seconds):" -- :73
|
||
ATTACK_TOOLTIP = ATTACK_TOOLTIP or "Attack Rating" -- :75
|
||
ATTACK_TOOLTIP_SUBTEXT = ATTACK_TOOLTIP_SUBTEXT or
|
||
"Your attack rating affects your chance to hit a target, and is based on the weapon skill of the weapon you are currently wielding." -- :76
|
||
DAMAGE_COLON = DAMAGE_COLON or "Damage:" -- :888
|
||
DAMAGE_PER_SECOND = DAMAGE_PER_SECOND or "Damage per Second:" -- :889
|
||
DPS_TEMPLATE = DPS_TEMPLATE or "(%.1f damage per second)" -- :942
|
||
MELEE_ATTACK = MELEE_ATTACK or "Melee Attack" -- :2680
|
||
MELEE_ATTACK_POWER = MELEE_ATTACK_POWER or "Melee Attack Power" -- :2681
|
||
MELEE_ATTACK_POWER_TOOLTIP = MELEE_ATTACK_POWER_TOOLTIP or
|
||
"Increases damage with melee weapons by %.1f damage per second." -- :2682
|
||
NOT_APPLICABLE = NOT_APPLICABLE or "N/A" -- :2802
|
||
RANGED_ATTACK = RANGED_ATTACK or "Ranged Attack" -- :3292
|
||
RANGED_ATTACK_POWER = RANGED_ATTACK_POWER or "Ranged Attack Power" -- :3293
|
||
RANGED_ATTACK_POWER_TOOLTIP = RANGED_ATTACK_POWER_TOOLTIP or
|
||
"Increases damage with ranged weapons by %.1f damage per second." -- :3294
|
||
RANGED_ATTACK_TOOLTIP = RANGED_ATTACK_TOOLTIP or "Ranged Attack Rating" -- :3295
|
||
INVTYPE_WEAPONMAINHAND = INVTYPE_WEAPONMAINHAND or "Main Hand" -- :2375
|
||
INVTYPE_WEAPONOFFHAND = INVTYPE_WEAPONOFFHAND or "Off Hand" -- :2376
|
||
INVTYPE_RANGED = INVTYPE_RANGED or "Ranged" -- :2366
|
||
|
||
SPELL_STAT0_NAME = SPELL_STAT0_NAME or "Strength" -- :4118
|
||
SPELL_STAT1_NAME = SPELL_STAT1_NAME or "Agility" -- :4119
|
||
SPELL_STAT2_NAME = SPELL_STAT2_NAME or "Stamina" -- :4120
|
||
SPELL_STAT3_NAME = SPELL_STAT3_NAME or "Intellect" -- :4121
|
||
SPELL_STAT4_NAME = SPELL_STAT4_NAME or "Spirit" -- :4122
|
||
|
||
RESISTANCE1_NAME = RESISTANCE1_NAME or "Holy Resistance" -- :3347 (unused: Holy never rides the paper doll)
|
||
RESISTANCE2_NAME = RESISTANCE2_NAME or "Fire Resistance" -- :3348
|
||
RESISTANCE3_NAME = RESISTANCE3_NAME or "Nature Resistance" -- :3349
|
||
RESISTANCE4_NAME = RESISTANCE4_NAME or "Frost Resistance" -- :3350
|
||
RESISTANCE5_NAME = RESISTANCE5_NAME or "Shadow Resistance" -- :3351
|
||
RESISTANCE6_NAME = RESISTANCE6_NAME or "Arcane Resistance" -- :3352
|
||
RESISTANCE_TYPE1 = RESISTANCE_TYPE1 or "holy" -- :3362
|
||
RESISTANCE_TYPE2 = RESISTANCE_TYPE2 or "fire" -- :3363
|
||
RESISTANCE_TYPE3 = RESISTANCE_TYPE3 or "nature" -- :3364
|
||
RESISTANCE_TYPE4 = RESISTANCE_TYPE4 or "frost" -- :3365
|
||
RESISTANCE_TYPE5 = RESISTANCE_TYPE5 or "shadow" -- :3366
|
||
RESISTANCE_TYPE6 = RESISTANCE_TYPE6 or "arcane" -- :3367
|
||
RESISTANCE_EXCELLENT = RESISTANCE_EXCELLENT or "Excellent" -- :3353
|
||
RESISTANCE_VERYGOOD = RESISTANCE_VERYGOOD or "Very Good" -- :3368
|
||
RESISTANCE_GOOD = RESISTANCE_GOOD or "Good" -- :3355
|
||
RESISTANCE_FAIR = RESISTANCE_FAIR or "Fair" -- :3354
|
||
RESISTANCE_POOR = RESISTANCE_POOR or "Poor" -- :3358
|
||
RESISTANCE_NONE = RESISTANCE_NONE or "None" -- :3357
|
||
RESISTANCE_TOOLTIP_SUBTEXT = RESISTANCE_TOOLTIP_SUBTEXT or
|
||
"Increases the ability to resist %s-based attacks, spells and abilities.\nResistance against level %d: |cffffffff%s|r" -- :3360
|
||
|
||
-- Default (class-agnostic) stat tooltips only — see this file's header comment on the
|
||
-- class-specific (WARRIOR_STRENGTH_TOOLTIP, …) omission.
|
||
DEFAULT_STRENGTH_TOOLTIP = DEFAULT_STRENGTH_TOOLTIP or "Increases attack power with melee weapons." -- :916
|
||
DEFAULT_AGILITY_TOOLTIP = DEFAULT_AGILITY_TOOLTIP or
|
||
"Increases attack power with ranged weapons.\nImproves chance to score a critical hit with all weapons.\nIncreases armor and chance to dodge attacks." -- :911
|
||
DEFAULT_STAMINA_TOOLTIP = DEFAULT_STAMINA_TOOLTIP or "Increases health points." -- :915
|
||
DEFAULT_INTELLECT_TOOLTIP = DEFAULT_INTELLECT_TOOLTIP or "Increases the rate at which weapon skills improve." -- :913
|
||
DEFAULT_SPIRIT_TOOLTIP = DEFAULT_SPIRIT_TOOLTIP or "Increases health and mana regeneration rates." -- :914
|
||
|
||
HEADSLOT = HEADSLOT or "Head" -- :2123
|
||
NECKSLOT = NECKSLOT or "Neck" -- :2713
|
||
SHOULDERSLOT = SHOULDERSLOT or "Shoulders" -- :3428
|
||
BACKSLOT = BACKSLOT or "Back" -- :133
|
||
CHESTSLOT = CHESTSLOT or "Chest" -- :746
|
||
SHIRTSLOT = SHIRTSLOT or "Shirt" -- :3427
|
||
TABARDSLOT = TABARDSLOT or "Tabard" -- :4217
|
||
WRISTSLOT = WRISTSLOT or "Wrist" -- :5458
|
||
HANDSSLOT = HANDSSLOT or "Hands" -- :2115
|
||
WAISTSLOT = WAISTSLOT or "Waist" -- :5428
|
||
LEGSSLOT = LEGSSLOT or "Legs" -- :2565
|
||
FEETSLOT = FEETSLOT or "Feet" -- :1967
|
||
FINGER0SLOT = FINGER0SLOT or "Finger" -- :1971
|
||
FINGER1SLOT = FINGER1SLOT or "Finger" -- :1972
|
||
TRINKET0SLOT = TRINKET0SLOT or "Trinket" -- :4313
|
||
TRINKET1SLOT = TRINKET1SLOT or "Trinket" -- :4314
|
||
MAINHANDSLOT = MAINHANDSLOT or "Main Hand" -- :2658
|
||
SECONDARYHANDSLOT = SECONDARYHANDSLOT or "Off Hand" -- :3400
|
||
RANGEDSLOT = RANGEDSLOT or "Ranged" -- :3291
|
||
AMMOSLOT = AMMOSLOT or "Ammo" -- :53
|
||
|
||
-- The font color codes/tables live in our Fonts.xml (the ref's own home for them, l.8-19 —
|
||
-- verified this session), loaded before every window.
|
||
|
||
-- ── This window's own constants (ref-PaperDollFrame.lua:1-4, Benilla-prefixed) ────────────
|
||
BENILLA_NUM_STATS = 5 -- NUM_STATS
|
||
BENILLA_NUM_RESISTANCE_TYPES = 5 -- NUM_RESISTANCE_TYPES
|
||
ATTACK_POWER_MAGIC_NUMBER = 14 -- ATTACK_POWER_MAGIC_NUMBER
|
||
-- MagicResFrame1..5's ref `id=` attrs (ref-PaperDollFrame.xml:654-759) — arcane/fire/nature/
|
||
-- frost/shadow; Holy(1) never rides the paper doll (only 5 of the 6 schools show here).
|
||
BENILLA_MAGICRES_IDS = { 6, 2, 3, 4, 5 }
|
||
-- The pages that exist so far (this file's header comment; the 0138 merchant-tab
|
||
-- precedent — tabs arrive with their pages), named exactly as the reference names them
|
||
-- ("PaperDollFrame"/"ReputationFrame"/"SkillFrame"). 1:1 with the tab row below (index N is
|
||
-- tab N's page) — decision 0437 phase 4 added the Skills page, decision 1057 the Pet one,
|
||
-- and the Reputation page (ReputationFrame.xml) takes the real client's own slot 3, which
|
||
-- puts Skills at the real client's own 4. The FIRST FOUR SLOTS ARE NOW THE REFERENCE'S:
|
||
-- only Honor (its tab 5) is still OMITTED, and it sits past the end where its absence
|
||
-- shifts nothing.
|
||
BENILLA_CHARACTERFRAME_SUBFRAMES = {
|
||
"PaperDollFrame", "PetPaperDollFrame", "ReputationFrame", "SkillFrame",
|
||
}
|
||
|
||
-- ── The window: toggle / tab dispatch (ref CharacterFrame.lua:1-48) ─────────────────────
|
||
-- Kept BARE (not Benilla-prefixed) — the addon-facing entry point, matching this codebase's
|
||
-- own ToggleQuestLog() precedent (QuestLogFrame.xml).
|
||
function CharacterFrame_ShowSubFrame(frameName)
|
||
for _, value in ipairs(BENILLA_CHARACTERFRAME_SUBFRAMES) do
|
||
-- Nil-guarded: a page's file may not be loaded (a test subset, a later
|
||
-- load-on-demand page) — a missing sibling must never break the switch.
|
||
local page = getglobal(value)
|
||
if page then
|
||
if value == frameName then page:Show() else page:Hide() end
|
||
end
|
||
end
|
||
end
|
||
|
||
function ToggleCharacter(tab)
|
||
local subFrame = getglobal(tab)
|
||
if not subFrame then return end
|
||
-- ref CharacterFrame.lua:4-6, the pet guard: asking for the pet page with no pet does
|
||
-- NOTHING — not even close an open window. The `not IsVisible()` half is what still
|
||
-- lets the page CLOSE itself when the pet vanishes underneath it (the pet page's own
|
||
-- OnEvent calls straight back in here to do exactly that). Hoisted below the getglobal
|
||
-- (the ref tests it above) purely so the visibility read has a frame to ask.
|
||
if tab == "PetPaperDollFrame" and not HasPetUI() and not subFrame:IsVisible() then
|
||
return
|
||
end
|
||
local frame = getglobal("CharacterFrame")
|
||
-- ref CharacterFrame.lua:11, and the line whose absence desynced this window: the tab
|
||
-- row is selected HERE, from the page's own id, on EVERY path — including the one that
|
||
-- closes the window. Having it only on the tab-button OnClick meant a KEYBIND
|
||
-- (`C`/`SHIFT-P`, which call straight in here) switched the page and left the row
|
||
-- selecting the old tab; clicking that old tab then took the "already visible" branch
|
||
-- and shut the window instead of switching back (director, 2026-08-06). The id is the
|
||
-- page's own `id=` attribute — the loader really does apply it (loader/geometry.rs
|
||
-- SetID) — and `character_tests.rs` pins every page's id against its slot in
|
||
-- BENILLA_CHARACTERFRAME_SUBFRAMES, which is what keeps the two in step the day
|
||
-- Reputation lands between them.
|
||
PanelTemplates_SetTab(frame, subFrame:GetID())
|
||
if frame:IsVisible() then
|
||
if subFrame:IsVisible() then
|
||
HideUIPanel(frame)
|
||
else
|
||
PlaySound("igCharacterInfoTab")
|
||
CharacterFrame_ShowSubFrame(tab)
|
||
end
|
||
else
|
||
ShowUIPanel(frame)
|
||
CharacterFrame_ShowSubFrame(tab)
|
||
end
|
||
end
|
||
|
||
-- The tab row's own OnClick (ref CharacterFrameTab_OnClick, l.35-48), now id-based (decision
|
||
-- 0437 phase 4 grows the row to 2) — the MerchantFrame.xml Merchant/Buyback pair's own shape
|
||
-- for a >1-tab window: `id` indexes BOTH the tab button and BENILLA_CHARACTERFRAME_SUBFRAMES
|
||
-- 1:1. It does NOT select the tab itself — the ref's own tab OnClick doesn't either, because
|
||
-- ToggleCharacter is the single place that does it (above), so the click path and the
|
||
-- keybind path cannot disagree. l.47's trailing PlaySound is the ref's OWN redundant second
|
||
-- igCharacterInfoTab — ToggleCharacter already plays it on the "switch to this tab" path, so
|
||
-- this second call only matters when the window is ALREADY open on this same tab, i.e. the
|
||
-- HideUIPanel branch — kept rather than "fixed" since it's the ref's own
|
||
-- behavior, not a bug of ours.
|
||
function BenillaCharacterFrameTab_OnClick(id)
|
||
ToggleCharacter(BENILLA_CHARACTERFRAME_SUBFRAMES[id])
|
||
PlaySound("igCharacterInfoTab")
|
||
end
|
||
|
||
function CharacterFrame_OnLoad()
|
||
local frame = this
|
||
-- GetID()/SetID() DO exist (landed with slice 2's BagFrame, which relies on them for
|
||
-- its own per-window bag id) — the STALE claim this comment used to make is corrected
|
||
-- here (0208 phase 1b touched this function anyway). 4 tabs ship with the Reputation
|
||
-- page (Character + Pet + Reputation + Skills — the reference's own first four); the
|
||
-- window opens on tab 1 by default, matching the real client. Tab 2 raises and hides
|
||
-- itself with the pet — PetTab_Update, which the pet page runs at its own OnLoad and on
|
||
-- every pet transition — so the COUNT here is the full row, not the currently-visible
|
||
-- one, exactly as the ref's SetNumTabs(5) is.
|
||
PanelTemplates_SetNumTabs(frame, 4)
|
||
PanelTemplates_SetTab(frame, 1)
|
||
-- The reference LABELS its numerals here (CharacterFrame_OnLoad l.55-57: "Health" on
|
||
-- the player's health bar, "XP" on the XP bar, with the power bar's following the
|
||
-- resource in UnitFrames.xml). Shipped in 1146 and cut the same day on the director's
|
||
-- call — bare numbers read better on our bars (decision 1147). The mechanism is
|
||
-- untouched and live: SetTextStatusBarTextPrefix works, and restoring any one of the
|
||
-- three is the one line the reference has. If it is ever restored, note the ordering
|
||
-- it depends on — a prefix only sticks on a bar that already has its TextString, and
|
||
-- this file's seat works only because the manifest runs UnitFrames (52) and ActionBar
|
||
-- (76) well ahead of it (149), exactly as FrameXML runs PlayerFrame (44) and
|
||
-- MainMenuBar (34) ahead of CharacterFrame (53).
|
||
end
|
||
|
||
-- ref CharacterFrame_OnShow (l.82-93). The Show*TextStatusBarText calls are LIVE as of
|
||
-- decision 1146 — the note that used to sit here ("no unit-frame bar-text-mode toggle
|
||
-- exists in this engine") was true when it was written and stopped being true when the
|
||
-- numerals landed (1143). Opening the character sheet reveals your own and your pet's
|
||
-- numbers for as long as it is open, whatever Status Bar Text says, through the same
|
||
-- lockShow refcount the hover uses — so a hover inside an open sheet still balances.
|
||
-- ShowWatchedReputationBarText is LIVE too, since the Reputation tab landed and with it a
|
||
-- driven watch bar (ActionBar.xml owns both, its own placement note says why): opening the
|
||
-- sheet reveals the watched faction's numbers for as long as it is open, exactly like the
|
||
-- five bars above. Guarded on existence like its neighbours — the engine's per-window
|
||
-- harnesses load this file without ActionBar.xml.
|
||
-- UnitPVPName -> plain UnitName (this file's header comment: PVP rank is a named deferral).
|
||
function CharacterFrame_OnShow()
|
||
if UpdateMicroButtons then UpdateMicroButtons() end
|
||
PlaySound("igCharacterInfoOpen")
|
||
SetPortraitTexture(getglobal("CharacterFramePortrait"), "player")
|
||
getglobal("CharacterNameText"):SetText(UnitName("player"))
|
||
if ShowTextStatusBarText then -- engine tests load file subsets
|
||
ShowTextStatusBarText(PlayerFrameHealthBar)
|
||
ShowTextStatusBarText(PlayerFramePowerBar)
|
||
ShowTextStatusBarText(MainMenuExpBar)
|
||
ShowTextStatusBarText(PetFrameHealthBar)
|
||
ShowTextStatusBarText(PetFrameManaBar)
|
||
end
|
||
if ShowWatchedReputationBarText then
|
||
ShowWatchedReputationBarText()
|
||
end
|
||
end
|
||
|
||
-- ref CharacterFrame_OnHide (l.95-104): everything past the PlaySound is the OnShow list's
|
||
-- Hide-twin — UpdateMicroButtons live (MicroMenu.xml), the bar-text calls live too (1146),
|
||
-- and the reputation one live since the watch bar started being driven. The subframe hide-loop is OUR OWN addition
|
||
-- (decision 0437 phase 4): PaperDollFrame is a true engine child of this frame (its
|
||
-- own header comment) so it hides for free on the parent's Hide-cascade, but
|
||
-- SkillFrame is a SEPARATE top-level file — this engine has no cross-file `parent=`
|
||
-- attachment (grepped `benilla-ui/src/loader`: the XML `parent=` attribute is read nowhere,
|
||
-- unlike `relativeTo`, which resolves by name regardless of tree position — see
|
||
-- SkillFrame.xml's own header comment) — so nothing else would hide it when the WHOLE
|
||
-- character window closes (ESC, the close X, or another left-slot window bumping it via
|
||
-- UIPanel exclusivity). Looping every BENILLA_CHARACTERFRAME_SUBFRAMES entry here, once,
|
||
-- keeps this correct as more non-nested tabs (Pet/Reputation/Honor) land the same way.
|
||
function CharacterFrame_OnHide()
|
||
if UpdateMicroButtons then UpdateMicroButtons() end
|
||
PlaySound("igCharacterInfoClose")
|
||
if HideTextStatusBarText then -- engine tests load file subsets
|
||
HideTextStatusBarText(PlayerFrameHealthBar)
|
||
HideTextStatusBarText(PlayerFramePowerBar)
|
||
HideTextStatusBarText(MainMenuExpBar)
|
||
HideTextStatusBarText(PetFrameHealthBar)
|
||
HideTextStatusBarText(PetFrameManaBar)
|
||
end
|
||
if HideWatchedReputationBarText then
|
||
HideWatchedReputationBarText()
|
||
end
|
||
for _, name in ipairs(BENILLA_CHARACTERFRAME_SUBFRAMES) do
|
||
local subFrame = getglobal(name)
|
||
if subFrame then subFrame:Hide() end
|
||
end
|
||
end
|
||
|
||
-- ── The paper-doll page (ref PaperDollFrame.lua) ────────────────────────────────────────
|
||
-- ref PaperDollFrame_OnLoad (l.6-29), the label lines (l.7-13) kept as the ref has them: the
|
||
-- labels live ONLY in the row template's $parentLabel (gold GameFontNormalSmall, LEFT) and
|
||
-- Lua fills them — never a re-declared region in an instance (the ref's instances carry no
|
||
-- <Layers> at all, and this engine's template expansion appends children, so a re-declared
|
||
-- $parentLabel creates a SECOND anchorless default-font FontString instead of merging — the
|
||
-- white/overlapping-label bug this line-set replaces). CHARACTER_POINTS_CHANGED/
|
||
-- UNIT_MODEL_CHANGED/PLAYER_DAMAGE_DONE_MODS/UNIT_RANGEDDAMAGE/PLAYER_GUILD_UPDATE/
|
||
-- SKILL_LINES_CHANGED are OMITTED (talents/guild/skills are out of this phase's event
|
||
-- contract; UNIT_RANGEDDAMAGE isn't in it either — UNIT_DAMAGE/UNIT_ATTACK_SPEED alone
|
||
-- trigger the ranged refresh below, same as the ref's own first branch already does).
|
||
function PaperDollFrame_OnLoad()
|
||
local frame = this
|
||
getglobal("CharacterAttackFrameLabel"):SetText(MELEE_ATTACK)
|
||
getglobal("CharacterDamageFrameLabel"):SetText(DAMAGE_COLON)
|
||
getglobal("CharacterAttackPowerFrameLabel"):SetText(ATTACK_POWER_COLON)
|
||
getglobal("CharacterRangedAttackFrameLabel"):SetText(RANGED_ATTACK)
|
||
getglobal("CharacterRangedDamageFrameLabel"):SetText(DAMAGE_COLON)
|
||
getglobal("CharacterRangedAttackPowerFrameLabel"):SetText(ATTACK_POWER_COLON)
|
||
getglobal("CharacterArmorFrameLabel"):SetText(ARMOR_COLON)
|
||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||
frame:RegisterEvent("UNIT_LEVEL")
|
||
frame:RegisterEvent("UNIT_RESISTANCES")
|
||
frame:RegisterEvent("UNIT_STATS")
|
||
frame:RegisterEvent("UNIT_DAMAGE")
|
||
frame:RegisterEvent("UNIT_ATTACK_SPEED")
|
||
frame:RegisterEvent("UNIT_ATTACK_POWER")
|
||
frame:RegisterEvent("UNIT_RANGED_ATTACK_POWER")
|
||
frame:RegisterEvent("UNIT_ATTACK")
|
||
end
|
||
|
||
-- Every row of the sheet, in the order a full repaint fills them. The order is load-bearing
|
||
-- at the ranged end: SetRangedAttack is what decides `PaperDollFrame.noRanged`, and the two
|
||
-- ranged rows after it read that flag rather than re-deciding it.
|
||
--
|
||
-- Rows are NAMED, not referenced. Resolving each through the global table at call time is
|
||
-- what the reference's own call-by-name got for free, and the paper-doll setters are prime
|
||
-- addon-hook territory: a table of captured function values would keep calling the setter an
|
||
-- addon replaced.
|
||
local paperDollRows = {
|
||
"PaperDollFrame_SetLevel",
|
||
"PaperDollFrame_SetStats",
|
||
"PaperDollFrame_SetResistances",
|
||
"PaperDollFrame_SetArmor",
|
||
"PaperDollFrame_SetDamage",
|
||
"PaperDollFrame_SetAttackPower",
|
||
"PaperDollFrame_SetAttackBothHands",
|
||
"PaperDollFrame_SetRangedAttack",
|
||
"PaperDollFrame_SetRangedDamage",
|
||
"PaperDollFrame_SetRangedAttackPower",
|
||
}
|
||
|
||
-- A weapon that changed how hard it hits and one that changed how often move the same four
|
||
-- rows, so the two events share a list rather than a copy of one.
|
||
local weaponRows = {
|
||
"PaperDollFrame_SetDamage",
|
||
"PaperDollFrame_SetRangedAttack",
|
||
"PaperDollFrame_SetRangedDamage",
|
||
"PaperDollFrame_SetAttackBothHands",
|
||
}
|
||
|
||
-- Which rows a single change moves, keyed by the event reporting it. The reference spells
|
||
-- this out as an if/elseif ladder and then keeps a second, hand-parallel copy of the row
|
||
-- order in OnShow; one table read two ways cannot drift the way two call lists do.
|
||
--
|
||
-- Only this file's registered subset appears (see PaperDollFrame_OnLoad's note on the
|
||
-- omitted events). PLAYER_ENTERING_WORLD is registered and deliberately absent: the ref's
|
||
-- one use for it is `CharacterModelFrame:SetUnit("player")`, and our CharacterModelFrame is
|
||
-- a booth-texture stand-in rather than a live model widget (this file's header) — there is
|
||
-- no SetUnit to call. It keeps its early return below rather than a row here, because the
|
||
-- reference returns on it before even the visibility check.
|
||
local rowsMovedBy = {
|
||
UNIT_LEVEL = { "PaperDollFrame_SetLevel" },
|
||
UNIT_STATS = { "PaperDollFrame_SetStats" },
|
||
UNIT_RESISTANCES = { "PaperDollFrame_SetResistances", "PaperDollFrame_SetArmor" },
|
||
UNIT_ATTACK = { "PaperDollFrame_SetAttackBothHands" },
|
||
UNIT_ATTACK_POWER = { "PaperDollFrame_SetAttackPower" },
|
||
UNIT_DAMAGE = weaponRows,
|
||
UNIT_ATTACK_SPEED = weaponRows,
|
||
UNIT_RANGED_ATTACK_POWER = {
|
||
"PaperDollFrame_SetRangedAttack", "PaperDollFrame_SetRangedAttackPower",
|
||
},
|
||
}
|
||
|
||
local function paintRows(rows)
|
||
for i = 1, table.getn(rows) do
|
||
getglobal(rows[i])()
|
||
end
|
||
end
|
||
|
||
-- `unit` is the DECLARED second parameter, not the `arg1` global: the XML passes `arg1` into
|
||
-- it exactly as the reference's own does (PaperDollFrame.xml), so an addon calling the 1.12
|
||
-- arity is read correctly rather than off a stale global. Only the player's own sheet is
|
||
-- driven from here (no pet/other-unit reuse in phase 1a), and only while it is on screen —
|
||
-- a hidden page is repainted wholesale by OnShow when it comes back.
|
||
function PaperDollFrame_OnEvent(event, unit)
|
||
if event == "PLAYER_ENTERING_WORLD" then
|
||
return
|
||
end
|
||
if not this:IsVisible() then return end
|
||
if unit ~= "player" then return end
|
||
local rows = rowsMovedBy[event]
|
||
if rows then
|
||
paintRows(rows)
|
||
end
|
||
end
|
||
|
||
-- ref PaperDollFrame_OnShow (l.568-585): SetGuild OMITTED (named deferral, this file's
|
||
-- header). The relic Hide/Show branch is DROPPED outright — UnitHasRelicSlot is
|
||
-- vanilla-always-false, so CharacterAmmoSlot simply never has a reason to hide; it ships
|
||
-- with no hidden="true" at all (see its own Button below).
|
||
function PaperDollFrame_OnShow()
|
||
paintRows(paperDollRows)
|
||
end
|
||
-- ref PaperDollFrame_OnHide (l.587-592) is entirely commented-out in the ref itself (the
|
||
-- ShoppingTooltip/compare-tooltip lines, OMIT) — nothing to transcribe, no OnHide wired.
|
||
|
||
-- UnitRace/UnitClass return nil,nil until the app's first player snapshot lands (unlike the
|
||
-- real client, whose race/class are synchronous local data, never network-raced) — "?" is a
|
||
-- harmless placeholder for that one-tick window, not a fidelity change (verified live: an
|
||
-- unguarded format() errors "string expected, got nil" the instant this fires before the
|
||
-- first snapshot).
|
||
function PaperDollFrame_SetLevel()
|
||
getglobal("CharacterLevelText"):SetText(
|
||
format(PLAYER_LEVEL, UnitLevel("player"), UnitRace("player") or "?", UnitClass("player") or "?"))
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetStats (l.131-174), including its per-refresh label SetText (l.140)
|
||
-- — the labels' one source of text (see PaperDollFrame_OnLoad's header comment).
|
||
function PaperDollFrame_SetStats()
|
||
for i = 1, BENILLA_NUM_STATS do
|
||
local label = getglobal("CharacterStatFrame" .. i .. "Label")
|
||
local text = getglobal("CharacterStatFrame" .. i .. "StatText")
|
||
local frame = getglobal("CharacterStatFrame" .. i)
|
||
local statName = getglobal("SPELL_STAT" .. (i - 1) .. "_NAME")
|
||
label:SetText(statName .. ":")
|
||
local stat, effectiveStat, posBuff, negBuff = UnitStat("player", i)
|
||
local tooltipText = HIGHLIGHT_FONT_COLOR_CODE .. statName .. " "
|
||
if posBuff == 0 and negBuff == 0 then
|
||
text:SetText(effectiveStat)
|
||
frame.tooltip = tooltipText .. effectiveStat .. FONT_COLOR_CODE_CLOSE
|
||
else
|
||
tooltipText = tooltipText .. effectiveStat
|
||
if posBuff > 0 or negBuff < 0 then
|
||
tooltipText = tooltipText .. " (" .. (stat - posBuff - negBuff) .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if posBuff > 0 then
|
||
tooltipText = tooltipText .. FONT_COLOR_CODE_CLOSE .. GREEN_FONT_COLOR_CODE .. "+" .. posBuff .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if negBuff < 0 then
|
||
tooltipText = tooltipText .. RED_FONT_COLOR_CODE .. " " .. negBuff .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if posBuff > 0 or negBuff < 0 then
|
||
tooltipText = tooltipText .. HIGHLIGHT_FONT_COLOR_CODE .. ")" .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
frame.tooltip = tooltipText
|
||
if negBuff < 0 then
|
||
text:SetText(RED_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
|
||
else
|
||
text:SetText(GREEN_FONT_COLOR_CODE .. effectiveStat .. FONT_COLOR_CODE_CLOSE)
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
-- One word for how well a school is covered, from resistance-per-level. The bands descend,
|
||
-- so the first threshold cleared names it and a ratio of zero or less clears none of them.
|
||
-- (The reference leaves the answer in an implicit global; a pure function of the ratio is
|
||
-- both the honest shape and one less name in our sandboxed globals table.)
|
||
local function resistanceBand(ratio)
|
||
if ratio > 5 then return RESISTANCE_EXCELLENT end
|
||
if ratio > 3.75 then return RESISTANCE_VERYGOOD end
|
||
if ratio > 2.5 then return RESISTANCE_GOOD end
|
||
if ratio > 1.25 then return RESISTANCE_FAIR end
|
||
if ratio > 0 then return RESISTANCE_POOR end
|
||
return RESISTANCE_NONE
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetResistances (l.176-228).
|
||
function PaperDollFrame_SetResistances()
|
||
for i = 1, BENILLA_NUM_RESISTANCE_TYPES do
|
||
local resistId = BENILLA_MAGICRES_IDS[i]
|
||
local text = getglobal("MagicResText" .. i)
|
||
local frame = getglobal("MagicResFrame" .. i)
|
||
local base, resistance, positive, negative = UnitResistance("player", resistId)
|
||
local resistanceName = getglobal("RESISTANCE" .. resistId .. "_NAME")
|
||
frame.tooltip = resistanceName .. " " .. resistance
|
||
if math.abs(negative) > positive then
|
||
text:SetText(RED_FONT_COLOR_CODE .. resistance .. FONT_COLOR_CODE_CLOSE)
|
||
elseif math.abs(negative) == positive then
|
||
text:SetText(resistance)
|
||
else
|
||
text:SetText(GREEN_FONT_COLOR_CODE .. resistance .. FONT_COLOR_CODE_CLOSE)
|
||
end
|
||
if positive ~= 0 or negative ~= 0 then
|
||
frame.tooltip = frame.tooltip .. " ( " .. HIGHLIGHT_FONT_COLOR_CODE .. base
|
||
if positive > 0 then
|
||
frame.tooltip = frame.tooltip .. GREEN_FONT_COLOR_CODE .. " +" .. positive
|
||
end
|
||
if negative < 0 then
|
||
frame.tooltip = frame.tooltip .. " " .. RED_FONT_COLOR_CODE .. negative
|
||
end
|
||
frame.tooltip = frame.tooltip .. FONT_COLOR_CODE_CLOSE .. " )"
|
||
end
|
||
-- The divisor floors at 20 because the reference does (l.210-211: it reads the level,
|
||
-- then clamps on the NEXT line — an earlier note here cited l.210 alone and called
|
||
-- the floor ours). The `or 1` is the only difference: the ref would error on a nil
|
||
-- level, and nothing is gained by reproducing that.
|
||
local unitLevel = math.max(UnitLevel("player") or 1, 20)
|
||
frame.tooltipSubtext = format(RESISTANCE_TOOLTIP_SUBTEXT,
|
||
getglobal("RESISTANCE_TYPE" .. resistId), unitLevel,
|
||
resistanceBand(resistance / unitLevel))
|
||
end
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetArmor (l.230-250). NOTE the ref's own third UnitArmor return
|
||
-- (`armor`) is unused here too — PaperDollFormatStat gets `base`, not `armor`; the ref's
|
||
-- behavior kept, not "fixed".
|
||
function PaperDollFrame_SetArmor(unit, prefix)
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local base, effectiveArmor, armor, posBuff, negBuff = UnitArmor(unit)
|
||
local frame = getglobal(prefix .. "ArmorFrame")
|
||
local text = getglobal(prefix .. "ArmorFrameStatText")
|
||
PaperDollFormatStat(ARMOR, base, posBuff, negBuff, frame, text)
|
||
local playerLevel = UnitLevel(unit)
|
||
local armorReduction = effectiveArmor / ((85 * playerLevel) + 400)
|
||
armorReduction = 100 * (armorReduction / (armorReduction + 1))
|
||
frame.tooltipSubtext = format(ARMOR_TOOLTIP, playerLevel, armorReduction)
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetDefense (l.252-272). Shared like every setter around it, but the
|
||
-- CHARACTER sheet has no defense row — the real client's doesn't either, and this exists
|
||
-- solely for the pet page (decision 1057), which is why it arrives now and not in 0208.
|
||
-- `UnitDefense`'s second return is a single signed modifier, not the pos/neg pair the other
|
||
-- setters get, so the split into posBuff/negBuff happens here.
|
||
function PaperDollFrame_SetDefense(unit, prefix)
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local base, modifier = UnitDefense(unit)
|
||
local frame = getglobal(prefix .. "DefenseFrame")
|
||
local text = getglobal(prefix .. "DefenseFrameStatText")
|
||
-- The sign is all that decides which side of the pair the one modifier lands on, and a
|
||
-- modifier of zero lands on neither.
|
||
PaperDollFormatStat(DEFENSE_COLON, base, math.max(modifier, 0), math.min(modifier, 0),
|
||
frame, text)
|
||
end
|
||
|
||
-- ── Weapon swings: the arithmetic and the wording, once ────────────────────────────────────
|
||
-- Three rows read a swing — main hand, off hand, ranged — and it is the same calculation
|
||
-- with the same vocabulary each time, so it lives here instead of two and three times over.
|
||
|
||
-- The sheet's green and red for damage figures. Byte-identical to Fonts.xml's GREEN/RED
|
||
-- font-colour codes today, and kept separate on purpose: these are the damage row's own
|
||
-- colours, and reading them off the font palette would tie one to the other.
|
||
local DAMAGE_UP_COLOR = "|cff20ff20"
|
||
local DAMAGE_DOWN_COLOR = "|cffff2020"
|
||
|
||
-- What the weapon itself swings for. The numbers we are handed already have the flat
|
||
-- physical bonuses added and the percent multiplier applied; the row shows those, but the
|
||
-- tooltip breaks out where they came from, so the un-modified swing has to be recovered.
|
||
-- Returns that raw range, the modified average, and the gap the modifiers make between them.
|
||
local function unscaleSwing(minDamage, maxDamage, bonusPos, bonusNeg, percent)
|
||
local rawMin = (minDamage / percent) - bonusPos - bonusNeg
|
||
local rawMax = (maxDamage / percent) - bonusPos - bonusNeg
|
||
local baseDamage = (rawMin + rawMax) * 0.5
|
||
local fullDamage = (baseDamage + bonusPos + bonusNeg) * percent
|
||
return rawMin, rawMax, fullDamage, fullDamage - baseDamage
|
||
end
|
||
|
||
-- Both ends of a swing, rounded outwards and floored at 1 — a weapon swinging for a
|
||
-- fraction of a point still reads as a weapon.
|
||
local function swingBounds(minDamage, maxDamage)
|
||
return math.max(math.floor(minDamage), 1), math.max(math.ceil(maxDamage), 1)
|
||
end
|
||
|
||
-- The range as a tooltip writes it, always spaced.
|
||
local function damageRange(minDamage, maxDamage)
|
||
local low, high = swingBounds(minDamage, maxDamage)
|
||
return low .. " - " .. high
|
||
end
|
||
|
||
-- The range as the ROW writes it: spaced while both ends are two digits, tightened up once
|
||
-- either reaches three and the row starts running out of width.
|
||
local function damageFigure(minDamage, maxDamage)
|
||
local low, high = swingBounds(minDamage, maxDamage)
|
||
if low < 100 and high < 100 then
|
||
return low .. " - " .. high
|
||
end
|
||
return low .. "-" .. high
|
||
end
|
||
|
||
-- The tooltip's account of what modified the raw range: the flat bonus, then the multiplier,
|
||
-- each coloured by which way it went and left out entirely when it is not in play.
|
||
local function damageModifiers(tooltip, bonusPos, bonusNeg, percent)
|
||
if bonusPos > 0 then
|
||
tooltip = tooltip .. DAMAGE_UP_COLOR .. " +" .. bonusPos .. "|r"
|
||
end
|
||
if bonusNeg < 0 then
|
||
tooltip = tooltip .. DAMAGE_DOWN_COLOR .. " " .. bonusNeg .. "|r"
|
||
end
|
||
if percent > 1 then
|
||
tooltip = tooltip .. DAMAGE_UP_COLOR .. " x" .. math.floor(percent * 100 + 0.5) .. "%|r"
|
||
elseif percent < 1 then
|
||
tooltip = tooltip .. DAMAGE_DOWN_COLOR .. " x" .. math.floor(percent * 100 + 0.5) .. "%|r"
|
||
end
|
||
return tooltip
|
||
end
|
||
|
||
-- Which way a modified figure is tinted. Only asked once a caller knows something modified
|
||
-- it, so a zero bonus never reaches here and has no colour of its own.
|
||
local function damageColor(totalBonus)
|
||
if totalBonus > 0 then
|
||
return DAMAGE_UP_COLOR
|
||
end
|
||
return DAMAGE_DOWN_COLOR
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetDamage (l.274-372). The off hand is the same swing read twice: the
|
||
-- flat bonuses and the multiplier are the character's, not the weapon's, so both hands take
|
||
-- them, and only the speed and the raw range differ.
|
||
function PaperDollFrame_SetDamage(unit, prefix)
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local damageText = getglobal(prefix .. "DamageFrameStatText")
|
||
local damageFrame = getglobal(prefix .. "DamageFrame")
|
||
local speed, offhandSpeed = UnitAttackSpeed(unit)
|
||
local minDamage, maxDamage, minOffHandDamage, maxOffHandDamage, physicalBonusPos, physicalBonusNeg, percent =
|
||
UnitDamage(unit)
|
||
local figure = damageFigure(minDamage, maxDamage)
|
||
local rawMin, rawMax, fullDamage, totalBonus =
|
||
unscaleSwing(minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent)
|
||
local damageTooltip = damageRange(rawMin, rawMax)
|
||
if totalBonus == 0 then
|
||
damageText:SetText(figure)
|
||
else
|
||
damageText:SetText(damageColor(totalBonus) .. figure .. "|r")
|
||
damageTooltip =
|
||
damageModifiers(damageTooltip, physicalBonusPos, physicalBonusNeg, percent)
|
||
end
|
||
damageFrame.damage = damageTooltip
|
||
damageFrame.attackSpeed = speed
|
||
damageFrame.dps = math.max(fullDamage, 1) / speed
|
||
if offhandSpeed then
|
||
local offMin, offMax, offhandFullDamage = unscaleSwing(
|
||
minOffHandDamage, maxOffHandDamage, physicalBonusPos, physicalBonusNeg, percent)
|
||
-- The off hand gets its breakdown unconditionally: it has no row of its own to keep
|
||
-- plain, only the tooltip's second half.
|
||
damageFrame.offhandDamage = damageModifiers(damageRange(offMin, offMax),
|
||
physicalBonusPos, physicalBonusNeg, percent)
|
||
damageFrame.offhandAttackSpeed = offhandSpeed
|
||
damageFrame.offhandDps = math.max(offhandFullDamage, 1) / offhandSpeed
|
||
else
|
||
damageFrame.offhandAttackSpeed = nil
|
||
end
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetAttackPower (l.374-389).
|
||
function PaperDollFrame_SetAttackPower(unit, prefix)
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local base, posBuff, negBuff = UnitAttackPower(unit)
|
||
local frame = getglobal(prefix .. "AttackPowerFrame")
|
||
local text = getglobal(prefix .. "AttackPowerFrameStatText")
|
||
PaperDollFormatStat(MELEE_ATTACK_POWER, base, posBuff, negBuff, frame, text)
|
||
frame.tooltipSubtext = format(MELEE_ATTACK_POWER_TOOLTIP,
|
||
math.max(base + posBuff + negBuff, 0) / ATTACK_POWER_MAGIC_NUMBER)
|
||
end
|
||
|
||
-- An attack rating and whatever is moving it, on one row. Nothing moving it means the bare
|
||
-- number and no hover plate to explain; otherwise the row carries the modified TOTAL, tinted
|
||
-- by which way it went, and the plate carries the split that produced it. Both the melee row
|
||
-- and the ranged row read exactly this way.
|
||
local function setAttackRating(frame, text, base, modifier)
|
||
if modifier == 0 then
|
||
text:SetText(base)
|
||
frame.tooltip = nil
|
||
return
|
||
end
|
||
local color = RED_FONT_COLOR_CODE
|
||
local sign = " "
|
||
if modifier > 0 then
|
||
color = GREEN_FONT_COLOR_CODE
|
||
sign = " +"
|
||
end
|
||
frame.tooltip = base .. color .. sign .. modifier .. FONT_COLOR_CODE_CLOSE
|
||
text:SetText(color .. (base + modifier) .. FONT_COLOR_CODE_CLOSE)
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetAttackBothHands (l.391-417) — the weapon-skill line (decision 0208's
|
||
-- own "to confirm": the equipped-weapon subclass -> skill-id table is UnitAttackBothHands's
|
||
-- own concern, not this file's).
|
||
function PaperDollFrame_SetAttackBothHands(unit, prefix)
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local mainHandAttackBase, mainHandAttackMod = UnitAttackBothHands(unit)
|
||
setAttackRating(getglobal(prefix .. "AttackFrame"),
|
||
getglobal(prefix .. "AttackFrameStatText"), mainHandAttackBase, mainHandAttackMod)
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetRangedAttack (l.419-466). `hasRelic`/UnitHasRelicSlot DROPPED
|
||
-- outright (this file's header comment: vanilla-always-false, dead code in the ref itself).
|
||
-- The literal slot id 18 is the ref's OWN hardcoded value (l.435) — quoted verbatim; what
|
||
-- GetInventoryItemTexture resolves id 18 to is the parallel binding's call, not re-derived
|
||
-- here.
|
||
function PaperDollFrame_SetRangedAttack(unit, prefix)
|
||
if unit == "pet" then return end
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local frame = getglobal(prefix .. "RangedAttackFrame")
|
||
local text = getglobal(prefix .. "RangedAttackFrameStatText")
|
||
local paperDoll = getglobal("PaperDollFrame")
|
||
local rangedAttackBase, rangedAttackMod = UnitRangedAttack(unit)
|
||
local rangedTexture = GetInventoryItemTexture("player", 18)
|
||
local oldValue = paperDoll.noRanged
|
||
if rangedTexture then
|
||
paperDoll.noRanged = nil
|
||
else
|
||
text:SetText(NOT_APPLICABLE)
|
||
paperDoll.noRanged = true
|
||
frame.tooltip = nil
|
||
end
|
||
if oldValue ~= paperDoll.noRanged then
|
||
PaperDollFrame_SetRangedAttackPower()
|
||
PaperDollFrame_SetRangedDamage()
|
||
end
|
||
if not rangedTexture then return end
|
||
setAttackRating(frame, text, rangedAttackBase, rangedAttackMod)
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetRangedDamage (l.468-537).
|
||
function PaperDollFrame_SetRangedDamage(unit, prefix)
|
||
if unit == "pet" then return end
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local damageText = getglobal(prefix .. "RangedDamageFrameStatText")
|
||
local damageFrame = getglobal(prefix .. "RangedDamageFrame")
|
||
local paperDoll = getglobal("PaperDollFrame")
|
||
if paperDoll.noRanged then
|
||
damageText:SetText(NOT_APPLICABLE)
|
||
damageFrame.damage = nil
|
||
return
|
||
end
|
||
local rangedAttackSpeed, minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent =
|
||
UnitRangedDamage(unit)
|
||
local figure = damageFigure(minDamage, maxDamage)
|
||
local rawMin, rawMax, fullDamage, totalBonus =
|
||
unscaleSwing(minDamage, maxDamage, physicalBonusPos, physicalBonusNeg, percent)
|
||
local damagePerSecond = math.max(fullDamage, 1) / rangedAttackSpeed
|
||
local tooltip = damageRange(rawMin, rawMax)
|
||
if totalBonus == 0 then
|
||
damageText:SetText(figure)
|
||
else
|
||
damageText:SetText(damageColor(totalBonus) .. figure .. "|r")
|
||
tooltip = damageModifiers(tooltip, physicalBonusPos, physicalBonusNeg, percent)
|
||
-- Only the modified case gets a hover plate here: an unmodified bow has nothing to
|
||
-- explain beyond the figure already on the row.
|
||
damageFrame.tooltip = tooltip .. " " .. format(DPS_TEMPLATE, damagePerSecond)
|
||
end
|
||
damageFrame.attackSpeed = rangedAttackSpeed
|
||
damageFrame.damage = tooltip
|
||
damageFrame.dps = damagePerSecond
|
||
end
|
||
|
||
-- ref PaperDollFrame_SetRangedAttackPower (l.539-566).
|
||
function PaperDollFrame_SetRangedAttackPower(unit, prefix)
|
||
if unit == "pet" then return end
|
||
unit = unit or "player"
|
||
prefix = prefix or "Character"
|
||
local frame = getglobal(prefix .. "RangedAttackPowerFrame")
|
||
local text = getglobal(prefix .. "RangedAttackPowerFrameStatText")
|
||
local paperDoll = getglobal("PaperDollFrame")
|
||
if paperDoll.noRanged then
|
||
text:SetText(NOT_APPLICABLE)
|
||
frame.tooltip = nil
|
||
return
|
||
end
|
||
if HasWandEquipped() then
|
||
text:SetText("--")
|
||
frame.tooltip = nil
|
||
return
|
||
end
|
||
local base, posBuff, negBuff = UnitRangedAttackPower(unit)
|
||
PaperDollFormatStat(RANGED_ATTACK_POWER, base, posBuff, negBuff, frame, text)
|
||
frame.tooltipSubtext = format(RANGED_ATTACK_POWER_TOOLTIP,
|
||
math.max(base + posBuff + negBuff, 0) / ATTACK_POWER_MAGIC_NUMBER)
|
||
end
|
||
|
||
-- ref PaperDollFormatStat (l.804-833).
|
||
function PaperDollFormatStat(name, base, posBuff, negBuff, frame, textString)
|
||
local effective = math.max(0, base + posBuff + negBuff)
|
||
local text = HIGHLIGHT_FONT_COLOR_CODE .. name .. " " .. effective
|
||
if posBuff == 0 and negBuff == 0 then
|
||
text = text .. FONT_COLOR_CODE_CLOSE
|
||
textString:SetText(effective)
|
||
else
|
||
if posBuff > 0 or negBuff < 0 then
|
||
text = text .. " (" .. base .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if posBuff > 0 then
|
||
text = text .. FONT_COLOR_CODE_CLOSE .. GREEN_FONT_COLOR_CODE .. "+" .. posBuff .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if negBuff < 0 then
|
||
text = text .. RED_FONT_COLOR_CODE .. " " .. negBuff .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if posBuff > 0 or negBuff < 0 then
|
||
text = text .. HIGHLIGHT_FONT_COLOR_CODE .. ")" .. FONT_COLOR_CODE_CLOSE
|
||
end
|
||
if negBuff < 0 then
|
||
textString:SetText(RED_FONT_COLOR_CODE .. effective .. FONT_COLOR_CODE_CLOSE)
|
||
else
|
||
textString:SetText(GREEN_FONT_COLOR_CODE .. effective .. FONT_COLOR_CODE_CLOSE)
|
||
end
|
||
end
|
||
frame.tooltip = text
|
||
end
|
||
|
||
-- ref PaperDollStatTooltip(unit, stat) (l.786-802), signature-exact: the button is the
|
||
-- ambient `this` the reference reads, NOT a leading parameter, and `unit` leads `stat`
|
||
-- exactly as the ref declares them (its pet rows pass "pet" — ref PetPaperDollFrame.xml:345;
|
||
-- ours likewise, decision 1057). A leading frame parameter here was a wrong SHAPE behind a
|
||
-- right name — 1199's failure mode — because an addon calls the 1.12 arity.
|
||
function PaperDollStatTooltip(unit, stat)
|
||
local button = this
|
||
-- classFileName nil until the first player snapshot lands (this file's SetLevel comment
|
||
-- on the same engine timing gap) — "" just misses every class lookup, falling through to
|
||
-- DEFAULT_*_TOOLTIP below exactly like an unrecognized class would. A pet has no class
|
||
-- either, so the pet rows land on the same DEFAULT_* fallback the ref gives them.
|
||
local _, classFileName = UnitClass(unit or "player")
|
||
classFileName = classFileName or ""
|
||
local classStatText = getglobal(strupper(classFileName) .. "_" .. stat .. "_TOOLTIP")
|
||
if not classStatText then
|
||
classStatText = getglobal("DEFAULT_" .. stat .. "_TOOLTIP")
|
||
end
|
||
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(button.tooltip)
|
||
GameTooltip:AddLine(classStatText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
end
|
||
|
||
-- ref CharacterDamageFrame_OnEnter (l.835-851).
|
||
function CharacterDamageFrame_OnEnter()
|
||
local button = this
|
||
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(INVTYPE_WEAPONMAINHAND, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(ATTACK_SPEED_COLON, string.format("%.2f", button.attackSpeed),
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(DAMAGE_COLON, button.damage,
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(DAMAGE_PER_SECOND, string.format("%.1f", button.dps),
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
if button.offhandAttackSpeed then
|
||
GameTooltip:AddLine(" ")
|
||
GameTooltip:AddLine(INVTYPE_WEAPONOFFHAND, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(ATTACK_SPEED_COLON, string.format("%.2f", button.offhandAttackSpeed),
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(DAMAGE_COLON, button.offhandDamage,
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(DAMAGE_PER_SECOND, string.format("%.1f", button.offhandDps),
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
end
|
||
GameTooltip:Show()
|
||
end
|
||
|
||
-- ref CharacterRangedDamageFrame_OnEnter (l.853-863).
|
||
function CharacterRangedDamageFrame_OnEnter()
|
||
local button = this
|
||
if not button.damage then return end
|
||
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(INVTYPE_RANGED, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(ATTACK_SPEED_COLON, string.format("%.2f", button.attackSpeed),
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(DAMAGE_COLON, button.damage,
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:AddDoubleLine(DAMAGE_PER_SECOND, string.format("%.1f", button.dps),
|
||
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
||
GameTooltip:Show()
|
||
end
|
||
|
||
-- ── The 19 equipment slots + ammo (ref PaperDollItemSlotButton_*, l.78-98/594-784) ──────
|
||
-- Cooldowns, the broken-red tint, and SHOW_COMPARE_TOOLTIP are still OMITTED this phase —
|
||
-- this file's header comment. What ships now (1b): the icon/count fill, the empty-slot
|
||
-- art, the hover tooltip, click-to-equip/-use (l.647-662, ctrl/shift named deferrals),
|
||
-- the drag trio, lock dimming, and the CURSOR_UPDATE ring highlight (emulated — header
|
||
-- comment).
|
||
function BenillaPaperDollSlot_OnLoad(button, slotName)
|
||
button:RegisterEvent("UNIT_INVENTORY_CHANGED")
|
||
local id, texture, checkRelic = GetInventorySlotInfo(slotName)
|
||
button.invSlotId = id
|
||
button.slotName = slotName
|
||
button.emptyTexture = texture
|
||
button.checkRelic = checkRelic -- UnitHasRelicSlot is vanilla-always-false (header OMIT): never read
|
||
button.icon = getglobal(button:GetName() .. "IconTexture")
|
||
button.count = getglobal(button:GetName() .. "Count")
|
||
-- Equip interactions on the 19 named slots use the equip-swap wire (PickupInventoryItem
|
||
-- pick/place/swap). The ammo slot (id 0) is different: it LOADS ammo by entry via
|
||
-- CMSG_SET_AMMO (decision 0526; wow-re cursor-dragdrop-slots.md — the client's auto-equip
|
||
-- sender forks ammo-class → SET_AMMO), so it only wants click-to-place + drag-drop, both
|
||
-- routed to BenillaCharacterAmmoSlot_OnClick (its own Scripts block below). It carries no
|
||
-- ring, so the CURSOR_UPDATE highlight / ITEM_LOCK dim legs would no-op anyway, and there
|
||
-- is no drag-OUT (you can't pick the loaded ammo off the slot).
|
||
if slotName ~= "AmmoSlot" then
|
||
button:RegisterEvent("ITEM_LOCK_CHANGED")
|
||
button:RegisterEvent("CURSOR_UPDATE")
|
||
-- The shift-compare listener (ref PaperDollFrame.lua l.83): the ENGINE fires
|
||
-- SHOW_COMPARE_TOOLTIP(slot, n) for an equippable item hover while shift is held;
|
||
-- only the matching visible doll slot answers (the OnEvent branch below).
|
||
button:RegisterEvent("SHOW_COMPARE_TOOLTIP")
|
||
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
|
||
-- The drag trio (decision 0216 §3): click-carry and drag are ONE code path in the
|
||
-- reference (PaperDollFrame.xml:20-25 routes OnDragStart/OnReceiveDrag to the same
|
||
-- OnClick("LeftButton", ignoreModifiers=1) the plain click uses) — see
|
||
-- BenillaPaperDollSlot_OnClick below for the identical routing, the bag slots' own
|
||
-- precedent (BagFrame.xml's BenillaBagSlot_OnClick).
|
||
button:RegisterForDrag("LeftButton")
|
||
else
|
||
button:RegisterForClicks("LeftButtonUp")
|
||
end
|
||
BenillaPaperDollSlot_Update(button)
|
||
end
|
||
|
||
-- Left: the reference's own modifier fork (ref l.647-660) — CTRL previews the worn
|
||
-- item in the dressing room (decision 1060), SHIFT posts its link into an open chat edit
|
||
-- box (decision 1059), and neither falls through to a pickup a real modified click would
|
||
-- never trigger. Plain click: PickupInventoryItem (pick up / place / swap / cancel,
|
||
-- ref l.656). Right: UseInventoryItem (ref l.659). `ignoreModifiers` is the drag-route
|
||
-- calls' own suppression, the bag slots' precedent.
|
||
function BenillaPaperDollSlot_OnClick(button, mouseButton, ignoreModifiers)
|
||
if mouseButton == "LeftButton" then
|
||
if IsControlKeyDown() and not ignoreModifiers then
|
||
DressUpItemLink(GetInventoryItemLink("player", button.invSlotId))
|
||
return
|
||
elseif IsShiftKeyDown() and not ignoreModifiers then
|
||
-- Both of the ref's guards (l.652: only when the edit box is up) plus our own
|
||
-- nil-link one live in the shared helper (UIParent.xml). A shift-click with chat
|
||
-- closed does nothing here — a worn item has no stack to split, unlike a bag slot.
|
||
BenillaChatEdit_InsertLink(GetInventoryItemLink("player", button.invSlotId))
|
||
return
|
||
end
|
||
PickupInventoryItem(button.invSlotId)
|
||
GameTooltip:Hide() -- the item just left the slot for the cursor
|
||
BenillaPaperDollSlot_Update(button)
|
||
elseif mouseButton == "RightButton" then
|
||
UseInventoryItem(button.invSlotId)
|
||
end
|
||
end
|
||
|
||
-- The ammo slot's own click/drop (decision 0526): ammo LOADS by entry (CMSG_SET_AMMO), it
|
||
-- doesn't swap into an equip slot, so it can't share BenillaPaperDollSlot_OnClick's
|
||
-- PickupInventoryItem path. Only an ammo payload fits (CursorCanGoInSlot(0) — ammo's
|
||
-- find_equip_slot is [0]); the drop funnels through AutoEquipCursorItem, whose app-side drain
|
||
-- resolves the held item's entry and forks to SET_AMMO — the reference's own shared auto-equip
|
||
-- sender (wow-re cursor-dragdrop-slots.md). An empty cursor or a non-ammo payload is inert
|
||
-- (nothing fits), and there is no unload-via-slot in 1.12.
|
||
function BenillaCharacterAmmoSlot_OnClick(button)
|
||
if CursorCanGoInSlot(button.invSlotId) then
|
||
AutoEquipCursorItem()
|
||
GameTooltip:Hide()
|
||
BenillaPaperDollSlot_Update(button)
|
||
end
|
||
end
|
||
|
||
function BenillaPaperDollSlot_Update(button)
|
||
local textureName = GetInventoryItemTexture("player", button.invSlotId)
|
||
if textureName then
|
||
button.icon:SetTexture(textureName)
|
||
local count = GetInventoryItemCount("player", button.invSlotId)
|
||
if (count or 0) > 1 then
|
||
button.count:SetText(count)
|
||
else
|
||
button.count:SetText("")
|
||
end
|
||
else
|
||
button.icon:SetTexture(button.emptyTexture)
|
||
button.count:SetText("")
|
||
end
|
||
-- A BROKEN item (durability 0) tints icon + slot ring red — ref
|
||
-- PaperDollItemSlotButton_Update (l.670-676: SetItemButtonTextureVertexColor +
|
||
-- SetItemButtonNormalTextureVertexColor, both 0.9, 0, 0). The bags carry no broken
|
||
-- tint in 1.12 (ContainerFrame.lua has none) — equipped only. Kept as a flag for
|
||
-- the cursor highlight's restore (Unlock below shares the ring).
|
||
button.broken = textureName and GetInventoryItemBroken("player", button.invSlotId)
|
||
-- A locked slot (its item is up on the cursor, or a send is still pending
|
||
-- server-side, decision 0216 §4/0218 §3) dims — the bag slots' own vertex-dim
|
||
-- convention (BagFrame.xml's BenillaBagSlot_Update), applied here instead of the
|
||
-- ref's separate SetItemButtonDesaturated call (no engine equivalent — header
|
||
-- comment). The transient drag-dim outranks the broken red on the icon.
|
||
if IsInventoryItemLocked(button.invSlotId) then
|
||
button.icon:SetVertexColor(0.4, 0.4, 0.4)
|
||
elseif button.broken then
|
||
button.icon:SetVertexColor(0.9, 0, 0)
|
||
else
|
||
button.icon:SetVertexColor(1, 1, 1)
|
||
end
|
||
BenillaPaperDollSlot_UnlockHighlight(button)
|
||
if GameTooltip:IsOwned(button) then
|
||
BenillaPaperDollSlot_OnEnter(button)
|
||
end
|
||
end
|
||
|
||
-- ref PaperDollItemSlotButton_OnEnter (l.739-748): the equipped hover is the INSTANCE
|
||
-- render — SetInventoryItem, never an id/template render. That carries the live
|
||
-- durability pair (an earlier SetItemById here read the template's full max/max), and
|
||
-- an instance hover never arms a shift-compare — the engine's SetInventoryItem doesn't
|
||
-- arm (a worn item compared with itself is no comparison; director-caught: shift over
|
||
-- the doll popped a "Currently Equipped" 0/XX beside a template-full main tooltip).
|
||
-- The ref's repair-mode branch (l.757-762) is INTERIM-omitted with the binding's
|
||
-- repairCost. checkRelic (l.744-746) is TBC-flagged dead weight in 1.12 — omitted.
|
||
function BenillaPaperDollSlot_OnEnter(button)
|
||
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
|
||
local hasItem = GameTooltip:SetInventoryItem("player", button.invSlotId)
|
||
if not hasItem then
|
||
-- Empty slot: the slot-name tooltip (ref strupper(strsub(name,10)) -> the matching
|
||
-- GlobalStrings key, e.g. "HeadSlot" -> HEADSLOT -> "Head").
|
||
GameTooltip:SetText(getglobal(strupper(button.slotName)) or button.slotName)
|
||
end
|
||
GameTooltip:Show()
|
||
end
|
||
|
||
-- CURSOR_UPDATE's LockHighlight/UnlockHighlight emulation (this file's header comment: no
|
||
-- true forced-highlight affordance exists — no benilla-ui Button method shows a state
|
||
-- texture off real hover). A vertex tint on the slot's own Quickslot2 ring
|
||
-- (`NormalTexture`, the template below) stands in: green while `CursorCanGoInSlot` says
|
||
-- the held payload fits here, white otherwise.
|
||
-- Both legs no-op on a ringless slot (the ammo slot carries no NormalTexture).
|
||
function BenillaPaperDollSlot_LockHighlight(button)
|
||
local ring = button:GetNormalTexture()
|
||
if ring then
|
||
ring:SetVertexColor(0, 1, 0)
|
||
end
|
||
end
|
||
|
||
-- The restore leg: back to the ring's resting color — broken red (ref l.672) or white.
|
||
function BenillaPaperDollSlot_UnlockHighlight(button)
|
||
local ring = button:GetNormalTexture()
|
||
if not ring then
|
||
return
|
||
end
|
||
if button.broken then
|
||
ring:SetVertexColor(0.9, 0, 0)
|
||
else
|
||
ring:SetVertexColor(1, 1, 1)
|
||
end
|
||
end
|
||
|
||
function BenillaPaperDollSlot_OnEvent(button)
|
||
if event == "UNIT_INVENTORY_CHANGED" and arg1 == "player" then
|
||
BenillaPaperDollSlot_Update(button)
|
||
elseif event == "ITEM_LOCK_CHANGED" then
|
||
-- Ref PaperDollItemSlotButton_OnEvent (l.601-604): no arg filtering — every doll
|
||
-- slot just repaints on any lock transition (cheap, idempotent; it's this SLOT's
|
||
-- own dim that's actually changing, if anything is).
|
||
BenillaPaperDollSlot_Update(button)
|
||
elseif event == "CURSOR_UPDATE" then
|
||
if CursorCanGoInSlot(button.invSlotId) then
|
||
BenillaPaperDollSlot_LockHighlight(button)
|
||
else
|
||
BenillaPaperDollSlot_UnlockHighlight(button)
|
||
end
|
||
elseif event == "SHOW_COMPARE_TOOLTIP" then
|
||
-- ref PaperDollItemSlotButton_OnEvent (l.621-640, quoted; button.invSlotId for the
|
||
-- ref's this:GetID()): arg1 = the doll slot the hovered item would equip into,
|
||
-- arg2 = which shopping tooltip. Both anchor to THIS doll slot (1 to its right,
|
||
-- 2 hanging below-right) — so compares appear only with the character window
|
||
-- open, the 1.12 behavior.
|
||
if arg1 ~= button.invSlotId or not button:IsVisible() or arg2 > NUM_SHOPPING_TOOLTIPS then
|
||
return
|
||
end
|
||
local tooltip = getglobal("ShoppingTooltip" .. arg2)
|
||
local anchor = "ANCHOR_RIGHT"
|
||
if arg2 > 1 then
|
||
anchor = "ANCHOR_BOTTOMRIGHT"
|
||
end
|
||
tooltip:SetOwner(button, anchor)
|
||
local hasItem = tooltip:SetInventoryItem("player", button.invSlotId)
|
||
if not hasItem then
|
||
tooltip:Hide()
|
||
end
|
||
end
|
||
end
|
||
|
||
-- ref PaperDollItemSlotButton_OnUpdate (l.766-784), the un-throttled BagFrame-style
|
||
-- re-enter loop (BagFrame.xml's own comment on why this ships unthrottled).
|
||
function BenillaPaperDollSlot_OnUpdate(button)
|
||
if GameTooltip:IsOwned(button) then
|
||
BenillaPaperDollSlot_OnEnter(button)
|
||
end
|
||
end
|
||
|
||
-- ── The model pane (ref UIParent.lua:1420-1462) ─────────────────────────────────────────
|
||
-- This file's header comment: a booth-texture stand-in, not a live widget. `frame` here is
|
||
-- CharacterModelFrame; `frame.facing` stands in for the ref's `model.rotation`.
|
||
-- These four are the ref's SHARED Model_* helpers (UIParent.lua), and they are shared here
|
||
-- too: the pet page's pane (decision 1057) drives the very same code. The one thing that
|
||
-- differs per pane is WHERE the yaw goes — the engine has no `Model:SetFacing` widget
|
||
-- method, so each pane has its own booth-yaw binding — so the pane names its own in
|
||
-- `frame.setFacing` at OnLoad and these read it. (The inspect window still carries its own
|
||
-- copy of this quartet, predating the generalization; folding it in is a follow-up, not
|
||
-- this slice's to churn.)
|
||
function BenillaPaperDollModel_OnLoad(frame)
|
||
frame.facing = 0.61 -- ref UIParent.lua:1422, the literal default facing
|
||
frame.setFacing(frame.facing)
|
||
end
|
||
|
||
-- ref Model_RotateLeft/Right (UIParent.lua:1426-1442) — the ref's OWN left/right sign
|
||
-- convention is preserved verbatim (rotate-left SUBTRACTS, rotate-right ADDS); not
|
||
-- re-derived, just quoted.
|
||
function BenillaPaperDollModel_RotateLeft(frame)
|
||
frame.facing = frame.facing - 0.03
|
||
frame.setFacing(frame.facing)
|
||
PlaySound("igInventoryRotateCharacter")
|
||
end
|
||
|
||
function BenillaPaperDollModel_RotateRight(frame)
|
||
frame.facing = frame.facing + 0.03
|
||
frame.setFacing(frame.facing)
|
||
PlaySound("igInventoryRotateCharacter")
|
||
end
|
||
|
||
-- ref Model_OnUpdate (UIParent.lua:1444-1462) — the HELD-button continuous spin, which is
|
||
-- where a rotate button's actual feel lives: half a turn per second (ROTATIONS_PER_SECOND,
|
||
-- UIParent.lua:2), against which the 0.03 click nudge above is a ~1.7° tap. Every 1.12 model
|
||
-- pane wires it (PaperDollFrame l.222-224, DressUpFrame.xml:202-204, TabardFrame.xml:406-408).
|
||
-- It used to be omitted here for want of `GetButtonState` — that method now answers "PUSHED"
|
||
-- for a LIVE mouse-held button (decision 0288 P3), so the ref's own loop ships here.
|
||
-- That INCLUDES the ref's sign quirk: the held branches run OPPOSITE to the click helpers
|
||
-- (held-LEFT adds, held-RIGHT subtracts). Not a slip of ours — the ref's own, not re-derived.
|
||
function BenillaPaperDollModel_OnUpdate(frame, elapsed)
|
||
local step = elapsed * 2 * PI * ROTATIONS_PER_SECOND
|
||
if getglobal(frame:GetName() .. "RotateLeftButton"):GetButtonState() == "PUSHED" then
|
||
frame.facing = frame.facing + step
|
||
if frame.facing < 0 then
|
||
frame.facing = frame.facing + (2 * PI)
|
||
end
|
||
frame.setFacing(frame.facing)
|
||
end
|
||
if getglobal(frame:GetName() .. "RotateRightButton"):GetButtonState() == "PUSHED" then
|
||
frame.facing = frame.facing - step
|
||
if frame.facing > (2 * PI) then
|
||
frame.facing = frame.facing - (2 * PI)
|
||
end
|
||
frame.setFacing(frame.facing)
|
||
end
|
||
end
|
||
|
||
-- ref CharacterModelFrame_OnMouseUp (l.31-35): a plain left-click-up on the model pane
|
||
-- also auto-equips a held container item — the model pane doubles as a drop target even
|
||
-- without a drag, not just via OnReceiveDrag. AutoEquipCursorItem is already a no-op for
|
||
-- every non-fitting/non-container payload (cursor.rs's own contract), so no CursorHasItem
|
||
-- guard is needed here.
|
||
function BenillaPaperDollModel_OnMouseUp(frame, button)
|
||
if button == "LeftButton" then
|
||
AutoEquipCursorItem()
|
||
end
|
||
end
|
||
]]></Script>
|
||
|
||
<!--
|
||
The shared equipment-slot button (ref PaperDollItemSlotButtonTemplate inherits
|
||
ItemButtonTemplate, ref-PaperDollFrame.xml:4-38 — ItemButtonTemplate's own XML isn't part of
|
||
this session's extraction, so its shape is taken from this codebase's own prior
|
||
reconstruction of it, BagFrame.xml's BenillaBagSlotTemplate: 37x37, a BACKGROUND icon fill,
|
||
an OVERLAY count, the 64x64 UI-Quickslot2 ring centered (0,-1), UI-Quickslot-Depress pushed,
|
||
ButtonHilight-Square ADD highlight — duplicated here rather than cross-file inherited so this
|
||
window's slot behavior (UNIT_INVENTORY_CHANGED refresh, tooltip) stays self-contained in one
|
||
file, the same posture Merchant's own item-row template takes vs. reusing Bag's).
|
||
Cooldown ($parentCooldown, CooldownFrameTemplate) OMITTED — no cooldown feed/engine
|
||
affordance exists yet. OnClick + the drag trio land THIS phase (1b) — the NormalTexture
|
||
ring below doubles as the CURSOR_UPDATE highlight's vertex-tint target (this file's header
|
||
comment: an emulation, no true LockHighlight/UnlockHighlight exists).
|
||
-->
|
||
<Button name="BenillaPaperDollItemSlotTemplate" virtual="true">
|
||
<Size><AbsDimension x="37" y="37"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentIconTexture"/>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="$parentCount" inherits="NumberFontNormal" justifyH="RIGHT">
|
||
<Anchors>
|
||
<Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="-5" y="2"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<NormalTexture file="Interface\Buttons\UI-Quickslot2">
|
||
<Size><AbsDimension x="64" y="64"/></Size>
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="-1"/></Offset></Anchor>
|
||
</Anchors>
|
||
</NormalTexture>
|
||
<PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
|
||
<HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
|
||
<Scripts>
|
||
<OnClick>BenillaPaperDollSlot_OnClick(self, arg1)</OnClick>
|
||
<OnEnter>BenillaPaperDollSlot_OnEnter(self)</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
<OnEvent>BenillaPaperDollSlot_OnEvent(self)</OnEvent>
|
||
<OnUpdate>BenillaPaperDollSlot_OnUpdate(self)</OnUpdate>
|
||
<!-- The reference routes both drag entries to the plain click path with modifiers
|
||
suppressed (PaperDollFrame.xml:20-25 — PickupInventoryItem, ignoring
|
||
shift/ctrl) — the bag slots' own precedent (BagFrame.xml). -->
|
||
<OnDragStart>BenillaPaperDollSlot_OnClick(self, "LeftButton", true)</OnDragStart>
|
||
<OnReceiveDrag>BenillaPaperDollSlot_OnClick(self, "LeftButton", true)</OnReceiveDrag>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!--
|
||
The ref defines TWO byte-identical templates for the attribute/stat rows — StatFrameTemplate
|
||
(ref-PaperDollFrame.xml:39-71) and AttributeFrameTemplate (l.72-105): same 104x13 size, same
|
||
Label(GameFontNormalSmall,LEFT) + $parentStat(30x13,RIGHT)/$parentText(GameFontHighlightSmall,
|
||
RIGHT) shape — a semantic-only distinction in the real client (armor/attack rows "are stats",
|
||
the 5 primaries "are attributes"). Collapsed to ONE template here (a legitimate reuse — no
|
||
behavior differs). `self:EnableMouse(true)` in OnLoad: a plain Frame isn't mouse-enabled by
|
||
construction in this engine (this file's header comment) — every instance below needs it to
|
||
receive its own OnEnter/OnLeave hover.
|
||
-->
|
||
<Frame name="BenillaStatRowTemplate" virtual="true">
|
||
<Size><AbsDimension x="104" y="13"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<FontString name="$parentLabel" inherits="GameFontNormalSmall">
|
||
<Anchors><Anchor point="LEFT"/></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<Frame name="$parentStat">
|
||
<Size><AbsDimension x="30" y="13"/></Size>
|
||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<FontString name="$parentText" inherits="GameFontHighlightSmall" justifyH="RIGHT">
|
||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
|
||
<!--
|
||
The magic-resistance icon frame (ref MagicResistanceFrameTemplate, ref-PaperDollFrame.xml:
|
||
106-123): 32x29, a generic tooltip OnEnter reading self.tooltip/self.tooltipSubtext (both
|
||
filled by PaperDollFrame_SetResistances) — no per-instance OnEnter override needed
|
||
(the ref's own MagicResFrame1-5 instances carry none either).
|
||
-->
|
||
<Frame name="BenillaMagicResistanceFrameTemplate" virtual="true">
|
||
<Size><AbsDimension x="32" y="29"/></Size>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>
|
||
if self.tooltip then
|
||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(self.tooltip)
|
||
GameTooltip:AddLine(self.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
end
|
||
</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<Frame name="CharacterFrame" hidden="true">
|
||
<!-- The real 384x512 window (ref-CharacterFrame.xml:5-7). No <Anchors> — ShowUIPanel/
|
||
SetLeftFrame SetPoints this to the left slot on open (UiPanels.xml's own convention). -->
|
||
<Size><AbsDimension x="384" y="512"/></Size>
|
||
<!-- HitRectInsets: transcribed for fidelity; no loader wiring exists yet (this file's header
|
||
comment) — a harmless no-op today. -->
|
||
<HitRectInsets><AbsInset left="0" right="30" top="0" bottom="45"/></HitRectInsets>
|
||
<Layers>
|
||
<!-- Portrait 60x60 TOPLEFT (7,-6) (ref l.19-31). Set in OnShow via SetPortraitTexture
|
||
(the live model-bake binding, UnitFrames.xml's own precedent) — "player" is a fixed
|
||
token here (unlike a unit frame, this window only ever shows the local player). -->
|
||
<Layer level="ARTWORK">
|
||
<Texture name="CharacterFramePortrait">
|
||
<Size><AbsDimension x="60" y="60"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- Name: CENTER (6,232) wrapper, GameFontNormal 300x12 centered inside it, white (ref
|
||
l.35-64). UnitPVPName -> plain UnitName (this file's header comment: PVP rank is a
|
||
named deferral) — CharacterTitleText/CharacterGuildText (both PVP-rank/guild-driven)
|
||
are dropped with it. -->
|
||
<Frame name="CharacterNameFrame">
|
||
<Size><AbsDimension x="109" y="12"/></Size>
|
||
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="6" y="232"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<Layer>
|
||
<FontString name="CharacterNameText" inherits="GameFontNormal" text="NAME">
|
||
<Size><AbsDimension x="300" y="12"/></Size>
|
||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||
<Color r="1.0" g="1.0" b="1.0"/>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnLoad>self:SetFrameLevel(self:GetFrameLevel() + 1)</OnLoad>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Close: the ref's own UIPanelCloseButton position (CENTER of TOPRIGHT, -44,-25 — ref
|
||
l.65-78), on the minimize-button art this codebase's close buttons all use (no
|
||
UIPanelCloseButton template ships — Gossip/Bag/Merchant's own header comments).
|
||
OnLoad raises the frame level +1 (the same idiom CharacterNameFrame above uses,
|
||
the ref's own): the window's own art is PaperDollFrame's BACKGROUND
|
||
layer — a full-window sibling created AFTER this button — so at the shared default
|
||
level 0 the page's art paints over the button by creation order (order.rs). The ref's
|
||
close button rides the client's show-time bucket-tail re-insertion instead; benilla
|
||
proxies that with creation order, so the explicit raise is the faithful compensation. -->
|
||
<Button name="CharacterFrameCloseButton">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors><Anchor point="CENTER" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-44" y="-25"/></Offset></Anchor></Anchors>
|
||
<NormalTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Up"/>
|
||
<PushedTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Down"/>
|
||
<HighlightTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Highlight" alphaMode="ADD"/>
|
||
<Scripts>
|
||
<OnLoad>self:SetFrameLevel(self:GetFrameLevel() + 1)</OnLoad>
|
||
<OnClick>HideUIPanel(getglobal("CharacterFrame"))</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<!-- The tab row (ref l.79-168 — CENTER of BOTTOMLEFT +60,+62 — each tab chained off the
|
||
last, MerchantFrame.xml's own Merchant/Buyback LEFT-of-RIGHT -16 offset). Four of
|
||
the real client's five, in ITS OWN ORDER and at ITS OWN NUMBERS:
|
||
Character/Pet/Reputation/Skills. The long-standing deviation flagged here — our
|
||
Skills tab rendering 3rd where the reference's is 4th, because Reputation did not
|
||
exist — is GONE with ReputationFrame.xml; only Honor (the reference's tab 5) is
|
||
still out, and it sits past the end where its absence shifts nothing (the "tabs
|
||
arrive with their pages" precedent, 0138/0208).
|
||
Tab 2 starts HIDDEN — it is raised only for a unit with a pet UI, by the pet page's
|
||
PetTab_Update, which also re-anchors tab 3 onto tab 2's LEFT while it is
|
||
down so the row has no gap (ref PetPaperDollFrame.lua:189-198). That is now the
|
||
reference's own function on the reference's own tab, Reputation and all. Tab 3's
|
||
XML anchor is the WITH-pet one; the update owns it from load onward.
|
||
No hover tooltip (MicroButtonTooltipText/TOGGLECHARACTER0 have no engine surface,
|
||
and each tab's own visible label already says what it does — Merchant's own tab
|
||
pair carries no hover tooltip either). -->
|
||
<!-- The four labels are the ref's own GlobalStrings KEYS (l.79/97/114/…), not literals —
|
||
the loader resolves a key-shaped `text=` through the shipped strings, so the row is
|
||
localized like the reference's. (Tabs 1/4 carried the enUS literals from 0437;
|
||
same words either way in enUS, and now the same mechanism.) -->
|
||
<Button name="CharacterFrameTab1" inherits="CharacterFrameTabButtonTemplate" text="CHARACTER">
|
||
<Anchors><Anchor point="CENTER" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="60" y="62"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnClick>BenillaCharacterFrameTab_OnClick(1)</OnClick></Scripts>
|
||
</Button>
|
||
<Button name="CharacterFrameTab2" inherits="CharacterFrameTabButtonTemplate" text="PET" hidden="true">
|
||
<Anchors><Anchor point="LEFT" relativeTo="CharacterFrameTab1" relativePoint="RIGHT"><Offset><AbsDimension x="-16" y="0"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnClick>BenillaCharacterFrameTab_OnClick(2)</OnClick></Scripts>
|
||
</Button>
|
||
<Button name="CharacterFrameTab3" inherits="CharacterFrameTabButtonTemplate" text="REPUTATION">
|
||
<Anchors><Anchor point="LEFT" relativeTo="CharacterFrameTab2" relativePoint="RIGHT"><Offset><AbsDimension x="-16" y="0"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnClick>BenillaCharacterFrameTab_OnClick(3)</OnClick></Scripts>
|
||
</Button>
|
||
<Button name="CharacterFrameTab4" inherits="CharacterFrameTabButtonTemplate" text="SKILLS">
|
||
<Anchors><Anchor point="LEFT" relativeTo="CharacterFrameTab3" relativePoint="RIGHT"><Offset><AbsDimension x="-16" y="0"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnClick>BenillaCharacterFrameTab_OnClick(4)</OnClick></Scripts>
|
||
</Button>
|
||
|
||
<!-- The paper-doll page (ref PaperDollFrame, ref-PaperDollFrame.xml:124-1059). Sized to
|
||
exactly fill the parent instead of the ref's setAllPoints="true" — the same effect
|
||
(see this file's header: frame-level setAllPoints was a no-op when this was written
|
||
and is wired now; the explicit size is equivalent, so it stays). -->
|
||
<Frame name="PaperDollFrame" id="1">
|
||
<Size><AbsDimension x="384" y="512"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
|
||
<HitRectInsets><AbsInset left="0" right="30" top="0" bottom="45"/></HitRectInsets>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- The four quadrant slabs that ARE this page's art (ref l.129-173): 256-wide
|
||
left halves, 128-wide right halves, each 256 tall, pinned per corner. -->
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-CharacterTab-L1">
|
||
<Size><AbsDimension x="256" y="256"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-CharacterTab-R1">
|
||
<Size><AbsDimension x="128" y="256"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="0"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-CharacterTab-BottomLeft">
|
||
<Size><AbsDimension x="256" y="256"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-256"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-CharacterTab-BottomRight">
|
||
<Size><AbsDimension x="128" y="256"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="256" y="-256"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
<!-- "Level %d %s %s" under the name (ref l.174-182). -->
|
||
<FontString name="CharacterLevelText" inherits="GameFontNormalSmall">
|
||
<Anchors>
|
||
<Anchor point="TOP" relativeTo="CharacterNameText" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="-6"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- The model pane (ref PlayerModel CharacterModelFrame, l.204-273): 233x224 at
|
||
TOPLEFT (65,-78). This file's header comment: a plain Frame sampling the
|
||
paper-doll booth bake, not a live <Model>/<PlayerModel> widget. -->
|
||
<Frame name="CharacterModelFrame">
|
||
<Size><AbsDimension x="233" y="224"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="65" y="-78"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<!-- BACKGROUND, not ARTWORK — the booth quad is an OPAQUE stand-in for a
|
||
widget the reference renders outside the layer batches, so it must
|
||
sit as low as a region goes or it swallows same-level siblings
|
||
(decision 1070; the pet page's diet icon is where that bit). -->
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentTexture">
|
||
<Size><AbsDimension x="233" y="224"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- Rotate buttons (ref l.234-271): 35x35, left then right chained off its
|
||
TOPRIGHT, both at the model pane's own TOPLEFT.
|
||
`RegisterForClicks("LeftButtonDown","LeftButtonUp")` is the ref's own
|
||
(l.241-243/l.263-265): OnClick fires on BOTH edges, so one tap nudges
|
||
twice (2 × 0.03), and holding hands the spin to the pane's OnUpdate. -->
|
||
<Button name="CharacterModelFrameRotateLeftButton">
|
||
<Size><AbsDimension x="35" y="35"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
|
||
<NormalTexture file="Interface\Buttons\UI-RotationLeft-Button-Up"/>
|
||
<PushedTexture file="Interface\Buttons\UI-RotationLeft-Button-Down"/>
|
||
<HighlightTexture file="Interface\Buttons\ButtonHilight-Round" alphaMode="ADD"/>
|
||
<Scripts>
|
||
<OnLoad>self:RegisterForClicks("LeftButtonDown", "LeftButtonUp")</OnLoad>
|
||
<OnClick>BenillaPaperDollModel_RotateLeft(getglobal("CharacterModelFrame"))</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<Button name="CharacterModelFrameRotateRightButton">
|
||
<Size><AbsDimension x="35" y="35"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterModelFrameRotateLeftButton" relativePoint="TOPRIGHT"/></Anchors>
|
||
<NormalTexture file="Interface\Buttons\UI-RotationRight-Button-Up"/>
|
||
<PushedTexture file="Interface\Buttons\UI-RotationRight-Button-Down"/>
|
||
<HighlightTexture file="Interface\Buttons\ButtonHilight-Round" alphaMode="ADD"/>
|
||
<Scripts>
|
||
<OnLoad>self:RegisterForClicks("LeftButtonDown", "LeftButtonUp")</OnLoad>
|
||
<OnClick>BenillaPaperDollModel_RotateRight(getglobal("CharacterModelFrame"))</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>
|
||
-- A plain Frame isn't mouse-enabled by construction (this file's
|
||
-- header comment) — needed THIS phase (1b) so the pane hit-tests
|
||
-- as an OnMouseUp/OnReceiveDrag drop target (ref l.226-231).
|
||
self:EnableMouse(true)
|
||
self.setFacing = BenillaPaperDollModel_SetFacing
|
||
BenillaPaperDollModel_OnLoad(self)
|
||
BenillaSetBoothTexture(getglobal(self:GetName() .. "Texture"), "paperdoll")
|
||
</OnLoad>
|
||
<!-- ref l.221-224: the pane drives the held-button spin every frame. -->
|
||
<OnUpdate>BenillaPaperDollModel_OnUpdate(self, arg1)</OnUpdate>
|
||
<OnMouseUp>BenillaPaperDollModel_OnMouseUp(self, arg1)</OnMouseUp>
|
||
<OnReceiveDrag>AutoEquipCursorItem()</OnReceiveDrag>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<!-- The attributes block (ref CharacterAttributesFrame, l.274-405): 230x78 at
|
||
TOPLEFT (67,-291), the 9-piece UI-Character-StatBackground plate (3 stacked
|
||
3-piece columns: player-stat / melee / ranged) behind the rows. -->
|
||
<Frame name="CharacterAttributesFrame">
|
||
<Size><AbsDimension x="230" y="78"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="67" y="-291"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="PlayerStatBackgroundTop" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0" bottom="0.125"/>
|
||
</Texture>
|
||
<Texture name="PlayerStatBackgroundMiddle" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="53"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="PlayerStatBackgroundTop" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0.125" bottom="0.1953125"/>
|
||
</Texture>
|
||
<Texture name="PlayerStatBackgroundBottom" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="PlayerStatBackgroundMiddle" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0.484375" bottom="0.609375"/>
|
||
</Texture>
|
||
<Texture name="MeleeAttackBackgroundTop" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="115" y="0"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0" bottom="0.125"/>
|
||
</Texture>
|
||
<Texture name="MeleeAttackBackgroundMiddle" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="12"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="MeleeAttackBackgroundTop" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0.125" bottom="0.1953125"/>
|
||
</Texture>
|
||
<Texture name="MeleeAttackBackgroundBottom" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="MeleeAttackBackgroundMiddle" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0.484375" bottom="0.609375"/>
|
||
</Texture>
|
||
<Texture name="RangedAttackBackgroundTop" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="MeleeAttackBackgroundBottom" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="2"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0" bottom="0.125"/>
|
||
</Texture>
|
||
<Texture name="RangedAttackBackgroundMiddle" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="11"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="RangedAttackBackgroundTop" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0.125" bottom="0.1953125"/>
|
||
</Texture>
|
||
<Texture name="RangedAttackBackgroundBottom" file="Interface\PaperDollInfoFrame\UI-Character-StatBackground">
|
||
<Size><AbsDimension x="115" y="16"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="RangedAttackBackgroundMiddle" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<TexCoords left="0" right="0.8984375" top="0.484375" bottom="0.609375"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- The 5 primary attributes (ref l.407-491): TOPLEFT (6,-3), chained
|
||
BOTTOMLEFT-to-BOTTOMLEFT. Labels are the TEMPLATE's $parentLabel,
|
||
filled from Lua exactly like the ref (PaperDollFrame_OnLoad /
|
||
_SetStats — see the former's header comment); instances declare no
|
||
regions of their own, same as the ref's. -->
|
||
<Frame name="CharacterStatFrame1" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="-3"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>PaperDollStatTooltip("player", "STRENGTH")</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<Frame name="CharacterStatFrame2" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterStatFrame1" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>PaperDollStatTooltip("player", "AGILITY")</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<Frame name="CharacterStatFrame3" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterStatFrame2" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>PaperDollStatTooltip("player", "STAMINA")</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<Frame name="CharacterStatFrame4" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterStatFrame3" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>PaperDollStatTooltip("player", "INTELLECT")</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<Frame name="CharacterStatFrame5" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterStatFrame4" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>PaperDollStatTooltip("player", "SPIRIT")</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Armor (ref l.492-511), chained off Stat5's BOTTOMLEFT. -->
|
||
<Frame name="CharacterArmorFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterStatFrame5" relativePoint="BOTTOMLEFT"/></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>
|
||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(self.tooltip)
|
||
GameTooltip:AddLine(self.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Melee attack rating (ref l.512-531), TOPLEFT of the attributes frame
|
||
itself (122,-2) — the second column's own chain root. -->
|
||
<Frame name="CharacterAttackFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterAttributesFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="122" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>
|
||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(ATTACK_TOOLTIP)
|
||
GameTooltip:AddLine(ATTACK_TOOLTIP_SUBTEXT, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Melee attack power (ref l.532-554), 99px-wide override (OnLoad). -->
|
||
<Frame name="CharacterAttackPowerFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterAttackFrame" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="5" y="1"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true); self:SetWidth(99)</OnLoad>
|
||
<OnEnter>
|
||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(self.tooltip)
|
||
GameTooltip:AddLine(self.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Melee damage (ref l.555-574), 99px-wide override, dedicated OnEnter. -->
|
||
<Frame name="CharacterDamageFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterAttackPowerFrame" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="1"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true); self:SetWidth(99)</OnLoad>
|
||
<OnEnter>CharacterDamageFrame_OnEnter()</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Ranged attack rating (ref l.575-594). -->
|
||
<Frame name="CharacterRangedAttackFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterDamageFrame" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="-5" y="-6"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true)</OnLoad>
|
||
<OnEnter>
|
||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(RANGED_ATTACK_TOOLTIP)
|
||
GameTooltip:AddLine(ATTACK_TOOLTIP_SUBTEXT, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Ranged attack power (ref l.595-619), 99px-wide, conditional tooltip
|
||
(the N/A / wand-equipped states leave self.tooltip nil). -->
|
||
<Frame name="CharacterRangedAttackPowerFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterRangedAttackFrame" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="5" y="1"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true); self:SetWidth(99)</OnLoad>
|
||
<OnEnter>
|
||
if self.tooltip then
|
||
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
GameTooltip:SetText(self.tooltip)
|
||
GameTooltip:AddLine(self.tooltipSubtext, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
||
GameTooltip:Show()
|
||
end
|
||
</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
<!-- Ranged damage (ref l.620-639), 99px-wide, dedicated OnEnter (no-ops
|
||
while self.damage is nil, e.g. no ranged weapon equipped). -->
|
||
<Frame name="CharacterRangedDamageFrame" inherits="BenillaStatRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterRangedAttackPowerFrame" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="1"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:EnableMouse(true); self:SetWidth(99)</OnLoad>
|
||
<OnEnter>CharacterRangedDamageFrame_OnEnter()</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
|
||
<!-- The 5 magic-resistance frames (ref CharacterResistanceFrame, l.642-760):
|
||
32x160 wrapper, TOPRIGHT of PaperDollFrame's own TOPLEFT (297,-77); each
|
||
32x29 icon TOP-chained off the previous, ids 6/2/3/4/5 (arcane/fire/nature/
|
||
frost/shadow — Holy(1) never rides the paper doll). -->
|
||
<Frame name="CharacterResistanceFrame">
|
||
<Size><AbsDimension x="32" y="160"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativeTo="PaperDollFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="297" y="-77"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Frames>
|
||
<Frame name="MagicResFrame1" inherits="BenillaMagicResistanceFrameTemplate">
|
||
<Anchors><Anchor point="TOP" relativeTo="CharacterResistanceFrame" relativePoint="TOP"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-ResistanceIcons">
|
||
<TexCoords left="0" right="1.0" top="0.2265625" bottom="0.33984375"/>
|
||
</Texture>
|
||
<FontString name="MagicResText1" inherits="GameFontHighlightSmall" text="X">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="3"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<Frame name="MagicResFrame2" inherits="BenillaMagicResistanceFrameTemplate">
|
||
<Anchors><Anchor point="TOP" relativeTo="MagicResFrame1" relativePoint="BOTTOM"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-ResistanceIcons">
|
||
<TexCoords left="0" right="1.0" top="0" bottom="0.11328125"/>
|
||
</Texture>
|
||
<FontString name="MagicResText2" inherits="GameFontHighlightSmall" text="X">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="3"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<Frame name="MagicResFrame3" inherits="BenillaMagicResistanceFrameTemplate">
|
||
<Anchors><Anchor point="TOP" relativeTo="MagicResFrame2" relativePoint="BOTTOM"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-ResistanceIcons">
|
||
<TexCoords left="0" right="1.0" top="0.11328125" bottom="0.2265625"/>
|
||
</Texture>
|
||
<FontString name="MagicResText3" inherits="GameFontHighlightSmall" text="X">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="3"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<Frame name="MagicResFrame4" inherits="BenillaMagicResistanceFrameTemplate">
|
||
<Anchors><Anchor point="TOP" relativeTo="MagicResFrame3" relativePoint="BOTTOM"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-ResistanceIcons">
|
||
<TexCoords left="0" right="1.0" top="0.33984375" bottom="0.453125"/>
|
||
</Texture>
|
||
<FontString name="MagicResText4" inherits="GameFontHighlightSmall" text="X">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="3"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
<Frame name="MagicResFrame5" inherits="BenillaMagicResistanceFrameTemplate">
|
||
<Anchors><Anchor point="TOP" relativeTo="MagicResFrame4" relativePoint="BOTTOM"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperDollInfoFrame\UI-Character-ResistanceIcons">
|
||
<TexCoords left="0" right="1.0" top="0.453125" bottom="0.56640625"/>
|
||
</Texture>
|
||
<FontString name="MagicResText5" inherits="GameFontHighlightSmall" text="X">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="3"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
|
||
<!-- The 19 equipment slots (ref l.761-931): left column TOPLEFT (21,-74) chained
|
||
BOTTOMLEFT -4, right column TOPLEFT (305,-74) chained the same way, the
|
||
weapon row off the page's own BOTTOMLEFT (122,127) then chained TOPRIGHT +5. -->
|
||
<Button name="CharacterHeadSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="21" y="-74"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "HeadSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterNeckSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterHeadSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "NeckSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterShoulderSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterNeckSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "ShoulderSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterBackSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterShoulderSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "BackSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterChestSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterBackSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "ChestSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterShirtSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterChestSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "ShirtSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterTabardSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterShirtSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "TabardSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterWristSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterTabardSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "WristSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterHandsSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="305" y="-74"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "HandsSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterWaistSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterHandsSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "WaistSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterLegsSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterWaistSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "LegsSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterFeetSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterLegsSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "FeetSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterFinger0Slot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterFeetSlot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "Finger0Slot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterFinger1Slot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterFinger0Slot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "Finger1Slot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterTrinket0Slot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterFinger1Slot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "Trinket0Slot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterTrinket1Slot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterTrinket0Slot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-4"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "Trinket1Slot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterMainHandSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="122" y="127"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "MainHandSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterSecondaryHandSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterMainHandSlot" relativePoint="TOPRIGHT"><Offset><AbsDimension x="5" y="0"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "SecondaryHandSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="CharacterRangedSlot" inherits="BenillaPaperDollItemSlotTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="CharacterSecondaryHandSlot" relativePoint="TOPRIGHT"><Offset><AbsDimension x="5" y="0"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>BenillaPaperDollSlot_OnLoad(self, "RangedSlot")</OnLoad></Scripts>
|
||
</Button>
|
||
|
||
<!-- The ammo slot (ref CharacterAmmoSlot, l.932-1043): 27x27, LEFT of the ranged
|
||
slot's RIGHT (+15,0). NOT built from the shared slot template — its own
|
||
hand-authored art (a pouch background + a corner-accent overlay cut from the
|
||
same sheet, no Quickslot2 ring) — but reuses the SAME Lua as the 19 named
|
||
slots (this file's header comment: the ref shares PaperDollItemSlotButton_*
|
||
verbatim here too, keyed off "AmmoSlot"). $parentCount's hidden="true" (ref
|
||
default) is DROPPED — every other count fontstring in this codebase (Bag/
|
||
Merchant/this file's own template) uses the same "blank text = invisible"
|
||
idiom with no Show/Hide dance, applied here too for consistency.
|
||
$parentStock OMITTED (this file's header comment: no ref Lua populates it). -->
|
||
<Button name="CharacterAmmoSlot">
|
||
<Size><AbsDimension x="27" y="27"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="CharacterRangedSlot" relativePoint="RIGHT"><Offset><AbsDimension x="15" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture file="Interface\PaperdollInfoFrame\UI-Character-AmmoSlot">
|
||
<Size><AbsDimension x="41" y="41"/></Size>
|
||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||
<TexCoords left="0" right="0.640625" top="0" bottom="0.640625"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="BORDER">
|
||
<Texture name="$parentIconTexture"/>
|
||
<FontString name="$parentCount" inherits="NumberFontNormal" justifyH="RIGHT">
|
||
<Anchors><Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-1" y="2"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<Texture file="Interface\PaperdollInfoFrame\UI-Character-AmmoSlot">
|
||
<Size><AbsDimension x="23" y="41"/></Size>
|
||
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="-22" y="0"/></Offset></Anchor></Anchors>
|
||
<TexCoords left="0.640625" right="1.0" top="0" bottom="0.640625"/>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnLoad>BenillaPaperDollSlot_OnLoad(self, "AmmoSlot")</OnLoad>
|
||
<OnEnter>BenillaPaperDollSlot_OnEnter(self)</OnEnter>
|
||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||
<OnEvent>BenillaPaperDollSlot_OnEvent(self)</OnEvent>
|
||
<OnUpdate>BenillaPaperDollSlot_OnUpdate(self)</OnUpdate>
|
||
<!-- Ammo loads by SET_AMMO, not the equip-swap wire (decision 0526):
|
||
click-to-place and drag-drop both route to the ammo handler. -->
|
||
<OnClick>BenillaCharacterAmmoSlot_OnClick(self)</OnClick>
|
||
<OnReceiveDrag>BenillaCharacterAmmoSlot_OnClick(self)</OnReceiveDrag>
|
||
</Scripts>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>PaperDollFrame_OnLoad()</OnLoad>
|
||
<OnEvent>PaperDollFrame_OnEvent(event, arg1)</OnEvent>
|
||
<OnShow>PaperDollFrame_OnShow()</OnShow>
|
||
</Scripts>
|
||
</Frame>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnLoad>CharacterFrame_OnLoad()</OnLoad>
|
||
<OnShow>CharacterFrame_OnShow()</OnShow>
|
||
<OnHide>CharacterFrame_OnHide()</OnHide>
|
||
</Scripts>
|
||
</Frame>
|
||
</Ui>
|