the world map blips are the reference's, and particles go untinted
- the world map speaks the reference's names: WorldMapTooltip is declared (six corpus addons index it; pfUI dies on it at file scope), the blackout frame takes the reference's spelling BlackoutWorld, and the four hand-written party blips become the reference's own unit-blip templates — all 44 blips as instances, each with its published $parentIcon, the name pfUI recolours group blips by class through; the 40 raid ones are declared but not positioned yet, named in the OUT list rather than faked
- the templates addons build their own buttons from are published: ItemButtonTemplate, ContainerFrameItemButtonTemplate and the bag-slot chain — most of the handler scripts were live the whole time (this client executes the reference's own container and paper-doll code off the player's patch chain), so what was missing was mostly declarations, not behaviour — the bank arm stays out and is named as the omission it is; OptionsFrameSliders becomes real, with the three Graphics rows reading it and re-reading on show, so an addon's write to a slider's bounds actually moves the slider; and the template census that had ranked these at "zero demand" learns to see a local bound to a literal — its table went from 3 rows to 7
- the bindings answer in 1.12's own shapes: Button:IsEnabled() answers the NUMBER 1 or 0 (0 is truthy in Lua, so the reference's own == 0 tests got false forever, and three of our files had hand-worked around the divergence rather than fixed it); the constructors' string arguments are four shapes, not one rule — pfUI passes a Button where a template name goes and the reference constructs the FontString and ignores it; SetInventoryItem returns THREE values, the reference's own paper doll destructuring repairCost; UnitIsVisible is object presence and nothing else (57 bytes, one branch, no distance — an out-of-range party member is exists-yes, visible-nil); the tapped pair reads the two dynamic-flags bits, and its repaint wire fires UNIT_FACTION on the tap bit — the half a passing predicate test never sees; UnitIsPartyLeader is two ORed legs covering disjoint failures (a held stranger leading their own party, and an out-of-range member with no object to read a flag from); and the four nameplate verbs take no argument and return zero values
- the ghost's wisps compose onto its body instead of over it — the four emitters drew at full strength over a body at alpha 0.5, and now inherit the instance alpha down the attachment chain the way the reference recomposes it per frame; the residual we had parked ("the wisps should inherit the tint too") is REFUTED at the bytes: a particle batch commits hardcoded white RGB, and even that never reaches the fragment — particles are never tinted, only the alpha crosses, applied to the alpha byte alone — so the shipped shader split was already right and no code changes
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,7 @@ use std::collections::HashSet;
|
||||
|
||||
use mlua::{Function, Lua, MultiValue, Table, Value};
|
||||
|
||||
use crate::script::binding_abi::optional_string;
|
||||
use crate::script::region::region_wrapper;
|
||||
use crate::script::{Model, RegionData, REG_SCRIPTS, SCRIPT_KINDS};
|
||||
use crate::widget::RegionKind;
|
||||
@@ -140,18 +141,30 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
// (CustomNameplates:437, a four-argument later-client form). Accepted rather than skipped
|
||||
// because dropping it silently is the very class this fixes, and one real site is still a site.
|
||||
m.set(
|
||||
// **The three argument positions are NOT one rule, and the fourth is the odd one.** Verified
|
||||
// together in wow-re `ui/scratch/xml-template-name-lookup.md` §5.2:
|
||||
//
|
||||
// · `name` and `layer` go through `0x6f3510` (is-number-**or**-string) then `0x6f3690`, and
|
||||
// **the result is never tested** — so a table is simply absent (unnamed / the pre-staged
|
||||
// default layer), and a NUMBER is accepted and stringified.
|
||||
// · `inherits` is gated by a raw `lua_type(L, 4) == LUA_TSTRING` (`0x773d28` / `0x773b06`)
|
||||
// asked BEFORE anything reads the slot — so a table AND a number are both ignored, and the
|
||||
// non-string leg jumps to the very block the two template-HIT legs jump to, with both
|
||||
// template handles NULL. It is the success path, not a bolted-on case.
|
||||
//
|
||||
// **Nothing here raises.** This mattered: benilla typed all three `Option<String>`, so mlua
|
||||
// raised `bad argument #4: error converting Lua table to String` — and pfUI, the most-installed
|
||||
// 1.12 addon, builds every buff-stack label as
|
||||
// `f.buffs[i]:CreateFontString(nil, "OVERLAY", f.buffs[i])`, passing the BUTTON as the third
|
||||
// argument. The real client constructs it, ignores the argument, and logs nothing.
|
||||
"CreateTexture",
|
||||
lua.create_function(
|
||||
|lua,
|
||||
(this, name, layer, inherits): (
|
||||
Table,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
)| {
|
||||
|lua, (this, name, layer, inherits): (Table, Value, Value, Value)| {
|
||||
let name = optional_string(lua, &name);
|
||||
let layer = optional_string(lua, &layer);
|
||||
let wrapper = create_region(lua, &this, RegionKind::Texture, name, layer)?;
|
||||
if let Some(from) = inherits.as_deref().filter(|s| !s.is_empty()) {
|
||||
apply_region_inherits(lua, &wrapper, from)?;
|
||||
if let Some(from) = strict_string_arg(&inherits).filter(|s| !s.is_empty()) {
|
||||
apply_region_inherits(lua, &wrapper, &from)?;
|
||||
}
|
||||
Ok(wrapper)
|
||||
},
|
||||
@@ -255,16 +268,12 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
m.set(
|
||||
"CreateFontString",
|
||||
lua.create_function(
|
||||
|lua,
|
||||
(this, name, layer, inherits): (
|
||||
Table,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
Option<String>,
|
||||
)| {
|
||||
|lua, (this, name, layer, inherits): (Table, Value, Value, Value)| {
|
||||
let name = optional_string(lua, &name);
|
||||
let layer = optional_string(lua, &layer);
|
||||
let wrapper = create_region(lua, &this, RegionKind::FontString, name, layer)?;
|
||||
if let Some(from) = inherits.as_deref().filter(|s| !s.is_empty()) {
|
||||
apply_region_inherits(lua, &wrapper, from)?;
|
||||
if let Some(from) = strict_string_arg(&inherits).filter(|s| !s.is_empty()) {
|
||||
apply_region_inherits(lua, &wrapper, &from)?;
|
||||
}
|
||||
Ok(wrapper)
|
||||
},
|
||||
@@ -442,6 +451,23 @@ fn apply_region_inherits(lua: &Lua, wrapper: &Table, from: &str) -> mlua::Result
|
||||
)))
|
||||
}
|
||||
|
||||
/// **The `lua_type(L, idx) == LUA_TSTRING` gate, asked before anything reads the slot** — the
|
||||
/// region constructors' `inherits` argument and nowhere else (`0x773d28` for `CreateFontString`,
|
||||
/// `0x773b06` for `CreateTexture`).
|
||||
///
|
||||
/// Strictly narrower than [`optional_string`]: a **number is refused here**, because the tag is
|
||||
/// tested raw with no coercion. That one-argument difference is the whole reason
|
||||
/// `f:CreateTexture(nil, nil, 5)` silently ignores the 5 while `CreateFrame("Frame", nil, nil, 5)`
|
||||
/// **raises** `Couldn't find inherited node "5"` — `CreateFrame` runs `lua_tostring(L, 4)` BEFORE
|
||||
/// its own `cmp 4`, and that retags the number's stack slot in place, so it arrives as a string.
|
||||
/// Same value, opposite outcome, from argument order alone.
|
||||
fn strict_string_arg(v: &Value) -> Option<String> {
|
||||
match v {
|
||||
Value::String(s) => s.to_str().ok().map(|s| s.to_owned()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_region(
|
||||
lua: &Lua,
|
||||
this: &Table,
|
||||
|
||||
Reference in New Issue
Block a user