the pet gets its frame, flooded rooms fill, and macros really fire
- the pet gets its own unit frame beside yours, and an Attack order no longer latches as the standing command - five rooms that always read bone dry hold their water now, the Deeprun Tram's flooded stretches among them - macros fire through the client's own chat door, and the macro window fits its tabs and scrolls its icon chooser - the options window grows a Nameplates page and its first dropdown, and the name-display switches apply live in the world Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Generated
+14
-7
@@ -570,6 +570,20 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
[[package]]
|
||||
name = "benilla"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"benilla-app",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "benilla-adt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"benilla-bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "benilla-app"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
@@ -599,13 +613,6 @@ dependencies = [
|
||||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "benilla-adt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"benilla-bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "benilla-assets"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
[package]
|
||||
name = "benilla-app"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
description = "The Bevy app as a library: everything but the build-id stamp, which lives in the `benilla` launcher shim so a commit dirties ~30 lines instead of this crate (decision 0993)"
|
||||
|
||||
[features]
|
||||
# Opt-in Tracy profiling: `cargo run --features tracy` streams per-system `info_span!` markers (on the
|
||||
# hot systems) + render-pass CPU timings to a Tracy capture (decision 0026 — the dev/player seam;
|
||||
# a `docs/howto/running.md` this once cited never landed).
|
||||
tracy = ["bevy/trace_tracy"]
|
||||
# Opt-in Chrome-trace profiling: `cargo run --features trace_chrome` writes a `trace-<ts>.json` of
|
||||
# every system's per-run span next to the working dir. Tracy's offline twin — it needs no GUI and no
|
||||
# live capture, so a per-system cost question is answerable from a terminal (and from a run someone
|
||||
# else recorded). Aggregate it with `scripts/tracesum.py`.
|
||||
trace_chrome = ["bevy/trace_chrome"]
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
# OS clipboard access for the UI EditBox copy/cut/paste chords (chat input). Already in the tree via
|
||||
# bevy_egui; pinned to the same 3.x so there's one copy. Read on the main thread (the UI key feed is
|
||||
# a NonSend system) — required for the macOS NSPasteboard path. Covers macOS, Windows, and Linux/BSD
|
||||
# under X11; the Wayland backend is `smithay-clipboard` below (decision 0702).
|
||||
arboard = "3"
|
||||
benilla-ui = { path = "../benilla-ui" }
|
||||
bevy.workspace = true
|
||||
bevy_egui = "0.39"
|
||||
# Zero-copy POD→bytes for the shared light storage buffer (one per-frame write_buffer). Already in
|
||||
# the tree transitively; pinned to the same version as the dev-dep below.
|
||||
bytemuck = { version = "1", features = ["derive"] }
|
||||
# `f16` decode for the booth-target dump (`WOW_BOOTH_DUMP`): the booth render targets are
|
||||
# `Rgba16Float` (decision 0804) and the PNG wants display bytes. Already in the tree via
|
||||
# wgpu/bevy_image; declared so the call site is an honest dependency.
|
||||
half = "2"
|
||||
# `getrusage` — whole-process CPU time (all threads), the perf probes' `cpu_ms`/`cpu_pct` metric
|
||||
# (`perf::process_cpu_secs`). Wall-clock fps is unusable as a regression instrument on a machine
|
||||
# running parallel sessions' builds; CPU-per-frame is, and it is also the number the Mac report
|
||||
# is about ("250 % CPU at 59 fps"). Already in the tree transitively; declared so the call site is
|
||||
# an honest dependency.
|
||||
libc = "0.2"
|
||||
# Resample varying-size layer BLPs to a uniform size for the terrain texture array; `png` so we can
|
||||
# encode the cursor image into data an NSImage can load (macOS hardware cursor).
|
||||
image = { version = "0.25", default-features = false, features = ["png"] }
|
||||
# The net↔ECS bridge: a background IO thread streams typed `SessionEvent`s to the ECS over a
|
||||
# channel, and outbound `ClientCommand`s back. crossbeam's Sender/Receiver are `Send + Sync + Clone`
|
||||
# (std's mpsc Sender is `!Sync`, so it can't be a Bevy resource without a mutex — the very thing this
|
||||
# bridge deletes). The canonical Bevy background-thread→channel→system idiom. See decision 0006.
|
||||
crossbeam-channel = "0.5"
|
||||
benilla-assets = { path = "../benilla-assets" }
|
||||
benilla-formats = { path = "../benilla-formats" }
|
||||
benilla-protocol = { path = "../benilla-protocol" }
|
||||
# The CVar config diff (`benilla/config.toml`, decision 0954): serde derive on the persisted
|
||||
# shape, toml as its format. (serde_json left with the era manifest — 0978.)
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
toml = "0.8"
|
||||
# ECS-native physics: collider storage + broadphase BVH + shape-casts, with a thin custom kinematic
|
||||
# character controller on top (decision 0009). Pinned 0.6: it targets Bevy 0.18 (verified single,
|
||||
# unified bevy 0.18.1 in the tree); 0.7.0 jumps to Bevy 0.19 and would duplicate bevy. avian wraps
|
||||
# parry under the hood, so adopting it lets us drop the *direct* parry3d dep once the bespoke
|
||||
# CollisionWorld is gone. See docs… (decision 0007 superseded by 0009).
|
||||
avian3d = "0.6"
|
||||
# The glyph shaper + rasterizer for `FontString` text (decision 0068 §2, probe-verified in
|
||||
# `probes/text-glyph`). Pinned to 0.16 — the exact version already in the tree transitively
|
||||
# (`bevy_text`'s own dependency, see `Cargo.lock`) — so this is the *same* `cosmic-text` the decision
|
||||
# already commits to, not a second copy. Used as a standalone shaper/rasterizer (`FontSystem` +
|
||||
# `Buffer` + `SwashCache`) in `ui_text.rs`, never `bevy_text`'s `Text2d`/ECS integration (that path
|
||||
# doesn't feed our custom z-ordered `UiQuad` stream).
|
||||
cosmic-text = "0.16"
|
||||
# The font database `cosmic-text` 0.16 uses (its `Source`/`ID`/`FaceInfo` appear in `ui_text.rs`'s
|
||||
# public-in-crate types). Pinned to the exact version `cosmic-text` 0.16 depends on so there's one
|
||||
# `fontdb` in the graph.
|
||||
fontdb = "0.23"
|
||||
kira = { version = "0.12", default-features = false, features = ["cpal", "mp3", "wav", "pcm", "adpcm"] }
|
||||
mint = "0.5"
|
||||
# The gossip greeting is a weighted random DRAW per menu-open, not a fixed pick (wow-re
|
||||
# `gossip-npctext-law.md` — the reference re-rolls in its gossip handler), so the app needs a uniform
|
||||
# source. Pinned to 0.8: the version `benilla-srp` already depends on, so this adds no crate.
|
||||
rand = "0.8"
|
||||
|
||||
# Linux/BSD: the Wayland clipboard (decision 0702). We build with bevy's `wayland` feature, so on a
|
||||
# Wayland session winit gives us a native Wayland window and `arboard` — X11-only without its
|
||||
# non-default `wayland-data-control` feature — finds no X11 CLIPBOARD owner and every paste fails.
|
||||
# smithay-clipboard is the right crate for a windowed app: it drives the *standard* `wl_data_device`
|
||||
# off our own `wl_display`/seat, which every compositor supports. arboard's own Wayland feature would
|
||||
# not do: it speaks `wlr-data-control`, a privileged manager protocol GNOME/Mutter deliberately
|
||||
# doesn't implement. `raw-window-handle` (pinned to the 0.6 already in the tree via bevy_window, so
|
||||
# this adds no crate) is only needed to read the `wl_display` out of bevy's `RawHandleWrapper`.
|
||||
[target.'cfg(all(unix, not(any(target_os = "macos", target_os = "android"))))'.dependencies]
|
||||
raw-window-handle = "0.6"
|
||||
smithay-clipboard = "0.7"
|
||||
|
||||
# macOS: drive a native NSCursor for a crisp hardware cursor. winit drives the cursor via AppKit
|
||||
# cursor-rects, which our continuous Metal render loop doesn't honor on mouse-move (custom cursor /
|
||||
# hide revert to the arrow); we bypass it with NSCursor + NSWindow::disableCursorRects. See
|
||||
# docs/knowledge/camera-control.md.
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
objc2 = "0.5"
|
||||
objc2-app-kit = { version = "0.2", features = [
|
||||
"NSApplication",
|
||||
"NSResponder",
|
||||
# NSApplicationActivationPolicy (bgwin's Accessory demotion) is declared in this header.
|
||||
"NSRunningApplication",
|
||||
"NSWindow",
|
||||
"NSView",
|
||||
"NSImage",
|
||||
"NSCursor",
|
||||
"NSEvent",
|
||||
] }
|
||||
objc2-foundation = { version = "0.2", features = ["NSArray", "NSData", "NSGeometry"] }
|
||||
@@ -542,6 +542,23 @@
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<!-- EXECUTE_CHAT_LINE — how a MACRO line reaches the chat grammar (0996, wow-re
|
||||
`macro-execution-law.md` §4, VERIFIED). The engine's macro runner (`0x4f14e0`)
|
||||
names no Lua function and walks no command table: per body line it fires
|
||||
`FrameScript_SignalEvent(0x188, "%s", line)`, and `0x188` is EXECUTE_CHAT_LINE
|
||||
(the registry slot `0xbe17b8` is written once, at `0x51b4ff`, with `0x852470`).
|
||||
ChatFrame.lua:1343 is the handler — `editBox:SetText(arg1)` → `ChatEdit_SendText`
|
||||
→ `ChatEdit_OnEscapePressed` — and ChatFrame1's OnLoad
|
||||
(FloatingChatFrame.xml:700) is its ONLY registrant in the whole default UI, so
|
||||
every macro line runs through THIS frame's box whichever one has focus. benilla's
|
||||
SubmitChatInput stands in for ChatEdit_SendText: the grammar behind it is Rust
|
||||
(0881), which is the only part of that chain we replace. -->
|
||||
<OnLoad>
|
||||
this:RegisterEvent("EXECUTE_CHAT_LINE")
|
||||
</OnLoad>
|
||||
<OnEvent>
|
||||
if event == "EXECUTE_CHAT_LINE" then SubmitChatInput(arg1) end
|
||||
</OnEvent>
|
||||
<OnUpdate>
|
||||
BenillaFCF_OnUpdate(arg1)
|
||||
BenillaChatFrame_OnUpdate(this, arg1)
|
||||
+31
-8
@@ -35,10 +35,13 @@
|
||||
(`BenillaMacroFrameText_Resize`), over-estimating wrapped lines rather than clipping.
|
||||
- `MacroFrameTextButton` (the ref's invisible click-to-focus plate over the body) is kept
|
||||
— this engine's EditBox does not self-focus on a click through a ScrollFrame.
|
||||
- The ref's `PanelTemplates_TabResize(-15, ...)` width tuning and its manual
|
||||
`HighlightTexture:SetWidth(GetTextWidth() + 31)` are dropped: `TabButtonTemplate`
|
||||
(UiPanels.xml) already does its own text-fit on load, and re-doing it here would fight
|
||||
it (the same call the trainer/talent tabs already omit).
|
||||
- The ref's `PanelTemplates_TabResize(-15, ...)` width tuning is KEPT, but expressed as
|
||||
two fields on the tab (`benillaTabPadding`/`benillaTabMaxWidth`) rather than as a call:
|
||||
`TabButtonTemplate` (UiPanels.xml) owns the one text-fit call site, and an instance
|
||||
calling TabResize itself would just be overwritten on the next settle. Dropping the
|
||||
tuning (0983's original call) is what let the character tab run off the window's right
|
||||
edge — 384 units does not hold two +0-fit tabs. The ref's manual
|
||||
`HighlightTexture:SetWidth(GetTextWidth() + 31)` stays folded into that same settle.
|
||||
- `PlaySound("igCharacterInfoOpen"/"Close"/"gsTitleOptionOK")` are kept — this engine has
|
||||
PlaySound and those kit names resolve.
|
||||
-->
|
||||
@@ -704,6 +707,10 @@
|
||||
<Button name="BenillaMacroFrameTab1" inherits="TabButtonTemplate" text="GENERAL_MACROS" id="1">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="65" y="-39"/></Offset></Anchor></Anchors>
|
||||
<Scripts>
|
||||
<!-- The ref's own `PanelTemplates_TabResize(-15)` (l.488) as a fit parameter —
|
||||
the template's settle loop owns the one call site (UiPanels.xml). -15 is
|
||||
what keeps a two-tab row inside a 384-wide window. -->
|
||||
<OnLoad>self.benillaTabPadding = -15</OnLoad>
|
||||
<OnClick>
|
||||
PanelTemplates_SetTab(getglobal("BenillaMacroFrame"), self:GetID())
|
||||
BenillaMacroFrame_SaveMacro()
|
||||
@@ -715,7 +722,14 @@
|
||||
<Button name="BenillaMacroFrameTab2" inherits="TabButtonTemplate" id="2">
|
||||
<Anchors><Anchor point="LEFT" relativeTo="BenillaMacroFrameTab1" relativePoint="RIGHT"/></Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>self:SetText(BenillaMacroFrame_TabTwoLabel())</OnLoad>
|
||||
<!-- ref l.511: `PanelTemplates_TabResize(-15, nil, nil, 150)`. The cap is
|
||||
load-bearing here and nowhere else — this label carries a player name, so
|
||||
it is the one tab whose width is not known when the window is written. -->
|
||||
<OnLoad>
|
||||
self.benillaTabPadding = -15
|
||||
self.benillaTabMaxWidth = 150
|
||||
self:SetText(BenillaMacroFrame_TabTwoLabel())
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
PanelTemplates_SetTab(getglobal("BenillaMacroFrame"), self:GetID())
|
||||
BenillaMacroFrame_SaveMacro()
|
||||
@@ -865,10 +879,19 @@
|
||||
</Scripts>
|
||||
</EditBox>
|
||||
|
||||
<!-- The icon grid's faux scroll (ref a ClassTrainerListScrollFrameTemplate). -->
|
||||
<!-- The icon grid's faux scroll (ref a ClassTrainerListScrollFrameTemplate, l.759-775).
|
||||
The frame itself is invisible; its ONE visible consequence is where the bar seats
|
||||
(BenillaFauxScrollFrameTemplate hangs it off the frame's right edge +6, inset 16
|
||||
top and bottom). So its rect is the reference's VERBATIM — 296x195 at TOPRIGHT
|
||||
(-39,-67) — which puts the bar at popup x 264..280, on the plate's groove and
|
||||
bracketing the 4-row grid. 0983 sized it to the grid instead (252x152 at TOPLEFT
|
||||
24,-85) and the bar came out at x 282..298: hanging half off the plate's right
|
||||
edge, over the world. -->
|
||||
<Frame name="BenillaMacroPopupScrollFrame" inherits="BenillaFauxScrollFrameTemplate">
|
||||
<Size><AbsDimension x="252" y="152"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="24" y="-85"/></Offset></Anchor></Anchors>
|
||||
<Size><AbsDimension x="296" y="195"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-39" y="-67"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<!-- The faux kit's contract (ScrollTemplates.xml): the owner sets updateFunc,
|
||||
the shared Slider handler calls it whenever the row offset moves. -->
|
||||
+190
-8
@@ -1,7 +1,8 @@
|
||||
<Ui>
|
||||
<!--
|
||||
The OPTIONS window (decision 0950) — the shell (slice A) and the real PAGES: Audio
|
||||
(0957), Graphics (0959), Controls (0961). Opened from the game menu's Options button.
|
||||
(0957), Graphics (0959), Controls (0961), Nameplates (0992, with the dropdown row
|
||||
shape). Opened from the game menu's Options button.
|
||||
Setting rows ride the CVar store (0954): rows read GetCVar on show, write SetCVar on
|
||||
interaction, and Defaults resets the visible page's CVars to their registered defaults.
|
||||
Pages without rows are still empty and keep Defaults disabled. One row defers — uiScale
|
||||
@@ -134,8 +135,8 @@
|
||||
Apply commits outright.
|
||||
- Era rows this slice does not ship: the Accessibility group, and Languages/Network under
|
||||
System — pages with no benilla knobs behind them yet (0950's "only rows backed by real
|
||||
knobs" rule). Nameplates joined in slice B (real nameplate knobs exist to back its
|
||||
page).
|
||||
knobs" rule). Nameplates joined the tree in slice B on the promise of its knobs; the
|
||||
page went LIVE in 0992 (the three UnitName* checkbox rows).
|
||||
- **The window rides a scale knob** (slice B): fonts ride frame scale engine-wide now
|
||||
(the 0219 §2 divergence closed), so ERA_WINDOW_SCALE × the era checkFit shrink
|
||||
(UIPanelUpdateScaleForFit, transcribed in this file) sizes the whole window; 0.78 is
|
||||
@@ -465,7 +466,8 @@
|
||||
"RowEnableAll", "RowMaster", "RowSound", "RowMusic", "RowAmbience",
|
||||
"RowEnableMusic", "RowEnableAmbience",
|
||||
},
|
||||
Graphics = { "RowUiScale" },
|
||||
Graphics = { "RowUiScale", "RowWorldDetail" },
|
||||
Nameplates = { "RowPlayerNames", "RowNpcNames", "RowOwnName" },
|
||||
}
|
||||
|
||||
function OptionsRow_OnLoad(row, cvar, label, child)
|
||||
@@ -500,6 +502,79 @@
|
||||
row.refreshing = nil
|
||||
end
|
||||
|
||||
-- ── The dropdown rows (0992): the 1.12 kit under the era row grammar ────────────────
|
||||
-- A dropdown row's choices: entries = { {text=..., value=...}, ... }, values the CVar's
|
||||
-- own strings. The kit does the rest: every open re-runs the init through the stored
|
||||
-- initialize (Toggle's own law), and AddButton auto-checks the entry matching the
|
||||
-- capsule's selectedValue — which OptionsDropdown_ShowValue keeps equal to the CVar.
|
||||
function OptionsDropdownRow_Setup(row, entries)
|
||||
row.entries = entries
|
||||
local dd = getglobal(row:GetName() .. "Dropdown")
|
||||
UIDropDownMenu_SetWidth(180, dd)
|
||||
UIDropDownMenu_Initialize(dd, OptionsDropdownRow_InitMenu)
|
||||
-- The capsule's toggle button is a mouse-enabled child — it steals hover from the
|
||||
-- row, so it carries the row wash like the checkbox rows' box does. Hooked here
|
||||
-- (SetScript) because the capsule template is the shared kit's, not this file's.
|
||||
local btn = getglobal(dd:GetName() .. "Button")
|
||||
btn:SetScript("OnEnter", function() OptionsRow_Hover(this:GetParent():GetParent(), 1) end)
|
||||
btn:SetScript("OnLeave", function() OptionsRow_Hover(this:GetParent():GetParent(), nil) end)
|
||||
end
|
||||
|
||||
function OptionsDropdownRow_InitMenu()
|
||||
local dd = getglobal(UIDROPDOWNMENU_INIT_MENU)
|
||||
local row = dd:GetParent()
|
||||
for _, e in ipairs(row.entries) do
|
||||
local info = {}
|
||||
info.text = e.text
|
||||
info.value = e.value
|
||||
info.func = OptionsDropdownEntry_OnClick
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end
|
||||
end
|
||||
|
||||
-- The entry click (`this` = the list row, the kit's own calling law): the write, then
|
||||
-- the capsule redisplay. The kit closes the list and plays its own click sound.
|
||||
function OptionsDropdownEntry_OnClick()
|
||||
local dd = UIDropDownMenu_GetCurrentDropDown()
|
||||
local row = dd:GetParent()
|
||||
SetCVar(row.cvar, this.value)
|
||||
OptionsDropdown_ShowValue(row)
|
||||
end
|
||||
|
||||
-- Paint the capsule from the CVar. selectedValue is written as a FIELD, not through
|
||||
-- SetSelectedValue: that helper's Refresh walks the SHARED DropDownList1 buttons, which
|
||||
-- may still hold another dropdown's rows on this refresh path (the list is closed) —
|
||||
-- a foreign row matching by value would repaint our capsule with its text. An off-grid
|
||||
-- CVar value (an env A/B, a hand edit) displays the NEAREST stop — 0959's out-of-range
|
||||
-- law, dropdown-flavored: control and check agree on the nearest expressible value,
|
||||
-- the engine keeps running the config's truth, nothing writes back until the player
|
||||
-- actually picks an entry. (A non-numeric stray shows raw and checks nothing.)
|
||||
function OptionsDropdown_ShowValue(row)
|
||||
local dd = getglobal(row:GetName() .. "Dropdown")
|
||||
local v = GetCVar(row.cvar)
|
||||
local match
|
||||
for _, e in ipairs(row.entries) do
|
||||
if e.value == v then match = e end
|
||||
end
|
||||
if not match and tonumber(v) then
|
||||
local best
|
||||
for _, e in ipairs(row.entries) do
|
||||
local d = tonumber(e.value) and math.abs(tonumber(e.value) - tonumber(v))
|
||||
if d and (not best or d < best) then
|
||||
best = d
|
||||
match = e
|
||||
end
|
||||
end
|
||||
end
|
||||
if match then
|
||||
dd.selectedValue = match.value
|
||||
UIDropDownMenu_SetText(match.text, dd)
|
||||
else
|
||||
dd.selectedValue = v
|
||||
UIDropDownMenu_SetText(v, dd)
|
||||
end
|
||||
end
|
||||
|
||||
-- The right-hand readout text (era MinimalSlider's RIGHT label seat): rounded percent
|
||||
-- (era FormatPercentage, Audio.lua l.406-411) unless the row asked for the raw number.
|
||||
function OptionsRow_FormatValue(row, value)
|
||||
@@ -606,6 +681,8 @@
|
||||
else
|
||||
box:SetChecked(GetCVar(row.cvar))
|
||||
end
|
||||
elseif row.entries then
|
||||
OptionsDropdown_ShowValue(row)
|
||||
else
|
||||
local slider = getglobal(base .. rkey .. "ControlSlider")
|
||||
row.refreshing = 1
|
||||
@@ -968,6 +1045,50 @@
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
<!-- One SETTING row, dropdown flavor (0992 — the first): the same era 26-tall row and
|
||||
label box; the control is the 1.12 dropdown CAPSULE verbatim (UIDropDownMenuTemplate —
|
||||
the kit every 1.12 dropdown wears, UIDropDownMenu.xml), seated so its box ink opens at
|
||||
the era control column (the capsule's Left cap carries ~10 units of air before its
|
||||
ink, hence the -71 anchor against the sliders' CENTER-80 + 19 groove seat; width 180
|
||||
makes the ink span read as the slider track's). SHAPE provenance: the era's own
|
||||
control for an enumerated setting is a dropdown (SettingsDropdownControlTemplate — its
|
||||
Graphics page is a zoo of them), while 1.12 shaped this row's setting as a 3-stop
|
||||
slider; the era grammar wins the shape by the director's call ("some with dropdown"),
|
||||
recorded in 0992. The list the capsule opens rides the kit's own law — shared
|
||||
DropDownList1, FULLSCREEN_DIALOG strata (above this HIGH window), the owner's
|
||||
effective scale (the kit's 0992 correction), the 2-second hover-away close. -->
|
||||
<Frame name="OptionsDropdownRowTemplate" virtual="true" enableMouse="true">
|
||||
<Size><AbsDimension y="26"/></Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture name="$parentHover" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-10" y="0"/></Offset></Anchor>
|
||||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Color r="1" g="1" b="1" a="0.1"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="$parentLabel" inherits="GameFontNormal" justifyH="LEFT">
|
||||
<Anchors>
|
||||
<Anchor point="LEFT"><Offset><AbsDimension x="37" y="0"/></Offset></Anchor>
|
||||
<Anchor point="RIGHT" relativePoint="CENTER"><Offset><AbsDimension x="-85" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Frame name="$parentDropdown" inherits="UIDropDownMenuTemplate">
|
||||
<Anchors><Anchor point="LEFT" relativePoint="CENTER"><Offset><AbsDimension x="-71" y="-2"/></Offset></Anchor></Anchors>
|
||||
</Frame>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnEnter>OptionsRow_Hover(this, 1)</OnEnter>
|
||||
<OnLeave>OptionsRow_Hover(this, nil)</OnLeave>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
<Frame name="OptionsFrame" frameStrata="HIGH" enableMouse="true" hidden="true">
|
||||
<Size><AbsDimension x="920" y="724"/></Size>
|
||||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||||
@@ -1383,14 +1504,17 @@
|
||||
</Frames>
|
||||
</Frame>
|
||||
<!-- The Graphics page (0959; farclip row retired 0961 — the
|
||||
director's call: the page holds UI Scale alone for now, the
|
||||
farclip CVar/knob/env/debug-panel plumbing all stay). Range/
|
||||
step/label are the 1.12 OptionsFrameSliders table verbatim
|
||||
director's call, standing: the farclip CVar/knob/env/
|
||||
debug-panel plumbing all stay, only the row is off the page).
|
||||
UI Scale is the 1.12 OptionsFrameSliders table verbatim
|
||||
(OptionsFrame.lua l.25): uiscale 0.64..1.0 step 0.01 (UI_SCALE
|
||||
"UI Scale", percent readout — era's own
|
||||
FormatPercentageRounded on its uiscale row). 1.12's useUiScale
|
||||
gate has no benilla knob (our scale always applies; off ≡ 1.0)
|
||||
so the checkbox doesn't ship. -->
|
||||
so the checkbox doesn't ship. Environment Detail (0992) is the
|
||||
same table's WorldDetail row (l.26: 0..2 step 1, label
|
||||
ENVIRONMENT_DETAIL) over the clutter-density knob, worn as the
|
||||
dropdown row — the shape call is the template's comment. -->
|
||||
<Frame name="$parentGraphics" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"/>
|
||||
@@ -1412,6 +1536,58 @@
|
||||
self.deferred = 1
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowWorldDetail" inherits="OptionsDropdownRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentRowUiScale" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT" relativeTo="$parentRowUiScale" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>
|
||||
OptionsRow_OnLoad(self, "WorldDetail", "Environment Detail")
|
||||
-- The stop names are the era preset ladder's (its
|
||||
-- quality dropdowns say Low/Medium/High); 1.12's
|
||||
-- slider had no stop labels to quote.
|
||||
OptionsDropdownRow_Setup(self, {
|
||||
{ text = "Low", value = "0" },
|
||||
{ text = "Medium", value = "1" },
|
||||
{ text = "High", value = "2" },
|
||||
})
|
||||
</OnLoad></Scripts>
|
||||
</Frame>
|
||||
</Frames>
|
||||
</Frame>
|
||||
<!-- The Nameplates page (0992 — the page 0952 promised when the
|
||||
category row joined): the three 1.12 UnitName* checkboxes over
|
||||
the overhead-name gates (nameplates.rs NameConfig), rows in
|
||||
the 1.12 Interface Options' own index order (21/30/67), labels
|
||||
its GlobalStrings verbatim (SHOW_PLAYER_NAMES/SHOW_NPC_NAMES/
|
||||
SHOW_OWN_NAME). -->
|
||||
<Frame name="$parentNameplates" hidden="true">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"/>
|
||||
<Anchor point="BOTTOMRIGHT"/>
|
||||
</Anchors>
|
||||
<Frames>
|
||||
<Frame name="$parentRowPlayerNames" inherits="OptionsCheckboxRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-12"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-20" y="-12"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "UnitNamePlayer", "Player Names")</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowNpcNames" inherits="OptionsCheckboxRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentRowPlayerNames" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT" relativeTo="$parentRowPlayerNames" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "UnitNameNPC", "NPC Names")</OnLoad></Scripts>
|
||||
</Frame>
|
||||
<Frame name="$parentRowOwnName" inherits="OptionsCheckboxRowTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentRowNpcNames" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
<Anchor point="TOPRIGHT" relativeTo="$parentRowNpcNames" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "UnitNameOwn", "Show Own Name")</OnLoad></Scripts>
|
||||
</Frame>
|
||||
</Frames>
|
||||
</Frame>
|
||||
<!-- The search-results group headers — one per page WITH rows
|
||||
@@ -1437,6 +1613,12 @@
|
||||
getglobal(self:GetName() .. "Label"):SetText("Graphics")
|
||||
</OnLoad></Scripts>
|
||||
</Button>
|
||||
<Button name="$parentSearchHeadNameplates" inherits="OptionsSearchHeadTemplate">
|
||||
<Scripts><OnLoad>
|
||||
self.categoryKey = "Nameplates"
|
||||
getglobal(self:GetName() .. "Label"):SetText("Nameplates")
|
||||
</OnLoad></Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
</Frame>
|
||||
</Frames>
|
||||
+5
-5
@@ -85,7 +85,7 @@
|
||||
merge and calls `SetScript` for each handler in order; `merge()` (`framexml.rs`) appends the
|
||||
base template's children before the instance's own, so the base's `<Scripts>` registers first
|
||||
and this file's own registers LAST — `SetScript` is a single-slot assignment, so the last
|
||||
registration wins outright. `BenillaFauxScrollBar_UpdateArrows`/`_Step` (ScrollTemplates.xml)
|
||||
registration wins outright. `BenillaScrollBar_UpdateArrows`/`_Step` (ScrollTemplates.xml)
|
||||
are reused as-is: both read only a Slider's own value/range/step, nothing faux-specific.
|
||||
|
||||
Named deferrals/OMITs (each re-cited at its own site below):
|
||||
@@ -811,11 +811,11 @@
|
||||
-- The real-ScrollFrame half of the documented Slider<->ScrollFrame wiring (`slider.rs`'s
|
||||
-- own header comment: "OnValueChanged -> this:GetParent():SetVerticalScroll(arg1)") —
|
||||
-- REPLACES BenillaScrollBarTemplate's inherited faux-offset handler (this file's header
|
||||
-- comment, verified via loader/scripts.rs). BenillaFauxScrollBar_UpdateArrows is faux-
|
||||
-- comment, verified via loader/scripts.rs). BenillaScrollBar_UpdateArrows is faux-
|
||||
-- agnostic (ScrollTemplates.xml's own body) and reused as-is for the arrow enable state.
|
||||
function BenillaTalentFrameScrollBar_OnValueChanged(scrollBar, value)
|
||||
scrollBar:GetParent():SetVerticalScroll(value)
|
||||
BenillaFauxScrollBar_UpdateArrows(scrollBar)
|
||||
BenillaScrollBar_UpdateArrows(scrollBar)
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
@@ -1178,8 +1178,8 @@
|
||||
<OnLoad>BenillaTalentFrameScroll_OnLoad(self)</OnLoad>
|
||||
<!-- WoW wheel convention: +1 per notch up (toward the top, decreasing scroll),
|
||||
-1 down (ChatFrame.xml's own handler quotes the same convention). Reuses
|
||||
BenillaFauxScrollBar_Step (this file's header comment: generic, Slider-only). -->
|
||||
<OnMouseWheel>BenillaFauxScrollBar_Step(getglobal("BenillaTalentFrameScrollBar"), arg1 > 0 and -1 or 1)</OnMouseWheel>
|
||||
BenillaScrollBar_Step (this file's header comment: generic, Slider-only). -->
|
||||
<OnMouseWheel>BenillaScrollBar_Step(getglobal("BenillaTalentFrameScrollBar"), arg1 > 0 and -1 or 1)</OnMouseWheel>
|
||||
<!-- The real ScrollFrame's own half of the documented pair (this file's header
|
||||
comment / BenillaTalentFrameScrollBar_OnValueChanged's own comment). -->
|
||||
<OnVerticalScroll>getglobal("BenillaTalentFrameScrollBar"):SetValue(arg1)</OnVerticalScroll>
|
||||
+15
-5
@@ -18,9 +18,10 @@
|
||||
OUT (each waits for its first customer): menu level 3 (UIDROPDOWNMENU_MAXLEVELS is 2 — the
|
||||
unit popups' deepest law; the XML gains DropDownList3 the day a customer needs it), color
|
||||
swatches + the color picker + opacity, info.textHeight's per-row SetFont, the newbie
|
||||
tooltips on rows (the ref gates them behind a default-off cvar), the uiScale correction
|
||||
(the engine owns global UI scale), message() on the row/level overflow guards (no dev-error
|
||||
dialog surface; the guard returns silently).
|
||||
tooltips on rows (the ref gates them behind a default-off cvar), message() on the row/level
|
||||
overflow guards (no dev-error dialog surface; the guard returns silently). The uiScale
|
||||
correction came IN with its first scaled customer (0992, the options window's dropdown
|
||||
rows): Toggle wears the owner's effective scale on the list.
|
||||
|
||||
Deviations, deliberate:
|
||||
- THE WIDTH SETTLE. The ref sizes each row at AddButton time from normalText:GetWidth() and
|
||||
@@ -535,6 +536,13 @@
|
||||
end
|
||||
listFrame:Hide();
|
||||
|
||||
-- The ref's uiScale correction, arriving with its first scaled customer (0992: the
|
||||
-- options window rides SetScale, and a list at scale 1 would draw its rows ~28%
|
||||
-- larger than the capsule they serve): the shared list wears the OWNER's effective
|
||||
-- scale for this open. Every pre-0992 customer sits at effective scale 1 — no-op.
|
||||
local listScale = tempFrame:GetEffectiveScale();
|
||||
listFrame:SetScale(listScale);
|
||||
|
||||
if ( level == 1 ) then
|
||||
if ( not dropDownFrame ) then
|
||||
dropDownFrame = this:GetParent();
|
||||
@@ -551,12 +559,14 @@
|
||||
relativeTo = dropDownFrame.relativeTo or dropDownFrame:GetName();
|
||||
relativePoint = dropDownFrame.relativePoint or "BOTTOMLEFT";
|
||||
elseif ( anchorName == "cursor" ) then
|
||||
-- Cursor coords are scale-1 units; a scaled list's anchor offsets are in
|
||||
-- ITS space, so divide (the ref's own cursor/uiScale dance).
|
||||
local cursorX, cursorY = GetCursorPosition();
|
||||
point = "TOPLEFT";
|
||||
relativeTo = "UIParent";
|
||||
relativePoint = "BOTTOMLEFT";
|
||||
xOffset = cursorX + (xOffset or 0);
|
||||
yOffset = cursorY + (yOffset or 0);
|
||||
xOffset = cursorX / listScale + (xOffset or 0);
|
||||
yOffset = cursorY / listScale + (yOffset or 0);
|
||||
listFrame.cursorMode = 1;
|
||||
else
|
||||
point = "TOPLEFT";
|
||||
@@ -1272,35 +1272,106 @@
|
||||
getglobal(name .. "RightDisabled"):Show()
|
||||
end
|
||||
|
||||
-- The text-fit (ref PanelTemplates_TabResize, UIPanelTemplates.lua l.31-88, the
|
||||
-- padding-only branch the tab template's OnShow uses — TabResize(0) → tab width =
|
||||
-- text + the two 20px end slices; both middle slices stretch to the text; the text box
|
||||
-- itself un-sizes to natural). One deviation: the ref reads the label's laid-out
|
||||
-- GetWidth (its C measure is synchronous); ours reads GetStringWidth — the same number
|
||||
-- once the measure round-trip lands, which is why the caller below re-checks from
|
||||
-- OnUpdate instead of OnShow.
|
||||
function PanelTemplates_TabResize(padding, tab)
|
||||
-- The text-fit (ref PanelTemplates_TabResize, UIPanelTemplates.lua l.31-88 — now the WHOLE
|
||||
-- function, `(padding, tab, absoluteSize, maxWidth)`): the tab is text + padding wide, plus
|
||||
-- the two end slices; both middle slices stretch to the text; the text box itself un-sizes
|
||||
-- to natural. `maxWidth` CAPS it and pins the label's box to the cap instead — the arm that
|
||||
-- keeps a long tab inside its window, which the first transcription dropped and the macro
|
||||
-- window's character tab then overran (0983 → 0991). `absoluteSize` is the ref's third arm,
|
||||
-- transcribed with it so the next caller has it.
|
||||
--
|
||||
-- One deviation: the ref reads the label's laid-out GetWidth (its C measure is synchronous);
|
||||
-- ours reads GetStringWidth — the same number once the measure round-trip lands, which is
|
||||
-- why the caller below re-checks from OnUpdate instead of OnShow.
|
||||
function PanelTemplates_TabResize(padding, tab, absoluteSize, maxWidth)
|
||||
local name = tab:GetName()
|
||||
local text = getglobal(name .. "Text")
|
||||
local width = text:GetStringWidth() + (padding or 24)
|
||||
text:SetWidth(0)
|
||||
local sideWidths = 2 * getglobal(name .. "Left"):GetWidth()
|
||||
local width, tabWidth
|
||||
if absoluteSize then
|
||||
if absoluteSize < sideWidths then
|
||||
width, tabWidth = 1, sideWidths
|
||||
else
|
||||
width, tabWidth = absoluteSize - sideWidths, absoluteSize
|
||||
end
|
||||
text:SetWidth(width)
|
||||
else
|
||||
width = text:GetStringWidth() + (padding or 24)
|
||||
if maxWidth and width > maxWidth then
|
||||
width = maxWidth + (padding or 24)
|
||||
text:SetWidth(width)
|
||||
else
|
||||
text:SetWidth(0)
|
||||
end
|
||||
tabWidth = width + sideWidths
|
||||
end
|
||||
getglobal(name .. "Middle"):SetWidth(width)
|
||||
getglobal(name .. "MiddleDisabled"):SetWidth(width)
|
||||
local sideWidths = 2 * getglobal(name .. "Left"):GetWidth()
|
||||
tab:SetWidth(width + sideWidths)
|
||||
tab:SetWidth(tabWidth)
|
||||
end
|
||||
|
||||
-- The tab template's per-frame settle: the ref does TabResize(0) + the highlight width
|
||||
-- in OnShow (CharacterFrameTemplates.xml l.77-80), trusting a synchronous text measure.
|
||||
-- Our GetStringWidth has a frame of latency, so the template's OnUpdate calls this until
|
||||
-- the measured width settles (then it's a no-op compare per frame).
|
||||
--
|
||||
-- The fit PARAMETERS live on the tab (`benillaTabPadding`/`benillaTabMaxWidth`, defaulting
|
||||
-- to the +0 fit every window tab has always used) rather than at the call site, because
|
||||
-- there is exactly one call site and it is this settle loop: an instance that ran its own
|
||||
-- TabResize in OnLoad — as the reference's macro tabs do — would just be overwritten on the
|
||||
-- next frame. Setting the two fields IS how an instance asks for the reference's own fit.
|
||||
-- THE STRUCTURAL CAP — a tab may never grow past its parent's right edge, whatever the
|
||||
-- instance asked for (decision 0997, the director's "fix this properly to prevent it ever
|
||||
-- again"). The reference's own caps are hand-tuned numbers against ITS font metrics: the
|
||||
-- macro window's 150 is enough for the enUS FRIZQT__ the 1.12 client rasterizes and not
|
||||
-- obviously enough for anything else, and a character name — the one label whose width no
|
||||
-- window author can know — is exactly where that runs out. So the per-instance cap becomes
|
||||
-- a PREFERENCE and the window's own edge becomes the law.
|
||||
--
|
||||
-- The arithmetic is TabResize's, inverted: its capped branch yields
|
||||
-- `tabWidth = cap + padding + sideWidths`, so `cap = room - padding - sideWidths`.
|
||||
function BenillaTab_Cap(tab, padding)
|
||||
local cap = tab.benillaTabMaxWidth
|
||||
local parent = tab:GetParent()
|
||||
if not parent then return cap end
|
||||
local left, right = tab:GetLeft(), parent:GetRight()
|
||||
if not left or not right then return cap end
|
||||
local sideWidths = 2 * getglobal(tab:GetName() .. "Left"):GetWidth()
|
||||
local room = (right - left) - padding - sideWidths - (tab.benillaTabRightInset or 0)
|
||||
if room < 1 then room = 1 end
|
||||
if not cap or room < cap then cap = room end
|
||||
return cap
|
||||
end
|
||||
|
||||
-- The tab template's per-frame settle: the ref does TabResize(0) + the highlight width
|
||||
-- in OnShow (CharacterFrameTemplates.xml l.77-80), trusting a synchronous text measure.
|
||||
-- Our GetStringWidth has a frame of latency, so the template's OnUpdate calls this until
|
||||
-- the measured width settles (then it's a no-op compare per frame).
|
||||
--
|
||||
-- The fit PARAMETERS live on the tab (`benillaTabPadding`/`benillaTabMaxWidth`, defaulting
|
||||
-- to the +0 fit every window tab has always used) rather than at the call site, because
|
||||
-- there is exactly one call site and it is this settle loop: an instance that ran its own
|
||||
-- TabResize in OnLoad — as the reference's macro tabs do — would just be overwritten on the
|
||||
-- next frame. Setting the two fields IS how an instance asks for the reference's own fit.
|
||||
--
|
||||
-- The settle compares BOTH inputs. Watching only the string width used to be enough; it
|
||||
-- isn't once the cap can move, because a tab's room depends on where the tab to its left
|
||||
-- ended up — so tab 2 must be able to re-fit on a frame where its own label did not change.
|
||||
function BenillaTabButton_OnUpdate(tab)
|
||||
local text = getglobal(tab:GetName() .. "Text")
|
||||
local w = (text and text:GetStringWidth()) or 0
|
||||
if w <= 0 or tab.benillaFitWidth == w then return end
|
||||
tab.benillaFitWidth = w
|
||||
PanelTemplates_TabResize(0, tab)
|
||||
getglobal(tab:GetName() .. "HighlightTexture"):SetWidth(w + 30)
|
||||
if w <= 0 then return end
|
||||
local pad = tab.benillaTabPadding or 0
|
||||
local cap = BenillaTab_Cap(tab, pad)
|
||||
if tab.benillaFitWidth == w and tab.benillaFitCap == cap then return end
|
||||
tab.benillaFitWidth, tab.benillaFitCap = w, cap
|
||||
PanelTemplates_TabResize(pad, tab, nil, cap)
|
||||
-- The ref sizes the highlight off the label too (MacroFrame's tabs spell it
|
||||
-- `GetTextWidth() + 31`); +30 is this kit's long-standing number for the +0 fit. Capped
|
||||
-- at the tab so a clamped tab never wears a highlight wider than itself.
|
||||
local hl = w + 30
|
||||
if hl > tab:GetWidth() + 14 then hl = tab:GetWidth() + 14 end
|
||||
getglobal(tab:GetName() .. "HighlightTexture"):SetWidth(hl)
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
+283
@@ -1152,4 +1152,287 @@
|
||||
<OnLoad>TargetFrameDropDown_OnLoad()</OnLoad>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
|
||||
<!--
|
||||
THE PET FRAME (decision 0990; ref PetFrame.xml + PetFrame.lua, quoted). 128×53 parented to
|
||||
the player frame at TOPLEFT (80,-60) — a static anchor, deliberately: the reference does not
|
||||
list PetFrame in UIPARENT_MANAGED_FRAME_POSITIONS at all, because it rides its parent.
|
||||
|
||||
The art is the SMALL targeting frame, and it has two states the ref swaps at runtime
|
||||
(PetFrame.lua's Update): `UI-SmallTargetingFrame` when the pet has a power bar, `-NoMana`
|
||||
when it has none. That is not decoration — a warlock's imp has mana and a hunter's boar has
|
||||
focus, but a skeleton has neither, and the plain art draws a mana rail with nothing in it.
|
||||
|
||||
DEFERRED, each with its reason (all named in 0990):
|
||||
- HAPPINESS. The ref's PetFrameHappiness icon needs `GetPetHappiness 0x4be900`, whose body
|
||||
is NOT carved in wow-re (its address is known from the binding table, nothing more). Its
|
||||
three thresholds, the damage percentage and the loyalty rate are exactly the kind of
|
||||
derived numbers 0988 was written about: vmangos has a HAPPINESS_LEVEL_SIZE and taking it
|
||||
would repeat that record's own mistake. So the icon is absent rather than guessed, and
|
||||
comes back with the carve — together with `GetPetLoyalty 0x4be700`,
|
||||
`GetPetTrainingPoints 0x4be790` and `GetPetExperience 0x4be840`, its uncarved siblings.
|
||||
- The PET right-click menu. Its four entries (UnitPopup.xml's PET_PAPERDOLL / PET_RENAME /
|
||||
PET_ABANDON / PET_DISMISS) each need a binding that is its own build — the pet paper doll
|
||||
most of all. They stay suppressed there; the frame takes no right click rather than
|
||||
opening a menu of four dead rows.
|
||||
- PartyMemberBuffTooltip on hover (the ref's OnEnter panel): it belongs to the party
|
||||
frames' own deferred buff panel, not here.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
function BenillaPetFrame_OnLoad(frame)
|
||||
frame.portrait = BenillaPetFrameTextureFramePortrait
|
||||
frame.healthBar = BenillaPetFrameHealthBar
|
||||
frame.manaBar = BenillaPetFrameManaBar
|
||||
frame.nameText = BenillaPetFrameTextureFrameName
|
||||
-- The recessed-bar look, the same mechanism the player/target/party frames use
|
||||
-- (decision 0884): push the bars one frame level BELOW the art's TextureFrame, because
|
||||
-- the draw layer is bucket-wide and the frame level is the one key term above it.
|
||||
local level = BenillaPetFrameTextureFrame:GetFrameLevel()
|
||||
frame.healthBar:SetFrameLevel(level - 1)
|
||||
frame.manaBar:SetFrameLevel(level - 1)
|
||||
frame.attackModeCounter = 0
|
||||
frame.attackModeSign = -1
|
||||
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
frame:RegisterEvent("UNIT_PET")
|
||||
frame:RegisterEvent("UNIT_AURA")
|
||||
frame:RegisterEvent("UNIT_HEALTH")
|
||||
frame:RegisterEvent("UNIT_MAXHEALTH")
|
||||
frame:RegisterEvent("UNIT_POWER_UPDATE")
|
||||
frame:RegisterEvent("UNIT_MAXPOWER")
|
||||
frame:RegisterEvent("UNIT_DISPLAYPOWER")
|
||||
frame:RegisterEvent("UNIT_NAME_UPDATE")
|
||||
frame:RegisterEvent("PET_ATTACK_START")
|
||||
frame:RegisterEvent("PET_ATTACK_STOP")
|
||||
frame:EnableMouse(true)
|
||||
BenillaPetFrame_Update(frame)
|
||||
end
|
||||
|
||||
-- ref PetFrame_OnEvent, with both of its argument gates kept: UNIT_PET names the OWNER
|
||||
-- ("player" — wow-re §9 carves the arg), every other UNIT_* names the pet itself. Getting
|
||||
-- those backwards is how a pet frame ends up repainting off the player's health.
|
||||
function BenillaPetFrame_OnEvent(frame)
|
||||
if event == "PET_ATTACK_START" then
|
||||
BenillaPetFrameAttackMode:SetVertexColor(1.0, 1.0, 1.0, 1.0)
|
||||
BenillaPetFrameAttackMode:Show()
|
||||
return
|
||||
elseif event == "PET_ATTACK_STOP" then
|
||||
BenillaPetFrameAttackMode:Hide()
|
||||
return
|
||||
elseif event == "UNIT_PET" then
|
||||
if arg1 ~= "player" then return end
|
||||
elseif string.sub(event, 1, 5) == "UNIT_" and arg1 ~= "pet" then
|
||||
return
|
||||
end
|
||||
BenillaPetFrame_Update(frame)
|
||||
end
|
||||
|
||||
function BenillaPetFrame_Update(frame)
|
||||
if not UnitExists("pet") then
|
||||
frame:Hide()
|
||||
return
|
||||
end
|
||||
frame:Show()
|
||||
SetPortraitTexture(frame.portrait, "pet")
|
||||
frame.nameText:SetText(UnitName("pet") or "")
|
||||
|
||||
frame.healthBar:SetMinMaxValues(0, UnitHealthMax("pet"))
|
||||
frame.healthBar:SetValue(UnitHealth("pet"))
|
||||
|
||||
-- The ref's art swap (PetFrame.lua's Update): a powerless pet wears the -NoMana plate,
|
||||
-- which has no mana rail to leave empty. The bar hides with it, as on every other frame.
|
||||
local maxPower = UnitPowerMax("pet")
|
||||
if maxPower > 0 then
|
||||
BenillaPetFrameTexture:SetTexture("Interface\\TargetingFrame\\UI-SmallTargetingFrame")
|
||||
frame.manaBar:Show()
|
||||
local _, token = UnitPowerType("pet")
|
||||
local c = BENILLA_POWER_COLORS[token] or BENILLA_POWER_COLORS.MANA
|
||||
frame.manaBar:SetStatusBarColor(c.r, c.g, c.b)
|
||||
frame.manaBar:SetMinMaxValues(0, maxPower)
|
||||
frame.manaBar:SetValue(UnitPower("pet"))
|
||||
else
|
||||
BenillaPetFrameTexture:SetTexture("Interface\\TargetingFrame\\UI-SmallTargetingFrame-NoMana")
|
||||
frame.manaBar:Hide()
|
||||
end
|
||||
|
||||
BenillaPetFrame_UpdateDebuffs()
|
||||
end
|
||||
|
||||
-- The four debuff buttons (ref PetFrame.xml's $parentDebuff1..4 over RefreshBuffs). The pet
|
||||
-- carries no aura timers — no unit but yourself does on the 1.12 wire (decision 0257 B6) —
|
||||
-- so these are icon + border + stack count, the party rows' shape exactly.
|
||||
function BenillaPetFrame_UpdateDebuffs()
|
||||
for i = 1, 4 do
|
||||
local button = getglobal("BenillaPetFrameDebuff" .. i)
|
||||
local _, icon, count = UnitDebuff("pet", i)
|
||||
if icon then
|
||||
getglobal(button:GetName() .. "Icon"):SetTexture(icon)
|
||||
local text = getglobal(button:GetName() .. "Count")
|
||||
if count and count > 1 then
|
||||
text:SetText(count)
|
||||
text:Show()
|
||||
else
|
||||
text:Hide()
|
||||
end
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ref PetFrame_OnUpdate: the attack overlay's alpha ramp — a 0.5 s-per-leg triangle between
|
||||
-- 55/255 and 1, sign-flipped at each end (the ref's own counter shape, transcribed).
|
||||
function BenillaPetFrame_OnUpdate(frame, elapsed)
|
||||
if not BenillaPetFrameAttackMode:IsVisible() then return end
|
||||
local counter = frame.attackModeCounter + elapsed
|
||||
local sign = frame.attackModeSign
|
||||
if counter > 0.5 then
|
||||
sign = -sign
|
||||
frame.attackModeSign = sign
|
||||
end
|
||||
counter = math.fmod(counter, 0.5)
|
||||
frame.attackModeCounter = counter
|
||||
local alpha
|
||||
if sign == 1 then
|
||||
alpha = (55 + (counter * 400)) / 255
|
||||
else
|
||||
alpha = (255 - (counter * 400)) / 255
|
||||
end
|
||||
BenillaPetFrameAttackMode:SetVertexColor(1.0, 1.0, 1.0, alpha)
|
||||
end
|
||||
|
||||
-- ref PetFrame_OnClick's left branch, all three legs in the ref's own order. The right
|
||||
-- branch (the PET menu) is the named deferral in the header.
|
||||
function BenillaPetFrame_OnClick(button)
|
||||
if button ~= "LeftButton" then return end
|
||||
if SpellIsTargeting() then
|
||||
SpellTargetUnit("pet")
|
||||
elseif CursorHasItem() then
|
||||
DropItemOnUnit("pet")
|
||||
else
|
||||
TargetUnit("pet")
|
||||
end
|
||||
end
|
||||
|
||||
function BenillaPetFrame_OnEnter(frame)
|
||||
if not UnitExists("pet") then return end
|
||||
GameTooltip_SetDefaultAnchor(GameTooltip, frame)
|
||||
if GameTooltip:SetUnit("pet") then
|
||||
getglobal("GameTooltipTextLeft1"):SetTextColor(GameTooltip_UnitColor("pet"))
|
||||
end
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
<!-- The debuff button — ref PartyBuffButtonTemplate (PartyFrameTemplates.xml l.3-35), which is
|
||||
what the ref's PetFrame inherits for its four: a 15×15 icon under a 17×17
|
||||
UI-Debuff-Overlays border. Untinted: the ref's party/pet buttons never recolor it (only the
|
||||
target frame's dispel-tint law does), so this stays the flat swatch the TexCoords select. -->
|
||||
<Button name="BenillaPetDebuffTemplate" virtual="true" hidden="true">
|
||||
<Size><AbsDimension x="15" y="15"/></Size>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="$parentIcon" setAllPoints="true"/>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<Texture name="$parentBorder" file="Interface\Buttons\UI-Debuff-Overlays">
|
||||
<Size><AbsDimension x="17" y="17"/></Size>
|
||||
<Anchors><Anchor point="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
|
||||
<TexCoords left="0.296875" right="0.5703125" top="0" bottom="0.515625"/>
|
||||
</Texture>
|
||||
<FontString name="$parentCount" inherits="NumberFontNormalSmall">
|
||||
<Anchors><Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-1" y="0"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEnter><![CDATA[
|
||||
if GameTooltip then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetUnitDebuff("pet", this:GetID())
|
||||
end
|
||||
]]></OnEnter>
|
||||
<OnLeave>GameTooltip:Hide()</OnLeave>
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
<!-- frameStrata LOW matches the party frames' and the target's — above the player frame's
|
||||
BACKGROUND (which it overlaps), still below the MEDIUM panels. -->
|
||||
<Frame name="BenillaPetFrame" parent="BenillaPlayerFrame" hidden="true" frameStrata="LOW">
|
||||
<Size><AbsDimension x="128" y="53"/></Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="80" y="-60"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<Frames>
|
||||
<!-- Bars first, pushed one frame level under the TextureFrame at load (see OnLoad). -->
|
||||
<StatusBar name="$parentHealthBar">
|
||||
<Size><AbsDimension x="70" y="8"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="47" y="-22"/></Offset></Anchor></Anchors>
|
||||
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
||||
<BarColor r="0" g="1.0" b="0"/>
|
||||
</StatusBar>
|
||||
<StatusBar name="$parentManaBar">
|
||||
<Size><AbsDimension x="70" y="8"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="47" y="-29"/></Offset></Anchor></Anchors>
|
||||
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
||||
<BarColor r="0" g="0" b="1.0"/>
|
||||
</StatusBar>
|
||||
<Frame name="$parentTextureFrame">
|
||||
<Size><AbsDimension x="128" y="53"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<!-- Portrait 37×37 TOPLEFT (7,-6), under the art's round window (ref
|
||||
PetPortrait, PetFrame.xml l.16-27). -->
|
||||
<Texture name="$parentPortrait">
|
||||
<Size><AbsDimension x="37" y="37"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="BORDER">
|
||||
<!-- 128×64 art at (0,-2) — it hangs 13 px below the frame rect, as
|
||||
authored. The file is swapped at update (see the header). -->
|
||||
<Texture name="BenillaPetFrameTexture" file="Interface\TargetingFrame\UI-SmallTargetingFrame">
|
||||
<Size><AbsDimension x="128" y="64"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="ARTWORK">
|
||||
<!-- The attacking overlay (ref PetAttackModeTexture, l.104-118): the right
|
||||
29.7% of UI-Player-AttackStatus, ADD-blended, pulsed by OnUpdate. -->
|
||||
<Texture name="BenillaPetFrameAttackMode" file="Interface\TargetingFrame\UI-Player-AttackStatus" alphaMode="ADD" hidden="true">
|
||||
<Size><AbsDimension x="76" y="64"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="-9"/></Offset></Anchor></Anchors>
|
||||
<TexCoords left="0.703125" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<Layer level="OVERLAY">
|
||||
<FontString name="$parentName" inherits="GameFontNormalSmall" justifyH="LEFT">
|
||||
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="50" y="33"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
</Frame>
|
||||
<!-- The four debuffs, ref geometry: the first at (48,-42), then +2 px chained right. -->
|
||||
<Button name="$parentDebuff1" inherits="BenillaPetDebuffTemplate" id="1">
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="48" y="-42"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
<Button name="$parentDebuff2" inherits="BenillaPetDebuffTemplate" id="2">
|
||||
<Anchors><Anchor point="LEFT" relativeTo="BenillaPetFrameDebuff1" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
<Button name="$parentDebuff3" inherits="BenillaPetDebuffTemplate" id="3">
|
||||
<Anchors><Anchor point="LEFT" relativeTo="BenillaPetFrameDebuff2" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
<Button name="$parentDebuff4" inherits="BenillaPetDebuffTemplate" id="4">
|
||||
<Anchors><Anchor point="LEFT" relativeTo="BenillaPetFrameDebuff3" relativePoint="RIGHT"><Offset><AbsDimension x="2" y="0"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>BenillaPetFrame_OnLoad(this)</OnLoad>
|
||||
<OnEvent>BenillaPetFrame_OnEvent(this)</OnEvent>
|
||||
<OnUpdate>BenillaPetFrame_OnUpdate(this, arg1)</OnUpdate>
|
||||
<OnEnter>BenillaPetFrame_OnEnter(this)</OnEnter>
|
||||
<OnLeave>GameTooltip:FadeOut()</OnLeave>
|
||||
<OnClick>BenillaPetFrame_OnClick(arg1)</OnClick>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
@@ -0,0 +1,60 @@
|
||||
//! **Which build is this?** — the commit the running binary was built from, stamped in at
|
||||
//! compile time by the `benilla` launcher shim's `build.rs` (which owns the *how*, including why
|
||||
//! the stamp can't go stale — and why it is stamped into the ~30-line shim rather than this
|
||||
//! crate: cargo re-dirties whatever package carries the stamp on every commit, decision 0993)
|
||||
//! and handed to [`crate::run`] as the [`BuildId`] resource.
|
||||
//!
|
||||
//! A report from someone else's machine — "the water reads wrong here", "it panicked on login" —
|
||||
//! is only actionable against known code, and nothing else in the binary identifies it: the crate
|
||||
//! version is a permanent `0.1.0`, and people build from a clone of the public snapshot repo whose
|
||||
//! HEAD moves with every sync. So the sha is the version.
|
||||
//!
|
||||
//! Two surfaces, deliberately:
|
||||
//!
|
||||
//! - **The startup log line** ([`banner`], run from [`crate::preflight`]'s banner family) — this is
|
||||
//! the one that matters for someone else's run. They launch from a terminal, so the id is already
|
||||
//! in the output they paste; nobody has to know a hotkey to answer "what are you on?".
|
||||
//! - **The debug panel's footer** (`` ` ``) — the same line where the reader already is when they
|
||||
//! are looking at a readout, click-to-copy for the full sha.
|
||||
//!
|
||||
//! Mapping a reported sha back: a **public** snapshot sha is tagged `pub/<short-sha>` on the private
|
||||
//! commit it shipped, so `git show pub/<sha>` (or `git rev-parse pub/<sha>^{commit}`) names the code
|
||||
//! exactly; a **private** sha is just a commit here.
|
||||
|
||||
use bevy::prelude::*;
|
||||
|
||||
/// The launcher shim's compile-time git stamp, inserted as a resource by [`crate::run`]. The git
|
||||
/// fields are empty when the build had no checkout to read (a downloaded source zip, or no `git`
|
||||
/// on `PATH`), which [`BuildId::summary`] reports as an unknown build.
|
||||
#[derive(Resource, Clone, Copy)]
|
||||
pub struct BuildId {
|
||||
/// The full 40-char sha of the commit this binary was built from (what the panel copies).
|
||||
pub sha: &'static str,
|
||||
/// Git's own abbreviation of [`sha`](Self::sha), from the same repo `pubsync` abbreviates in —
|
||||
/// so a public build's string is literally the suffix of its `pub/<sha>` tag.
|
||||
pub short: &'static str,
|
||||
/// The commit date of [`sha`](Self::sha) (`YYYY-MM-DD`) — a property of the sha, so it can
|
||||
/// never disagree with it.
|
||||
pub date: &'static str,
|
||||
/// The cargo profile directory this was built in: `debug`, `release`, or `ship` (0736). Half
|
||||
/// of every "it runs badly" report is a debug build.
|
||||
pub profile: &'static str,
|
||||
}
|
||||
|
||||
impl BuildId {
|
||||
/// The one-line build id: `f5fd009 · 2026-07-30 · release`.
|
||||
pub(crate) fn summary(&self) -> String {
|
||||
if self.short.is_empty() {
|
||||
format!("unknown (built without a git checkout) · {}", self.profile)
|
||||
} else {
|
||||
format!("{} · {} · {}", self.short, self.date, self.profile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Log the build id once at startup — the line that answers "what version are you on?" from a
|
||||
/// pasted terminal log. Not env-gated, for [`crate::preflight`]'s reason: an id nobody knows to
|
||||
/// switch on is not an id.
|
||||
pub(crate) fn banner(build: Res<BuildId>) {
|
||||
info!("benilla build {}", build.summary());
|
||||
}
|
||||
@@ -958,6 +958,21 @@ pub(super) fn seed_ui_fixture(
|
||||
warn!("capture: ui-options-graphics seed failed: {e}");
|
||||
}
|
||||
}
|
||||
UiFixture::OptionsWorldDetail => {
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
};
|
||||
// The dropdown list open (0992), same posture as the page fixtures: real CVar set,
|
||||
// the live open-select-toggle path. The list's width settles from its OnUpdate a
|
||||
// frame later (the kit's WIDTH SETTLE law) — inside the capture's settle frames.
|
||||
script.register_cvars(crate::cvars::REGISTERED.iter().copied());
|
||||
if let Err(e) = script.run(
|
||||
"ShowUIPanel(OptionsFrame); OptionsFrameCategoryListRowGraphics:Click(); \
|
||||
OptionsFrameContainerBodyGraphicsRowWorldDetailDropdownButton:Click()",
|
||||
) {
|
||||
warn!("capture: ui-options-worlddetail seed failed: {e}");
|
||||
}
|
||||
}
|
||||
UiFixture::OptionsSearch => {
|
||||
let Some(mut script) = script else {
|
||||
return;
|
||||
@@ -1032,6 +1047,46 @@ pub(super) fn seed_ui_fixture(
|
||||
warn!("capture: ui-spellbook seed failed to open the book: {e}");
|
||||
}
|
||||
}
|
||||
UiFixture::Macro | UiFixture::MacroPopup => {
|
||||
let Some(script) = script.as_mut() else {
|
||||
return;
|
||||
};
|
||||
// Made through the LIVE `CreateMacro` path, not by pushing a table in — so the capture
|
||||
// exercises engine table → `UPDATE_MACROS` → window exactly as a player's own edit
|
||||
// does. Icon indices are into the real `SpellIcon.dbc` catalog `ui_macro` loads at
|
||||
// PostStartup, so they resolve to real art. The bodies are the shapes that matter: a
|
||||
// plain `/cast`, a MULTI-LINE macro (what the body box has to lay out), and a
|
||||
// `/script` line (the non-`/cast` case the bar's bound-spell resolve must decline).
|
||||
const SEED: [(&str, u32, &str); 4] = [
|
||||
("Charge", 1, "/cast Charge"),
|
||||
(
|
||||
"Pull",
|
||||
9,
|
||||
"/cast Charge\n/say Incoming!\n/script CastSpellByName(\"Battle Shout\")",
|
||||
),
|
||||
("Shout", 17, "/cast Battle Shout"),
|
||||
("Sit", 25, "/sit"),
|
||||
];
|
||||
let mut seed = String::new();
|
||||
for (name, icon, body) in SEED {
|
||||
seed.push_str(&format!(
|
||||
"CreateMacro(\"{name}\", {icon}, \"{}\", 1, nil)\n",
|
||||
// Lua-escape the body: a `/script` line carries its own quotes, and a macro
|
||||
// body's newlines have to survive as newlines.
|
||||
body.replace('"', "\\\"").replace('\n', "\\n")
|
||||
));
|
||||
}
|
||||
// Slot 2 selected: the multi-line body, so the detail pane and the body box both have
|
||||
// something in them. The popup fixture then opens the chooser over that selection via
|
||||
// the ref's `MacroEditButton` path — an EDIT, so the name box arrives pre-filled.
|
||||
seed.push_str("ShowMacroFrame()\nBenillaMacroButton2:Click()\n");
|
||||
if fixture == UiFixture::MacroPopup {
|
||||
seed.push_str("BenillaMacroEditButton:Click()\n");
|
||||
}
|
||||
if let Err(e) = script.run(&seed) {
|
||||
warn!("capture: ui-macro seed failed: {e}");
|
||||
}
|
||||
}
|
||||
UiFixture::ChatEdit => {
|
||||
let Some(script) = script.as_mut() else {
|
||||
return;
|
||||
@@ -42,14 +42,14 @@
|
||||
//! WOW_DATA=<vanilla Data dir> WOW_CAPTURE_UI=1 WOW_CAPTURE=ui-unitframes \
|
||||
//! WOW_CAPTURE_OUT=/tmp/shot.png cargo run -q -p benilla
|
||||
//! ```
|
||||
//! `cargo run` exports `CARGO_MANIFEST_DIR`, which is how Bevy's `AssetServer` finds
|
||||
//! `crates/benilla/assets/` — in particular the WGSL shaders (`ui_quad.wgsl`, `sky.wgsl`, …).
|
||||
//! Launching `target/debug/benilla` directly has no manifest dir, so it resolves `assets/` next
|
||||
//! to the executable (`target/debug/assets/`, which doesn't exist), silently loads **no** shaders,
|
||||
//! and every custom-shader layer — the *entire* player UI, sky, liquid, models — renders blank
|
||||
//! while only the built-in-pipeline terrain still draws. A UI capture that comes back as bare sky
|
||||
//! is this mistake, not a UI bug. `WOW_CAPTURE_UI=1` opts the player UI into the shot (off by
|
||||
//! default so world baselines stay UI-free; omit it for world-only scenes). `WOW_CAPTURE=list`
|
||||
//! `cargo run` rebuilds first; a bare `target/debug/benilla` can silently be **stale code** — the
|
||||
//! classic way a capture "disproves" a fix that was never in the binary. (Before 0993 it was
|
||||
//! worse: `assets/` resolved through Cargo's runtime `CARGO_MANIFEST_DIR`, so a bare run silently
|
||||
//! loaded **no** WGSL shaders and every custom-shader layer — the *entire* player UI, sky, liquid,
|
||||
//! models — rendered blank, read as a UI bug for hours once. The app now bakes the absolute
|
||||
//! `crates/benilla-app/assets/` path in at compile time, so a bare run finds its shaders;
|
||||
//! staleness is the trap that remains.) `WOW_CAPTURE_UI=1` opts the player UI into the shot (off
|
||||
//! by default so world baselines stay UI-free; omit it for world-only scenes). `WOW_CAPTURE=list`
|
||||
//! prints the scenario names. `scripts/visual.sh` wraps all of this.
|
||||
|
||||
use std::path::Path;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user