79feebe - Add boss frames
[wowui.git] / OmaCD / Inspect.lua
1 -- Inspect.lua
2 local _;
3 local next, print = next, print;
4 local format = string.format;
5 local InCombatLockdown = InCombatLockdown;
6 local CTimerAfter = C_Timer.After;
7 local UnitIsDeadOrGhost = UnitIsDeadOrGhost;
8 local IsInGroup, IsInRaid = IsInGroup, IsInRaid;
9 local CheckInteractDistance = CheckInteractDistance;
10 local GetTime, UnitGUID, UnitName = GetTime, UnitGUID, UnitName;
11 local CanInspect, NotifyInspect, ClearInspectPlayer = CanInspect, NotifyInspect, ClearInspectPlayer;
12 local GetInventoryItemLink, GetInventoryItemID = GetInventoryItemLink, GetInventoryItemID;
13 local GetTalentInfo, GetInspectSpecialization = GetTalentInfo, GetInspectSpecialization;
14 local INVSLOT_MAINHAND, INVSLOT_WRIST = INVSLOT_MAINHAND, INVSLOT_WRIST;
15
16 local cache = {};
17 local pending = {};
18 local inspecting = nil;
19 local inspectui = nil;
20 local frame = CreateFrame("Frame", "OmaInspect");
21 frame:Hide();
22 OmaInspect = {};
23
24 local redoGuid = nil;
25 local function redo()
26     if redoGuid and inspecting == redoGuid then
27         print("hit redo timeout, putting back on pending");
28         pending[redoGuid] = true;
29         inspecting = nil;
30         redoGuid = nil;
31         if not inspectui then ClearInspectPlayer() end
32     end
33 end
34
35 local function inspect(id)
36     local guid = UnitGUID(id);
37     if not guid then return end
38     if InCombatLockdown() then
39         pending[guid] = true;
40     elseif not inspectui then
41         -- not necessary, but there's too many issues with inspect,
42         -- try to hope being nearby makes it more likely to work
43         -- no need to inspect people not in range before pull anyway
44         if not UnitIsDeadOrGhost("player") and
45            CheckInteractDistance(id, 1) and CanInspect(id) then
46             if not inspecting then
47                 inspecting = guid;
48                 NotifyInspect(id);
49                 redoGuid = guid;
50                 CTimerAfter(20, redo);
51             else
52                 pending[guid] = true;
53             end
54         else
55             pending[guid] = true;
56         end
57     else
58         pending[guid] = true;
59     end
60 end
61
62 hooksecurefunc("InspectUnit", function()
63     inspectui = true;
64 end);
65 hooksecurefunc("NotifyInspect", function(id)
66     if inspecting ~= UnitGUID(id) then
67         inspectui = true;
68     end
69 end);
70 hooksecurefunc("ClearInspectPlayer", function()
71     inspectui = nil;
72 end);
73
74 local function tick()
75     -- make sure pending inspects get done at some point
76     if not InCombatLockdown() then
77         local guid, _ = next(pending);
78         if guid and cache[guid] and cache[guid].id then
79             pending[guid] = nil;
80             inspect(cache[guid].id);
81         end
82     end
83     CTimerAfter(2, tick);
84 end
85
86 function OmaInspect.Request(guid, id, callback, needItems, force)
87     if not id or not CanInspect(id) then return false end
88     if not guid or UnitGUID(id) ~= guid then return false end
89
90     if not cache[guid] then cache[guid] = {} end
91     local cached = cache[guid];
92     if not cached.time then cached.time = 0 end
93     cached.id = id;
94     cached.cb = callback;
95     cached.needItems = needItems;
96     local elapsed = GetTime() - cached.time;
97     if force or (not needItems and (cached.talent == nil or elapsed > 60)) or
98        (needItems and (cached.weapon == nil or elapsed > 60)) then
99         if not pending[guid] or inspecting ~= guid then -- don't send multiple inspects
100             inspect(id);
101         end
102     else
103         cached.cb(guid, cached.spec, cached.talent, cached.weapon, cached.wrist);
104     end
105     return true;
106 end
107
108 local function inspectReady(guid)
109     if not guid or inspecting ~= guid then return end
110     local cached = cache[guid];
111     if cached and cached.id and UnitGUID(cached.id) == guid then
112         local time = GetTime();
113         local id = cached.id;
114         inspecting = nil;
115         redoGuid = nil;
116         if InCombatLockdown() and not inspectui then
117             pending[guid] = true;
118             if not inspectui then ClearInspectPlayer() end
119         else
120             if CanInspect(id) then
121                 -- new inspect
122                 cached.time = time;
123                 if cached.needItems then
124                     cached.weapon = GetInventoryItemLink(id, INVSLOT_MAINHAND);
125                     cached.wrist = GetInventoryItemID(id, INVSLOT_WRIST);
126                 end
127                 cached.spec = GetInspectSpecialization(id);
128                 local _, _, _, selected = GetTalentInfo(6, 1, 1, true, id);
129                 cached.talent = selected;
130             end
131             if not inspectui then ClearInspectPlayer() end
132             if (cached.needItems and cached.weapon) or
133                (not cached.needItems and cached.talent ~= nil) then
134                 cached.cb(guid, cached.spec, cached.talent, cached.weapon, cached.wrist);
135             else
136                 pending[guid] = true;
137             end
138         end
139     end
140 end
141
142 local function updateIds()
143     local size = 0;
144     local prefix = "solo";
145     if IsInGroup() then
146         if IsInRaid() then
147             size = 40;
148             prefix = "raid";
149         else
150             size = 4;
151             prefix = "party";
152         end
153     end
154     for i = 1,size do
155         local id = format("%s%i", prefix, i);
156         local guid = UnitGUID(id);
157         if cache[guid] then cache[guid].id = id end
158     end
159 end
160
161 local function create()
162     frame:UnregisterAllEvents();
163     frame:SetScript("OnEvent", function(self, event, guid)
164         if event == "INSPECT_READY" then
165             inspectReady(guid);
166         elseif event == "GROUP_ROSTER_UPDATE" then
167             updateIds();
168         end
169     end);
170     frame:RegisterEvent("INSPECT_READY");
171     frame:RegisterEvent("GROUP_ROSTER_UPDATE");
172     tick();
173 end
174
175 frame:SetScript("OnEvent", function(self, event)
176     return create();
177 end);
178 frame:RegisterEvent("PLAYER_LOGIN");