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:
Sam
2026-08-31 20:15:16 +07:00
co-authored by Claude Fable 5
parent e70654ac01
commit c4f432d0c3
68 changed files with 2186 additions and 272 deletions
@@ -118,6 +118,7 @@ fn push_raid(s: &mut UiScript, raid: Vec<RaidMemberInfo>) {
s.set_party(PartyState {
members,
leader_index: 0,
leader_guid: 0, // the player leads; their guid is unset in this fixture
raid,
loot_method: "group".into(),
master_looter: None,
@@ -218,7 +219,7 @@ fn convert_to_raid_is_live_only_for_a_party_leader() {
assert!(visible(&s, "RaidFrameRaidDescription"), "solo: the blurb");
assert!(visible(&s, "RaidFrameConvertToRaidButton"));
assert_eq!(
s.eval::<i64>("return RaidFrameConvertToRaidButton:IsEnabled() and 1 or 0")
s.eval::<i64>("return RaidFrameConvertToRaidButton:IsEnabled()")
.unwrap(),
0,
"solo there is no party to convert"
@@ -240,7 +241,7 @@ fn convert_to_raid_is_live_only_for_a_party_leader() {
});
s.fire_event("PARTY_MEMBERS_CHANGED", Vec::new());
assert_eq!(
s.eval::<i64>("return RaidFrameConvertToRaidButton:IsEnabled() and 1 or 0")
s.eval::<i64>("return RaidFrameConvertToRaidButton:IsEnabled()")
.unwrap(),
1,
"a party leader can convert"
@@ -263,7 +264,7 @@ fn convert_to_raid_is_live_only_for_a_party_leader() {
});
s.fire_event("PARTY_LEADER_CHANGED", Vec::new());
assert_eq!(
s.eval::<i64>("return RaidFrameConvertToRaidButton:IsEnabled() and 1 or 0")
s.eval::<i64>("return RaidFrameConvertToRaidButton:IsEnabled()")
.unwrap(),
0,
"a party member cannot"
@@ -714,7 +715,7 @@ fn the_raid_info_panel_lists_the_saved_lockouts() {
]);
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
assert_eq!(
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled() and 1 or 0")
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled()")
.unwrap(),
1
);
@@ -743,7 +744,7 @@ fn the_raid_info_panel_lists_the_saved_lockouts() {
s.set_saved_instances(Vec::new());
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
assert_eq!(
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled() and 1 or 0")
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled()")
.unwrap(),
0
);
@@ -781,7 +782,7 @@ fn a_player_with_no_lockouts_loses_the_raid_info_button_on_the_second_answer() {
// Answer two, saying the same nothing.
s.fire_event("UPDATE_INSTANCE_INFO", Vec::new());
assert_eq!(
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled() and 1 or 0")
s.eval::<i64>("return RaidFrameRaidInfoButton:IsEnabled()")
.unwrap(),
0,
"an empty lockout list is a dead button"