From 85cd750a4e6d8771fabc4c1c336ac06b97601c23 Mon Sep 17 00:00:00 2001 From: grom Date: Fri, 16 Oct 2026 19:49:41 -0600 Subject: [PATCH] vplates: no plate for a critter, by the client's own no-TAB flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit grom asked for the name and level over every rabbit to be gone. pfUI cannot do it: its option is "Hide Healthbar On Critters", and the branch it takes is `level:Show() name:Show() health:Hide()` — bar hidden, name and level kept, a label floating in empty space. The reason it shows up here and not on his real client is our own compat. That pfUI branch needs `UnitCreatureTypeID` on a plate GUID, which needs the `NAME_PLATE_UNIT_ADDED` → `C_NamePlate` → `UnitGUID` chain; stock 1.12 has none of it, so the check returns nil there and a critter gets an ordinary plate nobody notices. We answer all three, so the feature wakes up and strips the bar. So the gate goes where the plate is decided. It keys on `CreatureType.dbc`'s **no-TAB flag** rather than on the id 8: that bit is the client's own mark for a creature the player does not mean to interact with, it is set on Critter alone in the 1.12 data, and `target::scan`'s TAB gate already reads it — one notion of "critter", from the data, with two consumers. An unresolved type keeps its plate, as the scan treats an out-of-range index as targetable. A deviation, and `nameplateShowCritters` says so: the reference's ladder (`0x60f600`) has no creature-type test at all. Off by default, because the floating name is the thing nobody wants; `SetCVar("nameplateShowCritters", "1")` restores vanilla. Removing the plate leaves nothing behind — the overhead-name path gates a creature on `UnitNameNPC`, which is "0". Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn --- crates/benilla-app/src/cvars.rs | 13 +++++++ crates/benilla-app/src/target/mod.rs | 2 +- crates/benilla-app/src/target/scan.rs | 9 +++++ .../src/ui_script/options_tests.rs | 1 + crates/benilla-app/src/vplates.rs | 36 +++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) diff --git a/crates/benilla-app/src/cvars.rs b/crates/benilla-app/src/cvars.rs index 93793bdc..9ac29096 100644 --- a/crates/benilla-app/src/cvars.rs +++ b/crates/benilla-app/src/cvars.rs @@ -1217,6 +1217,19 @@ pub(crate) const REGISTERED: &[Registered] = &[ "UnitXP_SP3 addon-compat, LIVE: hide a nameplate whose unit is out of the camera's line \ of sight — see crate::vplates::VPlateMode::occlusion", ), + // Ours, and a deviation by existing: the reference plates a critter like any other + // attackable unit (`0x60f600`'s ladder has no creature-type test). It exists because our own + // compat woke a dormant pfUI feature — "Hide Healthbar On Critters" (default on) needs + // `UnitCreatureTypeID` on a plate GUID, which needs a nameplate-token chain stock 1.12 has + // not, so on a real client it never fires and here it does, leaving a bare name and level + // floating over every rabbit. Off by default because that floating name is the thing nobody + // wants; "1" restores the reference's own behaviour. + ours( + "nameplateShowCritters", + "0", + "ours, DEVIATION: plate creatures carrying CreatureType.dbc's no-TAB flag (Critter alone \ + in 1.12 data) — see crate::vplates::VPlateMode::critters", + ), // ── nampower v2's event-class switches (upstream `EVENTS.md`, `brues-code/nampower`) ─────── // // Five rows whose upstream meaning is one sentence: "make nampower emit this class of custom diff --git a/crates/benilla-app/src/target/mod.rs b/crates/benilla-app/src/target/mod.rs index ed452e35..b23ca496 100644 --- a/crates/benilla-app/src/target/mod.rs +++ b/crates/benilla-app/src/target/mod.rs @@ -39,7 +39,7 @@ mod relations; mod reticle; // `pub(crate)` for the hover inspector too: its faction catalog feeds `go_highlightable`. pub(crate) mod ring; -mod scan; +pub(crate) mod scan; /// `Faction.dbc` and our reputation table as one [`SystemParam`], since every reaction function /// takes both; `factions` is `Option` so a UI-only harness runs without client data. diff --git a/crates/benilla-app/src/target/scan.rs b/crates/benilla-app/src/target/scan.rs index 53e1ee1c..66b07e50 100644 --- a/crates/benilla-app/src/target/scan.rs +++ b/crates/benilla-app/src/target/scan.rs @@ -140,6 +140,15 @@ fn combat_with_me(store: &ObjectStore, me: Option) -> bool { #[derive(Resource)] pub(crate) struct CreatureTypes(CreatureTypeFlags); +impl CreatureTypes { + /// Whether this type carries the no-TAB bit — Critter alone on the 1.12 data. Read by the + /// TAB scan and by the nameplate gate (`vplates::VPlateMode::critters`), which is why it is + /// a method rather than the field being poked. + pub(crate) fn no_tab_target(&self, creature_type: u32) -> bool { + self.0.no_tab_target(creature_type) + } +} + pub(super) fn load_creature_types(mut commands: Commands, world_assets: Option>) { let Some(world_assets) = world_assets else { return; diff --git a/crates/benilla-app/src/ui_script/options_tests.rs b/crates/benilla-app/src/ui_script/options_tests.rs index 95e97bdf..bf60460d 100644 --- a/crates/benilla-app/src/ui_script/options_tests.rs +++ b/crates/benilla-app/src/ui_script/options_tests.rs @@ -1429,6 +1429,7 @@ fn a_world_entry_leaves_the_saved_nameplate_setting_alone() { friends: true, range_sq: 0.0, occlusion: false, + critters: false, }; let saved = |s: &mut UiScript| { s.set_cvar_host(crate::vplates::CVAR_ENEMIES, "1"); diff --git a/crates/benilla-app/src/vplates.rs b/crates/benilla-app/src/vplates.rs index 5fc26c1d..7040baf2 100644 --- a/crates/benilla-app/src/vplates.rs +++ b/crates/benilla-app/src/vplates.rs @@ -42,6 +42,18 @@ pub(crate) mod border; pub(crate) struct VPlateMode { pub(crate) enemies: bool, pub(crate) friends: bool, + /// `nameplateShowCritters`: whether a critter gets a plate at all. + /// + /// **A deviation, and off by default.** The reference plates a critter like any other + /// attackable unit — `0x60f600`'s ladder has no creature-type test — and pfUI's own + /// "Hide Healthbar On Critters" (default on) then hides the bar and *keeps* the level and + /// name, which is a name floating over every rabbit. Its branch needs `UnitCreatureTypeID` + /// on a plate GUID, which needs the `NAME_PLATE_UNIT_ADDED` → `C_NamePlate` → `UnitGUID` + /// chain this client has and a stock 1.12 one does not — so the feature is dormant on the + /// real client and awake here, and the result is a floating name nobody asked for. grom did + /// ask for them gone; `SetCVar("nameplateShowCritters", "1")` puts the reference's behaviour + /// back. + pub(crate) critters: bool, /// SuperWoW's `NameplateRange` (`docs/ADDON_COMPAT.md`), squared: the reference's own /// hardcoded 20 yd made tunable, the same CVar `nameplates.rs`'s overhead-name cull reads. /// Derived-default `0.0` until the boot-time `CvarChanged` observer seeds it, the same @@ -67,6 +79,7 @@ impl Default for VPlateMode { Self { enemies: false, friends: false, + critters: false, range_sq: 0.0, // Must equal the `NameplateOcclusion` row's registered default; `the_occlusion_default_ // agrees_with_its_registered_cvar` is that check. @@ -353,6 +366,9 @@ struct PlateWorld<'w, 's> { window: Query<'w, 's, &'static Window, With>, // The pending ground-target cast, for the plate's hit-test veto (`0x7cba30`). targeting: Res<'w, crate::spell::SpellTargeting>, + /// `CreatureType.dbc`'s flags, for the critter gate; absent after a load failure, when + /// nothing is filtered. + creature_types: Option>, // ── The `nameplate` / `nameplatetarget` token feed's own inputs ── // // Every one is read-only, which is why none of them moves the ambiguity ceiling: Bevy counts @@ -531,6 +547,21 @@ fn drive_vplates( if store.is_some_and(|s| s.0.unit_flags() & (1 << 25) != 0) { continue; } + // The critter gate ([`VPlateMode::critters`]), keyed on `CreatureType.dbc`'s own no-TAB + // flag rather than on the id 8: that bit is the client's own mark for "not a unit the + // player means to interact with", it is set on Critter alone in the 1.12 data, and the + // TAB scan already gates on it (`target::scan`). An unresolved type keeps its plate, as + // the scan treats an out-of-range index as targetable. + if !mode.critters + && benilla_protocol::guid::is_creature_or_pet(guid.0) + && world + .creature_types + .as_deref() + .zip(benilla_protocol::guid::entry(guid.0).and_then(|e| names.creature_type(e))) + .is_some_and(|(types, ty)| types.no_tab_target(ty)) + { + continue; + } // `0x60f600`'s first test: health 0 or less (absent reads 0) has no plate. Feign death // keeps health, so it passes here. if store.is_none_or(|s| s.0.unit_health().unwrap_or(0) == 0) { @@ -800,6 +831,9 @@ fn drive_vplates( #[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)] pub(crate) struct VPlateSet; +/// `nameplateShowCritters`, this client's own: see [`VPlateMode::critters`]. +pub(crate) const CVAR_CRITTERS: &str = "nameplateShowCritters"; + /// V-key nameplates: the toggles and the per-frame gate and draw. pub(crate) struct VPlatesPlugin; @@ -813,6 +847,8 @@ pub(crate) fn on_cvar(ev: On, mut mode: ResMut