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