update changes
[QuestHelper.git] / collect_achievement.lua
blob63eb5fecd600a396ced2fc10fdec0c29a97243e1
1 QuestHelper_File["collect_achievement.lua"] = "Development Version"
3 QHDataCollector.achievement = {}
5 --X 0 is a monster kill, asset is the monster ID
6 --X 1 is winning PvP objectives in a thorough manner (holding all bases, controlling all flags)
7 --X 7 is weapon skill, asset is probably a skill ID of some sort
8 --X 8 is another achievement, asset is achievement ID
9 --X 9 is completing quests globally
10 --X 10 is completing a daily quest every day
11 --X 11 is completing quests in specific areas
12 --X 14 is completing daily quests
13 --X 27 is a quest, asset is quest ID
14 --X 28 is getting a spell cast on you, asset is a spell ID
15 --X 29 is casting a spell (often crafting), asset is a spell ID
16 --X 30 is PvP objectives (flags, assaulting, defending)
17 --X 31 is PvP kills in battleground PvP locations
18 --X 32 is winning ranked arena matches in specific locations (asset is probably a location ID)
19 --X 34 is the Squashling (owning a specific pet?), asset is the spell ID
20 --X 35 is PvP kills while under the influence of something
21 --X 36 is acquiring items (soulbound), asset is an item ID
22 --X 37 is winning arenas
23 --X 41 is eating or drinking a specific item, asset is item ID
24 --X 42 is fishing things up, asset is item ID
25 --X 43 is exploration, asset is a location ID?
26 --X 45 is purchasing 7 bank slots
27 --X 46 is exalted rep, asset is presumably some kind of faction ID
28 --X 47 is 5 reputations to exalted
29 --X 49 is equipping items, asset is a slot ID (quality is presumably encoded into flags)
30 --X 52 is killing specific classes of player
31 --X 53 is kill-a-given-race, asset is race ID?
32 -- 54 is using emotes on targets, asset ID is likely the emote ID
33 --X 56 is being a wrecking ball in Alterac Valley
34 --X 62 is getting gold from quest rewards
35 --X 67 is looting gold
36 -- 68 is reading books
37 -- 70 is killing players in world PvP locations
38 -- 72 is fishing things from schools or wreckage
39 --X 73 is killing Mal'Ganis on Heroic. Why? Who can say.
40 --X 75 is obtaining mounts
41 -- 109 is fishing, either in general or in specific locations
42 -- 110 is casting spells on specific targets, asset ID is the spell ID
43 --X 112 is learning cooking recipes
44 --X 113 is honorable kills
45 local achievement_type_blacklist = {}
46 for _, v in pairs({0, 1, 7, 8, 9, 10, 11, 14, 27, 28, 29, 30, 31, 32, 34, 35, 36, 37, 41, 42, 43, 45, 46, 47, 49, 52, 53, 56, 62, 67, 73, 75, 112, 113}) do
47 achievement_type_blacklist[v] = true
48 end
50 local achievement_list = {}
52 --local crittypes = {}
53 --QuestHelper_ZorbaForgotToRemoveThis = {}
55 local function registerAchievement(id)
56 --if db.achievements[id] then return end
58 local _, title, _, complete = GetAchievementInfo(id)
59 --QuestHelper:TextOut(string.format("Registering %d (%s)", id, title))
60 local prev = GetPreviousAchievement(id)
61 local record = false
63 --[[
64 db.achievements[id] = {
65 previous = prev,
66 compete = complete,
67 name = title,
68 criterialist = {}
70 local dbi = db.achievements[id]
73 if prev then
74 registerAchievement(prev)
75 end
77 local critcount = GetAchievementNumCriteria(id)
78 if critcount == 0 then record = true end
80 for i = 1, critcount do
81 local crit_name, crit_type, crit_complete, crit_quantity, crit_reqquantity, _, _, crit_asset, _, crit_id = GetAchievementCriteriaInfo(id, i)
83 --if not QuestHelper_ZorbaForgotToRemoveThis[string.format("%d", crit_type)] then QuestHelper_ZorbaForgotToRemoveThis[string.format("%d", crit_type)] = {} end
84 --QuestHelper_ZorbaForgotToRemoveThis[string.format("%d", crit_type)][title .. " --- " .. mega[1]] = crit_asset
86 --[[
87 table.insert(dbi.criterialist, crit_id)
88 ass ert (not db.criteria[crit_id])
89 crittypes[crit_type] = (crittypes[crit_type] or 0) + 1]]
91 if not achievement_type_blacklist[crit_type] then record = true end
93 --[[
94 db.criteria[crit_id] = {
95 name = crit_name,
96 type = crit_type,
97 complete = crit_complete,
98 progress = crit_quantity,
99 progress_total = crit_reqquantity,
100 asset = crit_asset,
104 if record then achievement_list[id] = true end
107 function createAchievementList()
108 for _, catid in pairs(GetCategoryList()) do
109 for d = 1, GetCategoryNumAchievements(catid) do
110 registerAchievement(GetAchievementInfo(catid, d), db)
115 local achievement_stop_time = 0
117 local function ScanAchievementYield()
118 if GetTime() > achievement_stop_time then
119 -- As a safety, reset stop time to 0. If somehow we fail to set it next time,
120 -- we'll be sure to yield promptly.
121 achievement_stop_time = 0
122 coroutine.yield()
123 achievement_stop_time = GetTime() + 1e-3 -- this gives us like 1ms/frame, which is pretty crummy. TODO: unified architecture for coroutines?
127 local function retrieveAchievement(id, db, noyield)
128 if not noyield then ScanAchievementYield() end
130 local _, _, _, complete = GetAchievementInfo(id)
131 --QuestHelper:TextOut(string.format("Registering %d (%s)", id, title))
133 db.achievements[id] = QuestHelper:CreateTable("collect_achievement achievement")
134 db.achievements[id].complete = complete
136 local dbi = db.achievements[id]
138 local critcount = GetAchievementNumCriteria(id)
140 --QuestHelper:TextOut(string.format("%d criteria", crit))
141 for i = 1, critcount do
142 local _, _, crit_complete, crit_quantity, crit_reqquantity, _, _, _, _, crit_id = GetAchievementCriteriaInfo(id, i)
144 db.criteria[crit_id] = QuestHelper:CreateTable("collect_achievement criteria")
145 db.criteria[crit_id].complete = crit_complete
146 db.criteria[crit_id].progress = crit_quantity
150 function getAchievementDB(noyield)
151 local db = {}
152 db.achievements = {}
153 db.criteria = {}
155 for k in pairs(achievement_list) do
156 retrieveAchievement(k, db, noyield)
159 return db
162 local needsUpdate = true
164 local function OnEvent(frame, event)
165 needsUpdate = true
168 --[[
169 function QuestHelper:RunCoroutine()
170 if coroutine.status(update_route) ~= "dead" then
171 coroutine_running = true
172 -- At perf = 100%, we will run 5 ms / frame.
173 coroutine_stop_time = GetTime() + 4e-3 * QuestHelper_Pref.perf_scale * ((route_pass > 0) and 5 or 1)
174 local state, err = coroutine.resume(update_route, self)
175 coroutine_running = false
176 if not state then
177 self:TextOut("|cffff0000The routing co-routine just exploded|r: |cffffff77"..tostring(err).."|r")
178 QuestHelper_ErrorCatcher_ExplicitError(err, "", "(Routing error)\n")
181 end]]
184 local function ScanAchievements()
185 needsUpdate = false -- This prevents error spam.
187 local newADB = getAchievementDB()
188 local oldADB = QHDataCollector.achievement.AchievementDB
190 for k, v in pairs(newADB.achievements) do
191 if v.complete ~= oldADB.achievements[k].complete then
192 assert(v.complete and not oldADB.achievements[k].complete)
193 --QuestHelper:TextOut(string.format("Achievement complete, %s", select(2, GetAchievementInfo(k))))
197 for k, v in pairs(newADB.criteria) do
198 if v.complete ~= oldADB.criteria[k].complete then
199 assert(v.complete and not oldADB.criteria[k].complete)
200 --QuestHelper:TextOut(string.format("Criteria complete, %d", k))
201 --QuestHelper:TextOut(string.format("Criteria complete, %s", select(1, GetAchievementCriteriaInfo(k))))
202 elseif v.progress > oldADB.criteria[k].progress then
203 --QuestHelper:TextOut(string.format("Criteria progress, %d", k))
204 --QuestHelper:TextOut(string.format("Criteria progress, %s", select(1, GetAchievementCriteriaInfo(k))))
208 QHDataCollector.achievement.AchievementDB = newADB
210 for k, v in pairs(oldADB.achievements) do QuestHelper:ReleaseTable(v) end
211 for k, v in pairs(oldADB.criteria) do QuestHelper:ReleaseTable(v) end
214 local achievement_scanning = nil
216 local function OnUpdate()
217 if achievement_scanning then
218 QuestHelper:Assert(coroutine.status(achievement_scanning) ~= "dead")
219 local state, err = coroutine.resume(achievement_scanning)
220 if not state then
221 QuestHelper_ErrorCatcher_ExplicitError(err, "", "(Achievement scanning error)\n")
222 elseif coroutine.status(achievement_scanning) == "dead" then
223 achievement_scanning = nil
225 elseif needsUpdate and QHDataCollector.achievement.AchievementDB then
226 achievement_scanning = coroutine.create(function() ScanAchievements() end)
227 QuestHelper:Assert(achievement_scanning)
228 OnUpdate() -- this is just easier
232 QHDataCollector.achievement.frame = CreateFrame("Frame")
234 QHDataCollector.achievement.frame:UnregisterAllEvents()
235 QHDataCollector.achievement.frame:RegisterEvent("CRITERIA_UPDATE")
236 QHDataCollector.achievement.frame:RegisterEvent("ACHIEVEMENT_EARNED")
237 QHDataCollector.achievement.frame:SetScript("OnEvent", OnEvent)
238 QHDataCollector.achievement.frame:SetScript("OnUpdate", OnUpdate)
240 QHDataCollector.achievement.frame:Show()
243 function QH_InitAchievementCollector()
244 createAchievementList()
245 QHDataCollector.achievement.AchievementDB = getAchievementDB(true) -- 'coz we're lazy