update todo
[QuestHelper.git] / nag.lua
blob26a0ebbe23bdb6e4af6b74551b420ee89ac8f679
1 QuestHelper_File["nag.lua"] = "Development Version"
2 QuestHelper_Loadtime["nag.lua"] = GetTime()
4 local function FindStaticQuest(faction, level, name, hash)
5 local data = QuestHelper_StaticData[QuestHelper.locale]
6 data = data and data.quest
7 data = data and data[faction]
8 data = data and data[level]
9 data = data and data[name]
10 if data and data.hash and data.hash ~= hash then
11 data = data.alt and data.alt[hash]
12 end
13 return data
14 end
16 local function FindStaticObjective(cat, name)
17 local data = QuestHelper_StaticData[QuestHelper.locale]
18 data = data and data.objective
19 data = data and data[cat]
20 return data and data[name]
21 end
23 local function ListUpdated(list, static, compare, weight)
24 if not list then return false end
25 if not static then return true end
27 local high = 0
29 if #static >= 5 then
30 return false
31 end
33 for _, b in ipairs(static) do
34 high = math.max(high, weight(b))
35 end
37 for _, a in ipairs(list) do
38 local found = false
40 for _, b in ipairs(static) do
41 if weight(a) < high*0.2 or compare(a, b) then
42 found = true
43 break
44 end
45 end
47 if not found then return true end
48 end
49 return false
50 end
52 local function PositionWeight(a)
53 return a[4]
54 end
56 local function PositionCompare(a, b)
57 return a[1] == b[1] and
58 (a[2]-b[2])*(a[2]-b[2])+(a[3]-b[3])*(a[3]-b[3]) < 0.05*0.05
59 end
61 local function VendorCompare(a, b)
62 return a == b
63 end
65 local function VendorWeight(a)
66 return 1
67 end
69 local function PositionListUpdated(list, static)
70 return ListUpdated(list, static, PositionCompare, PositionWeight)
71 end
73 local function VendorListUpdated(list, static)
74 return ListUpdated(list, static, VendorCompare, VendorWeight)
75 end
77 local function DropListUpdated(list, static)
78 if not list then return false end
79 if not static then return next(list, nil) ~= nil end
80 local high = 0
82 for name in pairs(list) do
83 local monster_obj = FindStaticObjective("monster", name)
84 if monster_obj and monster_obj.looted and monster_obj.looted > 0 then
85 high = math.max(high, (static[name] or 0)/monster_obj.looted)
86 end
87 end
89 for name, v in pairs(list) do
90 local monster_obj1 = QuestHelper:GetObjective("monster", name)
91 local monster_obj2 = FindStaticObjective("monster", name)
93 local looted = math.ceil((monster_obj1.o.looted or 0)+((monster_obj2 and monster_obj2.looted) or 0))
94 if looted > 0 then
95 v = math.max(1, math.floor(v))/looted
96 if v > high*0.2 and not static[name] then return true end
97 end
98 end
99 return false
102 local function DropListMass(list)
103 if not list then return 0 end
104 local mass = 0
105 for item, count in pairs(list) do
106 mass = mass + count
108 return mass
111 local function PositionListMass(list)
112 if not list then return 0 end
113 local mass = 0
114 for _, pos in ipairs(list) do
115 mass = mass + pos[4]
117 return mass
120 local function CompareStaticQuest(info, faction, level, name, hash, data, verbose)
121 local static = FindStaticQuest(faction, level, name, hash)
123 if not static then
124 if data.finish or data.pos then
125 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing.") end
126 info.new.quest = (info.new.quest or 0) + 1
128 return
131 local updated = false
133 if data.finish and data.finish ~= static.finish then
134 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish NPC "..QuestHelper:HighlightText(data.finish)..".") end
135 updated = true
136 elseif not static.finish and PositionListUpdated(data.pos, static.pos) then
137 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish location.") end
138 updated = true
139 elseif data.item then
140 for item_name, item in pairs(data.item) do
141 local static_item = (static.item and static.item[item_name]) or FindStaticObjective("item", item_name)
143 if not static_item then
144 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing item "..QuestHelper:HighlightText(item_name)..".") end
145 updated = true
146 break
147 elseif item.drop then
148 if DropListUpdated(item.drop, static_item.drop) then
149 if DropListMass(item.drop) > PositionListMass(static_item.pos) then
150 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing drop for item "..QuestHelper:HighlightText(item_name)..".") end
151 updated = true
152 break
155 elseif item.pos and not static_item.drop and PositionListUpdated(item.pos, static_item.pos) then
156 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing position for item "..QuestHelper:HighlightText(item_name)..".") end
157 updated = true
158 break
163 if updated then
164 info.update.quest = (info.update.quest or 0)+1
168 local function CompareStaticObjective(info, cat, name, data, verbose)
169 if info.quest then
170 local static = FindStaticObjective(cat, name)
171 if not static then
172 if data.pos or data.drop or data.vendor then
173 if verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing.") end
174 info.new[cat.."_obj"] = (info.new[cat.."_obj"] or 0)+1
176 return
179 local updated = false
181 if data.vendor then
182 updated = VendorListUpdated(data.vendor, static.vendor)
183 if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing vendor.") end
184 elseif data.drop and not static.vendor then
185 updated = DropListUpdated(data.drop, static.drop) and DropListMass(data.drop) > PositionListMass(static.pos)
186 if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing monster drop.") end
187 elseif data.pos and not static.vendor and not static.drop then
188 if updated and verbose then QuestHelper:TextOut(Qstring.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing position.") end
189 updated = PositionListUpdated(data.pos, static.pos)
192 if updated then
193 info.update[cat.."_obj"] = (info.update[cat.."_obj"] or 0)+1
198 function QuestHelper:Nag(cmd)
199 do return end -- BZZT
201 local verbose, local_only = false, true
203 if QuestHelper_IsPolluted() then
204 self:TextOut(QHFormat("NAG_POLLUTED"))
205 return
208 if cmd then
209 if string.find(cmd, "verbose") then verbose = true end
210 if string.find(cmd, "all") then local_only = false end
213 local info =
215 new = {},
216 update = {}
219 for version, data in pairs(QuestHelper_Quests) do
220 for faction, level_list in pairs(data) do
221 if not local_only or faction == self.faction then
222 for level, name_list in pairs(level_list) do
223 for name, data in pairs(name_list) do
224 CompareStaticQuest(info, faction, level, name, data.hash, data, verbose)
225 if data.alt then
226 for hash, data in pairs(data.alt) do
227 CompareStaticQuest(info, faction, level, name, hash, data, verbose)
236 for version, data in pairs(QuestHelper_Objectives) do
237 for cat, name_list in pairs(data) do
238 for name, obj in pairs(name_list) do
239 CompareStaticObjective(info, cat, name, obj, verbose)
244 for version, data in pairs(QuestHelper_FlightInstructors) do
245 for faction, location_list in pairs(data) do
246 if not local_only or faction == self.faction then
247 for location, npc in pairs(location_list) do
248 local data = QuestHelper_StaticData[self.locale]
249 data = data and data.flight_instructors
250 data = data and data[faction]
251 data = data and data[location]
253 if not data or data ~= npc then
254 if verbose then self:TextOut(QuestHelper:HighlightText(faction).." flight master "..QuestHelper:HighlightText(npc).." was missing.") end
255 info.new["fp"] = (info.new["fp"] or 0)+1
262 for version, data in pairs(QuestHelper_FlightRoutes) do
263 for faction, start_list in pairs(data) do
264 if not local_only or faction == self.faction then
265 for start, dest_list in pairs(start_list) do
266 for dest, hash_list in pairs(dest_list) do
267 for hash, data in pairs(hash_list) do
268 if hash ~= "no_interrupt_count" and hash ~= "interrupt_count" then
269 local static = QuestHelper_StaticData[self.locale]
270 static = static and static.flight_routes
271 static = static and static[faction]
272 static = static and static[start]
273 static = static and static[dest]
274 static = static and static[hash]
276 if not static or static == true and type(data) == "number" then
277 if verbose then self:TextOut("Flight time from "..QuestHelper:HighlightText((select(3, string.find(start, "^(.*),")) or start)).." to "..QuestHelper:HighlightText((select(3, string.find(dest, "^(.*),")) or dest)).." was missing.") end
278 info.new["route"] = (info.new["route"] or 0)+1
288 local total = 0
290 for what, count in pairs(info.new) do
291 local what1 = count == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
292 QHFormat("NAG_MULTIPLE_"..string.upper(what), count)
294 total = total + count
295 local count2 = info.update[what]
296 if count2 then
297 total = total + count2
298 local what2 = count2 == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
299 QHFormat("NAG_MULTIPLE_"..string.upper(what), count2)
300 self:TextOut(QHFormat("NAG_MULTIPLE_NEW", what1, what2))
301 else
302 self:TextOut(QHFormat("NAG_SINGLE_NEW", what1))
306 for what, count in pairs(info.update) do
307 if not info.new[what] then
308 local what = count == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
309 QHFormat("NAG_MULTIPLE_"..string.upper(what), count)
310 total = total + count
311 self:TextOut(QHFormat("NAG_ADDITIONAL", what))
315 if total == 0 then
316 self:TextOut(QHText("NAG_NOT_NEW"))
317 else
318 self:TextOut(QHText("NAG_NEW"))
319 self:TextOut(QHText("NAG_INSTRUCTIONS"))
324 local day = 24 * 60 * 60
326 function QHNagInit()
327 if not QuestHelper_Pref.submit_nag_next_time then
328 QuestHelper_Pref.submit_nag_next_time = time() + 7 * day + 7 * day * math.random() -- at least a week, at most 2 weeks
329 QuestHelper_Pref.submit_nag_type = "OFF"
332 if QuestHelper_Pref.submit_nag_next_time < time() then
333 if QuestHelper_Pref.submit_nag_type == "OFF" then
334 -- we now begin nagging for 48 hours
335 QuestHelper_Pref.submit_nag_next_time = time() + 2 * day
336 QuestHelper_Pref.submit_nag_type = "ON"
337 else
338 -- we now stop nagging for 2-3 weeks
339 QuestHelper_Pref.submit_nag_next_time = time() + 14 * day + 7 * day * math.random()
340 QuestHelper_Pref.submit_nag_type = "OFF"
344 if time() > 1248462000 and time() < 1248685200 then return true end -- 7/24/2009 11am PST to 7/27/2009 1am PST
345 return QuestHelper_Pref.submit_nag_type == "ON"
349 local update_nag_yell_at = nil
351 function QHUpdateNagInit()
352 if not QuestHelper_Pref.update_nag_last_version or QuestHelper_Pref.update_nag_last_version ~= GetAddOnMetadata("QuestHelper", "Version") then
353 QuestHelper_Pref.update_nag_last_version = GetAddOnMetadata("QuestHelper", "Version")
354 QuestHelper_Pref.update_nag_next_notify = time() + 24 * day
357 if QuestHelper_Pref.update_nag_next_notify < time() then
358 update_nag_yell_at = time() + 60 * 10 + 50 * 60 * math.random() -- 10 to 60 minutes from now
362 function QHUpdateNagTick()
363 if update_nag_yell_at and update_nag_yell_at < time() then
364 --QuestHelper:TextOut(QHText("TIME_TO_UPDATE")) -- We're just disabling this, I think.
365 QuestHelper_Pref.update_nag_next_notify = time() + day * 6 + day * 2 * math.random()
366 update_nag_yell_at = nil