comparison ObjectiveTracker/Quests.lua @ 30:7583684becf4

- implement procedural block contents generation - redo anchor calculations to allow for transitional animation - attempt to sort out event handling quirks related to autopopup quest completion and turn-in - revise the data structures created by the different GetInfo's - start on trimming out redundant variables
author Nenue
date Thu, 14 Apr 2016 17:11:13 -0400
parents adcd7c328d07
children 48b3e3959a0a
comparison
equal deleted inserted replaced
29:adcd7c328d07 30:7583684becf4
1 local B = select(2,...).frame 1 local B = select(2,...).frame
2 local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame') 2 local T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
3 local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band 3 local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band
4 local GetAutoQuestPopUp, GetQuestLogCompletionText = GetAutoQuestPopUp, GetQuestLogCompletionText 4 local GetQuestWatchInfo, GetQuestLogCompletionText = GetQuestWatchInfo, GetQuestLogCompletionText
5 local GetQuestLogLeaderBoard, GetNumQuestLogEntries, GetQuestLogTitle = GetQuestLogLeaderBoard, GetNumQuestLogEntries, GetQuestLogTitle
6 local GetQuestLogSpecialItemInfo, GetQuestLogSpecialItemCooldown = GetQuestLogSpecialItemInfo, GetQuestLogSpecialItemCooldown
7 local GetSuperTrackedQuestID, GetMoney, C_Scenario, GetCVarBool, GetNumQuestWatches = GetSuperTrackedQuestID, GetMoney, C_Scenario, GetCVarBool, GetNumQuestWatches
8 local GetQuestTagInfo, GetMoneyString, GetDistanceSqToQuest, GetQuestFactionGroup = GetQuestTagInfo, GetMoneyString, GetDistanceSqToQuest, GetQuestFactionGroup
9 local QUEST_TAG_ACCOUNT, LE_QUEST_FACTION_HORDE, LE_QUEST_FREQUENCY_DAILY, LE_QUEST_FREQUENCY_WEEKLY = QUEST_TAG_ACCOUNT, LE_QUEST_FACTION_HORDE, LE_QUEST_FREQUENCY_DAILY, LE_QUEST_FREQUENCY_WEEKLY
10 local QUEST_TAG_TCOORDS, IsQuestSequenced = QUEST_TAG_TCOORDS, IsQuestSequenced
5 local Default, Quest = T.DefaultHandler, T.Quest 11 local Default, Quest = T.DefaultHandler, T.Quest
6 local format = format 12 local format = format
7 local print = B.print('Tracker') 13 local print = B.print('Tracker')
8 local lprint = B.print('Line') 14 local lprint = B.print('Line')
9 local iprint = B.print('Info') 15 local iprint = B.print('Info')
10 local colors = T.colors 16 local colors = T.colors
11 local tprint = B.print('Tracker') 17 local tprint = B.print('Tracker')
12 18
19 local superTrackQuestID, playerMoney, inScenario, showPOIs
13 Quest.Update = function(self, reason, ...) 20 Quest.Update = function(self, reason, ...)
14 local print = tprint 21 local print = tprint
15 print('QuestTracker:Update() received') 22 print('QuestTracker:Update() received')
16 T.UpdateActionButtons() 23 T.UpdateActionButtons()
17 Default.Update(self, reason, ...) 24 Default.Update(self, reason, ...)
89 96
90 line.displayColor = {r, g, b, a} 97 line.displayColor = {r, g, b, a}
91 line.status:SetTextColor(r, g, b, a) 98 line.status:SetTextColor(r, g, b, a)
92 line.displayText = data.text 99 line.displayText = data.text
93 100
94 return line 101 return data.text, nil
95 end 102 end
96 103
97 ----------------------------- 104 -----------------------------
98 --- QUEST 105 --- QUEST
99 Quest.POI = {}
100 Quest.QuestBlock = {} 106 Quest.QuestBlock = {}
101 Quest.LogBlock = {} 107 Quest.LogBlock = {}
102 Quest.LogInfo = {} 108 Quest.LogInfo = {}
103 109
104 function Quest:GetNumWatched () 110 function Quest:GetNumWatched ()
105 print(self.name, self) 111 print(self.name, self)
112 superTrackQuestID = GetSuperTrackedQuestID()
113 playerMoney = GetMoney();
114 inScenario = C_Scenario.IsInScenario();
115 showPOIs = GetCVarBool("questPOI");
106 self.numAll = GetNumQuestLogEntries() 116 self.numAll = GetNumQuestLogEntries()
107 self.numWatched = GetNumQuestWatches() 117 self.numWatched = GetNumQuestWatches()
108 return self.numWatched, self.numAll 118 return self.numWatched, self.numAll
109 end 119 end
120
121 --- Returns an iterable table from which tracker blocks can be filled out. Data includes:
122 -- All entry-layer GetXInfo return values
123 -- Manifest of line data to be displayed in relation to the tracked object
110 Quest.GetInfo = function (self, watchIndex) 124 Quest.GetInfo = function (self, watchIndex)
111 local print = iprint 125 local print = iprint
112 print('|cFF00DDFFQuest|r.|cFF0088FFGetInfo(|r'.. tostring(watchIndex)..'|r)') 126 print('')
113 local questID, title, questIndex, numObjectives, requiredMoney, _, 127 print('|cFF00DDFFindex: |r'.. tostring(watchIndex))
114 _, isAutoComplete, failureTime, timeElapsed, questType, _, _, _, _ = GetQuestWatchInfo(watchIndex) 128
115 129 local questID, title, questLogIndex, numObjectives, requiredMoney, isComplete, startEvent, isAutoComplete,
116 if not questIndex then 130 failureTime, timeElapsed, questType, isTask, isStory, isOnMap, hasLocalPOI = GetQuestWatchInfo(watchIndex)
131 if ( not questID ) then
117 return 132 return
118 end 133 end
119 134
120 135 local _, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, _, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(questLogIndex)
121 local _, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, _, startEvent, displayQuestID, isOnMap, hasLocalPOI, isTask, isStory = GetQuestLogTitle(questIndex) 136
122
123
124 if not questID then
125 return
126 end
127 Quest.Info[questID] = Quest.Info[questID] or {} 137 Quest.Info[questID] = Quest.Info[questID] or {}
128 138
139 local showQuest = true
140 if isTask then
141 showQuest = false
142 end
143
129 local q = Quest.Info[questID] 144 local q = Quest.Info[questID]
145 -- re-use Blizzard logic for consistency
146 local watchMoney = false;
147 local tagID, typeTag, frequencyTag, completionTag, completionText
148 local isAccount, isFaction, isWeekly, isDaily = false, false, false, false
149 local isBreadcrumb = false
150 local questFailed = false
151 local watchMoney = false
152 local timerInfo, moneyInfo = false, false
153 local objectives = q.objectives or {}
154
155
156 -- Case 1: completed quest or "go to thing" breadcrumb
157 -- * 1 line containing the completion text
158 if ( isComplete and isComplete < 0 ) then
159 isComplete = false
160 questFailed = true
161 elseif ( numObjectives == 0 and playerMoney >= requiredMoney and not startEvent ) then
162 isComplete = true;
163 questFailed = false
164 if ( requiredMoney == 0 ) then
165 isBreadcrumb = true;
166 end
167 end
168 print('|cFF0088FFflags:|r', (isComplete and 'isComplete' or ''), (questFailed and 'questFailed' or ''), (isBreadcrumb and 'isBreadcrumb' or ''))
169
170 -- completion message?
171 local isSequenced = IsQuestSequenced(questID)
172 local temp_status = ''
173 if ( isComplete ) then
174 temp_status = 'COMPLETED_OBJECTIVES'
175 objectives = Quest.GetObjectives(questLogIndex, numObjectives, true, isSequenced, isStory)
176 if ( isAutoComplete ) then
177 temp_status = 'AUTOCOMPLETE_OBJECTIVES'
178 completionText = _G.QUEST_WATCH_CLICK_TO_COMPLETE
179 else
180 if ( isBreadcrumb ) then
181 temp_status = 'COMPLETE_BREADCRUMB'
182 completionText = GetQuestLogCompletionText(questLogIndex)
183 else
184 temp_status = 'COMPLETE_READY_FOR_TURN_IN'
185 completionText = _G.QUEST_WATCH_QUEST_READY
186 end
187 end
188 elseif ( questFailed ) then
189 temp_status = 'FAILED'
190 -- Case 2: failed quest
191 -- * 1 status line; hide other info
192 completionText = _G.FAILED
193 else
194
195 temp_status = 'PROGRESS_OBJECTIVES'
196 -- Case 3: quest in progress
197 -- * Multiple objective lines
198 -- * Possible extra lines for money and timer data respectively
199 objectives = Quest.GetObjectives(questLogIndex, numObjectives, false, isSequenced, isStory)
200 q.objectives = objectives
201
202 --- anything past here gets appended to existing objectives
203
204 -- money
205 if ( requiredMoney > playerMoney ) then
206
207 temp_status = temp_status .. '_MONEY'
208 local text = GetMoneyString(playerMoney).." / "..GetMoneyString(requiredMoney);
209 moneyInfo = {
210 type = 'money',
211 text = text,
212 finished = false,
213 requiredMoney = requiredMoney,
214 playerMoney = playerMoney,
215 }
216 end
217
218 -- time limit
219 if ( failureTime ) then
220 temp_status = temp_status .. '_TIMED'
221 if ( timeElapsed and timeElapsed <= failureTime ) then
222 timerInfo = {
223 type = 'timer',
224 finished = false,
225 timeElapsed = timeElapsed,
226 failureTime = failureTime,
227 }
228 end
229 end
230 end
231 q.numObjectives = numObjectives
232 q.objectives = objectives
233 q.moneyInfo = moneyInfo
234 q.timerInfo = timerInfo
235 q.completionText = completionText
236
237 -- POI data
238 local POI = false
239 if ( showPOIs ) then
240 POI = {
241 questID = questID,
242 questLogIndex = questLogIndex,
243 }
244 local poiButton;
245 if ( hasLocalPOI ) then
246
247 if ( isComplete ) then
248 POI.type = 'normal'
249 else
250 POI.type = 'numeric'
251 end
252 elseif ( isComplete ) then
253 POI.type = 'remote'
254 end
255
256 local distance, onContinent = GetDistanceSqToQuest(questLogIndex)
257 if distance ~= nil and distance > 0 then
258 POI.distance = distance
259 POI.onContinent = onContinent
260 end
261 end
262 q.POI = POI
263
264 --- Block Tags
265 -- completionTag - in progres, complete, failed, autocomplete
266 -- typeTag - account, faction, pvp, dungeon, group
267 -- frequencyTag - daily/weekly
268 local questTagID, tagName = GetQuestTagInfo(questID)
269 local tagInfo = {}
270 local tagCoords = {}
271 local factionGroup = GetQuestFactionGroup(questID);
272 if( questTagID and questTagID == QUEST_TAG_ACCOUNT ) then
273 if( factionGroup ) then
274 tagID = "ALLIANCE"
275 if ( factionGroup == LE_QUEST_FACTION_HORDE ) then
276 tagID = "HORDE"
277 end
278 isFaction = true
279 else
280 tagID = QUEST_TAG_ACCOUNT
281 isAccount = true
282 end
283 tagInfo['typeTag'] = tagID
284 tagCoords['typeTag'] = QUEST_TAG_TCOORDS[tagID]
285 elseif ( factionGroup) then
286 tagID = "ALLIANCE"
287 if ( factionGroup == LE_QUEST_FACTION_HORDE ) then
288 tagID = "HORDE"
289 end
290 isFaction = true
291 tagInfo['typeTag'] = tagID
292 tagCoords['typeTag'] = QUEST_TAG_TCOORDS[tagID]
293 end
294
295 if( frequency == LE_QUEST_FREQUENCY_DAILY and (not isComplete or isComplete == 0) ) then
296 tagID = 'DAILY'
297 tagInfo['frequencyTag'] = tagID
298 tagCoords['frequencyTag'] = QUEST_TAG_TCOORDS[tagID]
299 isDaily = true
300 elseif( frequency == LE_QUEST_FREQUENCY_WEEKLY and (not isComplete or isComplete == 0) )then
301 tagID = 'WEEKLY'
302 tagInfo['frequencyTag'] = tagID
303 tagCoords['frequencyTag'] = QUEST_TAG_TCOORDS[tagID]
304 isWeekly = true
305 elseif( questTagID ) then
306 tagID = questTagID
307 end
308
309 if( isComplete ) then
310 tagInfo['completionTag'] = 'COMPLETED'
311 elseif ( questFailed ) then
312 tagInfo['completionTag'] = 'FAILED'
313 end
314 tagCoords['completionTag'] = QUEST_TAG_TCOORDS[tagInfo['completionTag']]
315
316 q.tagInfo = tagInfo
317 q.tagCoords = tagCoords
318 -- establishes the primary block tag for view compacting
319 q.tagID = tagID
320 q.tagName = tagName
321
322 -- action button information
323 local link, icon, charges = GetQuestLogSpecialItemInfo(questLogIndex)
324 local start, duration, enable = GetQuestLogSpecialItemCooldown(questLogIndex)
325 if link or icon or charges then
326 q.specialItem = {
327 questID = questID,
328 questLogIndex = questLogIndex,
329 link = link,
330 charges = charges,
331 icon = icon,
332 start = start,
333 duration = duration,
334 enable = enable,
335 }
336 end
337
338 -- resolved data
339
340 -- raw data
130 q.watchIndex = watchIndex 341 q.watchIndex = watchIndex
131 q.type = 'Quest' 342 q.type = 'Quest'
343 q.id = questID
132 q.questID = questID 344 q.questID = questID
133 q.title = title 345 q.title = title
134 q.level = level 346 q.level = level
135 q.displayQuestID = displayQuestID 347 q.displayQuestID = displayQuestID
136 q.suggestedGroup = suggestedGroup 348 q.suggestedGroup = suggestedGroup
137 q.questLogIndex = questIndex 349 q.questLogIndex = questLogIndex
138 q.numObjectives = numObjectives 350 q.numObjectives = numObjectives
139 q.requiredMoney = requiredMoney 351 q.requiredMoney = requiredMoney
140 q.isComplete = isComplete 352 q.isComplete = isComplete
141 q.startEvent = startEvent 353 q.startEvent = startEvent
142 q.isAutoComplete = isAutoComplete 354 q.isAutoComplete = isAutoComplete
150 q.frequency = frequency 362 q.frequency = frequency
151 q.isComplete = isComplete 363 q.isComplete = isComplete
152 q.isStory = isStory 364 q.isStory = isStory
153 q.isTask = isTask 365 q.isTask = isTask
154 366
155 --- resolve icon type and template 367 q.selected = (questID == superTrackQuestID) -- call directly so artifact data doesn't become an issue
156 local questTagID, tagName = GetQuestTagInfo(questID) 368 self.WatchInfo[watchIndex] = q
157 local tagID 369 self.LogInfo[questLogIndex] = q
158 370
159 local factionGroup = GetQuestFactionGroup(questID); 371 if Devian and Devian.InWorkspace() then
160 if( questTagID and questTagID == QUEST_TAG_ACCOUNT ) then 372 print('|cFF00DDFFstatus:|r', temp_status, '|cFF00FF00questLogIndex|r:', title)
161 if( factionGroup ) then 373 local temp ={}
162 tagID = "ALLIANCE"; 374 local data_txt = '|cFFFF4400values:|r'
163 if ( factionGroup == LE_QUEST_FACTION_HORDE ) then 375 for k,v in pairs(q) do
164 tagID = "HORDE"; 376 if type(v) =='number' then
165 end 377 data_txt = data_txt .. ' |cFFFFFF00'..k..'|r: ' .. tostring(v)
166 q.isFaction = true 378 elseif type(v) == 'table' then
167 else 379 tinsert(temp, k)
168 tagID = QUEST_TAG_ACCOUNT; 380 end
169 q.isAccount = true 381 end
170 end 382 print(data_txt)
171 q.typeTag = QUEST_TAG_TCOORDS[tagID] 383 sort(temp, function(a,b) return a < b end)
172 elseif ( factionGroup) then 384 for i, k in ipairs(temp) do
173 tagID = "ALLIANCE"; 385 print('|cFF00FF00'..k..'|r')
174 if ( factionGroup == LE_QUEST_FACTION_HORDE ) then 386 for kk,v in pairs(q[k]) do
175 tagID = "HORDE"; 387 print(' ', kk, '=', v)
176 end 388 end
177 q.isFaction = true 389 end
178 end 390 end
179 391
180 if( frequency == LE_QUEST_FREQUENCY_DAILY and (not isComplete or isComplete == 0) ) then 392 return q
181 tagID = "DAILY"; 393 end
182 q.frequencyTag = QUEST_TAG_TCOORDS["DAILY"] 394
183 q.isDaily = true 395 Quest.GetObjectives = function(questLogIndex, numObjectives, isComplete, isSequenced, isStory)
184 elseif( frequency == LE_QUEST_FREQUENCY_WEEKLY and (not isComplete or isComplete == 0) )then 396 local objectives = {}
185 tagID = "WEEKLY"; 397 for i = 1, numObjectives do
186 q.frequencyTag = QUEST_TAG_TCOORDS["WEEKLY"] 398 local text, type, finished = GetQuestLogLeaderBoard(i, questLogIndex)
187 q.isWeekly = true
188 elseif( questTagID ) then
189 tagID = questTagID;
190 end
191
192 if ( isComplete and isComplete < 0 ) then
193 q.completionTag = QUEST_TAG_TCOORDS["FAILED"]
194 q.isFailed = true
195 elseif isComplete then
196 q.completionTag = QUEST_TAG_TCOORDS["COMPLETED"]
197 end
198
199
200 q.tagID = questTagID
201 q.tagName = tagName
202 --q.isBreadCrumb = isBreadCrumb
203 q.completionText= GetQuestLogCompletionText(questIndex)
204 q.numObjectives = GetNumQuestLeaderBoards(questIndex)
205 q.objectives = {}
206 for i = 1, q.numObjectives do
207 local text, type, finished = GetQuestLogLeaderBoard(i, questIndex)
208 print(format(' #%d %s %s %s', i, tostring(type), tostring(text), tostring(finished))) 399 print(format(' #%d %s %s %s', i, tostring(type), tostring(text), tostring(finished)))
209 q.objectives[i] = { 400 objectives[i] = {
210 index = i, 401 index = i,
211 type = type, 402 type = type,
212 text = text, 403 text = text,
213 finished = finished 404 finished = finished
214 } 405 }
215 if type == 'event' then 406 end
216 elseif type == 'monster' then 407 return objectives
217 elseif type == 'object' then 408 end
218 elseif type == 'reputation' then 409
219 elseif type == 'item' then 410 local huge, sqrt = math.huge, math.sqrt
220 end
221 end
222
223 if requiredMoney >= 1 then
224 local money = GetMoney()
225 local moneyText = money
226 local requiredSilver, requiredCopper
227 local requiredGold = (requiredMoney > 10000) and (floor(requiredMoney/10000)) or nil
228 if mod(requiredMoney, 10000) ~= 0 then
229 requiredSilver = (requiredMoney > 100) and (mod(requiredMoney, 10000) / 100) or nil
230 if mod(requiredMoney, 100) ~= 0 then
231 requiredCopper = mod(requiredMoney, 100)
232 end
233 end
234
235 -- round the money value down
236 if requiredMoney > 9999 and not (requiredSilver or requiredCopper) then
237 moneyText = floor(money/10000)
238 elseif requiredMoney < 10000 and mod(requiredMoney,100) == 0 then
239 moneyText = floor(money/100)
240 end
241
242 local text = moneyText
243 local index = #q.objectives + 1
244 local finished = (GetMoney() >= requiredMoney)
245
246 if not finished then
247 text = text .. ' / ' .. GetCoinTextureString(requiredMoney, 12)
248 else
249 text = '' .. GetCoinTextureString(requiredMoney, 12)
250 end
251 q.objectives[index] = {
252 index = index,
253 type = 'progressbar',
254 quantity = money,
255 requiredQuantity = requiredMoney,
256 text = text,
257 finished = finished
258 }
259 print(format(' #%d %s %s %s', index, 'money', text, tostring(finished)))
260 end
261
262
263 local link, icon, charges = GetQuestLogSpecialItemInfo(questIndex)
264 local start, duration, enable = GetQuestLogSpecialItemCooldown(questIndex)
265 if link or icon or charges then
266 q.specialItem = {
267 questID = questID,
268 questIndex = questIndex,
269 link = link,
270 charges = charges,
271 icon = icon,
272 start = start,
273 duration = duration,
274 enable = enable,
275 }
276 end
277
278 if QuestHasPOIInfo(questID) then
279 local distance, onContinent = GetDistanceSqToQuest(questIndex)
280 if distance ~= nil and distance > 0 then
281 self.POI[questIndex] = {
282 questIndex = questIndex,
283 questID = questID,
284 distance = distance,
285 onContinent = onContinent
286 }
287 end
288 end
289
290
291 q.selected = (questID == GetSuperTrackedQuestID()) -- call directly so artifact data doesn't become an issue
292 self.WatchInfo[watchIndex] = q
293 self.LogInfo[questIndex] = q
294 print('- logIndex =', questIndex, 'title =', title)
295 for k,v in pairs(q) do
296 print('|cFFFFFF00'..k..'|r:', v)
297 end
298 return q
299 end
300
301 Quest.GetClosest = function() 411 Quest.GetClosest = function()
302 local minID, minTitle 412 local minID, minTitle
303 local minDist = math.huge 413 local minDist = huge
304 local numQuests = GetNumQuestLogEntries() 414 local numQuests = GetNumQuestLogEntries()
305 for questIndex = 1, numQuests do 415 for questIndex = 1, numQuests do
306 local distance, onContinent = GetDistanceSqToQuest(questIndex) 416 local distance, onContinent = GetDistanceSqToQuest(questIndex)
307 local title, level, _, _, _, _, _, _, questID = GetQuestLogTitle(questIndex) 417 local title, level, _, _, _, _, _, _, questID = GetQuestLogTitle(questIndex)
308 if onContinent and distance < minDist then 418 if onContinent and distance < minDist then
310 minTitle = title 420 minTitle = title
311 minID = questID 421 minID = questID
312 end 422 end
313 end 423 end
314 424
315 print('nearest quest is', minTitle, 'by', math.sqrt(minDist)) 425 print('nearest quest is', minTitle, 'by', sqrt(minDist))
316 return minID, minTitle, minDist 426 return minID, minTitle, minDist
317 end 427 end
318 428
319 Quest.OnTurnIn = function(self, questID, xp, money) 429 Quest.OnTurnIn = function(self, questID, xp, money)
320 430