Fixed a bug where an objective could be added to the route multiple times if inserted...
[QuestHelper.git] / questlog.lua
blob30817477889d2c2831e2adb0eb0efcdb33908616
1 --[[QuestHelper.debug_objectives =
3 ["Harbinger of Doom"] =
5 cat="quest", what="Harbinger of Doom", sub=
7 ["Slay Harbinger Skyriss"] =
9 cat="monster", what="Harbinger Skyriss"
13 }]]
15 function QuestHelper:LoadDebugObjective(name, data)
16 local obj = self:GetObjective(data.cat, data.what)
18 self:SetObjectivePriority(obj, 3)
19 self:AddObjectiveWatch(obj, name)
21 if data.sub then
22 for name, sdata in pairs(data.sub) do
23 self:ObjectiveObjectDependsOn(obj, QuestHelper:LoadDebugObjective(name, sdata))
24 end
25 end
27 return obj
28 end
30 local ITEM_PATTERN, REPUTATION_PATTERN, MONSTER_PATTERN, OBJECT_PATTERN = false, false, false, false
32 local function buildPatterns()
33 if not ITEM_PATTERN then
34 ITEM_PATTERN = QuestHelper:convertPattern(QUEST_OBJECTS_FOUND)
35 REPUTATION_PATTERN = QuestHelper:convertPattern(QUEST_FACTION_NEEDED)
36 MONSTER_PATTERN = QuestHelper:convertPattern(QUEST_MONSTERS_KILLED)
37 OBJECT_PATTERN = QuestHelper:convertPattern(QUEST_OBJECTS_FOUND)
38 replacePattern = nil
39 end
40 end
42 function QuestHelper:GetQuestLogObjective(quest_index, objective_index)
43 local text, category, done = GetQuestLogLeaderBoard(objective_index, quest_index)
45 buildPatterns()
47 local wanted, verb, have, need
49 if category == "monster" then
50 wanted, have, need = MONSTER_PATTERN(text)
51 verb = QHText("SLAY_VERB")
52 elseif category == "item" then
53 wanted, have, need = ITEM_PATTERN(text)
54 verb = QHText("ACQUIRE_VERB")
55 elseif category == "reputation" then
56 wanted, have, need = REPUTATION_PATTERN(text)
57 elseif category == "object" then
58 wanted, have, need = OBJECT_PATTERN(text)
59 elseif category == "event" then
60 wanted, have, need = text, 0, 1
61 else
62 QuestHelper:TextOut("Unhandled event type: "..category)
63 end
65 if not wanted then
66 verb = nil
68 _, _, wanted, have, need = string.find(text, "^%s*(.-)%s*:%s*(.-)%s*/%s*(.-)%s*$")
69 if not wanted then
70 _, _, wanted = string.find("^%s*(.-)%s*$")
71 have, need = 0, 1
72 end
73 end
75 if not need then need = 1 end
76 if done then have = need end
78 return category, verb, wanted or text, tonumber(have) or have, tonumber(need) or need
79 end
81 function QuestHelper:FixedGetQuestLogTitle(index)
82 local title, level, qtype, players, header, collapsed, status, daily = GetQuestLogTitle(index)
84 if title and level then
85 local _, _, real_title = string.find(title, "^%["..level.."[^%s]-%]%s?(.+)$")
86 title = real_title or title
87 end
89 return title, level, qtype, players, header, collapsed, status, daily
90 end
92 function QuestHelper:GetQuestLevel(quest_name)
93 local index = 1
94 while true do
95 local title, level = self:FixedGetQuestLogTitle(index)
96 if not title then return 0 end
97 if title == quest_name then
98 local original_entry = GetQuestLogSelection()
99 SelectQuestLogEntry(index)
100 local hash = self:HashString(select(2, GetQuestLogQuestText()))
101 SelectQuestLogEntry(original_entry)
102 return level, hash
104 index = index + 1
108 function QuestHelper:ItemIsForQuest(item_object, item_name)
109 if not item_object.o.quest then
110 return nil
111 else
112 for quest, lq in pairs(self.quest_log) do
113 if lq.goal then
114 for i, lo in ipairs(lq.goal) do
115 if lo.category == "item" and lo.wanted == item_name then
116 return quest
122 return nil
125 local first_time = true
127 function QuestHelper:ScanQuestLog()
128 local original_entry = GetQuestLogSelection()
129 local quests = self.quest_log
131 local party_levels = self.party_levels
132 if not party_levels then
133 party_levels = {}
134 self.party_levels = party_levels
137 local level_average = UnitLevel("player")
138 local users = 1
140 if not QuestHelper_Pref.solo then
141 for n=1,4 do
142 local level = UnitLevel("party"..n)
144 if level and level > 0 then
145 level_average = level_average + level
146 users = users + 1
151 level_average = level_average / users
153 for n = 1,5 do
154 party_levels[n] = level_average+15-15*math.pow(n/users, 0.4)
157 for i, quest in pairs(quests) do
158 -- Will set this to false if the player still has it.
159 quest.removed = true
162 local index = 1
163 while true do
164 local title, level, qtype, players, header, collapsed, status, daily = self:FixedGetQuestLogTitle(index)
166 if not title then break end
168 if players and players <= 0 then
169 players = nil
172 if not players then
173 players = qtype == nil and 1 or 5
174 else
175 players = math.min(5, math.max(1, players))
178 if not header then
179 SelectQuestLogEntry(index)
180 local hash = self:HashString(select(2, GetQuestLogQuestText()))
181 local quest = self:GetQuest(title, level, hash)
182 local lq = quests[quest]
183 local is_new = false
185 local ignored = party_levels[players]+QuestHelper_Pref.level < level
187 if self.quest_giver and self.quest_giver[title] then
188 quest.o.start = self.quest_giver[title]
189 self.quest_giver[title] = nil
192 if not lq then
193 lq = {}
195 quests[quest] = lq
197 if GetQuestLogTimeLeft() then
198 -- Quest has a timer, so give it a higher than normal priority.
199 self:SetObjectivePriority(quest, 2)
200 else
201 -- Use a normal priority.
202 self:SetObjectivePriority(quest, 3)
206 quest.o.id = self:GetQuestID(index)
208 -- Can't add the objective here, if we don't have it depend on the objectives
209 -- first it'll get added and possibly not be doable.
210 -- We'll add it after the objectives are determined.
211 is_new = true
214 lq.removed = false
216 if GetNumQuestLeaderBoards(index) > 0 then
217 if not lq.goal then lq.goal = {} end
218 for objective = 1, GetNumQuestLeaderBoards(index) do
219 local lo = lq.goal[objective]
220 if not lo then lo = {} lq.goal[objective] = lo end
221 local category, verb, wanted, have, need = self:GetQuestLogObjective(index, objective)
223 if not wanted or not string.find(wanted, "[^%s]") then
224 self.defered_quest_scan = true
225 elseif not lo.objective then
226 -- objective is new.
227 lo.objective = self:GetObjective(category, wanted)
228 lo.objective.o.quest = true -- If I ever decide to prune the DB, I'll have the stuff actually used in quests marked.
229 self:ObjectiveObjectDependsOn(quest, lo.objective)
231 if category == "item" then
232 -- So the objective knows in what context we're getting the item.
233 lo.objective.quest = quest
236 if verb then
237 lo.reason = QHFormat("OBJECTIVE_REASON", verb, wanted, title)
238 else
239 lo.reason = QHFormat("OBJECTIVE_REASON_FALLBACK", wanted, title)
242 lo.category = category
243 lo.wanted = wanted
244 lo.have = have
245 lo.need = need
247 QuestHelper:SetObjectiveProgress(lo.objective, UnitName("player"), have, need)
249 if have ~= need then -- If the objective isn't complete, watch it.
250 lo.objective:Share()
251 self:AddObjectiveWatch(lo.objective, lo.reason)
253 elseif lo.have ~= have then
254 QuestHelper:SetObjectiveProgress(lo.objective, UnitName("player"), have, need)
256 if lo.objective.peer then
257 for u, l in pairs(lo.objective.peer) do
258 -- Peers don't know about our progress.
259 lo.objective.peer[u] = math.min(l, 2)
263 if have == need or (type(have) == "number" and have > lo.have) then
264 if category == "item" then
265 self:AppendItemObjectivePosition(lo.objective, wanted, self:PlayerPosition())
266 else
267 self:AppendObjectivePosition(lo.objective, self:PlayerPosition())
271 if lo.have == need then -- The objective was done, but now its not.
272 lo.objective:Share()
273 self:AddObjectiveWatch(lo.objective, lo.reason)
274 elseif have == need then -- The objective is now finished.
275 lo.objective:Unshare()
276 self:RemoveObjectiveWatch(lo.objective, lo.reason)
279 lo.have = have
282 if lo.objective then -- Might not have loaded the objective yet, if it wasn't in the local cache and we defered loading it.
283 lo.objective.filter_level = ignored
284 lo.objective.filter_done = true
285 lo.objective.filter_blocked = not lo.objective:CouldBeFirst()
288 else
289 quest.goal = nil
292 if is_new then
293 lq.reason = QHFormat("OBJECTIVE_REASON_TURNIN", title)
294 quest:Share()
295 self:AddObjectiveWatch(quest, lq.reason)
298 -- Note whether the quest turn-in objective is blocked (i.e. quest not complete)
299 quest.filter_blocked = not quest:CouldBeFirst()
301 index = index + 1
304 for quest, lq in pairs(quests) do
305 if lq.removed then
306 if lq.goal then
307 for i, lo in ipairs(lq.goal) do
308 if lo.objective and lo.have ~= lo.need then
309 QuestHelper:SetObjectiveProgress(lo.objective, UnitName("player"), nil, nil)
311 lo.objective:Unshare()
312 self:RemoveObjectiveWatch(lo.objective, lo.reason)
317 quest:Unshare()
318 self:RemoveObjectiveWatch(quest, lq.reason)
319 quests[quest] = nil
323 if first_time then
324 first_time = false
325 self:ForceRouteUpdate(3)
328 SelectQuestLogEntry(original_entry)