e6e8cfdca174a5b804017675bb07989830eb83db
188
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
5911098543 |
water: the mist veil is an outdoor thing — a room's air is the room's
grom, 2026-11-09: *"well im in the inn having the improved water on seems to be
messing with our volumetric fog, that is terrible and needs to be fixed"*, and
again from the Deeprun Tram arch. Reproduced, bisected and fixed; the full
account is in `docs/WATER_PORT.md`, round three.
Short version. His own pin is an outdoor street and a still there shows almost
nothing (0.0098 normalised between the lanes, and no yaw over a 360-degree pan
burst beats 0.0106) — but both his SCREENSHOTS are from INSIDE a building, and
an interior scene reproduces it at once: `volfog-inn` moves 0.0846 (mean +16.7)
between the lanes where `stormwind-cathedral-interior` moves 0.0010. His levers
name the term: `mist` off takes it to 0.0036 while `glow`, the underwater family
and `grade` each change nothing.
`veil_apply` integrates its mist layer (scale height 2.5 yd) from the eye to
whatever each pixel shows, with the layer's base at the elected mirror plane —
and asks nothing about where the eye is standing. Its own log says the rest:
veil true: mirror plane 57.7 strength 1.00, in water false, mist true
The pond outside the Goldshire inn elects a plane at y 57.7 and the inn's floor
is at 58.8, so the eye sits 1.1 yards into a layer that is 64% dense there, and
the pond's dawn mist is laid over the hearth, the posts and the stairs.
So the veil takes the gate the sun shafts already take, for the same reason and
from the same resource (`volumetric_light`: *"Interiors (the camera in a WMO
room) take no shafts"*). Measured after: `volfog-inn` 0.0034 against the
reference lane, where switching `mist` off entirely gives 0.0036 — the veil was
all of it, and none of his mist was lost elsewhere.
Also here: the four capture scenarios at the two world positions grom sent
(`grom-fog`, `grom-fog-back`, `grom-rain`, `grom-rain-back`), which is how the
outdoor half above was measured.
Known gap, written down rather than fixed: a camera inside a tunnel looking out
over open water now gets no veil on the water either. The honest shape is a veil
that starts at the room's boundary rather than at the eye; turning it off indoors
is strictly better than painting the room, and is what ships today.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
2ca1861325 |
docs: four more theories on the washed frames, killed by measurement
None of the four console lines killed it, and ffxGlow is not a CVar this fork registers. grom also reports rain stopping short of the ground in much of Stormwind, "like it hits an invisible roof". Four theories raised and killed, recorded so nobody re-runs them: a stray liquid plane over the city (a census found 764 surfaces, highest at y 144.5, none wider than 33 yd), liquid colliders catching raindrops (liquid rides its own collision layer and the precip oracle cannot see it), the shared light buffer's rows shifting under the merge (the struct is row-for-row what it was), and his 2560x1440 breaking a half-res pass (clean at that size). One thing did reproduce and is worth its own fix: with rain forced on, the stylised lane loses the rain's impact rings on the water that the reference lane draws, RMSE 877 over water-canal-dusk. Ends with the 131-character line that would put a scenario exactly where grom is standing when the wash appears. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
71e425fa6b |
docs: what was tried on the washed frames, and the one question left
grom's two clips show a pale flat wash over the scene with Improved Water on. Nine configurations were tried, including his own config.toml through WOW_CAPTURE_CVARS, and every still came out clean or byte-identical between the two water styles. It is not a property of a settled frame: both clips show it while moving and after a zone change, which points at something accumulated. Two candidates checked by reading and cleared: the water's glow accumulator is sized for 8K and cleared per view before the water draws, and FroxelView is stamped on WorldCamera alone while his mirror camera is explicitly never one. What is left is four console lines for grom to try with the wash on screen, which name the lane in one step. Recorded rather than guessed at further. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
ea87dd61e3 |
docs: the water port works, and the lead I recorded was wrong
Records the end state (gates green, the look measured at RMSE 7492 reference vs Improved Water, the CVar path proved end to end) and, more usefully, that the render-layer lead in the section below it was wrong: the world camera carries layers [0, 30, 31] and the surfaces carry [31]. Measuring it is what proved it. The real fault was a block of ours left at the top of liquid.wgsl's fragment entry, returning before any of his code ran. Worth the note at the top of the file: when two forks both rewrote one shader entry point, the auto-merge keeps both preludes and the first `return` wins in silence. Four gaps listed, the fog model being the one that will actually show. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
3538b11dc8 |
docs: where the water port stands, and the one lead worth chasing first
The graft builds, runs and lints clean, and the water is invisible. Measured rather than guessed: 25 liquid surfaces spawn in the Stormwind canal scene and none is on screen, no material lookup fails, 5 frame sets load and the stylised pipeline compiles with its own defs. So the fault is not the shader, not the materials and not the classifier. The lead is the render layer: his seam puts every surface on layer 31 and stamps the world camera from an `Added<WorldCamera>` query, which this fork may well miss. Also recorded: the ocean visible in `water-ocean` is the WDL horizon mesh, not liquid, which is why the style A/B over it is byte-identical and why the toggle looked dead when it is not. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
055fc7baeb |
docs: the water port's shape, and where the three deleted files route
grom, with the measurement in front of him: full swap, drop the extras. Our five water files go, so does water_fx/bob.rs (the swimmer bob, which read the vertex swell that goes with them) and the Water Quality dropdown; his Improved Water checkbox and its two CVars come in; the Ironforge magma palette and our shader hooks are re-applied onto his liquid.wgsl. Also records where the three delete/modify files land -- his cvars rows into our own REGISTERED table, and his PANE_LAYER_BASE rule into UNIT_PANE_LAYER_BASE, which matters: his seam claims render layers 30 and 31 and our booth ladder grows upward with nothing stopping it reaching them. Three of the 34 conflicts resolved (ours: .gitignore, README, the input-wall cover -- his side there is his own workflow and upstream drift past our squash base, not water). The other 31 are open; scripts/water-port-merge.sh rebuilds the mid-merge tree in a couple of minutes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
a733f41223 |
docs: measure the Improved Water port before starting it
grom wants deepthought84/benilla-watershader's water in place of ours. Both forks descend from samwhosung/benilla, so a real common ancestor exists even though our own history is one squash and git cannot see it; scripts/water-port-merge.sh stitches one together and runs the merge, so the numbers below are measured rather than estimated. His delta is 203 commits, 168 files, +46240/-742 against a current upstream. 98 of those files are brand new (28382 lines) and graft; 70 already existed (18600 changed lines) and 59 of those we have also changed. A real three-way merge leaves 34 conflicted files, 82 hunks, 3831 lines -- 134 of his 168 files merge clean. Five files are ours alone and are what "replace" removes: enhanced_water.wgsl, liquid/reflect.rs, liquid/scene_depth.rs, liquid/waves.rs, liquid/lod.rs. water_fx/ stays -- it is upstream's, which is why both forks changed it and why it carries the most conflicts. No code moved yet. The doc also lists the three things of ours the swap lands on: the swimmer bob, which reads our vertex swell; the Water Quality dropdown against his Improved Water checkbox; and our hook modules and the Ironforge magma palette, which have to be re-applied onto his 9707-line liquid.wgsl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
f080e95b8d |
map: the windowed art buried the continent and zone dropdowns
grom: "the map is not properly sized in the window and I can't see/click the dropdowns at the top". What was on screen was two small circles and a search box above the parchment -- the bottom edge of pfUI's title bar, everything under it covered by the map sheet. Measured on his own saved variables: WORLDMAP_WINDOWED is 1, so the realm runs WorldMapFrame_Minimize at VARIABLES_LOADED, which pins WorldMapPositioningGuide to the window's own corners (WorldMapFrame.lua:201-203). Both dropdowns anchor to that guide at TOP -80,-35, so windowed they sit 34 to 66 below the window's top -- which is why the same function hides them three lines later. pfUI puts them back (SkinDropDown) into a window it sizes to WorldMapButton plus 15x55 of chrome, eleven short of the 66 their anchor wants. 31 of their 32 points ended up under WorldMapDetailFrame, level 3 against their 1. The reference buries them too; what is ours is that our canvas fills the window an addon sized rather than leaving the art a 701x468 patch in the corner of it, which makes the burial total. So WindowedInset gives them the band when they are visible: the art starts four points below the lower dropdown. Hidden -- stock, and every chain without a map addon -- the inset stays 1.12's own 33 and the windowed map is pixel-identical. The cost is 35 points of art height in that window, so the fit zoom lands at 0.95 and the sheet is pillarboxed about 17 points a side. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
b49dd872fa |
docs: the map tooltip is 70% by a pfUI default, and pfUI does skin WorldMapTooltip
Corrects one sentence in the 73%-opaque-art section. pfUI skins `WorldMapTooltip` after
all -- `skins/blizzard/tooltips.lua` does it in a `HookScript("OnShow")` rather than in
the loop over the other four tooltips three lines above, which is what a load-time grep
for the frame name finds. So the stock tooltip background was behind a solid pfUI panel
and was never what grom was reading through. OpenMap is retired, so the backing added to
that port is moot either way.
What the bare map actually does, measured with his own saved config: `WorldMapTooltip` is
a child of `WorldMapFrame`, pfUI scales that frame to 0.70, and pfUI's own
`UpdateTooltipScale` only undoes it when `appearance.worldmap.tooltipsize > 0`. The
shipped default is `"0"`, labelled *World Map Scale*, meaning inherit -- so every tooltip
on the world map draws at 70% while every other tooltip draws at 100%. Setting it to
100% gives `SetScale(1.429)` and an effective 1.000.
Also recorded: SoftSpoken Places paints `SSZMapLoreTooltip` at the cursor over the map
whenever the pointer is on a subzone it has lore for, ending in "Click to read its
story". Sweeping 247 points across Dun Morogh it resolved on the first one. Upstream ties
it to the same saved flag as the lore panel and the click, under a checkbox that reads
*Show Map Lore Panel*; `Addon_Ports/softspoken_port/` splits it onto its own `mapHover`
setting, default off, with its own checkbox.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
a9acfbf420 |
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
|
||
|
|
50a229c202 |
The script log called 34 addon packaging bugs "problems" with this client
Every one of the 34 rows a play session reported was read back out of a capture, and not one was ours: 16 are BetterCharacterPanel anchoring to Blizzard_InspectUI frames that LoadOnDemand has not built yet, 7 are .toc and <Script file> lines naming files their own package does not ship, 10 are TitanPanel/InfoBar/Outfitter integrations in addons whose optional dependencies this player does not have, and 4 are CVars 1.12 never had, which `cvars`'s own test says to leave unregistered. The reference shows the player none of that. A missing parent, a missing relativeTo and an unknown inherits are severity-2 lines in FrameXML.log, and SetCVar on an unregistered name raises into the pcall every addon that writes one already wraps it in. Our window was a deliberate superset, which is right for a compatibility report and wrong for a line reading "34 problems": a TitanPanel plugin with no TitanPanel is not a fault here. So `error` (code raised) and `load` (an addon is not running) stay problems and stay listed, and `warn` -- a call accepted that did not do what it said -- becomes a note, counted beside them and listed behind a new Show notes checkbox. Nothing is dropped; the log still carries every one and the window is one click from all of them. Also closes a hunt: the 283 `SetOwner: unknown anchor 'BOTTOMRIGHT'` warnings that three static sweeps of every installed addon could not place are the realm's own FrameXML, Shop_ProcessEntries forcing items into the cache through a hidden scan tooltip. The seven load failures and the two layout defects from the same session are fixed in the player's own addon copies, mirrored to Addon_Ports; both are written up in docs/ADDON_COMPAT.md. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
4974326381 |
Bring the nampower, UnitXP_SP3 and ClassicAPI ports up to the installed builds
The three DLLs beside the client were updated; re-measured each against the
thing with evidence behind it rather than against a wiki.
nampower's own string pool is exact — `^NP_[A-Za-z0-9]+$` is the CVar list and
`Usage:` the arity of every function — and both came back identical to this
tree: 49/49 CVars, 63/63 functions. The gap was events. Four built:
BUFF_ADDED_OTHER / BUFF_REMOVED_OTHER ui_aura.rs, AuraEdge::OtherBuff
KEY_DOWN / KEY_UP bindings.rs, key_event_code
The buff pair was argued against here before, on the cost of diffing the
helpful half of every streamed unit for events nothing registered. Answered
rather than overruled: both are gated on `has_listener`, which is the test
upstream now recommends over its own deprecated NP_Enable*Events CVars. The key
pair uses the client's own code table, which `chord::key_code` already held with
a comment saying it was the fact these events were blocked on.
UnitXP_SP3 is packed, so its contract comes from the wiki plus the call sites in
the installed addons — and those call sites exposed a live bug. Every
`UnitXP("target", …)` queued a SelectionRequest, but the mod selects INSIDE the
call and SuperCleveRoidMacros steps the cycle and reads `"target"` back on the
next line, fifty times in one Lua frame. Queued, every pass read the same unit
and the scan reported one enemy however many were there — truthy return, no
error. The pick now resolves in-call from a pool the app publishes
(`feed_unitxp_scan`), exactly the bargain TargetByName already strikes. All nine
target modes are built where four were; `behindThreshold` is real (the reach
feed publishes the angle now, not a verdict); `timer` rides the one timer queue;
`notify` reaches winit's user-attention request; `version`,
`performanceProfile` and a non-raising `debug` answer.
ClassicAPI carries its whole Lua addon gzipped in `.rsrc` — 173 files, a length
header then the bodies, keyed by the paths in its string pool — so the spec for
the installed build is on disk. `scripts/dll-surface.py --unpack` writes it out.
Read against a LIVE VM (not `UiScript::new()`, which is engine-only), the gap is
898 globals, and almost all of it is either a locale string or a subsystem this
client models differently. What is between those is now in
`compat/classic_api/shared_xml.rs`: the pure Blizzard_SharedXML helpers, in
1.12's grammar, from the shipped addon's own source. `tostringall` stays absent
on `tests/stdlib.rs`'s own rule.
The native half is mostly widget methods; intersected with what the corpus
actually calls it is four, of which three are built (`SetResizeBounds` over
1.12's own resize pair, `GetNormalFontObject` — which needed 1.12's missing
`GetTextFontObject` implemented first — and a bare `IsEquippedItem`) and
`SetMotionScriptsWhileDisabled` deliberately is not, because both its call sites
guard on it and the behaviour behind it does not exist here.
Also measured and recorded: every addon-load warning in grom's own logs is a
third-party optional dependency he has not installed or an addon's own packaging
bug, not a defect on this side. docs/ADDON_COMPAT.md has the table.
Ambiguity ceiling 4,956 -> 4,966, measured: nine for `feed_unitxp_scan` against
transform writers on non-networked lanes, one for the attention drain against a
system that also reads WinitWindows and neither writes.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
b39651d9b4 |
A <BarTexture name=…> never reached _G, which broke crafting in ATSW2
`apply_statusbar` read `file=` and `<Color>` off `<BarTexture>` and passed them to `SetStatusBarTexture`, which builds the fill region itself. Every other attribute of the element went unread, `name=` among them. In the reference `<BarTexture>` is a Texture element and StatusBar's LoadXML (`0x782ef0`) builds the fill through the shared texture ctor, which registers the name like any other region's. A dropped `name=` is invisible until Lua reaches for the global, and then it raises inside the addon. ATSW2's `SPELLCAST_START` arm shows the progress bar, tints it per ore through `ATSWProgressBarTexture` — the raise — and only then calls `ATSW_StartProcessing`. So every craft left the bar shown and running with nothing able to stop it: `Processing.Active` stayed false, `SPELLCAST_INTERRUPTED` was never registered, `ATSW_StopProcessing` and `ATSW_CompleteTask` were never reached, and the guard `not Processing.Active` let the next craft re-raise. grom's log has seven in a row and his screenshot has the stuck bar. Named now, both ways, as Button state textures already were: the region registry for `GetName()` and `_G` for `getglobal` and a sibling's `relativeTo`. A named `<BarTexture>` with no paint still builds its region. Four of the five in this install declare a name (`ATSWProgressBarTexture`, `ATSWRankFrameBar`, two QuickHeal bars); no stock FrameXML status bar names its fill, so the base interface neither gains a global nor collides with one. Also in the skin: the reagent names under the icons take the same black-to-white `SetTextColor` wrapper as the tools line. They measured 1/1/1 in a capture and read as correct, but a capture has no item links and `ATSW_ShowRecipe` paints the title black only inside `if RLink then` — in play it runs. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
14a1a7179e |
A tooltip Lua sized itself kept being re-sized out from under it
benilla auto-sizes a GameTooltip in a pre-pass of every resolve, because that is where the text measures are. The reference re-sizes one only from its own content verbs and from Show: 0x530640 has six call sites (0x52ad59, 0x52b029, 0x52e5ae, 0x52f869, 0x530615, 0x530aad), all inside the tooltip code and none in the frame pump. So a SetWidth/SetHeight written after that call stands, and ours was overwritten on the next frame. ATSW2's recipe tooltip is the case grom hit: it Show()s, then sizes the plate from its own line count and a column width floored at 350. Losing that width collapsed the backdrop to the one blank line it pushes through our AddLine and dropped every right column, anchored RIGHT -10 to the plate, on top of its left column -- "Required reagents:" and "Bags Bank Alts" sharing a pixel column in his screenshot. TooltipState::authored_size: set by SetWidth/SetHeight through the existing note_authored_size, cleared by any content change, by SetMinimumWidth/SetPadding, and by Show. layout_tooltips skips a plate that carries it. Clearing it on Show matters: pfUI and pfQuest both SetWidth the shared GameTooltip *before* Show, where the reference overwrites it -- honouring those would pin every later hover at 180 or 100 px. Two tests pin both halves. Measured, driving ATSW2's own OnEnter: plate 350.0 x 81.7, the two columns 232 px apart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
9860c092de |
ATSW2: readable task rows, and the background painting comes off
grom: "make it inherit pfui font colors or something because that is super hard to read, for the right panel obviously, I still want recipes colored by difficulty" / "let's remove the background art on the left, looks goofy." The task rows are the right panel's only difficulty-coloured text, and the ramp is dark orange through red — fine on the left list over its own dark backing, unreadable on the stripped right half with the world through it. pfUI's font, outlined, white; the recipe list keeps the ramp. Both the write and the restore needed answering: ATSW_UpdateTasks repaints every row on each bag, craft and task change, and the template's OnLeave puts back this.R/G/B, which that same function sets. ATSWBackground is hidden rather than blanked, because the addon re-sets its texture on every open and every profession switch; Hide survives both. Nothing anchors to it or to its shadow. The skin lives in benilla-config/, which is gitignored; this is its record. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
8bc3db634b |
ATSW2's skin: the gaps a probe found, and the queue that is a task list
grom: "Tho skin hasn't applied like it did the same for atsw1 and its missing all of the queue buttons." The queue buttons are not missing — version 2 replaced the queue with a task list and its bottom bar is four buttons upstream too. Driven offline the task rows, counts and delete buttons all appear. The skin gaps were real. Instead of comparing by eye, walk every visible region under ATSWFrame and report any texture still pointing at stock art: pfUI's StripTextures nils each region, so a leftover was either never visited or re-set afterwards. 15 before, 10 after. The ones that went: the task list's SECOND scrollframe, which exists only to hold the trainer scrollbar art and showed as two outlined boxes floating over the world, and the Attributes checkbox, the one stock box among pfUI's. Two more read off version 1's skin beside this one: the rank bar (a 637x17 slab once the frame art is stripped — version 1 resizes it to a 12 px pfUI bar, so this does too) and the task delete buttons, which inherit UIPanelCloseButton and want SkinCloseButton, not SkinButton — the latter leaves an empty box where the addon draws a red X. The skin itself lives in benilla-config/, which is gitignored; this is the record of it, with the probe that found the gaps. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
6bcf937986 |
A second profession's opener updated the book instead of reopening it
grom: "nothing happens when I click another tab." Advanced Trade Skill Window's side tabs cast the other profession's opener and rebuild on TRADE_SKILL_SHOW alone — its update arm is `UpdateCount = UpdateCount + 1` and nothing else — so the book swapped underneath it and nothing redrew. The open is `0x4fc910` and it carries three rules we had one of: * one event per *open*, not per change: a changed line while a book is up is a fresh TRADE_SKILL_SHOW (`0x4fc99f`), never an update. The update is a change *within* one open book — a filter, a fold, an item count. * the opener is the window's toggle (`0x4fc918`): casting the profession already open closes it, which is what ATSW2's `this.Name ~= <current>` guard exists to avoid tripping. * an empty rebuilt list signals nothing (`0x4fc997`, the count at `0xbde04c`): the line is open and no window appears. Written over the list about to be pushed rather than as the literal count read, because ours can be a frame late where the reference builds its list inside the open call — a book that arrives empty and fills next frame still fires exactly one show, on the frame it fills. `0x4f6000` is the Craft window's same function, keyed on the craft type at `0xbdcfb8` and with no empty guard; both of its rules land in `feed_craft` and `open_trade_skill` too, since CRAFT_SHOW is the arm ATSW2 takes for Enchanting. The filter reset on a line change was already right — `set_trade_skill` keys it on `trade_skill_last_line`, the `0xbde064` above. Verified end to end offline with $WOW_CAPTURE_SPELLS seating a real book: Blacksmithing open with 24 recipes, `ATSWFrameTab3:Click()`, and the window comes back title=Cooking line=Cooking rows=9 with the check moved to tab 3. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
167d4721e7 |
Write down the 2026-11-05 session: five defects, and the map cost that measured clean
One section per report, each with the measurement that settled it and the mechanism underneath — the saved-variables nil poison and the harness gap beside it, the pfUI skin that was applied as far as its own line 63, the arrow left outside the canvas, the stratum a reparent took away, the fade, the two verbs our own port already called, and the sound cut that was a frame late. The last section is the one with nothing in it: grom reported the map as laggy and it measures 2.90 ms a frame panning with no addons, 5.43 at 2560x1440 with his, 0 glyph-sheet resets, and his own log's eleven hitches are all at world entry. The numbers are written down so the next report can be read against them, and so is what would still explain it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
e15a415da9 |
securecallfunction, because modern libraries upvalue it at load time
The stitched map never said which zone you were looking at — from about zoom 3.5 to 10
there is no zone name anywhere on screen, which is the band a player pans around in.
Wiring up a port file that draws them took the addon corpus's "survived a UI probe" column
from 86/87 to 85/87, and the failure was not in the new file:
OpenMapClassic/Libs/CallbackHandler-1.0/CallbackHandler-1.0.lua:19:
attempt to call upvalue `securecallfunction' (a nil value)
Ace3's modern `CallbackHandler` takes `local securecallfunction = securecallfunction` on
its line 10 and calls it for **every handler of every event**, so the first callback any
addon shipping a post-2022 Ace fires raises. Nothing had fired one before, because
OpenMap's whole callback surface was dead for an unrelated reason until tonight.
1.12 has no taint, so there is nothing here to make secure — but the half of the contract
that is not about taint is the half library code leans on: it is a **protected** call, so
one raising handler does not stop the registry walking the rest. `securecallfunction`,
`securecall` (which also takes a global's name), `issecure` and `forceinsecure` go beside
`seterrorhandler` in `stdlib.rs`, with failures reaching `geterrorhandler()` exactly as the
reference's own secure dispatch does, so an addon bug still lands in the log rather than
being swallowed. Back to 86/87.
`the_rest_of_the_bare_globals` asserted these ABSENT, on the reasoning that the restricted
-execution system has no 1.12 counterpart and nothing here implements it. That was half
right; the half it missed is above, so the assertion is inverted with the measurement
written beside it — the same shape `hooksecurefunc` already has one line below.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
65520a4a58 |
The map's darkening was a grid of grey boxes, and a 12 px string was emptying the glyph sheet
grom, after a night's play: "make the map look a lot nicer, it would be really nice if the darkened areas were not just massive squares on the map, and should fit into the actual edges of the terrian if that makes sense, also I shouldn't see this dark squares on the fully zoomed out world map, also make it so the world map can't move out of the frame." **The darkening.** OpenMapClassic washed a never-visited zone through its land mask, and those masks are Voronoi-dilated by construction — "so that the forty of them tile each continent with no gap" — so for any zone that is not on a coast the mask *is* the rectangle. Measured at zoom 2.5 over the Eastern Kingdoms: a grid of flat grey boxes. Nothing could have been softened into a fix; it was the wrong mask. It was also drawn at full strength from every zoom, and at the whole-world view the zones' own art is not drawn at all, so the only thing on the world map was the fog's rectangles. A land mask for every map now comes from the client's own data. `bake_zone_land` unions a map's `WorldMapOverlay` alpha — the pieces the game reveals as you explore, cut to the shape of the ground — into one 256×256 mask; `overlay-land-index.py` writes the list the addon reads, because Lua cannot test for a file and a *missing* mask is dropped by the engine so the whole rectangle draws, which is the defect itself. 53 maps have overlays (the 40 stock zones, Hyjal, Alterac Valley and 11 of the realm's own); 76 have none and keep a wide feather. `clip-zone-masks-to-coast.py` then takes the open sea off the dilated sheet masks — the same dilation was painting parchment out on the ocean — by closing the continent's own land by 500 yards (above the 466-yard inradius of the widest enclosed gap, so seams fill and the sea does not) and keeping a 300-yard coastal margin. Twelve landlocked zones lose 0.0%; Darkshore loses 53%, all of it water. `tga.rs` gains the writer that needed, `script::uimap` and `ui_world_map::build_catalog` become public so an example can reach the uiMapID graph, and `WorldMapUiData` grows the accessor so the VM push and an outside caller cannot build different lists. **The glyph sheet.** His four-minute session reset it 27 times, each dropping every cached glyph. The producer is Atlas-CFM's cursor coordinate readout: a 12 px FontString on `WorldMapDetailFrame`, rewritten every frame, and the canvas zooms by scaling that frame — 140 device px a digit at a canvas scale of 6.9. Nothing about the addon is wrong, so the fix is ours: `raster_ppem` rasterizes exactly up to 64 device px and snaps to a third-octave ladder above it, with `cell()` scaling the cached bitmap and metrics left keyed on the true ppem, so advances and `GetStringWidth` are bit-for-bit unchanged. Same 1,200-frame reproduction: 3 resets and 6,623 rasters before, **0 and 1,424** after, with occupancy climbing once to 30% and stopping. A reset no longer clears the metric cache either, and `note_loud_cell` names the string that asked for an outsized cell. **Four more from the same log.** `SetOwner`'s unknown-anchor line named a forwarding wrapper rather than the addon that chose the anchor (two `debugstack` frames now); the nine reference anchor names are written down with the binary evidence, because `ANCHOR_TOP` is not one of them. The `CreateFrame(kind, …, inherits=<other kind>)` warning fired in both directions and only one drops content — `kind_subsumes` makes it directional, and `type_chain` moves out of `install` so `IsObjectType` and the loader read one table. A string that is not UTF-8 now names the Lua line that cut it, and both glyph warnings quote the string the character came out of. The three world-map CVars stay unregistered, with the argument written down beside `REGISTERED` so it is not re-litigated from the warning. **`World.zmp`.** The realm's `WorldMapArea` carries a third `areaID == 0` row, `694 World`, with an all-zero rect; the reference ships no bitmap for it and neither the graph nor the realm's own `Overrides.lua` lets it reach a player. The miss is a `debug!` line now, and the `warn!` is kept for a continent that *has* a rectangle and is missing its bitmap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
ee125519f1 |
The map arrow pointed the wrong way wherever the player was not
`UpdateWorldMapArrowFrames` (`0x4a8d10`, `0x4a7c20`) copies the camera-tracked object's facing to both arrows and leaves them alone when there is no such object. Ours tested "is there a player" with `WorldMapState::player_uv.is_some()` — a different question, because that field is `GetPlayerMapPosition` on the **displayed** map. A view sitting on a continent, on another zone, or on an addon's stitched world answers `None` on every frame, so the arrow kept the heading it had when the window last sat on the player's own zone: the right place, pointing the wrong way. `player_facing_seen` is the real test, set the first time the feed pushes a facing — the feed runs only where there is a player, and the facing it pushes is unconditional. The existing test asserted the old behaviour on its second leg; that leg now asserts the heading following off the displayed map, and a new test holds the other half (before any feed, the login and loading screens, the arrow is left alone). `scripts/soften-zone-masks.py` is for the OpenMapClassic port rather than the client: its per-zone land masks are binary, so every map sheet ends on a crisp line against a differently-lit neighbour. It writes a softened copy — dilate, blur, times a rect ramp that reaches zero inside the sheet's own torn parchment border, which is the half that is not obvious. `docs/ADDON_COMPAT.md` writes up this round's five: the flashing arrow (two writers on one frame, measured `h h S h h S`), why correcting after 1.12 rather than suppressing it cost a full UI conversion every frame, pfQuest's pins being both behind the map and only ever for one zone, the softened masks, and the cities. Gates: fmt 0, clippy 0, benilla-ui 2121 pass / 0 fail, benilla-app 4233 pass / 12 fail (the recorded data-golden baseline). Corpus 86/87 load clean, 86/87 survive a session start and a UI probe, 0 raised on hover, click or drag — unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
841fd1a81f |
Two map verbs disagreed about where the player was
`UnitState::position` was fed from a streamed object's `Transform` read raw, and a
`Transform` is Bevy's frame (`wow_to_bevy` is `(-y, z, -x)`): the two map axes arrived
swapped and negated and the third was the player's height. Three compat verbs answer
from that field — SuperWoW's `UnitPosition`, UnitXP_SP3's `getUnitPosition`, and
`C_Map.GetPlayerMapPosition` — while 1.12's own `GetPlayerMapPosition` reads the
per-frame world-map feed, which converts. So the client sat in a state no real client
can be in: the 1.12 verb placing the arrow and the modern verb saying the player was on
no map at all.
Against a zone's few-thousand-yard rect the height lands outside 0..1, so the modern
verb answered nil — which is OpenMapClassic's "You aren't on the world map here" and
`Map/StockArrow.lua` declining to place anything, leaving 1.12's wrong placement
standing. Against a continent's sixteen thousand it lands inside, and answers a
plausible wrong fraction. Both of grom's reports, one cause. Distance was the one read
this never broke, the transform being rigid.
Measured in a capture with the player at Northshire: `UnitPosition` now answers
(-8980, -160, 110), `C_Map.GetPlayerMapPosition(1415)` and the 1.12 verb agree to seven
places, Elwynn answers a fraction and Dun Morogh correctly answers nil; and with the
view centred on the player's own world point the arrow lands on the container's centre
to a third of a pixel.
A capture could not have shown any of this — its synthetic player carried no position,
so every reader answered nil there. `demo_unit_feed` now publishes the capture's own
parking spot, at the cost of one ambiguous pair written up at the ceiling.
Also here, from the same play test:
* A canvas zoom now tells the two 1.12 frames their scale moved, with their own scale,
which is the only way a 1.12 map addon can hear it. pfQuest counter-scales its pins
inside that hook; the canvas scales its child, so the hook never fired and the pins
kept the size they were built at. Measured 0 -> 24 `pfMap.ResizeNodes` over one
glide from 8x to 30x.
* `CLASSIC_API_VERSION` says 1.18.0, at grom's ask. The DLL beside the real client
reads 1.15.0.0 and nothing in his folder gates above pfUI's 11400 nag threshold, so
the trade is the one that constant has always named and the floor is unchanged.
The addon-side half is in `Addon_Ports/openmap_port`: `unexploredMode` defaults to
`reveal`, with the one-shot migration this addon already uses for the kind. A 1.12 zone
sheet is blank parchment and every scrap of terrain is an overlay the client hands out
only where explored, while a city sheet has no overlays at all — which is the whole of
"it will only paint the actual map art for cities".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
d5beabab42 |
The map's mouse surface shrank with the zoom, and four things fell through the hole
The canvas gave the map no mouse surface of its own, on the reasoning that 1.12's
`WorldMapButton` covers the same rect and sits above it. The button is pinned INSIDE the canvas
child, so it scales with it: measured at the world view with OpenMapClassic driving, at grom's
own 2560x1440, a 23x16 px button inside a 701x468 px window. Every press on the other 99%
reached the next frame that takes the mouse — `WorldMapFrame`, which pfUI makes a
`RegisterForDrag("LeftButton")` handle for the whole window. So dragging the map moved the
window, and a click never reached `ProcessCanvasClickHandlers`, which is the only way a map pin
is ever dropped.
The container takes the mouse and the wheel now and drives the same controller. The frame level
mattered as much: the hit sweep probes one `(strata, level)` plane in link order and a parent is
linked before its children (`0x764aa0`), so a container tied with `WorldMapFrame` still lost
every press to its own parent. It is re-seated to `map:GetFrameLevel() + 1` on every show,
because the window is level 0 while the canvas builds and level 1 once `ShowUIPanel` has raised
it.
With the click arriving, a pin still stored nothing: `Map/Pins.lua:57` trims the note with
`strtrim`, which was deliberately absent. `script/stdlib.rs` still refuses it on the 1.12
surface, for the reason written there; this build claims ClassicAPI, whose client does have it,
so it lands in `compat/classic_api/string_trim.rs` with both spellings. Eight bare call sites in
the corpus, one of them in pfUI's search box on every keystroke.
`SCALE_RATE` 10 -> 24. A 0.7 s half-life was tolerable only while a wheel notch could not
accumulate; now that they add up the glide is what you feel ("scrolls past where I stop
scrolling").
`BenillaMapTextures(uiMapID)` is `GetExploredMapTextures` without the explored filter. Reveal
mode draws unexplored overlays out of a retail FileDataID table this client resolves none of —
captured with the fog default and with reveal, the same view came back pixel-identical, 0 by
`compare -metric AE`. The overlays were never missing, only their names: `Map/RevealFromClient.lua`
in `Addon_Ports/openmap_port` rebuilds the table from this, with paths.
Also in the port, not here: the arrow is placed through OpenMap's own coordinates, because
1.12 measures `GetPlayerMapPosition` against Blizzard's world and continent art and the stitched
map is a different projection.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
c92f078c39 |
A wheel notch stepped from the easing scale, so notches did not accumulate
`ZoomIn`/`ZoomOut` walked the zoom ladder from `GetCanvasScale()` — the live value the smooth zoom is still easing — so the notch after the first re-read a scale that had barely moved, found the same rung still under it, and re-set the target it already had. The ease closes about 63% of the gap a second, which is slow enough that a player never gets a second rung. Driven at grom's own 2560x1440 with his addons loaded, over OpenMapClassic's 27-step ladder (0.0330 .. 2.3734): four wheel-down notches at the map's centre all set target 0.9030 — one rung — and two more at the far right set nothing. That is his "it will only scroll very little out … and will only very little scroll in period". `GetZoomStepScale()` steps from `targetScale` instead, so notches accumulate and either end of a long ladder is reachable. The same measurement rules out the position dependence he saw: the wheel's target over the centre and 19 px from the right edge is `WorldMapButton` both times, because `wheel_target` walks past frames that take no wheel the way `0x7664f0` does. `docs/ADDON_COMPAT.md` also records the two addon-side causes of "I can't see any buttons but I can use them", both fixed in `Addon_Ports/` rather than here: OpenMapClassic's rail tabs are never dressed because `mod.FaceSideTab` ships in a file the release omits, and the OpenMap tab's own icon is absent from this patch chain. And pfQuest's minimap icons now fade with the minimap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
a32963b3f8 |
Find the frame that was eating every map click, and the realm LibStub behind it
A real pointer at the middle of the world map reached nothing: a hook on
`WorldMapButton`'s `OnMouseDown` counted 0 hits from a real click although the
button was shown, mouse-enabled and held the point. A Lua walk of every visible
mouse-enabled frame containing that point answered with exactly one frame, and
it was an unnamed 700x500 `FULLSCREEN_DIALOG` child of `UIParent` — AceGUI's
default container, carrying the title "SoftSpoken Stories" and no content. The
blank login popup grom has reported twice was parked over the centre of the
screen swallowing the map's clicks. One frame, both defects.
`hit_test_name` had been printing `<nothing>` there all along: it answers `None`
for an unnamed frame. Ask what holds the point, not what it is called.
The window is left shown because `AceGUI:Create` shows a container on acquire
and `self.frame:Hide()` is the last line of a `pcall`ed build step, so anything
raising in between leaves it up. The recorded reason was
`AceGUI-3.0.lua:282: attempt to perform arithmetic on local 'argc'`, and the
cause is the realm's own
`Interface\FrameXML\AIO\Dep_LibWindow-1.1\LibStub.lua`, loaded by its
`FrameXML.toc:162` before any addon, which hardcodes `minor = 2` where upstream
parses it — 1.12's Lua 5.0 has no `strmatch`. Every library therefore registers
as minor 2 and the first copy loaded keeps it, while `RegisterWidgetType` still
upgrades individual widgets by their real version: an Ace3v core
(`Fire(self,name,argc,…)`) hosting modern widgets (`Fire(self,name,…)`), whose
first payload value lands in `argc`.
Client side, one real divergence came out of it: `string.split` answered a table,
because BigWigs' `Plugins/SpellRequests.lua` defines `function string:split` and
never calls it, while Ace3's `AceConfigDialog-3.0` and `TreeGroup` both spell
`("\001"):split(uniquevalue)`. `string.split` is now `strsplit` — same function,
same argument order — in the beyond-1.12 string family, with a test pinning the
Ace3 call shape and the arity.
Also: `Step::Drag` moved the cursor eight times without advancing a frame, and a
canvas pans from its `OnUpdate`. It now ticks per move, and the same drag walks
0.5000,0.5000 -> 0.7582,0.3073 -> 0.5080,0.5161 — exactly what driving
`OnMouseDown` by hand produced. The frame between the moves is the gesture.
Four worth recording as dead ends, each of which looked like a client bug: a
verbatim replica of the LibStub file registers correctly; `strfind(34,"(%d+)")`
coerces the number and captures `34` as the reference does; the live function's
`getfenv` is `_G` with every global correct; and a known-good `NewLibrary`
installed on the live table stores the right value. `debug` is absent here, so
the way to ask a Lua function where it came from is to make it raise.
Addon-side edits (gitignored live copies, mirrored to Addon_Ports): the `Fire`
arity shim in both Ace3v AceGUI copies, `issecretvalue` guarded in
OpenMapClassic's vendored Heading widget the way its own Core.lua guards it, and
BigWigs' unused `string:split` removed.
Verified: SoftSpoken `initializationErrors` 0 and `shown=0` at login, nothing
over the map, `--drag` pans it, all three registered options tables open clean
(MasterTradeSkills 4 labels, OpenMapClassic 83, SpokenQuests 6), tabs switch
pages with no raise, session errors 0. fmt and clippy clean; benilla-ui 2119
pass; benilla-app 12 failures, the recorded pre-existing data-golden baseline.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
b7a8e1c406 |
ui: a Slider answers SetEnabled, because OpenMap's options tab called it unguarded
Rendering a panel is not the same as it working. OpenMap's options menu opens 500x500 with 104 frames and 49 placed labels, and its Look tab died on click: Map/Panel.lua:1084, attempt to call method SetEnabled (a nil value) -- so a third of its options was unreachable. SetEnabled is already composed here for Button, CheckButton, EditBox and LootButton, and was deliberately withheld from a Slider, because addons duck-type on the enabled family to tell a control from a bar. What they duck-type on is the TRIO (if widget.IsEnabled then), that is what tests/slider.rs pins, and the trio is still absent; SetEnabled is beyond 1.12 by construction, so a Slider answering it invents no 1.12 behaviour. OpenMapClassic calls it unguarded on every options row with a condition, which is what settles it. On a Slider it writes the mouse -- input refused, art not greyed, which 1.12 has no slot for; the caller dims it itself on the next line. The argument goes through GetBoolOrDefault, not Lua truthiness, because DoiteAuras writes SetEnabled(ok and 1 or 0) and 0 is truthy in Lua. Also records a count that was wrong until checked: grep says 39 calls across five addons, but AdvancedTradeSkillWindow2 and SoftSpoken define their own SetEnabled and SoftSpoken's slider row is fully guarded. The real unguarded widget calls are OpenMap's three and DoiteAuras' two -- and SoftSpoken's blank settings page is NOT this, and is still open. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
c3a0315985 |
map: the three capitals with no traced outline drew nothing; feather them
OpenMapClassic declines to stitch a city with no MapCityMask and names the three it means -- Stormwind, Ironforge, Darnassus -- so zooming into the Alliance capitals showed the zone's painting of them and never their own map. The stated worry is a hard box. PlaceBase takes the outline and the edge feather in the same argument, and the city branch passes MapCityMask\<id> as the feather, so a city with no outline can go through the slot its outline would have used. A feather is the answer to a hard box. Captured at zoom 30: Stormwind's districts, harbour and Valley of Heroes, blended into Elwynn. The trace was attempted first, and this is the record so nobody re-runs it: unlike the zones' land masks there are three shipped city masks to fit against, so it is a fitting problem. Local-contrast detail, threshold, close at quarter scale, largest blob, holes filled, dilate, blur, swept over the three parameters -- best IoU 0.58, and it swallows the sheet's own torn frame, because a city sheet is busy out to its border where Orgrimmar's is blank parchment. A 0.58 hull shipped as a trace would be a guess in an artist's clothes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
758665d4d8 |
map: a 1.12 zone sheet is opaque, so the feather belongs on
What was left after the stitch's geometry was hard rectangles at the zone zooms. Bisected with OpenMapClassic's own /omc layer switches: baked and gaps draw nothing on this build, zoneart draws them all. A zone's sheet is masked to its land only for ids in ZoneMaskIDs, the contiguous Classic range 1411-1452. On this realm that leaves thirty-four without one -- every city, the dungeons, and every zone the realm added of its own. Cities already decline to draw unmasked; zones do not, and Gilneas puts its whole sheet, painted sea and torn parchment border, over Silverpine. PlaceBase takes a feather for exactly this and the zone walk passed nil behind `(not era) and FEATHER`. Sound on Era, where a C_Map zone texture carries a transparent surround; not true here, where the realm's sheets are alpha 255 across every texel. The port passes it. PlaceBase prefers the land mask where there is one, so the forty masked zones are untouched -- 0.57% of pixels moved in a Dun Morogh view. scripts/make-map-feather.py generates the three masks as 256x256 uncompressed TGA. A feather is a smoothstep ramp, which upstream's own NOTICE calls generated rather than cut from art. A per-zone LAND mask was deliberately not attempted: on this art sea and land do not separate by colour (a sea patch reads R-B +28, a land patch +34) and the realm's zone bitmap is 13x13 cells for its largest zone. Either would be a guess dressed as art. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
7cb45769c5 |
docs: the OpenMap zoom ladder end to end, and carrying the tile mode to an existing save
A default only reaches a fresh install, and grom's OpenMapClassic.lua pins minimapTileMode = "fdid" -- the one value that cannot work on a client with no FileDataIDs. OpenMapClassic has a migration hook for exactly this, so the port edit adds a one-shot flip beside its own six, flagged so an explicit /omc tilemode fdid afterwards is respected. Measured through the real wheel handler: thirty clicks out reaches 0.0330, the ladder's floor and the whole world; forty back in reaches 2.3734, its ceiling; 27 steps between. Captured at zoom 1, 3, 9 and 40 with his own settings and no setup chunk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
2281179874 |
capture: a setup slot, for the moment before the scene ages
$WOW_CAPTURE_SETUP runs a chunk at the first aging frame -- early enough to change a setting or create a frame and still have the scene settle around it. $WOW_CAPTURE_EVAL keeps the shutter, where a read and the pixels it explains come from the same frame; anything created there is a frame too late to be in the image. The deep-zoom map shot needed it. OpenMapClassic resolves minimap tiles by FileDataID by default and this client has none, so its own status reports "loaded YES=0 NO=8"; it already carries a path mode for exactly that, asking for textures\Minimap\<folder>\map<col>_<row>, of which this install ships 10,884. Switching to it raised, because S.ResetMinimapTiles reads lodPieces/lodPool sixteen lines before they are declared and so bound them to nil globals -- an upstream bug, and the only two callers are the two switches that reach the mode. Port edits mirrored to Addon_Ports/openmap_port/: the declaration moved above its use, and the tile mode defaulted to the one that can work here. Captured at zoom 40: the minimap's terrain draws through the map. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
62c019e744 |
blp: a short colour map is a sliver tile, not a corrupt file
Seven of the client's own Interface\WorldMap textures never decoded. They are the last column or row of an exploration overlay -- a few pixels wide, a handful of colours -- so they store a SHORT BGRA palette and end: RuinsOfThaurissan4.blp is 404 bytes in all, LakeEverstill6.blp is 276. The parser sliced the nominal 256 entries and refused anything shorter. A refused decode is a quad that draws nothing, and until this week a texture that drew nothing also said nothing, so the symptom was a hole in a zone's revealed art -- indistinguishable from not having explored it. It surfaced only once the stitched world's geometry was right and the render reached those tiles. The palette is now as much of it as the file has, and an index past the end is transparent black, which is what an unused index means. every_world_map_texture_in_the_chain_decodes walks the folder: 3553 textures, 7 failures before, 0 after, and it fails on the old decoder. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
6c01be926d |
map: a map's rect on another map describes art, not a click box
C_Map.GetMapRectOnMap answered, for a continent on the world sheet, the world-level CLICK box -- continent_sheet_rect over the WorldMapContinent tile bounds, which trace the landmass. That is the reference's own 0x4a5d00 and right for what 1.12 uses it for; the two boxes are disjoint so a world-level click can tell the continents apart. It is the wrong answer for art. A continent's map is a 1002x668 sheet with the land inside it and ocean around, so its art rect is 792x528 px on the world where its click box is 300x564. Every 1.12 WorldMapArea rect is 3:2, because every sheet is, and the click box is 0.355 of that. A zone's rect on its continent was already a yard un-lerp and correct, so the error entered once and multiplied through every zone: Dun Morogh landed 42x79 where its art is 1002x668. A stitched world built on that draws each zone a third as wide as it should be, which is why the canvas was grey between the zoom steps. map_proj::continent_art_rect carries the continent's own WorldMapArea rect through world_uv, the projection a position on it already takes. The view keeps both rects; the Era map graph publishes the art one. Measured after: EK, Kalimdor, Dun Morogh and Elwynn all 1.500. Rendered: the zone view stitches its neighbours in, zoom 3 is the continent with its zones, zoom 1 is the world sheet. The stock 1.12 map is unchanged. Three instruments came with it and are kept: $WOW_CAPTURE_EVAL, a chunk run in a capture's VM at the shutter; $WOW_CAPTURE_EXPLORED, so a stitched map's fog can be ruled out in one run; and a named warning for a UI texture that is not in the chain, which is what refuted the other hypothesis -- a grep said the addon's art was missing, a real render asked for zero missing files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
388db114fc |
docs: what OpenMapClassic is for, and why the canvas is grey between zoom steps
Two recordings made the target concrete: one continuous stitched world, wheel from Azeroth through continent, zone, city, dungeon floor, no mode change and no seams. Ours navigates correctly -- breadcrumb coordinates, lore panel, Find me, zone crossing -- and draws the continent step, and is grey everywhere between. Grey is "the tile files were never asked for": PlaceTiles stores tex.omFile instead of setting it, and ShowZone only queues it when inView[mapID] == true, which S.Cull writes from a view box built out of GetCurrentScrollX/Y, GetCanvasScale, the container's size and GetMapArtLayers(947). Four of our verbs and one constant art size decide whether a zone's art is ever loaded. Also records the third BetterCharacterPanel port edit: the stats column pinned to the panel's own backdrop, so the overflow is reached by scrolling inside the window instead of hanging over its edge -- window 359, content 699, needs 340, bar reaches 340. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
3e8c995e27 |
map: clamp the canvas onto whatever zoom ladder is in force
CreateZoomLevels is the extension point a canvas addon replaces wholesale, and OnLoad seeds a scale (1) before any addon has run. OpenMapClassic's ladder is 27 steps from 0.0125 to 0.899 -- its whole range below this file's own 1, because its stitched world is reached by zooming out past a single zone -- so the canvas sat above its own ceiling, and ZoomIn, which walks the ladder by value, found no step at all above it. ClampZoomToLevels is called from GetCanvasScale rather than from CreateZoomLevels, because CreateZoomLevels is exactly the function the addon will not have. Also records where OpenMapClassic stands, measured: 808 textures in the canvas subtree, 166 placed, spanning ~4600x3270 units of a 1002x668 canvas, with a ladder floor implying a world ~80000 units wide. The Era map tree under it answers correctly; GetMapArtLayers answering one constant for every map is the thread to pull next. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
07dd5ba7e1 |
capture: a shot taken of a player's config must not write it back
`$WOW_CAPTURE_SAVED` points the saved-variable READS at a real player's configuration so a capture reproduces their interface. The writes went to the same place, so taking the shot overwrote the configuration it was taken to study -- with a fixture character's state, since a capture logs in as nobody. Three of grom's files came back changed from one run, his pfUI chat scrollback among them. The shutdown tail returns before all four writes under $WOW_CAPTURE. Verified by md5 across a run: zero files touched, zero "wrote" lines. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
abb4dc1dd3 |
ui: a Slider's thumb had no rect, so no scrollbar could be skinned or seen
"Half of the info on the right is out of the frame" was a scroll frame with 699 of content in a 451 window, a scroll range of 228, and a scrollbar with nothing on it. The thumb's rect was only ever computed inside the draw pass and never published: the region sweep skips anchor-less regions and a thumb is anchor-less by construction, because its slider places it. That is invisible until something anchors to the thumb, and pfUI does -- SkinScrollbar clears the stock knob and hangs its own off the thumb's two corners, so every pfUI-skinned scrollbar in the interface was a bare track, which is a scroll frame that silently cannot be scrolled. The thumb is on the layout roster now, survives the unanchored prune, and carries its slider's (vertical, fraction) in its node hash -- without the last one a named touch moved no hash and seeded nothing, which WOW_LAYOUT_VERIFY caught as an incremental pass disagreeing with a full derivation. The draw pass keeps computing its own rect: extract takes &self and cannot resolve, so a SetValue with no resolve behind it still has to draw where the value now is. map: the window has two layouts and the canvas only knew the maximized one Pressing maximize left the art a 1002x668 patch in a full-screen window and minimize could not put it back, because the container was anchored once at build to WorldMapPositioningGuide TOP -502,-69 -- 1.12's maximized anchor against a fixed 1024x768 guide. Read as insets instead (15/4/33/20 windowed, 10/12/69/31 maximized) the same numbers reproduce 1.12 at 1.12's sizes and fit any other window, which is also the left-edge bleed: -502 against a guide stretched to pfUI's 1017-wide window starts the art 6.5 px in, not 10. The zoom ladder's floor follows: it was the constant 1, true only while the container was the art's own size, and is the measured fit now. A view outside a rebuilt ladder is pulled onto its nearest end, since ZoomIn and ZoomOut walk it by value -- OpenMapClassic's whole ladder sits below ours. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
41512a04ef |
map: 1002x668 is a contract, and the canvas counted the windowed scale twice
Every 1.12 map addon places its art in absolute pixels against WorldMapDetailFrame's 1002x668 — pfUI's reveal does it one SetPoint per tile — so that size is a contract, not a size. 1.12's windowed mode puts WORLDMAP_WINDOWED_SCALE (0.7) on the frame and on WorldMapButton; under the canvas the container already carries the window, so that scale was a second authority on the same rect and, with the frame SetAllPoints to the canvas, came back as unit size: 1002 / 0.7 = 1431. Everything absolute inside it landed at 0.700 of where it belonged, which is grom's screenshot drawing Coldridge Valley twice, and the mouse surface kept its XML 1002x668 beside 1431 units of art, so clicks read 70% of the map. SeatOnCanvas makes the canvas the only authority: both frames to scale 1, the frame filling the canvas, the button given the art's rect — explicitly sized, because pfUI reads WorldMapButton:GetSize() to size the whole map window. Re-run after Minimize/Maximize and on every canvas resize. Rescaling the children this file knows the names of could never have fixed it; the children it does not know the names of are the point. saved-variables.lua now goes through saved_home, so a capture with $WOW_CAPTURE_SAVED opens the player's windowed map — the only mode the bug exists in, and the reason neither harness had ever seen it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
d11b9ac3ae |
map: the art was scaled by the canvas while living in the detail frame
Grom's report was "textures are not where they should be", and still wrong with OpenMapClassic switched off — which is what made it ours rather than the addon's. His own session log had both states 1.4 s apart, same map, same frame: detail 1431x954 with tiles 366x366 (correct, 1431/1002 = 1.428), then the same frame with tiles back at the XML's raw 256x256. `ScaleDetailArt` was being handed the CANVAS's size by `OnCanvasSizeChanged`, on the assumption that the detail frame is `SetAllPoints` to the canvas. 1.12's own windowed mode breaks that: `WorldMapFrame_Minimize` puts `WORLDMAP_WINDOWED_SCALE` (0.7) on `WorldMapDetailFrame` and `WorldMapButton` and insets the frame to TOPLEFT 15,-33, so the same screen area is a different number of units in each — his trace reads `detail 1431x954 scale 0.56` inside `WorldMapCanvas 1002x668 scale 0.80`. Scaled by the canvas's number every tile came out 1.43x too small: the art covered 1024x768 of a 1431x954 frame, the rest bare parchment, and the exploration overlays scaled to match landed nowhere near their terrain. The detail frame's own size is the authority, since the tiles are its children. `GetLeft()` first to settle the layout, because that size is derived and an unforced read answers the size from before the last change; the caller's w,h stay as the fallback for what cannot be measured. Reproduced and controlled in the harness with the windowed scale applied as the stock code applies it: tile 362 where 362 is wanted, against the control's 256 — the number his log recorded. Two earlier attempts are written up in docs/ADDON_COMPAT.md as dead ends, because both looked right: caching the canvas size prefers the wrong quantity, and converting it by the effective-scale ratio gives 366 against a true 362, since Minimize also insets the frame. `$WOW_CAPTURE_SAVED` now reaches the enable-state file too, so a capture reproduces which addons the player has turned off — without which "I turned it off and it is still wrong" cannot be answered offline at all. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
5e2f1f3ac8 |
ui: SetClipsChildren, the verb an addon asked for by name and was refused in silence
Era's map canvas is built on one verb this client did not have. Its `ScrollContainer` is a plain Frame with `SetClipsChildren(true)`, and everything below it is cropped to that rect. `assets/ui/MapCanvas.lua` substituted a real ScrollFrame, because this client crops a ScrollFrame's scroll child and every descendant of it — which covers the canvas and nothing parented to the container beside it. On Era that distinction does not exist, so an addon has no reason to respect it. OpenMapClassic parents its pin layer exactly there and asks for the verb by name, behind an `if` (`Map/Stitch.lua:1070`), so the absence was not a raise. Measured on grom's own config with the map open: the container sits at L=1151 R=2153 and its seventeen direct children span L=-765 R=2153 — about 1900 units off the left edge, cropped by nothing. Implemented as a frame flag with `SetClipsChildren`/`DoesClipChildren` and the `clipChildren` XML attribute; `effective_clip` now intersects the rect of every ancestor that sets it alongside the ScrollFrame rule, so the two nest and compose. An ancestor rather than the frame itself: the verb clips a frame's children, and stock art overhangs its own frame on purpose. `MapCanvas.lua` sets it on the container too, so a layer parked there is cropped whether or not its addon thought to ask. Not claimed as the whole of grom's map report: the screenshot behind it has not been reproduced offline, because a capture's view is not the view his session was in. This is the part that was provably drawing uncropped. Also here, both from the same play-test: - `C_Spell.CancelSpellByID` answered with `SpellStopCasting` only. pfUI's buff bar cancels with it on a right click where 1.12's calls `CancelPlayerBuff`, so with nothing in flight the click did nothing and said nothing — grom had never been able to right-click a buff off on this client. It now finds the id among the player's own auras and queues it on the same CMSG_CANCEL_AURA path, behind the same `cancel_authorized` gate, and falls through to the cast only when there is no such aura. - `$WOW_CAPTURE_SAVED=<Realm>-<Character>` points a capture at a player's own saved variables. A capture is hermetic by design, but with addons and no saved variables it renders an interface nobody is configured to be looking at — which is why the world map came out correct in a capture while being wrong on his screen. It doubles as the identity because a capture never logs in and `pfUI_config` is SavedVariablesPerCharacter. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
c9756de381 |
docs: the anchor pfUI's save dropped, the seed that was only a comment, and the fade
Four sections, three of them for changes that cannot be committed because `benilla-config/` is gitignored (it carries the credentials). Those live on disk and in the mirrors: Addon_Ports/pfui_port/pfUI/api/api.lua AnchorOffsets + SaveMovable/LoadMovable Addon_Ports/pfui_port/pfUI/modules/autofade.lua new module Addon_Ports/pfui_port/pfUI/modules/gui.lua the "Auto Fade" page Addon_Ports/pfui_port/pfUI/init/modules.xml the include Addon_Ports/openmap_port/OpenMapClassic/Map/Window.lua SetClampedToScreen either way - The map opened with nothing on screen because pfUI's `SaveMovable` kept three of `GetPoint`'s five values, dropping `relativeTo` and `relativePoint`, and `LoadMovable` replayed the offsets against the frame's own point. OpenMapClassic anchors the map TOPLEFT to UIParent's BOTTOMLEFT, so the save moved it a full screen height — measured at 1800 units on a 1440 screen at scale 0.8. It was latent while OpenMap's own hook kept putting the window back, and surfaced the moment that stood down. Fixed by measuring the offsets for the point being named instead of copying a pair in another convention, which also fixes an anchor to a third frame; plus an off-screen check on restore, so a save already written heals once instead of every login. - The player's first buff of each session (the commit before this one). - pfUI: a config section added to `LoadConfig` does not survive a character that already has a config, because `pfUI_config` is SavedVariablesPerCharacter and the saved table replaces the one LoadConfig filled. `C` is not a stable table either — `GetEnvironment()` rebinds it per module. - The fade grom actually wanted out of Cinematic, which he has now removed: pfUI's own `EnableAutohide`, which only the action bars and four info panels could reach, pointed at the minimap, the buff rows, the unit frames and Location Plus, every switch defaulting off and each with a row in pfUI's GUI. Buffs and debuffs are separate switches on purpose, and the one real cost is stated rather than hidden: with the unit frame switch on, the fade and pfUI's out-of-range dimming both write one alpha. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
92237dfb54 |
docs: the first-open pickers, the pfUI window handover, and why not to bake the map in
Records both fixes with the measurements behind them, the rejected first attempt and what made its test worthless, and the answer to "should this just be built into the client": no — the addon's art already draws correctly here, it is ~26k lines and 67 MB of assets, and Questie and TomTom reach it through `OpenMapAPI`, which exists only because it is an addon. The pfUI handover itself is a change to the port's `Map/Window.lua` and `Map/Compat.lua`, which cannot be committed here: `benilla-config/` is gitignored because it carries the login credentials, so the addon tree lives only on disk and in the `Addon_Ports/openmap_port` mirror, verified identical. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
d1382f8e01 |
docs: the nameplate row count is twenty-one, not seventeen
Ten friendly names and five hostile ones are the per-kind switches, plus `nameplateMaxDistance` — sixteen live — and five more registered and inert. The previous commit's message carries the same wrong total; this is the correction, since the doc is the thing anyone reads later. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
ae929e8a81 |
map: the art trace reads the tiles the repaint put up, not the ones it replaced
The line fired in the frame the selection changed, and the repaint that fills the tiles is Lua's own off a queued `WORLD_MAP_UPDATE` — so it read the map that had just left. That is how "the map shows Dun Morogh and the tiles are Azeroth's" got into a log with nothing wrong behind it. It now waits three frames, restarting the wait if the selection moves again. docs/ADDON_COMPAT.md records this round: the cursor position no verb ever wrote, the per-kind nameplate CVars a nil answer greys out, the capture lever above with its two limits measured rather than assumed, and one open item — the map's Continent and Zone pickers, blank on the first open with the addons loaded and fine without them, with the candidate engine fix rejected because the test for it passed either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
5308b5a200 |
docs: a hosted page draws under its host, and art that does not resolve is invisible
Two defects from one play report, both in the ports, both measured rather than guessed: `SetParent` re-levels the frame it is called on and nothing under it (0x76ab65 pushes propagate = 0, and a script SetFrameLevel does the same), so Cinematic's pages -- built parentless, widgets at levels 1..20 -- landed 25 levels under the window its toplevel raise had lifted to the top of DIALOG. The page drew under the window's own backdrop and the backdrop took every click. The screenshot named it before any code was read: the page's white read (103,103,103) and its gold (103,84,0), the same 0.404 factor as a nameplate behind the window, where a fade on top would have composited to (110,116,121). Multiply, not blend, means underneath. And the map's resize grips are shown, mouse-enabled at level 9002, and take the cursor -- but wear a 2.0 texture this chain has not got, and a Lua caller cannot find that out: SetTexture returns nothing and GetTexture echoes the path it was handed either way. Twenty pixels of nothing in the map's corners. The client's own `texture miss:` warning is the only instrument that names one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
eef0d06852 |
docs: the map, answered by its own trace
What `WOW_MAP_TRACE` printed, what it meant, and the two smaller faults that came out with it — including the harness note that `--tick` does not call `resolve`, so `OnSizeChanged` never fires under it and its silence there is not evidence of an engine gap. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
4ea1d97faf |
docs: the realm's LibStub decides by load order, not version
`Interface\FrameXML\AIO\Dep_LibWindow-1.1\LibStub.lua` loads before every addon and its `NewLibrary` has the minor-version parse replaced by a literal `minor = 2`. So every library registers as 2, every later copy is refused, and the FIRST copy of a library to load wins whatever its version says. Measured: all 22 registered libraries carry minor 2. What it did to grom: three AceGUI-3.0 copies, a 1.12 backport's core wins by load order, but widget files never go through LibStub (RegisterWidgetType compares widget versions itself), so the install runs the retail tree's newer widgets against the older core. `AceGUI.tooltip` is created by the retail core and neither backport, so TreeGroup's button tooltips raised on every category row the mouse touched. The `OptionsListButtonTemplate.toggle` gap from the previous round is the same shape one layer out. Also records WOW_MAP_TRACE and why it exists. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
08689e7d14 |
docs: the plate token, Cinematic's own settings window, and two more fade rows
Second round of grom's 2026-11-02 session. The nameplate half is engine work and is written up beside the rest of the compat record; the settings window and the two support rows are in the ported addons, which live outside this tree, so this is where they are recorded. The general note worth keeping out of the Cinematic-specific part: an addon that registers its options with a host and ships no fallback is one stomped global away from having no settings UI at all, and no engine can stop an addon reassigning a global. Shipping `InterfaceOptions_*` is still worth doing for the addons that have no window of their own — it cannot be the whole answer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
607ce45788 |
docs: six things an Era addon assumes a 1.12 template or verb carries
grom's 2026-11-02 session, written down where the deviations live: the AceGUI TreeGroup toggle that 1.12's OptionsListButtonTemplate has no child for (and why the newest of three installed AceGUI copies is the one every addon's config window gets), the two templates that reach their children through the frame's own name so the frame must be named, GetDebugName, the override-binding family, pfUI's two panel bars, and the rule that a minimap-button drawer only collects the minimap's own children. Five of the six are fixes in the two ported addons, which live outside this tree; this section is where they are recorded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |
||
|
|
09aca72c46 |
ui: hooksecurefunc over a widget method has to read through the metatable
The table form read the field with `raw_get`, on the reasoning that a field
reached only through a metatable is not that table's to replace. A widget
carries none of its own methods — they live on the shared class table its
metatable's `__index` names (`object.rs`'s `frame_meta_for`) — so
`hooksecurefunc(frame, "SetAlpha", cb)` found nothing there and armed nothing,
silently. Measured: **0 fires across 2 `SetAlpha` calls**, while the plain-table
and global forms fired correctly.
That is not a corner of the form, it is what the form is for. 29 files in grom's
corpus hook a widget method, and the one that shows: `Cinematic/Frames.lua:459`
hooks each pfUI frame's own `SetAlpha` so that Cinematic can **follow** pfUI's
out-of-combat fade rather than fight it ("their frames have SetAlpha followed
straight away by ours, taking what it set as the new base"). With the hook
unarmed the two wrote a different alpha to the same frame on alternating frames
— which is grom's "still flashing my unitframe when cinematic is on".
The reference reads `table[name]` plainly and assigns the wrapper back, which
*shadows* the class method for that one object and leaves its siblings alone.
This now does the same: read through `__index`, write raw onto the host, which
is the same thing a plain assignment would do since frame metatables carry no
`__newindex`. A test pins both halves — the chain fires on the hooked frame, and
a sibling of the same kind is untouched, with nothing written to the class table.
Corpus roster byte-identical (87 ok / 1 fail) with all 29 files' hooks armed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
|
||
|
|
18c8cdd762 |
docs: the MOD blend, SetEnabled, and the label that rode into the canvas
One new section and two amended rows, the three faults behind what grom recorded on 2026-11-02: the fullscreen tint that drew white, the chat traceback from picking a profile, and the hovered-zone label scaling with the map's zoom. Each carries what was measured rather than what was argued, and the general shape — answering half of an era's call shapes is what makes an addon's `pcall` feature test lie. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn |