unitscan's horn cut whatever SoftSpoken was reading, because the only stop 1.12 has stops everything

`SetCVar("MasterSoundEffects", 0)` then `..., 1)` is the only way a 1.12 addon
can stop a sound that is already playing, and the reference's stop is
indiscriminate: `0x7a67a0` walks the live instance list and stops every node in
it, whoever started them. That is fine while one addon at a time uses the toggle,
and this install has two that use it for opposite ends — SoftSpoken to stop its
own clip before replaying it and to duck the game's NPC bark, unitscan to make
sure of a channel for its alert (`unitscan.lua:55-59` is the pair, then
`PlaySoundFile`). It is not aimed at anybody.

grom: "unitscan sound will interupt softspoken reading something, this is really
annoying, I still want the unitscan sound but for softspoken to not be silenced."

Nothing in the toggle separates the two uses, so the caller does.
`PlaySoundFile` and the toggle each now carry the addon whose file made the call
— attribution by chunk name, since an addon's files load as
`@Interface\AddOns\<Name>\<file>`. A cut still stops every kit-played bark, which
is the game's own audio and what an addon ducking "the sound" means by it; among
`PlaySoundFile` audio it stops only the calling addon's own. Both of SoftSpoken's
uses survive intact: stopping its own clip is cutter == owner, and ducking NPC
dialogue is a kit.

