1 QuestHelper_File
["collect_quest.lua"] = "Development Version"
2 QuestHelper_Loadtime
["collect_quest.lua"] = GetTime()
4 local debug_output
= false
5 if QuestHelper_File
["collect_quest.lua"] == "Development Version" then debug_output
= true end
19 local function RegisterQuestData(category
, location
, GetQuestLogWhateverInfo
)
23 local ilink
= GetQuestLogItemLink(category
, index
)
24 if not ilink
then break end
26 if not localspot
then if not location
["items_" .. category
] then location
["items_" .. category
] = {} end localspot
= location
["items_" .. category
] end
28 local name
, tex
, num
, qual
, usa
= GetQuestLogWhateverInfo(index
)
29 localspot
[GetItemType(ilink
)] = num
31 --QuestHelper:TextOut(string.format("%s:%d - %d %s %s", category, index, num, tostring(ilink), tostring(name)))
37 local complete_suffix
= string.gsub(string.gsub(string.gsub(ERR_QUEST_OBJECTIVE_COMPLETE_S
, "%%s", ""), "%)", "%%)"), "%(", "%%(")
39 QuestHelper
:TextOut("^.*: (%d+)/(%d+)(" .. complete_suffix
.. ")?$")
42 local function ScanQuests() -- make it local once we've debugged it
50 if not GetQuestLogTitle(index
) then break end
52 local qlink
= GetQuestLink(index
)
54 --QuestHelper:TextOut(qlink)
55 --QuestHelper:TextOut(string.gsub(qlink, "|", "||"))
56 local id
, level
= GetQuestType(qlink
)
58 --QuestHelper:TextOut(string.format("%s - %d %d", qlink, id, level))
62 if not selected
then selected
= GetQuestLogSelection() end
63 SelectQuestLogEntry(index
)
66 QHCQ
[id
].level
= level
68 RegisterQuestData("reward", QHCQ
[id
], GetQuestLogRewardInfo
)
69 RegisterQuestData("choice", QHCQ
[id
], GetQuestLogChoiceInfo
)
71 --QuestHelper:TextOut(string.format("%d", GetNumQuestLeaderBoards(index)))
72 for i
= 1, GetNumQuestLeaderBoards(index
) do
73 local desc
, type = GetQuestLogLeaderBoard(i
, index
)
74 QHCQ
[id
][string.format("criteria_%d_text", i
)] = desc
75 QHCQ
[id
][string.format("criteria_%d_type", i
)] = type
76 --QuestHelper:TextOut(string.format("%s, %s", desc, type))
79 local title
, _
, tag, groupcount
, _
, _
, _
, daily
= GetQuestLogTitle(index
)
83 QHCQ
[id
].groupcount
= (groupcount
or -1)
84 QHCQ
[id
].daily
= (not not daily
)
90 --QuestHelper:TextOut(string.format("%d", GetNumQuestLeaderBoards(index)))
92 for i
= 1, GetNumQuestLeaderBoards(index
) do
93 local desc
, _
, done
= GetQuestLogLeaderBoard(i
, index
)
95 -- If we wanted to parse everything here, we'd do something very complicated.
96 -- Fundamentally, we don't. We only care if numeric values change or if something goes from "not done" to "done".
97 -- Luckily, the patterns are identical in all cases for this (I think.)
98 local have
, needed
= string.match(desc
, "^.*: (%d+)/(%d+)$")
100 needed
= tonumber(needed
)
102 --[[QuestHelper:TextOut(desc)
103 QuestHelper:TextOut("^.*: (%d+)/(%d+)(" .. complete_suffix .. ")?$")
104 QuestHelper:TextOut(string.gsub(desc, complete_suffix, ""))
105 QuestHelper:TextOut(string.format("%s %s", tostring(have), tostring(needed)))]]
106 if not have
or not needed
then
107 have
= done
and 1 or 0
108 needed
= 1 -- okay so we don't really use this unless we're debugging, shut up >:(
118 if selected
then SelectQuestLogEntry(selected
) end -- abort abort bzzt bzzt bzzt awoooooooga dive, dive, dive
125 local function Looted(message
)
126 local ltype
= GetItemType(message
, true)
127 table.insert(eventy
, {time
= GetTime(), event
= string.format("I%di", ltype
)})
128 --if debug_output then QuestHelper:TextOut(string.format("Added event %s", string.format("I%di", ltype))) end
131 local function Combat(_
, event
, _
, _
, _
, guid
)
132 if event
~= "UNIT_DIED" then return end
133 if not IsMonsterGUID(guid
) then return end
134 local mtype
= GetMonsterType(guid
, true)
135 table.insert(eventy
, {time
= GetTime(), event
= string.format("M%dm", mtype
)})
136 --if debug_output then QuestHelper:TextOut(string.format("Added event %s", string.format("M%dm", mtype))) end
139 local changed
= false
142 local function Init()
146 local function LogChanged()
150 local function WatchUpdate() -- we're currently ignoring the ID of the quest that was updated for simplicity's sake.
154 local function AppendMember(tab
, key
, dat
)
155 tab
[key
] = (tab
[key
] or "") .. dat
158 local function StartOrEnd(se
, id
)
159 local targuid
= UnitGUID("target")
161 if targuid
and IsMonsterGUID(targuid
) then
162 chunk
= string.format("M%dm", GetMonsterType(targuid
))
164 chunk
= chunk
.. GetLoc()
166 AppendMember(QHCQ
[id
], se
, chunk
)
169 local abandoncomplete
= ""
170 local abandoncomplete_timestamp
= nil
172 local GetQuestReward_Orig
= GetQuestReward
173 GetQuestReward
= function (...)
174 abandoncomplete
= "complete"
175 abandoncomplete_timestamp
= GetTime()
176 GetQuestReward_Orig(...)
179 local AbandonQuest_Orig
= AbandonQuest
180 AbandonQuest
= function ()
181 abandoncomplete
= "abandon"
182 abandoncomplete_timestamp
= GetTime()
186 local function UpdateQuests()
187 if first
then deebey
= ScanQuests() first
= false end
188 if not changed
then return end
191 local tim
= GetTime()
193 local noobey
= ScanQuests()
197 local dsize
, nsize
= QuestHelper
:TableSize(deebey
), QuestHelper
:TableSize(noobey
)
199 for k
, _
in pairs(deebey
) do traverse
[k
] = true end
200 for k
, _
in pairs(noobey
) do traverse
[k
] = true end
203 if QuestHelper:TableSize(deebey) ~= QuestHelper:TableSize(noobey) then
204 QuestHelper:TextOut(string.format("%d %d", QuestHelper:TableSize(deebey), QuestHelper:TableSize(noobey)))
207 while #eventy
> 0 and eventy
[1].time
< GetTime() - 1 do table.remove(eventy
, 1) end -- slurp
213 for k
, _
in pairs(traverse
) do
214 if not deebey
[k
] then
215 -- Quest was acquired
216 if debug_output
then QuestHelper
:TextOut(string.format("Acquired! Questid %d", k
)) end
217 StartOrEnd("start", k
)
220 elseif not noobey
[k
] then
221 -- Quest was dropped or completed
222 if abandoncomplete
== "complete" and abandoncomplete_timestamp
+ 30 >= GetTime() then
223 if debug_output
then QuestHelper
:TextOut(string.format("Completed! Questid %d", k
)) end
227 if debug_output
then QuestHelper
:TextOut(string.format("Dropped! Questid %d", k
)) end
233 QuestHelper
: Assert(#deebey
[k
] == #noobey
[k
])
234 for i
= 1, #deebey
[k
] do
237 if not (noobey[k][i] >= deebey[k][i]) then
238 QuestHelper:TextOut(string.format("%s, %s", type(noobey[k][i]), type(deebey[k][i])))
239 QuestHelper:TextOut(string.format("%d, %d", noobey[k][i], deebey[k][i]))
240 for index = 1, 100 do
241 local qlink = GetQuestLink(index)
242 if qlink then qlink = GetQuestType(qlink) end
244 QuestHelper:TextOut(GetQuestLogLeaderBoard(i, index))
248 QuestHelper: Assert(noobey[k][i] >= deebey[k][i]) -- man I hope this is true]] -- This entire section can fail if people throw away quest items, or if quest items have a duration that expires. Sigh.
250 if noobey
[k
][i
] > deebey
[k
][i
] then
253 for k
, v
in pairs(eventy
) do token
= token
.. v
.event
end
255 token
= token
.. "L" .. GetLoc() .. "l"
259 if noobey
[k
][i
] - 1 ~= deebey
[k
][i
] then
260 ttok
= string.format("C%dc", noobey
[k
][i
] - deebey
[k
][i
]) .. ttok
263 AppendMember(QHCQ
[k
], string.format("criteria_%d_satisfied", i
), ttok
)
265 if debug_output
then QuestHelper
:TextOut(string.format("Updated! Questid %d item %d count %d tok %s", k
, i
, noobey
[k
][i
] - deebey
[k
][i
], debugtok
)) end
274 QuestHelper
: Assert(diffs
<= 5, string.format("excessive quest diffs - delta is %d, went from %d to %d", diffs
, dsize
, nsize
))
275 --QuestHelper:TextOut(string.format("done in %f", GetTime() - tim))
278 function QH_Collect_Quest_Init(QHCData
, API
)
279 if not QHCData
.quest
then QHCData
.quest
= {} end
282 GetQuestType
= API
.Utility_GetQuestType
283 GetItemType
= API
.Utility_GetItemType
284 IsMonsterGUID
= API
.Utility_IsMonsterGUID
285 GetMonsterType
= API
.Utility_GetMonsterType
286 QuestHelper
: Assert(GetQuestType
)
287 QuestHelper
: Assert(GetItemType
)
288 QuestHelper
: Assert(IsMonsterGUID
)
289 QuestHelper
: Assert(GetMonsterType
)
291 GetLoc
= API
.Callback_LocationBolusCurrent
292 QuestHelper
: Assert(GetLoc
)
294 deebey
= ScanQuests()
296 API
.Registrar_EventHook("UNIT_QUEST_LOG_CHANGED", LogChanged
)
297 API
.Registrar_EventHook("QUEST_LOG_UPDATE", UpdateQuests
)
298 API
.Registrar_EventHook("QUEST_WATCH_UPDATE", WatchUpdate
)
300 API
.Registrar_EventHook("CHAT_MSG_LOOT", Looted
)
301 API
.Registrar_EventHook("COMBAT_LOG_EVENT_UNFILTERED", Combat
)
303 -- Here's a pile of events that seem to trigger during startup that also don't seem like would trigger while questing.
304 -- We'll lose a few quest updates from this, but that's OK.
305 API
.Registrar_EventHook("PLAYER_ENTERING_WORLD", Init
)
306 API
.Registrar_EventHook("UNIT_MODEL_CHANGED", Init
)
307 API
.Registrar_EventHook("GUILDBANK_UPDATE_WITHDRAWMONEY", Init
)
308 API
.Registrar_EventHook("UPDATE_TICKET", Init
)