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
]
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
]
23 local function ListUpdated(list
, static
, compare
, weight
)
24 if not list
then return false end
25 if not static
then return true end
33 for _
, b
in ipairs(static
) do
34 high
= math
.max(high
, weight(b
))
37 for _
, a
in ipairs(list
) do
40 for _
, b
in ipairs(static
) do
41 if weight(a
) < high
*0.2 or compare(a
, b
) then
47 if not found
then return true end
52 local function PositionWeight(a
)
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
61 local function VendorCompare(a
, b
)
65 local function VendorWeight(a
)
69 local function PositionListUpdated(list
, static
)
70 return ListUpdated(list
, static
, PositionCompare
, PositionWeight
)
73 local function VendorListUpdated(list
, static
)
74 return ListUpdated(list
, static
, VendorCompare
, VendorWeight
)
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
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
)
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))
95 v
= math
.max(1, math
.floor(v
))/looted
96 if v
> high
*0.2 and not static
[name
] then return true end
102 local function DropListMass(list
)
103 if not list
then return 0 end
105 for item
, count
in pairs(list
) do
111 local function PositionListMass(list
)
112 if not list
then return 0 end
114 for _
, pos
in ipairs(list
) do
120 local function CompareStaticQuest(info
, faction
, level
, name
, hash
, data
, verbose
)
121 local static
= FindStaticQuest(faction
, level
, name
, hash
)
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
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
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
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
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
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
164 info
.update
.quest
= (info
.update
.quest
or 0)+1
168 local function CompareStaticObjective(info
, cat
, name
, data
, verbose
)
170 local static
= FindStaticObjective(cat
, name
)
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
179 local updated
= false
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
)
193 info
.update
[cat
.."_obj"] = (info
.update
[cat
.."_obj"] or 0)+1
198 function QuestHelper
:Nag(cmd
)
199 local verbose
, local_only
= false, true
201 if QuestHelper_IsPolluted() then
202 self
:TextOut(QHFormat("NAG_POLLUTED"))
207 if string.find(cmd
, "verbose") then verbose
= true end
208 if string.find(cmd
, "all") then local_only
= false end
217 for version
, data
in pairs(QuestHelper_Quests
) do
218 for faction
, level_list
in pairs(data
) do
219 if not local_only
or faction
== self
.faction
then
220 for level
, name_list
in pairs(level_list
) do
221 for name
, data
in pairs(name_list
) do
222 CompareStaticQuest(info
, faction
, level
, name
, data
.hash
, data
, verbose
)
224 for hash
, data
in pairs(data
.alt
) do
225 CompareStaticQuest(info
, faction
, level
, name
, hash
, data
, verbose
)
234 for version
, data
in pairs(QuestHelper_Objectives
) do
235 for cat
, name_list
in pairs(data
) do
236 for name
, obj
in pairs(name_list
) do
237 CompareStaticObjective(info
, cat
, name
, obj
, verbose
)
242 for version
, data
in pairs(QuestHelper_FlightInstructors
) do
243 for faction
, location_list
in pairs(data
) do
244 if not local_only
or faction
== self
.faction
then
245 for location
, npc
in pairs(location_list
) do
246 local data
= QuestHelper_StaticData
[self
.locale
]
247 data
= data
and data
.flight_instructors
248 data
= data
and data
[faction
]
249 data
= data
and data
[location
]
251 if not data
or data
~= npc
then
252 if verbose
then self
:TextOut(QuestHelper
:HighlightText(faction
).." flight master "..QuestHelper
:HighlightText(npc
).." was missing.") end
253 info
.new
["fp"] = (info
.new
["fp"] or 0)+1
260 for version
, data
in pairs(QuestHelper_FlightRoutes
) do
261 for faction
, start_list
in pairs(data
) do
262 if not local_only
or faction
== self
.faction
then
263 for start
, dest_list
in pairs(start_list
) do
264 for dest
, hash_list
in pairs(dest_list
) do
265 for hash
, data
in pairs(hash_list
) do
266 if hash
~= "no_interrupt_count" and hash
~= "interrupt_count" then
267 local static
= QuestHelper_StaticData
[self
.locale
]
268 static
= static
and static
.flight_routes
269 static
= static
and static
[faction
]
270 static
= static
and static
[start
]
271 static
= static
and static
[dest
]
272 static
= static
and static
[hash
]
274 if not static
or static
== true and type(data
) == "number" then
275 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
276 info
.new
["route"] = (info
.new
["route"] or 0)+1
288 for what
, count
in pairs(info
.new
) do
289 local what1
= count
== 1 and QHText("NAG_SINGLE_"..string.upper(what
)) or
290 QHFormat("NAG_MULTIPLE_"..string.upper(what
), count
)
292 total
= total
+ count
293 local count2
= info
.update
[what
]
295 total
= total
+ count2
296 local what2
= count2
== 1 and QHText("NAG_SINGLE_"..string.upper(what
)) or
297 QHFormat("NAG_MULTIPLE_"..string.upper(what
), count2
)
298 self
:TextOut(QHFormat("NAG_MULTIPLE_NEW", what1
, what2
))
300 self
:TextOut(QHFormat("NAG_SINGLE_NEW", what1
))
304 for what
, count
in pairs(info
.update
) do
305 if not info
.new
[what
] then
306 local what
= count
== 1 and QHText("NAG_SINGLE_"..string.upper(what
)) or
307 QHFormat("NAG_MULTIPLE_"..string.upper(what
), count
)
308 total
= total
+ count
309 self
:TextOut(QHFormat("NAG_ADDITIONAL", what
))
314 self
:TextOut(QHText("NAG_NOT_NEW"))
316 self
:TextOut(QHText("NAG_NEW"))
317 self
:TextOut(QHText("NAG_INSTRUCTIONS"))
322 local day
= 24 * 60 * 60
325 if not QuestHelper_Pref
.nag_next_time
then
326 QuestHelper_Pref
.nag_next_time
= time() + 7 * day
+ 14 * day
* math
.random() -- at least a week, at most 3 weeks
327 QuestHelper_Pref
.nag_type
= "OFF"
330 if QuestHelper_Pref
.nag_next_time
< time() then
331 if QuestHelper_Pref
.nag_type
== "OFF" then
332 -- we now begin nagging for 72 hours
333 QuestHelper_Pref
.nag_next_time
= time() + 3 * day
334 QuestHelper_Pref
.nag_type
= "ON"
336 -- we now stop nagging for 2-3 weeks
337 QuestHelper_Pref
.nag_next_time
= time() + 14 * day
+ 7 * day
* math
.random()
338 QuestHelper_Pref
.nag_type
= "OFF"
342 return QuestHelper_Pref
.nag_type
== "ON"
346 local update_nag_yell_at
= nil
348 function QHUpdateNagInit()
349 if not QuestHelper_Pref
.update_nag_last_version
or QuestHelper_Pref
.update_nag_last_version
~= GetAddOnMetadata("QuestHelper", "Version") then
350 QuestHelper_Pref
.update_nag_last_version
= GetAddOnMetadata("QuestHelper", "Version")
351 QuestHelper_Pref
.update_nag_next_notify
= time() + 24 * day
354 if QuestHelper_Pref
.update_nag_next_notify
< time() then
355 update_nag_yell_at
= time() + 60 * 10 + 50 * 60 * math
.random() -- 10 to 60 minutes from now
359 function QHUpdateNagTick()
360 if update_nag_yell_at
and update_nag_yell_at
< time() then
361 QuestHelper
:TextOut(QHText("TIME_TO_UPDATE"))
362 QuestHelper_Pref
.update_nag_next_notify
= time() + day
* 6 + day
* 2 * math
.random()
363 update_nag_yell_at
= nil