If the static data table doesn't exist, then create a dummy table so that life can...
[QuestHelper.git] / nag.lua
bloba47eccfb6ea40b4f8cbea5ce3b04e544040d1c6b
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 for _, b in ipairs(static) do
29 high = math.max(high, weight(b))
30 end
32 for _, a in ipairs(list) do
33 local found = false
35 for _, b in ipairs(static) do
36 if weight(a) < high*0.2 or compare(a, b) then
37 found = true
38 break
39 end
40 end
42 if not found then return true end
43 end
44 return false
45 end
47 local function PositionWeight(a)
48 return a[4]
49 end
51 local function PositionCompare(a, b)
52 return a[1] == b[1] and
53 (a[2]-b[2])*(a[2]-b[2])+(a[3]-b[3])*(a[3]-b[3]) < 0.05*0.05
54 end
56 local function VendorCompare(a, b)
57 return a == b
58 end
60 local function VendorWeight(a)
61 return 1
62 end
64 local function PositionListUpdated(list, static)
65 return ListUpdated(list, static, PositionCompare, PositionWeight)
66 end
68 local function VendorListUpdated(list, static)
69 return ListUpdated(list, static, VendorCompare, VendorWeight)
70 end
72 local function DropListUpdated(list, static)
73 if not list then return false end
74 if not static then return next(list, nil) ~= nil end
75 local high = 0
77 for name in pairs(list) do
78 local monster_obj = FindStaticObjective("monster", name)
79 if monster_obj and monster_obj.looted and monster_obj.looted > 0 then
80 high = math.max(high, (static[name] or 0)/monster_obj.looted)
81 end
82 end
84 for name, v in pairs(list) do
85 local monster_obj1 = QuestHelper:GetObjective("monster", name)
86 local monster_obj2 = FindStaticObjective("monster", name)
88 local looted = math.ceil((monster_obj1.looted or 0)+((monster_obj2 and monster_obj2.looted) or 0))
89 if looted > 0 then
90 v = math.max(1, math.floor(v))/looted
91 if v > high*0.2 and not static[name] then return true end
92 end
93 end
94 return false
95 end
97 local function DropListMass(list)
98 if not list then return 0 end
99 local mass = 0
100 for item, count in pairs(list) do
101 mass = mass + count
103 return mass
106 local function PositionListMass(list)
107 if not list then return 0 end
108 local mass = 0
109 for _, pos in ipairs(list) do
110 mass = mass + pos[4]
112 return mass
115 local function CompareStaticQuest(info, faction, level, name, hash, data, verbose)
116 local static = FindStaticQuest(faction, level, name, hash)
118 if not static then
119 if data.finish or data.pos then
120 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing.") end
121 info.new.quest = (info.new.quest or 0) + 1
123 return
126 local updated = false
128 if data.finish and data.finish ~= static.finish then
129 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish NPC "..QuestHelper:HighlightText(data.finish)..".") end
130 updated = true
131 elseif not static.finish and PositionListUpdated(data.pos, static.pos) then
132 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing finish location.") end
133 updated = true
134 elseif data.item then
135 for item_name, item in pairs(data.item) do
136 local static_item = (static.item and static.item[item_name]) or FindStaticObjective("item", item_name)
138 if not static_item then
139 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing item "..QuestHelper:HighlightText(item_name)..".") end
140 updated = true
141 break
142 elseif item.drop then
143 if DropListUpdated(item.drop, static_item.drop) then
144 if DropListMass(item.drop) > PositionListMass(static_item.pos) then
145 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing drop for item "..QuestHelper:HighlightText(item_name)..".") end
146 updated = true
147 break
150 elseif item.pos and not static_item.drop and PositionListUpdated(item.pos, static_item.pos) then
151 if verbose then QuestHelper:TextOut("Quest "..QuestHelper:HighlightText(name).." was missing position for item "..QuestHelper:HighlightText(item_name)..".") end
152 updated = true
153 break
158 if updated then
159 info.update.quest = (info.update.quest or 0)+1
163 local function CompareStaticObjective(info, cat, name, data, verbose)
164 if info.quest then
165 local static = FindStaticObjective(cat, name)
166 if not static then
167 if data.pos or data.drop or data.vendor then
168 if verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing.") end
169 info.new[cat.."_obj"] = (info.new[cat.."_obj"] or 0)+1
171 return
174 local updated = false
176 if data.vendor then
177 updated = VendorListUpdated(data.vendor, static.vendor)
178 if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing vendor.") end
179 elseif data.drop and not static.vendor then
180 updated = DropListUpdated(data.drop, static.drop) and DropListMass(data.drop) > PositionListMass(static.pos)
181 if updated and verbose then QuestHelper:TextOut(string.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing monster drop.") end
182 elseif data.pos and not static.vendor and not static.drop then
183 if updated and verbose then QuestHelper:TextOut(Qstring.gsub(cat, "^(.)", string.upper).." "..QuestHelper:HighlightText(name).." was missing position.") end
184 updated = PositionListUpdated(data.pos, static.pos)
187 if updated then
188 info.update[cat.."_obj"] = (info.update[cat.."_obj"] or 0)+1
193 function QuestHelper:Nag(cmd)
194 local verbose, local_only = false, true
196 if cmd then
197 if string.find(cmd, "verbose") then verbose = true end
198 if string.find(cmd, "all") then local_only = false end
201 local info =
203 new = {},
204 update = {}
207 for faction, level_list in pairs(QuestHelper_Quests) do
208 if not local_only or faction == self.faction then
209 for level, name_list in pairs(level_list) do
210 for name, data in pairs(name_list) do
211 CompareStaticQuest(info, faction, level, name, data.hash, data, verbose)
212 if data.alt then
213 for hash, data in pairs(data.alt) do
214 CompareStaticQuest(info, faction, level, name, hash, data, verbose)
222 for cat, name_list in pairs(QuestHelper_Objectives) do
223 for name, obj in pairs(name_list) do
224 CompareStaticObjective(info, cat, name, obj, verbose)
228 for faction, location_list in pairs(QuestHelper_FlightInstructors) do
229 if not local_only or faction == self.faction then
230 for location, npc in pairs(location_list) do
231 local data = QuestHelper_StaticData[self.locale]
232 data = data and data.flight_instructors
233 data = data and data[faction]
234 data = data and data[location]
236 if not data or data ~= npc then
237 if verbose then self:TextOut(QuestHelper:HighlightText(faction).." flight master "..QuestHelper:HighlightText(npc).." was missing.") end
238 info.new["fp"] = (info.new["fp"] or 0)+1
244 for faction, start_list in pairs(QuestHelper_FlightRoutes) do
245 if not local_only or faction == self.faction then
246 for start, dest_list in pairs(start_list) do
247 for dest, hash_list in pairs(dest_list) do
248 for hash, data in pairs(hash_list) do
249 local static = QuestHelper_StaticData[self.locale]
250 static = static and static.flight_routes
251 static = static and static[faction]
252 static = static and static[start]
253 static = static and static[dest]
254 static = static and static[hash]
256 if not static or static == true and type(data) == "number" then
257 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
258 info.new["route"] = (info.new["route"] or 0)+1
266 local total = 0
268 for what, count in pairs(info.new) do
269 total = total + count
270 local count2 = info.update[what]
271 if count2 then
272 total = total + count2
273 self:TextOut(QHFormat("NAG_MULTIPLE_NEW", count, count2, QHText("NAG_"..string.upper(what))))
274 else
275 self:TextOut(QHFormat("NAG_SINGLE_NEW", QHFormat(count==1 and "NAG_SINGLE" or "NAG_PLURAL", count, QHText("NAG_"..string.upper(what)))))
279 for what, count in pairs(info.update) do
280 if not info.new[what] then
281 total = total + count
282 self:TextOut(QHFormat("NAG_ADDITIONAL", QHFormat(count==1 and "NAG_SINGLE" or "NAG_PLURAL", count, QHText("NAG_"..string.upper(what)))))
286 if total == 0 then
287 self:TextOut(QHText("NAG_NOT_NEW"))
288 else
289 self:TextOut(QHText("NAG_NEW"))
291 if total > 20 then
292 self:TextOut(QHText("NAG_INSTRUCTIONS"))