# Data Flow Map: Outline and Overlay Detection System
This document maps how the DLL detects and gathers information for rendering outlines on corpses, targets, and raid-marked units.
## Overview
The system uses multiple data sources and hooks to identify which models should receive outline rendering:
1. **Object Manager Iteration** - Scans all visible game objects each frame
2. **WoW Function Calls** - Uses native game functions for GUID/object resolution
3. **Direct Memory Reads** - Reads game structures at known offsets
4. **Model Render Hooks** - Intercepts WoW's rendering pipeline to associate models with objects
---
## High-Level Data Flow
```mermaid
flowchart TB
subgraph FrameStart["Frame Start (RenderDeadOverlay)"]
Reset["ModelOutline_ResetFrame()"]
ClearTarget["ModelOutline_ClearTarget()"]
ClearRaid["ModelOutline_ClearRaidMarked() (throttled 100ms)"]
CacheRaid["wow_cache_raid_targets()"]
end
subgraph Detection["Object Detection Loop"]
ObjFirst["wow_object_first()"]
ObjNext["wow_object_next()"]
ObjType["wow_object_type()"]
subgraph PlayerCheck["Player Processing"]
IsDead["wow_unit_is_dead()"]
IsFriendly["wow_unit_is_friendly()"]
GetGUID["wow_object_guid()"]
GetModel["Read model ptr @ +0xD8, +0xDC"]
GetRaidMark["wow_get_raid_target_index()"]
end
subgraph CorpseCheck["Corpse Processing"]
IsSkeleton["wow_corpse_is_skeleton()"]
OwnerGUID["wow_corpse_owner_guid()"]
OwnerLookup["wow_get_object_by_guid()"]
GroupCheck["wow_is_guid_in_group()"]
end
subgraph TargetCheck["Target Processing"]
GetTarget["wow_get_target()"]
TargetType["wow_object_type()"]
IsEnemy["!wow_unit_is_friendly()"]
IsEnemyPet["wow_unit_is_enemy_pet()"]
end
subgraph UnitCheck["NPC/Unit Processing"]
UnitGUID["wow_object_guid()"]
UnitRaidMark["wow_get_raid_target_index()"]
end
end
subgraph Registration["Model Registration"]
AddDead["ModelOutline_AddDeadPlayer(GUID)"]
AddDeadModel["ModelOutline_AddDeadPlayerModel(ptr)"]
AddRaidMark["ModelOutline_AddRaidMarkedModel(ptr, index)"]
SetTarget["ModelOutline_SetTargetModel(ptr)"]
SetOwner["ModelOutline_SetModelOwner(ptr, obj)"]
end
subgraph RenderHooks["Render Hook Pipeline"]
ManageRender["CM2Model_ManageRenderListNode Hook"]
DrawBatchProj["DrawBatchProj Hook"]
DIPHook["DrawIndexedPrimitive Hook"]
end
subgraph Storage["Tracking Storage"]
GUIDSet["g_deadPlayerGUIDs (unordered_set)"]
ModelSet["g_deadPlayerModels (unordered_set)"]
RaidMap["g_raidMarkedModels (unordered_map)"]
OwnerMap["g_modelToOwner (unordered_map)"]
TargetVar["g_targetModel, g_targetModelAlt"]
FrameSet["g_frameDeadModels (per-frame)"]
end
FrameStart --> Detection
Detection --> Registration
Registration --> Storage
Storage --> RenderHooks
```
---
## Memory Address Sources
```mermaid
flowchart LR
subgraph GlobalPointers["Global Pointers (wow_1121.h)"]
ObjMgr["0x00B41414 - Object Manager Ptr"]
WorldFrame["0x00B4B2BC - World Frame Ptr"]
RaidArray["0x00B71368 - Raid Target Array (8 GUIDs)"]
RaidTex["0x00CE878C - Raid Target Texture Ptr"]
end
subgraph Functions["Native WoW Functions"]
WorldToScreen["0x00483EE0 - WorldToScreen"]
DDCtoNDC["0x0041ADE0 - DDC_to_NDC"]
UnitGUIDFunc["0x00515970 - UnitGUID('player','target')"]
GetObjByGUID["0x00464870 - GetObjectByGUID"]
UnitReaction["0x006061E0 - UnitReaction"]
GetUnitName["0x00609210 - CGUnit::GetUnitName"]
GetObjName["0x006264E0 - GetObjectName"]
end
subgraph HookTargets["Hook Target Addresses"]
RenderDraw["0x0070B360 - CM2SceneRenderDraw"]
ManageList["0x00710B90 - CM2Model_ManageRenderListNode"]
DrawBatch["0x0070CF70 - DrawBatch"]
DrawBatchP["0x0070CB30 - DrawBatchProj"]
NameplateVB["0x006C724A - Nameplate VB Hook"]
end
subgraph ObjectOffsets["Object Memory Offsets"]
ObjType2["Object+0x14 - Type"]
ObjGUID["Object+0x30 - GUID (8 bytes)"]
UnitDesc["Unit+0x110 - Descriptor Ptr"]
UnitMove["Unit+0x118 - Movement Ptr"]
ModelD8["Unit+0xD8 - Model Ptr (primary)"]
ModelDC["Unit+0xDC - Model Ptr (secondary)"]
Scale1["Unit+0x90 - Scale Factor 1"]
Scale2["Unit+0xBC - Scale Factor 2"]
end
subgraph DescriptorOffsets["Descriptor Field Offsets"]
HP["Descriptor+0x40 - Current HP"]
MaxHP["Descriptor+0x48 - Max HP"]
Flags["Descriptor+0x224 - Unit Flags"]
Summoned["Descriptor+0x18 - UNIT_FIELD_SUMMONEDBY"]
DuelArb["Descriptor+0x2D8 - PLAYER_DUEL_ARBITER"]
end
subgraph CorpseOffsets["Corpse Field Offsets"]
CorpseDesc["Corpse+0x8 - Descriptor Ptr"]
CorpseOwner["Desc+0x18 - Owner GUID"]
CorpsePosX["Desc+0x24 - Position X"]
CorpsePosY["Desc+0x28 - Position Y"]
CorpsePosZ["Desc+0x2C - Position Z"]
CorpseFlags["Desc+0x8C - Corpse Flags"]
end
```
---
## Detection Flow by Category
### Dead Player Detection
```mermaid
flowchart TB
Start["Object Iteration"] --> CheckType{"Object Type?"}
CheckType -->|"Type 4 (Player)"| CheckDead{"wow_unit_is_dead()"}
CheckType -->|"Type 7 (Corpse)"| ProcessCorpse
CheckType -->|Other| NextObj
CheckDead -->|Dead| CheckFriendly{"wow_unit_is_friendly()"}
CheckDead -->|Alive| RemoveTracking["Remove from tracking
(handles resurrection)"]
CheckFriendly -->|Friendly| ReadPlayerData["Read GUID @ +0x30
Read Model @ +0xD8, +0xDC"]
CheckFriendly -->|Enemy| NextObj["Next Object"]
ReadPlayerData --> RegisterDead["ModelOutline_AddDeadPlayer(GUID)
ModelOutline_AddDeadPlayerModel(ptr)
ModelOutline_SetModelOwner(ptr, obj)"]
subgraph CorpseFlow["Corpse Processing"]
ProcessCorpse --> CheckSkeleton{"wow_corpse_is_skeleton()?"}
CheckSkeleton -->|Yes| NextObj
CheckSkeleton -->|No| GetOwner["wow_corpse_owner_guid()"]
GetOwner --> FindOwner{"wow_get_object_by_guid()"}
FindOwner -->|Found| CheckOwnerFriendly{"wow_unit_is_friendly(owner)"}
FindOwner -->|Not Found| CheckGroup{"wow_is_guid_in_group(guid)"}
CheckOwnerFriendly -->|Friendly| ReadCorpseModel["Read Model @ +0xD8, +0xDC"]
CheckOwnerFriendly -->|Enemy| NextObj
CheckGroup -->|In Group| ReadCorpseModel
CheckGroup -->|Not In Group| NextObj
ReadCorpseModel --> RegisterCorpse["ModelOutline_AddDeadPlayer(ownerGUID)
ModelOutline_AddDeadPlayerModel(ptr)"]
end
RegisterDead --> NextObj
RegisterCorpse --> NextObj
RemoveTracking --> NextObj
```
### Target Detection
```mermaid
flowchart TB
Start["Frame Start"] --> GetTarget["wow_get_target()"]
GetTarget --> CheckValid{"targetObj != 0 &&
targetObj != localPlayer?"}
CheckValid -->|No| ClearTarget["ModelOutline_ClearTarget()"]
CheckValid -->|Yes| GetType["wow_object_type(target)"]
GetType --> CheckUnit{"Type == Unit (3)?"}
CheckUnit -->|No| ClearTarget
CheckUnit -->|Yes| CheckHostile{"!wow_unit_is_friendly()?"}
CheckHostile -->|Friendly| ClearTarget
CheckHostile -->|Hostile| CheckPet{"!wow_unit_is_enemy_pet()?"}
CheckPet -->|Enemy Pet| ClearTarget
CheckPet -->|Not Pet| CheckAlive{"!wow_unit_is_dead()?"}
CheckAlive -->|Dead| ClearTarget
CheckAlive -->|Alive| ReadModel["Read Model @ +0xD8, +0xDC"]
ReadModel --> RegisterTarget["ModelOutline_SetTargetModel(ptrD8)
ModelOutline_SetTargetModelAlt(ptrDC)
ModelOutline_SetModelOwner(ptr, obj)"]
```
### Raid Mark Detection
```mermaid
flowchart TB
Start["Frame Start"] --> CheckThrottle{"Time since last check
>= 100ms?"}
CheckThrottle -->|No| SkipScan["Use cached data"]
CheckThrottle -->|Yes| CacheTargets["wow_cache_raid_targets()
(copies 8 GUIDs from 0x00B71368)"]
CacheTargets --> ClearOld["ModelOutline_ClearRaidMarked()
ModelOutline_ClearModelOwners()"]
ClearOld --> ObjectLoop["Object Iteration Loop"]
ObjectLoop --> CheckType{"Object Type?"}
CheckType -->|"Type 4 (Player)"| ProcessPlayer
CheckType -->|"Type 3 (Unit/NPC)"| ProcessNPC
CheckType -->|Other| NextObj["Next Object"]
subgraph PlayerMarks["Player Raid Mark Check"]
ProcessPlayer --> CheckPlayerAlive{"Alive?"}
CheckPlayerAlive -->|No| NextObj
CheckPlayerAlive -->|Yes| CheckNotSelf{"Not LocalPlayer?"}
CheckNotSelf -->|Self| NextObj
CheckNotSelf -->|Other| GetPlayerGUID["wow_object_guid()"]
GetPlayerGUID --> CheckPlayerMark{"wow_get_raid_target_index(guid)"}
CheckPlayerMark -->|No Mark| NextObj
CheckPlayerMark -->|Has Mark 1-8| ReadPlayerModel["Read Model @ +0xD8, +0xDC"]
ReadPlayerModel --> RegisterPlayerMark["ModelOutline_AddRaidMarkedModel(ptr, index)"]
end
subgraph NPCMarks["NPC Raid Mark Check"]
ProcessNPC --> CheckNPCAlive{"Alive?"}
CheckNPCAlive -->|No| NextObj
CheckNPCAlive -->|Yes| CheckNotPet{"!wow_unit_is_enemy_pet()?"}
CheckNotPet -->|Enemy Pet| NextObj
CheckNotPet -->|OK| GetNPCGUID["wow_object_guid()"]
GetNPCGUID --> CheckNPCMark{"wow_get_raid_target_index(guid)"}
CheckNPCMark -->|No Mark| NextObj
CheckNPCMark -->|Has Mark 1-8| ReadNPCModel["Read Model @ +0xD8, +0xDC"]
ReadNPCModel --> RegisterNPCMark["ModelOutline_AddRaidMarkedModel(ptr, index)"]
end
RegisterPlayerMark --> NextObj
RegisterNPCMark --> NextObj
```
---
## Model-to-Object Linking
```mermaid
flowchart TB
subgraph Sources["Model Pointer Sources"]
UnitD8["Unit Object + 0xD8
(Primary Model)"]
UnitDC["Unit Object + 0xDC
(Secondary Model)"]
CorpseD8["Corpse Object + 0xD8"]
CorpseDC["Corpse Object + 0xDC"]
end
subgraph ModelOwnerLookup["Model -> Owner Lookup (in hooks)"]
Callback["Model + 0x3C0
(SetRenderCallbacks owner)"]
Direct["Model + 0x28
(InitializeModelWithParameters owner)"]
end
subgraph Storage2["Storage"]
OwnerMap2["g_modelToOwner
unordered_map"]
end
subgraph Usage["Usage in Hooks"]
ManageHook["ManageRenderListNode Hook"]
BatchHook["DrawBatchProj Hook"]
GetCategory["GetModelCategory()"]
end
Sources -->|"SetModelOwner()"| Storage2
ModelOwnerLookup -->|"Fallback lookup"| Usage
Storage2 -->|"GetModelOwner()"| Usage
```
---
## Hook Pipeline
```mermaid
flowchart TB
subgraph WoWRender["WoW Render Pipeline"]
AddModel["Model added to render list"]
RenderScene["CM2SceneRenderDraw"]
DrawBatchP["DrawBatchProj (batch type 0)"]
DIP["IDirect3DDevice9::DrawIndexedPrimitive"]
end
subgraph OurHooks["Our Hooks"]
ManageHook["ManageRenderListNode Hook
@ 0x00710B90"]
RenderHook["CM2SceneRenderDraw Hook
@ 0x0070B360"]
BatchProjHook["DrawBatchProj Hook
@ 0x0070CB30"]
DIPHook["D3D9 DIP Hook"]
end
subgraph Actions["Hook Actions"]
CheckMatch["Check if model is tracked
(dead/raid/target)"]
AddToFrame["Add to g_frameDeadModels"]
SetFlag["Set g_bRenderingCorpse = true
Set g_currentRenderingModel"]
ApplyStencil["Apply stencil outline effect"]
end
AddModel --> ManageHook
ManageHook --> CheckMatch
CheckMatch -->|Match| AddToFrame
RenderScene --> RenderHook
RenderHook --> BatchProjHook
BatchProjHook --> SetFlag
SetFlag --> DIP
DIP --> DIPHook
DIPHook --> ApplyStencil
```
---
## Group Membership Detection
```mermaid
flowchart TB
subgraph Cache["Group Cache (refreshed every 5s)"]
Party["UnitGUID('party1'..'party4')"]
Raid["UnitGUID('raid1'..'raid40')"]
Storage3["g_groupGUIDs[40]
g_groupMemberCount"]
end
subgraph Lookup["wow_is_guid_in_group(guid)"]
CheckStale{"Cache > 5s old?"}
Refresh["wow_refresh_group_cache()"]
Search["Linear search g_groupGUIDs"]
end
CheckStale -->|Yes| Refresh
Refresh --> Search
CheckStale -->|No| Search
Party --> Storage3
Raid --> Storage3
Storage3 --> Search
```
---
## Summary: Memory Reads Per Frame
| Operation | Memory Source | Frequency |
|-----------|--------------|-----------|
| Object Iteration | Object Manager @ 0x00B41414 | Every frame, O(n) objects |
| Object Type | Object + 0x14 | Per object |
| Object GUID | Object + 0x30 | Per player/unit/corpse |
| Unit Dead Check | Descriptor + 0x40 (HP), + 0x224 (flags) | Per unit |
| Unit Friendly Check | UnitReaction function call | Per relevant unit |
| Model Pointer | Object + 0xD8, + 0xDC | Per tracked object |
| Raid Targets | 0x00B71368 (8 GUIDs) | Every 100ms |
| Current Target | UnitGUID("target") function | Every frame |
| Group Members | UnitGUID("party#", "raid#") | Every 5 seconds |
| Corpse Owner | Descriptor + 0x18 | Per corpse |
| Corpse Skeleton Flag | Descriptor + 0x8C | Per corpse |
---
## Potential Consolidation Opportunities
1. **Batch GUID Resolution**: Currently `wow_get_raid_target_index()` is called per-object. Could cache GUID->mark_index map once per 100ms.
2. **Unified Object Cache**: Could build a single per-frame cache of {object_ptr, guid, type, model_ptrs, is_dead, is_friendly} to avoid repeated reads.
3. **Reduce Hook Count**: Consider if ManageRenderListNode + DrawBatchProj could be consolidated into a single hook point.
4. **Group Cache Integration**: Group membership check could be integrated with the raid target cache refresh cycle.
5. **Model Owner Association**: Currently done both at detection time AND in hooks via fallback. Could be consolidated to detection-only.