vanilla-wow-addons – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | local tablet = AceLibrary("Tablet-2.0") |
2 | local abacus = AceLibrary("Abacus-2.0") |
||
3 | |||
4 | local L = AceLibrary("AceLocale-2.2"):new("FuBar_ToFu") |
||
5 | ToFu = AceLibrary("AceAddon-2.0"):new("AceEvent-2.0", "AceHook-2.0", "AceConsole-2.0", "AceDB-2.0", "FuBarPlugin-2.0") |
||
6 | |||
7 | ToFu:RegisterDB("ToFuDB") |
||
8 | ToFu:RegisterDefaults('account', { |
||
9 | paths = { |
||
10 | Alliance = {['*']={}}, |
||
11 | Horde = {['*']={}}, |
||
12 | }, |
||
13 | }) |
||
14 | ToFu:RegisterDefaults('profile', { |
||
15 | hook = { |
||
16 | oCD = false, |
||
17 | oCB = false, |
||
18 | BigWigs = false, |
||
19 | Chronometer = false, |
||
20 | }, |
||
21 | }) |
||
22 | local optionsTable = { |
||
23 | handler = ToFu, |
||
24 | type = 'group', |
||
25 | args = { |
||
26 | hook = { |
||
27 | type = 'group', name = L['Hooks'], |
||
28 | desc = L['Other addons to hook into'], |
||
29 | args = { |
||
30 | oCD = { |
||
31 | type = 'toggle', name = 'oCD', |
||
32 | desc = 'otravi_CoolDown', |
||
33 | get = function() return ToFu.db.profile.hook.oCD end, |
||
34 | set = function(t) ToFu.db.profile.hook.oCD = t end, |
||
35 | disabled = function() |
||
36 | if oCD then return false |
||
37 | else return true end |
||
38 | end, |
||
39 | }, |
||
40 | oCB = { |
||
41 | type = 'toggle', name = 'oCB', |
||
42 | desc = 'otravi_CastBar', |
||
43 | get = function() return ToFu.db.profile.hook.oCB end, |
||
44 | set = function(t) ToFu.db.profile.hook.oCB = t end, |
||
45 | disabled = function() |
||
46 | if oCB then return false |
||
47 | else return true end |
||
48 | end, |
||
49 | }, |
||
50 | BigWigs = { |
||
51 | type = 'toggle', name = 'BigWigs', |
||
52 | desc = 'BigWigs_CustomBar', |
||
53 | get = function() return ToFu.db.profile.hook.BigWigs end, |
||
54 | set = function(t) ToFu.db.profile.hook.BigWigs = t end, |
||
55 | disabled = function() |
||
56 | if BigWigs and BigWigsCustomBar then return false |
||
57 | else return true end |
||
58 | end, |
||
59 | }, |
||
60 | Chronometer = { |
||
61 | type = 'toggle', name = 'Chronometer', |
||
62 | desc = 'Chronometer', |
||
63 | get = function() return ToFu.db.profile.hook.Chronometer end, |
||
64 | set = function(t) ToFu.db.profile.hook.Chronometer = t end, |
||
65 | disabled = function() |
||
66 | if Chronometer then return false |
||
67 | else return true end |
||
68 | end, |
||
69 | }, |
||
70 | }, |
||
71 | }, |
||
72 | data = { |
||
73 | type = 'group', name = L['Data'], |
||
74 | desc = L["Various options to do with saved flight data"], |
||
75 | args = { |
||
76 | clear = { |
||
77 | type = 'execute', name = L['Clear Data'], |
||
78 | desc = L["Delete *ALL* saved flight path data for your faction."], |
||
79 | func = "ClearData", |
||
80 | }, |
||
81 | default = { |
||
82 | type = 'execute', name = L['Default Data'], |
||
83 | desc = L["Load the default flight-time dataset."], |
||
84 | func = function() ToFu:LoadDefaults() end, disabled = function() return type(ToFu.LoadDefaults) ~= 'function' end, |
||
85 | }, |
||
86 | }, |
||
87 | }, |
||
88 | }, |
||
89 | } |
||
90 | |||
91 | ToFu:RegisterChatCommand({"/tofu",}, optionsTable) |
||
92 | ToFu.OnMenuRequest = optionsTable |
||
93 | |||
94 | ToFu.version = "2.0." .. string.sub("$Revision: 15647 $", 12, -3) |
||
95 | ToFu.date = string.sub("$Date: 2006-10-31 20:34:49 -0500 (Tue, 31 Oct 2006) $", 8, 17) |
||
96 | ToFu.hasIcon = "Interface\\TaxiFrame\\UI-Taxi-Icon-Green" |
||
97 | ToFu.hideWithoutStandby = true |
||
98 | |||
99 | function ToFu:OnInitialize() |
||
100 | self.start = nil |
||
101 | self.destination = nil |
||
102 | self.inFlight = false |
||
103 | self.timeFlown = 0 |
||
104 | self.avgTime = nil |
||
105 | self.last, self.nodes, self.steps = {}, {}, {} |
||
106 | |||
107 | --Stolen from AceDB |
||
108 | local _,race = UnitRace("player") |
||
109 | if race == "Orc" or race == "Scourge" or race == "Troll" or race == "Tauren" then |
||
110 | self.faction = 'Horde' |
||
111 | else |
||
112 | self.faction = 'Alliance' |
||
113 | end |
||
114 | |||
115 | if not self.db.account.version then |
||
116 | self:ScheduleEvent(function() |
||
117 | --Check to see whether we need to shrink the database. |
||
118 | for faction, data in self.db.account.paths do |
||
119 | self:Print('Upgrading', faction, 'flight data') |
||
120 | --Convert the old data to the new format, and clear out the old DB in the process. |
||
121 | if type(data) ~= 'table' then data = nil |
||
122 | else |
||
123 | local newdata = {} |
||
124 | for start,dests in pairs(data) do |
||
125 | local newstart = self:LessName(start) |
||
126 | newdata[newstart] = {} |
||
127 | if type(dests) == 'table' then |
||
128 | for dest, info in pairs(dests) do |
||
129 | newdata[newstart][self:LessName(dest)] = info |
||
130 | data[start][dest] = nil |
||
131 | end |
||
132 | end |
||
133 | data[start] = nil |
||
134 | end |
||
135 | --Now apply the new data to the DB. |
||
136 | --(Can't merge this with the previous step, as it would be editing the table while looping over it.) |
||
137 | for start, dests in pairs(newdata) do |
||
138 | for dest, info in pairs(dests) do |
||
139 | if not self.db.account.paths[start] then self.db.account.paths[start] = {} end |
||
140 | self.db.account.paths[start][dest] = info |
||
141 | end |
||
142 | end |
||
143 | end |
||
144 | end |
||
145 | |||
146 | self.db.account.version = 1 |
||
147 | end, 1) |
||
148 | end |
||
149 | end |
||
150 | |||
151 | function ToFu:OnEnable() |
||
152 | self:RegisterEvent("TAXIMAP_OPENED") |
||
153 | self:Hook("TakeTaxiNode") |
||
154 | self:Hook("TaxiNodeOnButtonEnter") |
||
155 | end |
||
156 | |||
157 | |||
158 | function ToFu:OnTextUpdate() |
||
159 | if self.inFlight then |
||
160 | if self.timeAvg ~= 0 then |
||
161 | --Time remaining, if we've taken this path before. |
||
162 | self:SetText(abacus:FormatDurationCondensed(self.timeAvg - self.timeFlown, true)) |
||
163 | else |
||
164 | --Time flown so far. |
||
165 | self:SetText(abacus:FormatDurationCondensed(self.timeFlown, true)) |
||
166 | end |
||
167 | else |
||
168 | self:SetText('') |
||
169 | end |
||
170 | end |
||
171 | |||
172 | function ToFu:OnTooltipUpdate() |
||
173 | local cat = tablet:AddCategory( |
||
174 | 'columns', 2, |
||
175 | 'text', L["Current Flight"], |
||
176 | 'child_textR', 1, 'child_textG', 0.82, 'child_textB', 0, |
||
177 | 'child_text2R', 1, 'child_text2G', 1, 'child_text2B', 1 |
||
178 | ) |
||
179 | if self.inFlight then |
||
180 | cat:AddLine('text', L["From"], 'text2', self.start) |
||
181 | cat:AddLine('text', L["To"], 'text2', self.destination) |
||
182 | cat:AddLine('text', L["Time Taken"], 'text2', abacus:FormatDurationCondensed(self.timeFlown, true)) |
||
183 | cat:AddLine('text', L["Average Time"], 'text2', abacus:FormatDurationCondensed(self.timeAvg, true)) |
||
184 | cat:AddLine('text', L["Cost"], 'text2', abacus:FormatMoneyCondensed(self.cost, true)) |
||
185 | else |
||
186 | cat:AddLine('text', L["Not in flight"]) |
||
187 | end |
||
188 | |||
189 | cat = tablet:AddCategory( |
||
190 | 'columns', 2, |
||
191 | 'text', L["Previous Flight"], |
||
192 | 'child_textR', 1, 'child_textG', 0.82, 'child_textB', 0, |
||
193 | 'child_text2R', 1, 'child_text2G', 1, 'child_text2B', 1 |
||
194 | ) |
||
195 | if self.last.start ~= nil then |
||
196 | cat:AddLine('text', L["From"], 'text2', self.last.start) |
||
197 | cat:AddLine('text', L["To"], 'text2', self.last.destination) |
||
198 | cat:AddLine('text', L["Time Taken"], 'text2', abacus:FormatDurationCondensed(self.last.time, true)) |
||
199 | cat:AddLine('text', L["Cost"], 'text2', abacus:FormatMoneyCondensed(self.last.cost, true)) |
||
200 | else |
||
201 | cat:AddLine('text', L["No previous flight"]) |
||
202 | end |
||
203 | |||
204 | if self.inFlight then |
||
205 | tablet:SetHint(L["Click to copy the time remaining in flight to the chatbox."]) |
||
206 | end |
||
207 | end |
||
208 | |||
209 | function ToFu:OnClick() |
||
210 | -- Paste time remaining to chatbox. |
||
211 | if ChatFrameEditBox:IsVisible() and self.inFlight then |
||
212 | local text = "" |
||
213 | local _, time = self:GetFlightData(self.start, self.destination) |
||
214 | |||
215 | if time ~= 0 then |
||
216 | --Time remaining, if we've taken this path before. |
||
217 | text = abacus:FormatDurationFull(time - self.timeFlown, false) |
||
218 | else |
||
219 | --Time flown so far. |
||
220 | text = string.format("??? (%s %s)", abacus:FormatDurationFull(self.timeFlown, false), L["So Far"]) |
||
221 | end |
||
222 | |||
223 | ChatFrameEditBox:Insert(text) |
||
224 | end |
||
225 | end |
||
226 | |||
227 | |||
228 | function ToFu:OnUpdate(timeSinceLast) |
||
229 | self.timeFlown = self.timeFlown + timeSinceLast |
||
230 | if (UnitOnTaxi("player") ~= 1) and (self.timeFlown > 5) then |
||
231 | -- Cheap test to make sure that we don't do all this right at the beginning of a flight |
||
232 | self:CancelScheduledEvent(self.name) |
||
233 | self.inFlight = false |
||
234 | |||
235 | local cost, time, taken = self:GetFlightData(self.start, self.destination) |
||
236 | |||
237 | --Average time this route has taken (there's a chance we'll be getting inaccuracies, etc.): |
||
238 | if time ~= 0 then |
||
239 | time = (time + self.timeFlown) / 2 |
||
240 | |||
241 | if self.db.profile.hook.oCB and oCB then |
||
242 | oCB:SpellStop(true) |
||
243 | end |
||
244 | else |
||
245 | time = self.timeFlown |
||
246 | end |
||
247 | |||
248 | self:SaveFlightData(self.start, self.destination, cost, time, taken + 1) |
||
249 | |||
250 | self.last.start = self.start |
||
251 | self.last.destination = self.destination |
||
252 | self.last.time = self.timeFlown |
||
253 | self.last.cost = cost |
||
254 | |||
255 | self.start = nil |
||
256 | self.destination = nil |
||
257 | self.timeFlown = nil |
||
258 | end |
||
259 | self:UpdateDisplay() |
||
260 | end |
||
261 | |||
262 | |||
263 | function ToFu:TAXIMAP_OPENED() |
||
264 | local numNodes = NumTaxiNodes() |
||
265 | -- Have to scan all the slots first to get the "CURRENT" slot. |
||
266 | for i=1, numNodes do |
||
267 | if TaxiNodeGetType(i) == "CURRENT" then |
||
268 | self.start = TaxiNodeName(i) |
||
269 | else |
||
270 | local x,y = TaxiNodePosition(i) |
||
271 | self.nodes[x..':'..y] = i |
||
272 | end |
||
273 | end |
||
274 | end |
||
275 | |||
276 | function ToFu:TakeTaxiNode(slot) |
||
277 | if TaxiNodeGetType(slot) == "REACHABLE" then |
||
278 | self.destination = TaxiNodeName(slot) |
||
279 | self.timeFlown = 0 |
||
280 | self.inFlight = true |
||
281 | |||
282 | self.cost, self.timeAvg = self:GetFlightData(self.start, self.destination) |
||
283 | |||
284 | if self.timeAvg ~= 0 then |
||
285 | if self.db.profile.hook.oCD and oCD then |
||
286 | --oCD:StartBar(id, time, text, icon) |
||
287 | oCD:StartBar('ToFu', self.timeAvg, self:LessName(self.destination), "Interface\\TaxiFrame\\UI-Taxi-Icon-Green") |
||
288 | end |
||
289 | if self.db.profile.hook.oCB and oCB then |
||
290 | --oCB:SpellStart(text, time, inSeconds, dontRegister) |
||
291 | oCB:SpellStart(self:LessName(self.destination), self.timeAvg, true, true) |
||
292 | end |
||
293 | if self.db.profile.hook.BigWigs and BigWigs and BigWigsCustomBar then |
||
294 | --BWCB(seconds, message) |
||
295 | BWLCB(self.timeAvg, "Flying to "..self:LessName(self.destination)) |
||
296 | end |
||
297 | if self.db.profile.hook.Chronometer and Chronometer then |
||
298 | -- Chronometer:AddTimer(kind, name, duration, targeted, isgain, selforselect, extra) |
||
299 | if not Chronometer.timers[Chronometer.SPELL]['ToFu'] then Chronometer:AddTimer(Chronometer.SPELL, 'ToFu', self.timeAvg, 0, 0, 0) end |
||
300 | Chronometer:StartTimer(Chronometer.timers[Chronometer.SPELL]['ToFu'], self:LessName(self.destination)) |
||
301 | end |
||
302 | end |
||
303 | |||
304 | for key in pairs(self.nodes) do self.nodes[key] = nil end |
||
305 | |||
306 | self:ScheduleRepeatingEvent(self.name, self.OnUpdate, 1, self, 1) |
||
307 | end |
||
308 | self.hooks["TakeTaxiNode"].orig(slot) |
||
309 | end |
||
310 | |||
311 | function ToFu:TaxiNodeOnButtonEnter(button) |
||
312 | self.hooks["TaxiNodeOnButtonEnter"].orig(button) |
||
313 | |||
314 | local index = button:GetID() |
||
315 | if TaxiNodeGetType(index) == "REACHABLE" then |
||
316 | local destination, cost = TaxiNodeName(index), TaxiNodeCost(index) |
||
317 | local oldcost, time, taken = self:GetFlightData(self.start, destination) |
||
318 | if oldcost ~= cost then self:SaveFlightData(self.start, destination, cost, time, taken) end |
||
319 | |||
320 | local estimate = '' |
||
321 | if time == 0 then |
||
322 | --Try to estimate. This is mainly stolen from Blizzard's TaxiFrame.lua, which uses a much more in-depth version of this to draw the lines. |
||
323 | local _,reversetime = self:GetFlightData(destination, self.start) |
||
324 | if reversetime > 0 then |
||
325 | time = reversetime |
||
326 | estimate = L['reversed'] |
||
327 | else |
||
328 | local numRoutes = GetNumRoutes(index) |
||
329 | if numRoutes > 1 then |
||
330 | TaxiNodeSetCurrent(index) --This is magic -- it renumbers the 'hops' between index and current so the for loop below works. |
||
331 | self.steps[1] = self.start |
||
332 | |||
333 | local length = 0 |
||
334 | for i=1, numRoutes do |
||
335 | local l, dx, dy = self:LineLength(index, i) |
||
336 | length = length + l |
||
337 | |||
338 | self.steps[i+1] = TaxiNodeName(self.nodes[dx..':'..dy]) |
||
339 | end |
||
340 | |||
341 | local skipped = 0 |
||
342 | local node = 1 |
||
343 | while node <= numRoutes do |
||
344 | --self.steps[node] is the 'current' node. |
||
345 | --Step backwards through self.steps to find the longest interval that gives us a time. |
||
346 | local nnode = numRoutes+1 |
||
347 | while nnode > node do |
||
348 | local _,t = self:GetFlightData(self.steps[node], self.steps[nnode]) |
||
349 | if t==0 then _,t = self:GetFlightData(self.steps[nnode], self.steps[node]) end |
||
350 | nnode = nnode - 1 |
||
351 | if t > 0 then |
||
352 | -- We know this! Add it to the time and skip the rest of the loops. |
||
353 | time = time + t |
||
354 | break |
||
355 | elseif nnode == node then |
||
356 | -- We have no idea. Mark down that we skipped this. |
||
357 | skipped = skipped + self:LineLength(index, node-1) -- Is 'node-1' right here? |
||
358 | break |
||
359 | end |
||
360 | end |
||
361 | node = nnode + 1 |
||
362 | end |
||
363 | if time > 0 then |
||
364 | if skipped > 0 then time = time + ((time/(length-skipped)) * skipped) end |
||
365 | estimate = ' '..L['estimated'] |
||
366 | end |
||
367 | |||
368 | for key in pairs(self.steps) do self.steps[key] = nil end |
||
369 | end |
||
370 | end |
||
371 | end |
||
372 | |||
373 | GameTooltip:AddLine(L["Takes"]..": "..abacus:FormatDurationCondensed(time)..estimate, 1, 1, 1) |
||
374 | GameTooltip:AddLine(string.format(L["Flown %s times"], taken), 1, 1, 1) |
||
375 | GameTooltip:SetHeight(GameTooltip:GetHeight() + 28) |
||
376 | end |
||
377 | end |
||
378 | |||
379 | function ToFu:LessName(s) |
||
380 | --For "Thelsamar, Loch Modan" return "Thelsamar". |
||
381 | if not s then return '' end |
||
382 | if ( GetLocale() ~= "koKR" ) then |
||
383 | local c = string.find(s, ', ') |
||
384 | if c then s = string.sub(s, 1, c-1) end |
||
385 | else |
||
386 | local c = string.find(s, ' %(') |
||
387 | if c then _,_,s = string.find(s, "(.-) %(") end |
||
388 | end |
||
389 | return s |
||
390 | end |
||
391 | |||
392 | -- Data in format: ":1000:120:3:" |
||
393 | -- Means: "cost 1000 copper, taking 120 seconds, taken 3 times." |
||
394 | |||
395 | function ToFu:GetFlightData(start, destination, faction) |
||
396 | -- cost, time, taken = ToFu:GetFlightData(start, destination, faction) |
||
397 | -- Fetches the saved information about a flight path, from [start] to [destination]. |
||
398 | -- If any arguments are left as nil, the current value of self.<<argument>> will be used. |
||
399 | local s = self.db.account.paths[faction or self.faction][self:LessName(start or self.start)][self:LessName(destination or self.destination)] |
||
400 | if type(s) ~= 'string' then return 0,0,0 end |
||
401 | local _, _, cost, time, taken = string.find(s, ":(%d+):(%d+):(%d+):") |
||
402 | return (tonumber(cost) or 0), (tonumber(time) or 0), (tonumber(taken) or 0), self:LessName(start or self.start), self:LessName(destination or self.destination) |
||
403 | end |
||
404 | |||
405 | function ToFu:SaveFlightData(start, destination, cost, time, taken, faction) |
||
406 | self.db.account.paths[faction or self.faction][self:LessName(start)][self:LessName(destination)] = ':'..(cost or 0)..':'..math.floor((time and tonumber(time) or 0))..':'..(taken or 0)..':' |
||
407 | return true |
||
408 | end |
||
409 | |||
410 | function ToFu:ClearData() |
||
411 | self.db.account.paths[self.faction] = {} |
||
412 | end |
||
413 | |||
414 | function ToFu:LineLength(index, i) |
||
415 | --Start node: |
||
416 | local sx = TaxiGetSrcX(index, i) |
||
417 | local sy = TaxiGetSrcY(index, i) |
||
418 | --End node: |
||
419 | local ex = TaxiGetDestX(index, i) |
||
420 | local ey = TaxiGetDestY(index, i) |
||
421 | -- Determine dimensions |
||
422 | local dx,dy = ex - sx, ey - sy; |
||
423 | -- Normalize direction if necessary |
||
424 | if (dx < 0) then |
||
425 | dx,dy = -dx,-dy; |
||
426 | end |
||
427 | -- Return length and endpoint coords. |
||
428 | return sqrt((dx * dx) + (dy * dy)), ex, ey |
||
429 | end |