A deviation from the reference, written down in `docs/ADDON_COMPAT.md` with the
two reports on either side of it, together with the Who-list font colours and the
73%-opaque tooltip art behind the unreadable map tooltip.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
2026-11-08 12:58:33 -07:00
co-authored by Claude Opus 5
parent b394d39481
commit a9acfbf420
6 changed files with 386 additions and 28 deletions
+97
View File
@@ -5133,3 +5133,100 @@ caller is not an addon at all — it is the realm's own FrameXML:
`SetHyperlink`, to force each item into the cache. The tooltip is a hidden scanner that is never
positioned for anyone to read, so the reference's own fallback to mode 0 / `ANCHOR_LEFT` is all
that code ever got from it either. A true report of sloppy realm code, not a gap here.
## `NORMAL_FONT_COLOR:GetRGB()` — the `/who` list, and a global 1.12 never had
`ui_script/manifest.rs`'s `install_font_colour_objects`, beside the other load-time repairs.
1.12 ships its font colours as loose `{ r, g, b }` tables (`Fonts.xml`); every client from Legion
on makes them `ColorMixin` objects, and addon code written in that dialect calls methods on them
rather than reading the fields. pfUI does, in both directions at once:
| call | where | what it did here |
| --- | --- | --- |
| `NORMAL_FONT_COLOR:GetRGB()` | `modules/socialmod.lua:145`, `modules/loot.lua` ×5 | **26 raises in one of grom's sessions**, every one from opening the Who tab. The list drew uncoloured and each row cost a traceback; the master-loot menu has the same five lines waiting for a raid |
| `WHITE_FONT_COLOR:GetRGB()` / `:GetRGBA()` | `modules/totems.lua:22-23`, `modules/minimap.lua:198` | indexes a nil: 1.12 has no such global at all |
The six `*_FONT_COLOR` tables 1.12 declares (`reference/1.12-globals.tsv`) get `ColorMixin` as a
**metatable**, not a `Mixin` copy. Upstream copies the methods in as fields, which is right for an
object it owns; these tables are FrameXML's, FrameXML reads them, and a 1.12 addon is free to walk
one with `pairs`. `__index` leaves `pairs(NORMAL_FONT_COLOR)` answering exactly the keys it answers
on the reference while the methods still resolve. `a` is the one field added, because `GetRGBA` has
to answer something and modern WoW's own `NORMAL_FONT_COLOR` is opaque. A table that already
carries a metatable is left exactly as found. `WHITE_FONT_COLOR` is created only if absent.
It lives in `manifest.rs` rather than beside `ColorMixin` in `compat/classic_api/color_mixin.rs`
for the same mechanical reason as `/focus`: the tables do not exist until `Fonts.xml` has run, and
the compat layer installs before FrameXML loads. Named one by one rather than swept out of `_G`,
so an addon's own table of the same name never gets a metatable it did not ask for.
## `MasterSoundEffects`: whose sound a cut may stop
`script/sound.rs`'s `SoundRequest::CutEffects`, spent by `sound/kit.rs`'s `stopped_by_reenable`.
**A deviation, and a deliberate one.**
`SetCVar("MasterSoundEffects", 0)` then `..., 1)` is the only way a 1.12 addon can stop a sound
that is already playing, and the reference's stop is indiscriminate: `0x7a67a0` walks the live
instance list and stops every node in it, whoever started them. That is fine while one addon at a
time uses the toggle, and this install has two that use it for opposite ends:
* **SoftSpoken** spends it to stop *its own* clip before replaying it, and to duck the game's own
NPC bark while it reads.
* **unitscan** spends it to make sure of a channel for its alert — `unitscan.lua:55-59` is the
pair, then `PlaySoundFile`. It is not aimed at anybody.
With the reference's rule, unitscan's horn cuts whatever SoftSpoken is in the middle of reading.
grom, 2026-11-08: *"unitscan sound will interupt softspoken reading something, this is really
annoying, I still want the unitscan sound but for softspoken to not be silenced."*
Nothing in the toggle separates the two uses, so the **caller** does. `PlaySoundFile` and the
toggle each carry the addon whose file made the call (`script/mod.rs`'s `calling_addon`, attribution
by chunk name: an addon's files load as `@Interface\AddOns\<Name>\<file>`). A cut still stops every
kit-played bark — that is the game's own audio, which is what an addon ducking "the sound" means by
it — and among `PlaySoundFile` audio it stops only the calling addon's own. Both of SoftSpoken's
uses survive intact: stopping its own clip is cutter == owner, and ducking NPC dialogue is a kit.
## An item template nobody answered
`items.rs`'s `retry_unanswered_templates`, over `query_cache.rs`'s `sweep_pending`.
The ask-once rule is the reference's `DBCache` and it is right for everything it covers: one query
per entry per connection, a negative cached as a negative. What it has no arm for is a query that
goes out and is **never answered**, and the whole cost of that falls on the player —
`GetItemInfo` keeps returning nil, so the item draws with no name, no icon and no tooltip, in the
bag, in the quest frame's required-items box and in every addon that reads it, for the rest of the
session, and nothing ever asks again.
grom, 2026-11-08: *"on the quest Deeprun Rat Roundup, I'm not recieving the flute item needed for
the quest"*. He had it the whole time. Entry **17117**, Rat Catcher's Flute, bag 0 slot 2 — the
only trace of it in a three-hour log is `GetNumJunkItems` reporting one carried slot *"with no
item template yet"*, twice.
An unanswered ask is now re-sent after 5 s, three sends in all, and then reported once. Bounded on
purpose: an entry the server genuinely does not know is answered with the high-bit miss and never
reaches the sweep, so a key that survives three sends is one nothing is going to answer. The same
gap exists for creature names and gameobject templates and is left alone until one of them is seen
to drop an answer, since a nameless mob is at least self-correcting on the next
`SMSG_UPDATE_OBJECT`.
## The map tooltip that read as invisible — 73% opaque art, not a draw order
Not a client change: `OpenMapClassic/Map/Extras.lua` (the port), `BackTooltip`.
grom sent two screenshots of a pfQuest quest-node tooltip he could not read on the world map, and
it looks exactly like a strata fault — so that is what was measured first, and it is not one.
`WorldMapTooltip` reports `strata=TOOLTIP lvl=1`, the stitched art is `FULLSCREEN` at levels 3..190,
`ZKey` puts the stratum in its top four bits, alphas are 1.0 on both sides, and the frame trees of
`WorldMapTooltip` and `ItemRefTooltip` are identical down to pfUI's backdrop child.
Four panels over the same stitched map settled it: `WorldMapTooltip` and a **fresh frame with a
stock backdrop** both read through; the same frame with a flat `ARTWORK` texture instead of a
backdrop is solid, on `WorldMapFrame` and on `UIParent` alike. So it is the backdrop art, and
decoding it says so outright — every texel of `Interface\Tooltips\UI-Tooltip-Background.blp` is
RGBA(142, 140, 142, **187**), 73% opaque.
Which is faithful. On Blizzard's muted parchment zone map the quarter that comes through is
invisible; on OpenMap's bright, busy stitched map it is all you see. `GameTooltip` does not have
the problem because pfUI puts a solid panel behind it, and nothing skins `WorldMapTooltip`. The
port now does the same: one opaque texture on the tooltip's `BACKGROUND` layer, inset past the
border so it still looks like a tooltip, shown only while the stitched map is what is underneath.