1 QuestHelper_File
["director_achievement.lua"] = "Development Version"
2 QuestHelper_Loadtime
["director_achievement.lua"] = GetTime()
4 local debug_output
= false
5 if QuestHelper_File
["director_achievement.lua"] == "Development Version" then debug_output
= true end
9 local function IsDoable(id
)
10 if achieveable
[id
] == nil then
11 -- First we just see if we have a DB entry for it
12 -- This can be made *much* more efficient.
13 if not DB_Ready() then
14 print("DB not yet ready, please wait")
18 if DB_HasItem("achievement", id
) then
19 --print(id, "achieveable via db")
20 achieveable
[id
] = true
24 local crit
= GetAchievementNumCriteria(id
)
26 -- Whenever I write "crit" as a variable name it always slightly worries me.
28 -- Y'see, several years ago I competed in a programming competition called Topcoder. At the end of a big tournament, if you did well, they flew you to a casino or a hotel and you competed against a bunch of other geeks, with your code posted on monitors. This was uncommonly boring unless you were a geek, but luckily, we were, so it was actually kind of exciting.
29 -- So I'm up there competing, and I need a count, so I make a variable called "cont". "count", y'see, is a function, so I can't use that. But then I need another one. I can't go with "cout" because that's actually a global variable in C++, which is what I'm using. And, well, I want to keep the first and last letters preserved, because that way it reminds me it's a count.
31 -- So I start typing the first thing that comes to mind that fulfills all the requirements.
33 -- Luckily, I stop myself in time, and write "cnt" instead.
35 -- Once or twice in QuestHelper I've needed a few variables about criteria. And there's . . . something . . . which is only one letter off from "crit", and is something I should probably not be typing in publicly accessible sourcecode.
37 -- So now you know. Back to the code.
39 -- (although let's be honest with the amount of profanity scattered throughout this codebase I'm not quite sure why I care buuuuuuuuuut here we are anyway)
43 local _
, typ
, _
, _
, _
, _
, _
, asset
, _
, cid
= GetAchievementCriteriaInfo(id
, i
)
45 -- Monster kill. We're good! We can do these.
48 if not IsDoable(asset
) then
49 achieveable
[id
] = false
53 achieveable
[id
] = false
58 if achieveable
[id
] == nil then
59 --print(id, "achieveable via occlusion")
60 achieveable
[id
] = true
63 --print(id, "not achieveable due to wizard casting what the fuck")
64 achieveable
[id
] = false
68 return achieveable
[id
]
71 local function GetListOfAchievements(category
)
72 local ct
= GetCategoryNumAchievements(category
)
74 local available_achieves
= {}
77 local id
, _
, _
, complete
= GetAchievementInfo(category
, i
)
78 if not complete
and IsDoable(id
) then
79 table.insert(available_achieves
, i
)
83 return available_achieves
86 local function FilterFunction(category
)
87 local aa
= GetListOfAchievements(category
)
92 local function SetQHVis(button
, ach
, _
, _
, complete
)
93 button
.qh_checkbox
:Hide()
94 if complete
or not IsDoable(ach
) then
95 button
.qh_checkbox
:Hide()
97 button
.qh_checkbox
:Show()
103 local function ABDA_Replacement(button
, category
, achievement
, selectionID
)
105 -- i am sneaky like fish
109 if ABDA_permit
and ACHIEVEMENTUI_SELECTEDFILTER
== FilterFunction
then
110 local aa
= GetListOfAchievements(category
)
111 local ach
= aa
[achievement
]
112 ABDA(button
, category
, ach
, selectionID
)
113 SetQHVis(button
, GetAchievementInfo(category
, ach
))
115 ABDA(button
, category
, achievement
, selectionID
)
116 SetQHVis(button
, GetAchievementInfo(category
, achievement
))
121 local function AFAU_Replacement(...)
127 local TrackedAchievements
= {}
128 local MetaAchievements
= {}
129 local Update_Objectives
131 local function MarkAchieveable(id
, setto
)
132 TrackedAchievements
[id
] = setto
134 local crit
= GetAchievementNumCriteria(id
)
136 local _
, typ
, _
, _
, _
, _
, _
, asset
, _
, cid
= GetAchievementCriteriaInfo(id
, i
)
138 MetaAchievements
[id
] = true
139 MarkAchieveable(asset
, setto
)
146 function QH_COC_GIX(id
)
147 MarkAchieveable(id
, true)
151 local function check_onclick(self
)
152 if self
:GetChecked() then
153 MarkAchieveable(self
:GetParent().id
, true)
155 MarkAchieveable(self
:GetParent().id
, nil)
159 for i
= 1, #AchievementFrameAchievements
.buttons
do
160 check_onshow(AchievementFrameAchievements
.buttons
[i
].qh_checkbox
)
164 local function check_onenter(self
)
165 GameTooltip
:SetOwner(self
, "ANCHOR_RIGHT")
166 GameTooltip
:SetText(QHText("ACHIEVEMENT_CHECKBOX"))
168 local function check_onleave(self
)
171 function check_onshow(self
)
172 self
:SetChecked(TrackedAchievements
[self
:GetParent().id
])
175 local AAFOC
= AchievementAlertFrame_OnClick
176 function AAF_onclick(self
)
177 if not self
.id
then return end
179 local _
, _
, _
, complete
= GetAchievementInfo(self
.id
);
180 if complete
and ACHIEVEMENTUI_SELECTEDFILTER
== FilterFunction
then -- yipyipyipyipyipyipyip
181 AchievementFrame_SetFilter(ACHIEVEMENT_FILTER_ALL
) -- uh-huh, uh-huh
186 AchievementAlertFrame_OnClick
= AAF_onclick
188 QH_Event("ADDON_LOADED", function (addonid
)
189 if addonid
== "Blizzard_AchievementUI" then
191 table.insert(AchievementFrameFilters
, {text
="QuestHelpable", func
=FilterFunction
})
193 ABDA
= AchievementButton_DisplayAchievement
194 AchievementButton_DisplayAchievement
= ABDA_Replacement
196 AFAU
= AchievementFrameAchievements_Update
197 AchievementFrameAchievements_Update
= AFAU_Replacement
198 AchievementFrameAchievementsContainer
.update
= AFAU_Replacement
199 ACHIEVEMENT_FUNCTIONS
.updateFunc
= AFAU_Replacement
201 for i
= 1, #AchievementFrameAchievements
.buttons
do
202 local framix
= CreateFrame("CheckButton", "qh_arglbargl_" .. i
, AchievementFrameAchievements
.buttons
[i
], "AchievementCheckButtonTemplate")
203 framix
:SetPoint("BOTTOMRIGHT", AchievementFrameAchievements
.buttons
[i
], "BOTTOMRIGHT", -22, 7.5)
204 framix
:SetScript("OnEnter", check_onenter
)
205 framix
:SetScript("OnLeave", check_onleave
)
207 framix
:SetScript("OnShow", check_onshow
)
208 framix
:SetScript("OnClick", check_onclick
)
210 _G
["qh_arglbargl_" .. i
.. "Text"]:Hide() -- no
212 local sigil
= framix
:CreateTexture("BACKGROUND")
215 sigil
:SetTexture("Interface\\AddOns\\QuestHelper\\sigil")
216 sigil
:SetPoint("RIGHT", framix
, "LEFT", -1, 0)
217 sigil
:SetVertexColor(0.6, 0.6, 0.6)
219 AchievementFrameAchievements
.buttons
[i
].qh_checkbox
= framix
225 local function horribledupe(from
)
226 if not from
then return nil end
229 for k
, v
in pairs(from
) do
230 if k
== "__owner" then
231 elseif type(v
) == "table" then
232 rv
[k
] = horribledupe(v
)
240 local achievement_list
= setmetatable({}, {__mode
="k"})
241 function GetAchievementMetaObjective(achievement
)
242 if achievement_list
[achievement
] then return achievement_list
[achievement
] end
244 local db
= DB_GetItem("achievement", achievement
, true)
247 ite
.desc
= select(2, GetAchievementInfo(achievement
))
248 ite
.tracker_desc
= ite
.desc
249 ite
.tracker_split
= true
252 if db
and db
.achieved
then
253 table.insert(itlist
, {cid
= "achieved", chunk
= db
.achieved
})
255 local crit
= GetAchievementNumCriteria(achievement
)
258 local name
, typ
, _
, _
, _
, _
, _
, asset
, _
, cid
= GetAchievementCriteriaInfo(achievement
, i
)
265 chunk
= DB_GetItem("monster", asset
)
273 local soldupe
= horribledupe(chunk
.solid
)
274 for i
= 1, #chunk
.loc
do
275 table.insert(itlist
, {cid
= cid
, solid
= soldupe
, loc
= chunk
.loc
[i
], name
= name
})
278 table.insert(itlist
, {cid
= cid
, chunk
= chunk
, name
= name
})
283 for _
, data
in pairs(itlist
) do
287 ttx
.solid
= horribledupe(data
.chunk
.solid
)
288 if data
.chunk
.loc
then for _
, v
in ipairs(data
.chunk
.loc
) do
289 table.insert(ttx
, {loc
= {x
= v
.x
, y
= v
.y
, c
= QuestHelper_ParentLookup
[v
.p
], p
= v
.p
}})
294 ttx
.solid
= data
.solid
295 table.insert(ttx
, {loc
= {x
= data
.loc
.x
, y
= data
.loc
.y
, c
= QuestHelper_ParentLookup
[data
.loc
.p
], p
= data
.loc
.p
}})
299 table.insert(ttx
, {loc
= {x
= 5000, y
= 5000, c
= 0, p
= 2}, icon_id
= 7, type_quest_unknown
= true}) -- this is Ashenvale, for no particularly good reason
300 ttx
.type_achievement_unknown
= true
303 for _
, v
in ipairs(ttx
) do
304 v
.map_desc
= {ite
.desc
, data
.name
}
305 v
.tracker_desc
= data
.name
or ite
.desc
306 v
.tracker_hide_dupes
= true
307 v
.hidden_desc
= data
.name
and string.format("%s: %s", ite
.desc
, data
.name
) or ite
.desc
308 v
.arrow_desc
= v
.hidden_desc
309 if not data
.name
then v
.tracker_hidden
= true end
315 if not ite
[data
.cid
] then
319 table.insert(ite
[data
.cid
], ttx
)
322 achievement_list
[achievement
] = ite
324 return achievement_list
[achievement
]
327 local function achievement_is_unified(ach
)
328 return GetAchievementMetaObjective(ach
).achieved
332 local current_aches
= {}
333 local next_aches
= {}
335 local ach_locational
= {}
337 local function AchUpdateStart()
340 local function AchUpdateAdd(ach
, crit
)
341 if not next_aches
[ach
] then next_aches
[ach
] = {} end
342 next_aches
[ach
][crit
] = true
344 local function AchUpdateEnd()
346 for k
, v
in pairs(current_aches
) do
348 if not next_aches
[k
] or not next_aches
[k
][c
] then
349 local meta
= GetAchievementMetaObjective(k
)
351 for i
= 1, #meta
[c
] do
352 QH_Route_ClusterRemove(meta
[c
][i
])
354 for _
, nod
in ipairs(meta
[c
][i
]) do
355 ach_locational
[nod
.loc
] = nil
362 for k
, v
in pairs(next_aches
) do
365 if not current_aches
[k
] or not current_aches
[k
][c
] then
366 local meta
= GetAchievementMetaObjective(k
)
368 for i
= 1, #meta
[c
] do
369 QH_Route_ClusterAdd(meta
[c
][i
])
371 for _
, nod
in ipairs(meta
[c
][i
]) do
372 if not ach_locational
[nod
.loc
] then
374 kacey
= QuestHelper
:CreateTable("ach_locational")
378 --print(nod.loc.p, nod.loc.x, nod.loc.y)
379 ach_locational
[nod
.loc
] = kacey
387 current_aches
= next_aches
-- yaaaaaaaay
390 function qh_critupd()
393 local c
, z
, x
, y
= QuestHelper
.routing_c
, QuestHelper
.routing_z
, QuestHelper
.routing_ax
, QuestHelper
.routing_ay
395 if not c
or not z
or not x
or not y
then return end
397 local p
= QuestHelper_IndexLookup
[c
][z
]
399 if not p
then return end
401 local closest_dist
= 1000000000000
402 local closest_item
= nil
403 for k
, v
in pairs(ach_locational
) do
405 local dx
, dy
= x
- k
.x
, y
- k
.y
406 dx
, dy
= dx
* dx
, dy
* dy
408 if dd
< closest_dist
then
415 --print(closest_item)
416 if not closest_item
then return end -- bort bort bort
417 local k
, c
= closest_item
.k
, closest_item
.c
419 if not (type(k
) == "number" and type(c
) == "number") then return end
421 local _
, _
, crit_complete
= GetAchievementCriteriaInfo(c
)
422 --print(crit_complete)
423 if not crit_complete
then return end -- welp
425 --print("desplicing", k, c)
426 assert(current_aches
[k
][c
])
427 -- hey we found it gee jay
428 current_aches
[k
][c
] = nil
429 local meta
= GetAchievementMetaObjective(k
)
431 for i
= 1, #meta
[c
] do
432 QH_Route_ClusterRemove(meta
[c
][i
])
433 for _
, nod
in ipairs(meta
[c
][i
]) do
434 ach_locational
[nod
.loc
] = nil
438 QH_Event("CRITERIA_UPDATE", qh_critupd
)
443 function Update_Objectives(_
, new
)
444 if not new
then new
= db
end -- sometimes we're just told to update thanks to a change in checkmarks, and this is the easiest way to keep a DB around
447 if not new
then QH_AchievementManagerRegister_Poke() return end
452 for k
in pairs(TrackedAchievements
) do
453 --print("updating achievement", k)
455 if not MetaAchievements
[k
] then
456 local achid
= new
.achievements
[k
]
457 if not achid
then print(k
) end
459 if achid
.complete
then
462 if achievement_is_unified(k
) then
463 AchUpdateAdd(k
, "achieved")
465 local critcount
= GetAchievementNumCriteria(k
)
466 for i
= 1, critcount
do
467 local _
, _
, _
, _
, _
, _
, _
, _
, _
, crit
= GetAchievementCriteriaInfo(k
, i
)
469 if not new
.criteria
[crit
].complete
then
470 AchUpdateAdd(k
, crit
)
478 for k
in pairs(oblit
) do
479 TrackedAchievements
[k
] = nil
485 QH_AchievementManagerRegister(Update_Objectives
)
486 QH_AchievementManagerRegister_Poke()