diff --git a/crates/benilla-app/src/ui_tooltip/mod.rs b/crates/benilla-app/src/ui_tooltip/mod.rs index 6d2f1f0b..8c476393 100644 --- a/crates/benilla-app/src/ui_tooltip/mod.rs +++ b/crates/benilla-app/src/ui_tooltip/mod.rs @@ -566,6 +566,22 @@ pub(crate) struct HoverMemo<'s> { last_lines: Local<'s, crate::ui_script::VmMemo>>, /// The hover probe's last trace line, so a stationary probe logs it once. trace: Local<'s, String>, + /// The last `(corpse guid, reason)` the corpse plate gave up on, so a hover that cannot draw + /// one says why **once** instead of every frame it is held. + corpse_miss: Local<'s, Option<(u64, &'static str)>>, +} + +/// Report a corpse plate this hover could not build, once per corpse and reason. +/// +/// Each of the three ways out below is a silent `return` on a path a player can see: hovering a +/// grave and getting nothing. "Mousing over player graves provides no tooltip" could be any of +/// them and the log said nothing at all, so the first thing to fix was the silence. +fn note_corpse_miss(last: &mut Option<(u64, &'static str)>, guid: u64, why: &'static str) { + if *last == Some((guid, why)) { + return; + } + *last = Some((guid, why)); + info!("corpse plate: {guid:#x} draws none — {why}"); } fn lines_view(s: &UnitState) -> UnitState { @@ -692,9 +708,17 @@ fn drive_mouseover_tooltip( } // A corpse carries `CORPSE_FIELD_OWNER`, not a name; the plate waits for the name query. let Some(owner) = store.0.corpse_owner() else { + note_corpse_miss(&mut memo.corpse_miss, guid, "CORPSE_FIELD_OWNER is empty"); return; }; let Some(name) = names.resolve(owner, &commands).map(str::to_string) else { + // Normally transient — the query is in flight and the next frame has it. Logged once + // per corpse all the same: if it never answers, this line is the whole diagnosis. + note_corpse_miss( + &mut memo.corpse_miss, + guid, + "the owner's name has not answered a CMSG_NAME_QUERY yet", + ); return; }; // `CORPSE_TOOLTIP`, the builder's key; no string, no plate. @@ -703,6 +727,11 @@ fn drive_mouseover_tooltip( "CORPSE_TOOLTIP", &[Arg::S(&name)], ) else { + note_corpse_miss( + &mut memo.corpse_miss, + guid, + "the CORPSE_TOOLTIP global is missing from this chain's FrameXML", + ); return; }; // No object id: a corpse is not a GameObject, it only borrows this plate's one gold line.