a cinematic holds your body, and all seven chat windows exist
- a cinematic holds the body while the camera flies: the streaming focus follows the eye — a Tauren's intro opens 1741 yards from the body — so the body's own ground unloaded under it and it fell (z=59.4 to −62 in four seconds, the server yanking it back); it now takes the same hold a teleport takes, released when the focus comes home to a resident tile, and the loading screen stops covering the fly-by, because "the player's ground is missing" is not a load while the eye is deliberately elsewhere; the letterbox measurement prints as an ordinary info line, and scripts/cine.sh plays any one cinematic and prints what the client did to the world while the camera was away
- a cinematic cuts the zone music: the reference asserts exactly what /console EnableMusic 0 asserts, through the same setter — a cut, not a fade, the music pump dead for the duration, resume at +3.000 s — and benilla asserts the runtime flag, never the CVar, or quitting during a 102-second intro would save your music off for good; ambience is deliberately untouched (it has the analogous flag and the cinematic never writes it); the narration has no category at all — its flags route it around every volume slider but not around the Enable Sound Effects gate, so unchecking that silences it — and the listener follows the cinematic camera for the duration
- addons can walk a UI they did not build: GetChildren/GetNumChildren/GetRegions/GetNumRegions land — the basis of the whole reskin genre, and the wall four of the twenty most-installed 1.12 addons died on: pfUI drew nothing before and now paints 495 quads, ShaguDPS 71 → 566, pfQuest 3 → 498 — with the reference's own quirks: the title region is NOT in the list (its creation path never reaches the region linker), hidden regions are returned and counted, oldest first; the Model widget gets its Lua surface (SetModel, SetSequence and siblings, read out of the shipped image, where SetRotation and SetFacing are two names for one slot); and the Minimap carries the nine engine-created Model children no XML declares, built before the frame descent, the ninth being the player arrow Questie indexes as ({Minimap:GetChildren()})[9]
- all seven chat windows come from one template, tabs and scroll buttons included — whether a window is shown has nothing to do with whether its globals resolve, and pfUI reads every piece of that furniture for all seven, unguarded; isDocked only on windows 1 and 2 (the chat-cache's own DOCKED byte), SELECTED_CHAT_FRAME seated beside DEFAULT_CHAT_FRAME (the window you last picked vs the one FrameXML prints to), and the four per-window fields ChatFrame_OnLoad seats; two more one-name walls fell — a Button's label answers to its second spelling NormalText, which KLHThreatMeter's column headers die without, and ABANDON_QUEST_WITH_ITEMS is declared because a dialog table is read at load whatever the client later shows, leaving Questie with zero errors of ours — and the addon harness gains --probe/--eval, one addon's VM answering a question in ~5 s instead of a 90 s corpus run
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -198,6 +198,101 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
}
|
||||
})?,
|
||||
)?;
|
||||
// ── The four structure queries: GetChildren / GetNumChildren / GetRegions / GetNumRegions ──
|
||||
//
|
||||
// Registered bindings in 1.12 (`GetNumRegions 0x773e60`, `GetRegions 0x773f60`,
|
||||
// `GetNumChildren 0x774080`, `GetChildren 0x774180` — wow-re `ui/ledger.tsv`, classified
|
||||
// ORCHESTRATION: each marshals and delegates, with no fidelity math of its own).
|
||||
//
|
||||
// **Why these four are worth more than their size.** They are how an addon walks a frame it did
|
||||
// not build, which is the whole basis of the "reskin the default UI" genre — and that genre is
|
||||
// the top of the 1.12 popularity list. `pfUI.api.StripTextures` is the canonical shape:
|
||||
//
|
||||
// for _, v in ipairs({ frame:GetRegions() }) do
|
||||
// if v.SetTexture then ... v:SetTexture(nil) end
|
||||
// end
|
||||
//
|
||||
// With `GetRegions` absent that is `attempt to call method 'GetRegions' (a nil value)`, raised
|
||||
// out of a library file every one of pfUI, pfQuest, pfQuest-turtle and ShaguDPS embeds — four
|
||||
// separate top-20 addons dying on one missing verb, plus Questie on `GetChildren`.
|
||||
//
|
||||
// **Two lists, one walk each, in the arena's own order.** `Frame::children` is insertion order,
|
||||
// which our own arena documents as the client's `+0x300` child-list order; `Frame::regions` is
|
||||
// creation order. Neither is re-sorted here — a structure query reports the structure, and the
|
||||
// draw order is `crate::order`'s separate concern.
|
||||
//
|
||||
// **A detached region is not in the list.** `Region:SetParent(nil)` sets `Region::detached`
|
||||
// rather than removing the entry, because the owner's `Vec` is what `WidgetArena::destroy`
|
||||
// frees from — but the client's re-link virtual `0x77fd10` with a null parent genuinely
|
||||
// **unlinks from the old parent's draw layer and region list** (wow-re
|
||||
// `widget-api-batch-benilla.md` Q7, VERIFIED), so a detached region must be invisible to both
|
||||
// region verbs. Our flag is a representation choice; the list these report is the client's.
|
||||
//
|
||||
// **The count and the list come from ONE function** ([`regions_of`] / [`children_of`]), never
|
||||
// two walks with the same filter written twice. `GetNumRegions` disagreeing with
|
||||
// `#{GetRegions()}` is a bug an addon would hit as an off-by-one deep inside a loop it did not
|
||||
// write, and the only way to make it impossible is to have one of them BE the other.
|
||||
//
|
||||
// **Settled by a wow-re §5 quartet** (`ui/scratch/widget-list-bindings.md`), which confirmed two
|
||||
// of these choices and corrected a third:
|
||||
//
|
||||
// · **The order is link order, oldest first, and no reversal.** `CSimpleFrame` embeds a punned
|
||||
// `TSList` header whose ctor sets `tail = &header`, `head = (&header)|1`, and BOTH linkers
|
||||
// append at the TAIL (`0x76a750` for regions, `0x76aa20` for children — one caller each).
|
||||
// Our insertion-order `Vec` is the same order. (wow-re's own `draw-order-law.md` said
|
||||
// "tail→head"; that was a direction mislabel and was corrected in the same round.)
|
||||
// · **Hidden children and regions are returned and counted** — a VERIFIED negative: the
|
||||
// counting loops never load the visibility word at all.
|
||||
// · **The TITLE REGION IS NOT RETURNED**, and that one corrected us. Both creation paths
|
||||
// (`0x773910`, `0x769b79`) dispatch a vtable slot that is a bare `[this+0x9c] = parent` and
|
||||
// never reach the linker `0x77fd10`, so a title region is not in the list this walks —
|
||||
// corroborated by `Hide`/`Show` carrying an explicit extra `[frame+0xa8]` case *because* the
|
||||
// list walk misses it. Ours is in `Frame::regions` (so the arena can free it), so
|
||||
// [`regions_of`] filters it out.
|
||||
// · **A Button's label (`+0x338`) and its four state textures ARE in the list** — they link
|
||||
// through `0x77fd10` like any region, which is what ours do too.
|
||||
// · **The return shape**: N separate values, never a table; a frame with none returns ZERO
|
||||
// values (no `lua_newtable` in either body), and `GetNum*` returns one number — `0` when
|
||||
// empty, never nil.
|
||||
//
|
||||
// The one thing still out of reach is not these bindings': Questie reads
|
||||
// `({Minimap:GetChildren()})[9]` for the player arrow, and the §5 found why that index works —
|
||||
// `CMinimap`'s ctor `0x4edbc0` builds NINE engine-owned `Model` children before the XML
|
||||
// `<Frames>` descent, the ninth (`[Minimap+0x338]`) being the arrow `SetPlayerFacing 0x4eb8e0`
|
||||
// writes. We create none of them, so index 9 is nil here until the minimap grows its engine
|
||||
// children.
|
||||
m.set(
|
||||
"GetChildren",
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let ids = children_of(lua, &this)?;
|
||||
let mut out = Vec::with_capacity(ids.len());
|
||||
for id in ids {
|
||||
out.push(Value::Table(frame_wrapper(lua, id)?));
|
||||
}
|
||||
Ok(mlua::MultiValue::from_vec(out))
|
||||
})?,
|
||||
)?;
|
||||
m.set(
|
||||
"GetNumChildren",
|
||||
lua.create_function(|lua, this: Table| Ok(children_of(lua, &this)?.len()))?,
|
||||
)?;
|
||||
m.set(
|
||||
"GetRegions",
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let ids = regions_of(lua, &this)?;
|
||||
let mut out = Vec::with_capacity(ids.len());
|
||||
for id in ids {
|
||||
out.push(Value::Table(crate::script::region::region_wrapper(
|
||||
lua, id,
|
||||
)?));
|
||||
}
|
||||
Ok(mlua::MultiValue::from_vec(out))
|
||||
})?,
|
||||
)?;
|
||||
m.set(
|
||||
"GetNumRegions",
|
||||
lua.create_function(|lua, this: Table| Ok(regions_of(lua, &this)?.len()))?,
|
||||
)?;
|
||||
// SetParent — the runtime reparent, per the byte-verified law (wow-re
|
||||
// `ui/scratch/setparent-runtime-strata-level.md`; decision 1323). The binding half
|
||||
// (`0x7a1550`): the parent argument is a frame table, a NAME string (`0x76c760`), or an
|
||||
@@ -763,3 +858,52 @@ fn set_shown(lua: &Lua, this: &Table, shown: bool) -> mlua::Result<()> {
|
||||
event::fire_visibility_changes(lua, changed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This frame's child frames as stable ids, in the arena's insertion order — the one walk
|
||||
/// `GetChildren` and `GetNumChildren` share (see their comment on why they must share it).
|
||||
fn children_of(lua: &Lua, this: &Table) -> mlua::Result<Vec<u32>> {
|
||||
let h = frame_handle_of(lua, this)?;
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model");
|
||||
let Some(children) = model.arena.frame(h).map(|f| f.children.clone()) else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
Ok(children.into_iter().map(|c| model.frame_id(c)).collect())
|
||||
}
|
||||
|
||||
/// This frame's live regions as stable ids, in creation order — **the title region and detached
|
||||
/// ones excluded** — the one walk `GetRegions` and `GetNumRegions` share.
|
||||
///
|
||||
/// Both exclusions are the client's list, not tidiness, and both are VERIFIED (wow-re
|
||||
/// `ui/scratch/widget-list-bindings.md`, §5 quartet):
|
||||
///
|
||||
/// - **Detached.** `SetParent(nil)` reaches `0x77fd55` → the removal virtual `0x76a7f0`, which
|
||||
/// unlinks the node and frees it, and then skips every re-link. We keep the entry only so the
|
||||
/// arena can still free the slot; reporting it would hand an addon an object the reference never
|
||||
/// would, and `StripTextures`-shaped code would "strip" a region already off the screen.
|
||||
/// - **The title region.** Its two creation paths (`0x773910`, `0x769b79`) dispatch a vtable slot
|
||||
/// that is a bare `[this+0x9c] = parent` and never reach the linker at all, so it was never in
|
||||
/// this list — corroborated by `Hide`/`Show` needing an explicit extra `[frame+0xa8]` case
|
||||
/// *because* the walk misses it. Ours is in [`crate::widget::Frame::regions`] so that
|
||||
/// `WidgetArena::destroy` still frees it, which makes that a representation detail this walk
|
||||
/// must not leak.
|
||||
///
|
||||
/// A Button's label and its four state textures are deliberately NOT excluded: they link through
|
||||
/// `0x77fd10` like any other region in the reference, and ours go through `create_region` like any
|
||||
/// other region here.
|
||||
fn regions_of(lua: &Lua, this: &Table) -> mlua::Result<Vec<u32>> {
|
||||
let h = frame_handle_of(lua, this)?;
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model");
|
||||
let Some((regions, title)) = model
|
||||
.arena
|
||||
.frame(h)
|
||||
.map(|f| (f.regions.clone(), f.title_region))
|
||||
else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let live: Vec<_> = regions
|
||||
.into_iter()
|
||||
.filter(|r| Some(*r) != title)
|
||||
.filter(|r| model.arena.region(*r).is_some_and(|reg| !reg.detached))
|
||||
.collect();
|
||||
Ok(live.into_iter().map(|r| model.region_id(r)).collect())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user