From 60bb2e3d6d5e09069e27d48950fca67f0c8742b0 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 16 Sep 2026 06:39:15 +0700 Subject: [PATCH] a nameplate takes a right-click, and four wrong verb rows come off - a right-click on a nameplate selects and interacts again: the plate registers the right button, as the reference's does (2233) - the verb-events table files each fire site against its function's verified extent, and four rows it had misattributed are gone (2257) Co-Authored-By: Claude Fable 5.1 --- .../src/ui_script/verb_event_gate.rs | 59 ++++++++----------- crates/benilla-ui/src/script/nameplate.rs | 15 +++++ .../benilla-ui/src/script/tests/nameplate.rs | 44 ++++++++++++++ 3 files changed, 83 insertions(+), 35 deletions(-) diff --git a/crates/benilla-app/src/ui_script/verb_event_gate.rs b/crates/benilla-app/src/ui_script/verb_event_gate.rs index 710edceb..d2d80518 100644 --- a/crates/benilla-app/src/ui_script/verb_event_gate.rs +++ b/crates/benilla-app/src/ui_script/verb_event_gate.rs @@ -57,11 +57,6 @@ struct Pair { /// written — the trigger is named so the next reader can check it in one grep. const ELSEWHERE: &[(&str, &str, &str)] = &[ // The sell slot's diff (`auction_sell_item` moved, or `sell_slot_dirty`). - ( - "CalculateAuctionDeposit", - "NEW_AUCTION_UPDATE", - "benilla-app/src/ui_auction/mod.rs", - ), ( "ClickAuctionSellItemButton", "NEW_AUCTION_UPDATE", @@ -146,12 +141,6 @@ const ELSEWHERE: &[(&str, &str, &str)] = &[ "UPDATE_BINDINGS", "benilla-app/src/bindings.rs", ), - // A `SessionRequest::StopCinematic`; the player stops, and the flip to not-playing fires. - ( - "StopCinematic", - "CINEMATIC_STOP", - "benilla-app/src/cinematic.rs", - ), // The toggle goes to the wire and the bar's pushed key changes on the reply — one round trip // later than the reference, which fires locally from the toggle. ( @@ -172,54 +161,54 @@ const GAP: &[(&str, &str, &str)] = &[ ( "CancelSkillUps", "SKILL_LINES_CHANGED", - "resets temp skill points over a table this model keeps empty (`skills.rs`), so nothing \ - moves and `ui_char.rs`'s feed has nothing to announce; the reference fires from \ - 0x4d3e3a regardless", + "the reference's verb (0x4d3e30) calls the temp-point reset and fires unconditionally at \ + 0x4d3e35; here the reset runs over a table this model keeps empty (`skills.rs`), so \ + nothing moves and `ui_char.rs`'s feed has nothing to announce — the stock SkillFrame \ + would only repaint under its own Close", ), ( "ClickTargetTradeButton", "TRADE_REPLACE_ENCHANT", - "the trade-window enchant leg (0592 P3) is not built; the verb is the money arm only", + "the reference runs the enchant clash check 0x496170 when a spell is on the cursor and \ + fires this for the replace dialog (wow-re staticpopup-dialog-bindings.md §3.1); casting \ + an enchant onto the partner's slot is not built here — `ui_trade.rs` only mirrors the \ + wire's enchant slot — so the verb is the money arm alone", ), ( "CloseTrade", "PLAYER_TRADE_MONEY", - "`ui_trade.rs` announces money only while the trade is open; the reference's close zeroes \ - the offer and fires from 0x4bfdce, under a frame that TRADE_CLOSED hides first", + "fired only when the trade did not complete (`[0xb71748] == 0`, wow-re \ + incoming-trade-request-law.md), zeroing the cancelled offer under a frame TRADE_CLOSED \ + hides; here `TradeSession::begin` resets the whole session at the next trade, so the \ + next window opens at zero either way", ), ( "CollapseCraftSkillLine", "CRAFT_UPDATE", - "a no-op here: the craft list is flat, the header law was never ported from TradeSkill \ - (0446, 0530's follow-up), so there is nothing to repaint", + "the reference commits through the 21-byte thunk 0x4f6be0 (the trainer's 0x4d8c90 shape); \ + a no-op here because the craft list is flat — the header law was never ported from \ + TradeSkill (0446, 0530's follow-up) — so there is nothing to repaint. Whether any 1.12 \ + craft list carries more than one group is not established: 0446 covers Enchanting, \ + Beast Training is unchecked", ), ( "ExpandCraftSkillLine", "CRAFT_UPDATE", - "the same no-op as CollapseCraftSkillLine", + "the same thunk and the same no-op as CollapseCraftSkillLine", ), ( "PetDismiss", "PET_DISMISS_START", - "no shipped file listens; the dismiss reaches the wire through the app's drain and the \ - pet frame follows the unit's departure", + "the worker 0x4bd6e0 sends the dismiss and fires this with a duration (`%d`, 10000 — \ + wow-re pet-action-bar-api.md §10.8); no shipped file listens, the dismiss reaches the \ + wire through the app's drain, and the pet frame follows the unit's departure", ), ( "SelectGossipOption", "GOSSIP_ENTER_CODE", - "the code-entry gossip option kind is not modelled (`reference_ui`'s UNPRODUCED row)", - ), - ( - "SetPetStablePaperdoll", - "PET_STABLE_UPDATE_PAPERDOLL", - "inert by design: the stable's model pane is an app-side booth that follows the selection \ - every frame (1676), so the VM has nothing to point", - ), - ( - "UpdateMapHighlight", - "CLOSE_WORLD_MAP", - "0x4a7620 fires it (site 0x4a7835, id 373) on a path the highlight verb reaches under a \ - condition nobody has read; unbuilt until carved", + "a coded option raises the code-entry popup in the reference (the worker 0x4e2320); here \ + coded options are greyed and unselectable (0081 v1, `reference_ui`'s UNPRODUCED row), and \ + vmangos's `gossip_menu_option` carries zero coded rows, so no NPC on this server reaches it", ), ]; diff --git a/crates/benilla-ui/src/script/nameplate.rs b/crates/benilla-ui/src/script/nameplate.rs index bf20dde7..78eba700 100644 --- a/crates/benilla-ui/src/script/nameplate.rs +++ b/crates/benilla-ui/src/script/nameplate.rs @@ -499,6 +499,21 @@ impl Plate { // app through [`UiScript::hovered_nameplate`] and [`UiScript::take_nameplate_clicks`]. // `EnableMouse`/`IsMouseEnabled` are in the corpus's own call set, and pfUI's vanilla // click-through block drives them. + // + // **And it takes BOTH buttons, on the UP edge**: `RegisterForClicks(0x500)` at `0x7cb637` + // (`0x779730` → `[this+0x330] = 0x500` = LeftButtonUp | RightButtonUp). The arena's Button + // default is `{"LeftButtonUp"}` alone, so without this a physical right-click on a plate + // fired no click at all — [`super::button::wants_click`] refused the release, the funnel + // never ran, and since 2233 took the camera off a press that lands on a plate there was no + // other path left: right-clicking a nameplate did nothing. The reference's own slot + // (`0x7cb910`) takes mask 1 → `0x4925d0` select and mask 4 → `0x492820` select+interact. + if let Some(KindState::Button(bs)) = model.arena.frame_mut(frame).map(|f| &mut f.kind_state) + { + bs.registered_clicks = ["LeftButtonUp", "RightButtonUp"] + .into_iter() + .map(str::to_string) + .collect(); + } // The blend modes are the ctor's own, one `0x7703f0` call per texture (wow-re // `nameplate-vkey.md` §8.1, byte-arbitrated): everything is BLEND(2) except the **glow, diff --git a/crates/benilla-ui/src/script/tests/nameplate.rs b/crates/benilla-ui/src/script/tests/nameplate.rs index 56b110be..5ff89c55 100644 --- a/crates/benilla-ui/src/script/tests/nameplate.rs +++ b/crates/benilla-ui/src/script/tests/nameplate.rs @@ -703,6 +703,50 @@ fn a_completed_click_on_a_plate_reaches_the_app() { ); } +/// **The right button too** — `RegisterForClicks(0x500)` is LeftButtonUp | **RightButtonUp** +/// (`0x7cb637` → `[this+0x330]`), and the reference's slot forks on it: mask 1 → `0x4925d0` +/// select, mask 4 → `0x492820` select **and interact** (decision 2233, wow-re +/// `ui/scratch/mouselook-mouseover-and-nameplate-click-law.md` §6.4). +/// +/// The regression this pins: a plate is created as a plain `Button`, whose default registered set +/// is `{"LeftButtonUp"}` alone, so the release was refused before the click funnel — and with 2233 +/// taking the camera off a press that lands on a plate, that left right-clicking a nameplate doing +/// nothing whatsoever. +#[test] +fn a_physical_right_click_on_a_plate_reaches_the_app() { + let mut s = vm(); + let wolf = plate("Wolf", 30.0, 40.0); + let key = wolf.key; + drive(&mut s, &[wolf]); + s.mouse_move(500.0, 384.0); + + s.mouse_button(500.0, 384.0, "RightButton", true); + assert!( + s.take_nameplate_clicks().is_empty(), + "the UP lane only: `0x500` has no ButtonDown bit" + ); + s.mouse_button(500.0, 384.0, "RightButton", false); + let clicks = s.take_nameplate_clicks(); + assert_eq!(clicks.len(), 1); + assert_eq!(clicks[0].key, key); + assert_eq!(clicks[0].button, "RightButton"); +} + +/// A button the plate never registered fires nothing — the mask is exactly `0x500`, not "any +/// button". +#[test] +fn a_middle_click_on_a_plate_fires_nothing() { + let mut s = vm(); + drive(&mut s, &[plate("Wolf", 30.0, 40.0)]); + s.mouse_move(500.0, 384.0); + s.mouse_button(500.0, 384.0, "MiddleButton", true); + s.mouse_button(500.0, 384.0, "MiddleButton", false); + assert!( + s.take_nameplate_clicks().is_empty(), + "`0x500` is Left|Right on the up edge and nothing else" + ); +} + /// pfUI's click-through calls `plate:Click("LeftButton")` (`nameplates.lua:1274`), and /// CustomNameplates and `_Nameplates` do the same. A scripted click has to select the unit like a /// physical one — in the reference both go through the button's one click slot, and here they go