update changes
[QuestHelper.git] / nag.lua
blob97d910f3dc676f533217b12d0293940172e8841a
1 QuestHelper_File["nag.lua"] = "Development Version"
3 local function FindStaticQuest(faction, level, name, hash)
4 local data = QuestHelper_StaticData[QuestHelper.locale]
5 data = data and data.quest
6 data = data and data[faction]
7 data = data and data[level]
8 data = data and data[name]
9 if data and data.hash and data.hash ~= hash then
10 data = data.alt and data.alt[hash]
11 end
12 return data
13 end
15 local function FindStaticObjective(cat, name)
16 local data = QuestHelper_StaticData[QuestHelper.locale]
17 data = data and data.objective
18 data = data and data[cat]
19 return data and data[name]
20 end
22 local function ListUpdated(list, static, compare, weight)
23 if not list then return false end
24 if not static then return true end
26 local high = 0
28 if #static >= 5 then
29 return false
30 end
32 for _, b in ipairs(static) do
33 high = math.max(high, weight(b))
34 end
36 for _, a in ipairs(list) do
37 local found = false
39 for _, b in ipairs(static) do
40 if weight(a) < high*0.2 or compare(a, b) then
41 found = true
42 break
43 end
44 end
46 if not found then return true end
47 end
48 return false
49 end
51 local function PositionWeight(a)
52 return a[4]
53 end
55 local function PositionCompare(a, b)
56 return a[1] == b[1] and
57 (a[2]-b[2])*(a[2]-b[2])+(a[3]-b[3])*(a[3]-b[3]) < 0.05*0.05
58 end
60 local function VendorCompare(a, b)
61 return a == b
62 end
64 local function VendorWeight(a)
65 return 1
66 end
68 local function PositionListUpdated(list, static)
69 return ListUpdated(list, static, PositionCompare, PositionWeight)
70 end
72 local function VendorListUpdated(list, static)
73 return ListUpdated(list, static, VendorCompare, VendorWeight)
74 end
76 local function DropListUpdated(list, static)
77 if not list then return false end
78 if not static then return next(list, nil) ~= nil end
79 local high = 0
81 for name in pairs(list) do
82 local monster_obj = FindStaticObjective("monster", name)
83 if monster_obj and monster_obj.looted and monster_obj.looted > 0 then
84 high = math.max(high, (static[name] or 0)/monster_obj.looted)
85 end
86 end
88 for name, v in pairs(list) do
89 local monster_obj1 = QuestHelper:GetObjective("monster", name)
90 local monster_obj2 = FindStaticObjective("monster", name)
92 local looted = math.ceil((monster_obj1.o.looted or 0)+((monster_obj2 and monster_obj2.looted) or 0))
93 if looted > 0 then
94 v = math.max(1, math.floor(v))/looted
95 if v > high*0.2 and not static[name] then return true end
96 end
97 end
98 return false
99 end
101 local function DropListMass(list)
102 if not list then return 0 end
103 local mass = 0
104 for item, count in pairs(list) do
105 mass = mass + count
107 return mass
110 local function PositionListMass(list)
111 if not list then return 0 end
112 local mass = 0
113 for _, pos in ipairs(list) do
114 mass = mass + pos[4]
116 return mass
119 local function CompareStaticQuest(info, faction, level, name, hash, data, verbose)
120 local static = FindStaticQuest(faction, level, name, hash)
122 if not static then
123 if data.finish or data.pos then
124 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing.") end
125 info.new.quest = (info.new.quest or 0) + 1
127 return
130 local updated = false
132 if data.finish and data.finish ~= static.finish then
133 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish NPC "..QuestHelper:HighlightText(data.finish)..".") end
134 updated = true
135 elseif not static.finish and PositionListUpdated(data.pos, static.pos) then
136 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish location.") end
137 updated = true
138 elseif data.item then
139 for item_name, item in pairs(data.item) do
140 local static_item = (static.item and static.item[item_name]) or FindStaticObjective("item", item_name)
142 if not static_item then
143 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing item "..QuestHelper:HighlightText(item_name)..".") end
144 updated = true
145 break
146 elseif item.drop then
147 if DropListUpdated(item.drop, static_item.drop) then
148 if DropListMass(item.drop) > PositionListMass(static_item.pos) then
149 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing drop for item "..QuestHelper:HighlightText(item_name)..".") end
150 updated = true
151 break
154 elseif item.pos and not static_item.drop and PositionListUpdated(item.pos, static_item.pos) then
155 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing position for item "..QuestHelper:HighlightText(item_name)..".") end
156 updated = true
157 break
162 if updated then
163 info.update.quest = (info.update.quest or 0)+1
167 local function CompareStaticObjective(info, cat, name, data, verbose)
168 if info.quest then
169 local static = FindStaticObjective(cat, name)
170 if not static then
171 if data.pos or data.drop or data.vendor then
172 if verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing.") end
173 info.new[cat.."_obj"] = (info.new[cat.."_obj"] or 0)+1
175 return
178 local updated = false
180 if data.vendor then
181 updated = VendorListUpdated(data.vendor, static.vendor)
182 if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing vendor.") end
183 elseif data.drop and not static.vendor then
184 updated = DropListUpdated(data.drop, static.drop) and DropListMass(data.drop) > PositionListMass(static.pos)
185 if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing monster drop.") end
186 elseif data.pos and not static.vendor and not static.drop then
187 if updated and verbose then QuestHelper:TextOut(Qstring.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing position.") end
188 updated = PositionListUpdated(data.pos, static.pos)
191 if updated then
192 info.update[cat.."_obj"] = (info.update[cat.."_obj"] or 0)+1
197 function QuestHelper:Nag(cmd)
198 local verbose, local_only = false, true
200 if QuestHelper_IsPolluted() then
201 self:TextOut(QHFormat("NAG_POLLUTED"))
202 return
205 if cmd then
206 if string.find(cmd, "verbose") then verbose = true end
207 if string.find(cmd, "all") then local_only = false end
210 local info =
212 new = {},
213 update = {}
216 for version, data in pairs(QuestHelper_Quests) do
217 for faction, level_list in pairs(data) do
218 if not local_only or faction == self.faction then
219 for level, name_list in pairs(level_list) do
220 for name, data in pairs(name_list) do
221 CompareStaticQuest(info, faction, level, name, data.hash, data, verbose)
222 if data.alt then
223 for hash, data in pairs(data.alt) do
224 CompareStaticQuest(info, faction, level, name, hash, data, verbose)
233 for version, data in pairs(QuestHelper_Objectives) do
234 for cat, name_list in pairs(data) do
235 for name, obj in pairs(name_list) do
236 CompareStaticObjective(info, cat, name, obj, verbose)
241 for version, data in pairs(QuestHelper_FlightInstructors) do
242 for faction, location_list in pairs(data) do
243 if not local_only or faction == self.faction then
244 for location, npc in pairs(location_list) do
245 local data = QuestHelper_StaticData[self.locale]
246 data = data and data.flight_instructors
247 data = data and data[faction]
248 data = data and data[location]
250 if not data or data ~= npc then
251 if verbose then self:TextOut(QuestHelper:HighlightText(faction).." flight master "..QuestHelper:HighlightText(npc).." was missing.") end
252 info.new["fp"] = (info.new["fp"] or 0)+1
259 for version, data in pairs(QuestHelper_FlightRoutes) do
260 for faction, start_list in pairs(data) do
261 if not local_only or faction == self.faction then
262 for start, dest_list in pairs(start_list) do
263 for dest, hash_list in pairs(dest_list) do
264 for hash, data in pairs(hash_list) do
265 if hash ~= "no_interrupt_count" and hash ~= "interrupt_count" then
266 local static = QuestHelper_StaticData[self.locale]
267 static = static and static.flight_routes
268 static = static and static[faction]
269 static = static and static[start]
270 static = static and static[dest]
271 static = static and static[hash]
273 if not static or static == true and type(data) == "number" then
274 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
275 info.new["route"] = (info.new["route"] or 0)+1
285 local total = 0
287 for what, count in pairs(info.new) do
288 local what1 = count == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
289 QHFormat("NAG_MULTIPLE_"..string.upper(what), count)
291 total = total + count
292 local count2 = info.update[what]
293 if count2 then
294 total = total + count2
295 local what2 = count2 == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
296 QHFormat("NAG_MULTIPLE_"..string.upper(what), count2)
297 self:TextOut(QHFormat("NAG_MULTIPLE_NEW", what1, what2))
298 else
299 self:TextOut(QHFormat("NAG_SINGLE_NEW", what1))
303 for what, count in pairs(info.update) do
304 if not info.new[what] then
305 local what = count == 1 and QHText("NAG_SINGLE_"..string.upper(what)) or
306 QHFormat("NAG_MULTIPLE_"..string.upper(what), count)
307 total = total + count
308 self:TextOut(QHFormat("NAG_ADDITIONAL", what))
312 if total == 0 then
313 self:TextOut(QHText("NAG_NOT_NEW"))
314 else
315 self:TextOut(QHText("NAG_NEW"))
316 self:TextOut(QHText("NAG_INSTRUCTIONS"))