325e20b - Add Shaman TellMeWhen
[wowui.git] / OmaUF / CastBar.lua
1 -- CastBar.lua
2 local _;
3 local unpack = unpack;
4 local ssub = string.sub;
5 local min = math.min;
6 local ceil = math.ceil;
7 local UnitCastingInfo, UnitChannelInfo = UnitCastingInfo, UnitChannelInfo;
8 local GetTime = GetTime;
9 local CTimerAfter = C_Timer.After;
10
11 local barTexture = "Interface\\AddOns\\OmaRF\\images\\minimalist";
12 local castingColor = {1, 0.49, 0}; -- from Quartz defaults
13 local nointerruptColor = {0.6, 0.6, 0.6};
14 local channelingColor = {0.32, 0.3, 1};
15 local updaters = {};
16
17 local M = {};
18 OmaUFCastBar = M;
19
20 local function onUpdate(bar)
21     if not bar.updating then return end
22     --local width = bar.icon:IsShown() and bar.cast.width or bar.cast.width; -- TODO fullwidth
23     local startTime, endTime = bar.startTime, bar.endTime;
24     local currentClamped = min(GetTime(), endTime);
25     local remaining = endTime - currentClamped;
26     local percent;
27     if bar.channeling then
28         percent = remaining / (endTime - startTime);
29     else
30         percent = (currentClamped - startTime) / (endTime - startTime);
31     end
32
33     local width = percent*bar.cast.width;
34     if width <= 0 then
35         bar.cast:SetWidth(0.1);
36     else
37         bar.cast:SetWidth(width);
38     end
39     bar.time:SetFormattedText("%.1f", remaining);
40     CTimerAfter(0.03, updaters[bar]);
41 end
42 M.OnUpdate = onUpdate;
43
44 local function showBar(bar)
45     bar:Show();
46     bar.updating = true;
47     onUpdate(bar);
48 end
49
50 local function hideBar(bar)
51     bar:Hide();
52     bar.updating = nil;
53 end
54
55 local function toggleInterruptible(bar, nointr)
56     if bar.unit == "player" then return end
57     if nointr then
58         bar.cast:SetVertexColor(unpack(nointerruptColor));
59         bar.shield:Show();
60     else
61         bar.cast:SetVertexColor(unpack(bar.cast.color));
62         bar.shield:Hide();
63     end
64 end
65
66 local function startCast(bar, unit, channeling)
67     local name, icon, startTime, endTime, noInterrupt, id;
68     if channeling then
69         name, _, _, icon, startTime, endTime, _, noInterrupt = UnitChannelInfo(unit);
70         if not startTime or not endTime then return nil end
71         bar.channeling = true;
72         bar.cast.color = channelingColor;
73     else
74         _, _, name, icon, startTime, endTime, _, _, noInterrupt, id = UnitCastingInfo(unit);
75         if not startTime or not endTime then return nil end
76         bar.channeling = nil;
77         bar.cast.color = castingColor;
78     end
79     bar.startTime = startTime / 1000;
80     bar.endTime = endTime / 1000;
81     -- don't show samwise for non-existent icons
82     if icon ~= "Interface\\Icons\\Temp" then
83         bar.icon:SetTexture(icon);
84         bar.icon:Show();
85         bar.cast:SetWidth(channeling and bar.cast.width or 0.1);
86     else
87         bar.icon:Hide();
88         bar.cast:SetWidth(channeling and bar.cast.width or 0.1); -- TODO use fullwidth
89     end
90     bar.spell:SetText(ssub(name, 1, bar.spell.count));
91     bar.time:SetFormattedText("%.1f", (endTime - startTime)/1000);
92     bar.cast:SetVertexColor(unpack(bar.cast.color));
93     showBar(bar);
94     toggleInterruptible(bar, noInterrupt);
95     return true;
96 end
97 M.StartCast = startCast;
98
99 local function applyDelay(bar, unit, channeling)
100     local startTime, endTime;
101     if channeling then
102         _, _, _, _, startTime, endTime = UnitChannelInfo(unit);
103     else
104         _, _, _, _, startTime, endTime = UnitCastingInfo(unit);
105     end
106     if not startTime or not endTime then return hideBar(bar) end
107     bar.startTime = startTime / 1000;
108     bar.endTime = endTime / 1000;
109     -- TODO show delay text
110 end
111 M.ApplyDelay = applyDelay;
112
113 local events = {
114     ["UNIT_SPELLCAST_START"] = function(bar, unit)
115         startCast(bar, unit);
116     end,
117     ["PLAYER_TARGET_CHANGED"] = function(bar)
118         hideBar(bar);
119         if not startCast(bar, bar.unit) then
120             startCast(bar, bar.unit, true);
121         end
122     end,
123     ["UNIT_SPELLCAST_CHANNEL_START"] = function(bar, unit)
124         startCast(bar, unit, true);
125     end,
126     ["UNIT_SPELLCAST_STOP"] = function(bar)
127         hideBar(bar);
128     end,
129     ["UNIT_SPELLCAST_DELAYED"] = function(bar, unit)
130         applyDelay(bar, unit);
131     end,
132     ["UNIT_SPELLCAST_CHANNEL_UPDATE"] = function(bar, unit)
133         applyDelay(bar, unit, true);
134     end,
135     ["UNIT_SPELLCAST_INTERRUPTIBLE"] = function(bar)
136         toggleInterruptible(bar, false);
137     end,
138     ["UNIT_SPELLCAST_NOT_INTERRUPTIBLE"] = function(bar)
139         toggleInterruptible(bar, true);
140     end,
141 };
142 events["UNIT_SPELLCAST_CHANNEL_STOP"] = events["UNIT_SPELLCAST_STOP"];
143
144 local function onEvent(bar, event, unit)
145     if unit == bar.unit or (bar.unit == "player" and unit == "vehicle") then
146         events[event](bar, unit);
147     elseif event == "PLAYER_TARGET_CHANGED" then
148         events[event](bar);
149     end
150 end
151
152 function M.RegisterCastEvents(bar)
153     --bar:RegisterEvent("UNIT_SPELLCAST_SENT");
154     bar:RegisterEvent("UNIT_SPELLCAST_START");
155     bar:RegisterEvent("UNIT_SPELLCAST_STOP");
156     --bar:RegisterEvent("UNIT_SPELLCAST_FAILED");
157     --bar:RegisterEvent("UNIT_SPELLCAST_INTERRUPTED");
158     bar:RegisterEvent("UNIT_SPELLCAST_DELAYED");
159     bar:RegisterEvent("UNIT_SPELLCAST_CHANNEL_START");
160     bar:RegisterEvent("UNIT_SPELLCAST_CHANNEL_STOP");
161     bar:RegisterEvent("UNIT_SPELLCAST_CHANNEL_UPDATE");
162     bar:RegisterEvent("UNIT_SPELLCAST_INTERRUPTIBLE");
163     bar:RegisterEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE");
164     if bar.unit == "target" then bar:RegisterEvent("PLAYER_TARGET_CHANGED") end
165     -- trigger initial check
166     if not startCast(bar, bar.unit) then
167         startCast(bar, bar.unit, true);
168     end
169 end
170
171 function M.UnregisterCastEvents(bar)
172     bar:UnregisterAllEvents();
173     hideBar(bar);
174 end
175
176 function M.CreateCastBar(parent, unit, yoffset)
177     local bar = CreateFrame("Frame", parent:GetName().."CastBar", parent);
178     bar.unit = unit;
179     bar:SetPoint("BOTTOMLEFT", parent, "TOPLEFT", 0, yoffset);
180     bar:SetPoint("BOTTOMRIGHT", parent, "TOPRIGHT", 0, yoffset);
181     bar:SetHeight(22);
182     bar:Hide();
183     bar.back = bar:CreateTexture(nil, "BACKGROUND");
184     bar.back:SetAllPoints();
185     bar.back:SetColorTexture(0, 0, 0, 0.7);
186     bar.icon = bar:CreateTexture(nil, "ARTWORK", 1);
187     bar.icon:SetPoint("TOPLEFT", bar, "TOPLEFT", 1, -1);
188     bar.icon:SetPoint("BOTTOMRIGHT", bar, "BOTTOMLEFT", 21, 1);
189     bar.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93); -- remove borders (from Quartz)
190     bar.shield = bar:CreateTexture(nil, "ARTWORK");
191     bar.shield:SetPoint("CENTER", bar.icon, "CENTER", -2, -1);
192     bar.shield:SetWidth(28);
193     bar.shield:SetHeight(50);
194     bar.shield:SetTexture("Interface\\CastingBar\\UI-CastingBar-Small-Shield");
195     bar.shield:SetTexCoord(0, 36/256, 0, 1);
196     bar.shield:Hide();
197     bar.cast = bar:CreateTexture(nil, "ARTWORK");
198     bar.cast:SetPoint("TOPLEFT", bar.icon, "TOPRIGHT", 1, 0);
199     bar.cast:SetPoint("BOTTOMLEFT", bar.icon, "BOTTOMRIGHT", 1, 0);
200     bar.cast.width = bar:GetWidth() - bar.icon:GetWidth() - 3;
201     bar.cast.fullwidth = bar:GetWidth() - 2; -- for casts without icon
202     bar.cast:SetWidth(bar.cast.width);
203     bar.cast:SetTexture(barTexture);
204     bar.cast.color = castingColor;
205     bar.spell = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
206     bar.spell:SetPoint("LEFT", bar.icon, "RIGHT", 2, 0);
207     bar.spell.count = ceil(bar.cast:GetWidth()/10);
208     bar.time = bar:CreateFontString(nil, "OVERLAY", "GameFontHighlight");
209     bar.time:SetPoint("RIGHT", bar, "RIGHT", -2, 0);
210     bar:SetScript("OnEvent", onEvent);
211     updaters[bar] = function() onUpdate(bar) end
212     return bar;
213 end