2525// `UnitFlag bit 0x2000` ("focus glow" rendering hint, introduced in
2626// TBC for the default focus frame). Vanilla addons (pfUI) render
2727// their own focus indicator and don't need the hint.
28+ //
29+ // Auto-clear on despawn: modern WoW fires `PLAYER_FOCUS_CHANGED`
30+ // when the focused unit leaves the client's object table (out of
31+ // rendering range, despawn) and does NOT auto-refocus when they
32+ // come back. We mirror that by probing `ObjectByGUID(g_focusGUID)`
33+ // every world tick — when it returns null, we `Set(0)` which fires
34+ // the event. Cost is one hash-table lookup per tick while focus is
35+ // set; zero when no focus is active.
2836
2937#include " Focus.h"
3038
3139#include " Game.h"
3240#include " Offsets.h"
3341#include " event/Custom.h"
42+ #include " tick/WorldTick.h"
3443
3544#include < cstdint>
3645
@@ -44,6 +53,9 @@ const Event::Custom::AutoReserve _reserve{kEventName};
4453uint64_t g_focusGUID = 0 ;
4554
4655using TokenToGUID_t = uint64_t (__fastcall *)(const char *token);
56+ using ResolveByGUID_t = void *(__fastcall *)(int type, const char *debugName,
57+ uint32_t guidLo, uint32_t guidHi,
58+ int priority);
4759
4860uint64_t ResolveTokenGUID (const char *token) {
4961 if (token == nullptr || *token == ' \0 ' )
@@ -53,6 +65,23 @@ uint64_t ResolveTokenGUID(const char *token) {
5365 return fn (token);
5466}
5567
68+ // Per-tick despawn watcher. Probes the engine's object table for
69+ // `g_focusGUID`; when it disappears (out of range, fully despawned),
70+ // clear focus and fire PLAYER_FOCUS_CHANGED — matches modern's
71+ // "leaves render distance → focus drops" behavior. Won't refocus
72+ // when the unit comes back, also matching modern.
73+ void OnWorldTick () {
74+ if (g_focusGUID == 0 )
75+ return ;
76+ auto resolve = reinterpret_cast <ResolveByGUID_t>(
77+ static_cast <uintptr_t >(Offsets::FUN_OBJECT_RESOLVE_BY_GUID));
78+ if (resolve (Offsets::OBJ_TYPE_UNIT, " Focus" ,
79+ static_cast <uint32_t >(g_focusGUID),
80+ static_cast <uint32_t >(g_focusGUID >> 32 ),
81+ 0x172 ) == nullptr )
82+ Set (0 );
83+ }
84+
5685// `FocusUnit(unit)` — sets focus to whatever GUID `unit` currently
5786// resolves to. Modern signature: `FocusUnit(unit)` with the token
5887// argument. Modern also allows `FocusUnit()` (no arg) to mean
@@ -80,6 +109,7 @@ void RegisterLuaFunctions() {
80109}
81110
82111const Game::ModuleAutoRegister _autoreg{&RegisterLuaFunctions};
112+ const Tick::WorldTick::AutoSubscribe _tickSub{&OnWorldTick};
83113
84114} // namespace
85115
0 commit comments