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