Added rightclick to map button, desaturated it when QuestHelper is hidden, and change...
[QuestHelper.git] / Development / compiler.lua
bloba7c969a0fa08f8c804bf3efea6184ff15c47424c
1 WoWData = {item={},quest={},npc={}} -- The build script will replace this with actual data if it can.
3 local StaticData = {}
5 function CreateAverage()
6 return {}
7 end
9 function AppendToAverage(average, value)
10 table.insert(average, value)
11 end
13 function CollapseAverage(average)
14 table.sort(average)
15 local to_remove = math.floor(#average*0.2)
16 for i = 1,to_remove do
17 table.remove(average, 1)
18 table.remove(average)
19 end
20 if #average == 0 then
21 return nil
22 end
23 local sum = 0
24 for _, v in pairs(average) do
25 sum = sum + v
26 end
27 return sum/#average
28 end
30 function GetQuest(locale, faction, level, name, hash)
31 local l = StaticData[locale]
32 if not l then
33 l = {}
34 l.quest = {}
35 l.objective = {}
36 StaticData[locale] = l
37 end
38 local a = l.quest[faction]
39 if not a then
40 a = {}
41 l.quest[faction] = a
42 end
43 local b = a[level]
44 if not b then
45 b = {}
46 a[level] = b
47 end
48 local q = b[name]
49 if not q then
50 q = {}
51 b[name] = q
52 q.hash = hash
53 q.alt = {}
54 end
56 if hash and hash ~= q.hash then
57 if q.alt[hash] then
58 return q.alt[hash]
59 end
60 end
62 if hash and not q.hash then
63 -- If the old quest didn't have a hash, we'll assume this is it. If we're wrong, we'll
64 -- hopefully overwrite it with the correct data.
65 q.hash = hash
66 elseif not hash and q.hash then
67 -- If the old quest had a hash, but this one doesn't, we'll return a dummy object
68 -- so that we don't overwrite it with the wrong quest data.
69 q = {}
70 elseif hash ~= q.hash then
71 local q2 = {}
72 q2.hash = hash
73 q.alt[hash] = q2
74 q = q2
75 end
77 return q
78 end
80 function GetObjective(locale, category, name)
81 local l = StaticData[locale]
82 if not l then
83 l = {}
84 l.quest = {}
85 l.objective = {}
86 StaticData[locale] = l
87 end
88 local list = l.objective[category]
89 if not list then
90 list = {}
91 l.objective[category] = list
92 end
93 local obj = list[name]
94 if not obj then
95 obj = {}
96 list[name] = obj
97 end
98 return obj
99 end
101 local function Distance(a, b)
102 -- Doing this, because the distances are going to be rounded later, and I don't want it to create
103 -- a situation where if you ran TidyPositionList twice, it would have a different result.
105 local x, y = math.floor(a[2]*10000+0.5)/10000-math.floor(b[2]*10000+0.5)/10000,
106 math.floor(a[3]*10000+0.5)/10000-math.floor(b[3]*10000+0.5)/10000
108 return math.sqrt(x*x+y*y)
111 local function TidyDropList(locale, list)
112 local high = 0
114 for monster, count in pairs(list) do
115 local monster_obj = GetObjective(locale, "monster", monster)
116 if monster_obj.looted and monster_obj.looted > 0 then
117 high = math.max(high, math.max(1, math.floor(count))/math.ceil(monster_obj.looted))
121 for monster, count in pairs(list) do
122 local monster_obj = GetObjective(locale, "monster", monster)
123 count = math.max(1, math.floor(count))
125 if monster_obj.looted and monster_obj.looted > 0 and count/math.ceil(monster_obj.looted) > high*0.2 then
126 list[monster] = count
127 else
128 list[monster] = nil
134 local function TidyContainedList(locale, list)
135 local high = 0
137 for item, count in pairs(list) do
138 local item_obj = GetObjective(locale, "item", item)
139 if item_obj.opened and item_obj.opened > 0 then
140 high = math.max(high, math.max(1, math.floor(count))/math.ceil(item_obj.opened))
144 for item, count in pairs(list) do
145 local item_obj = GetObjective(locale, "item", item)
146 count = math.max(1, math.floor(count))
148 if item_obj.opened and item_obj.opened > 0 and count/math.ceil(item_obj.opened) > high*0.2 then
149 list[item] = count
150 else
151 list[item] = nil
156 local function TidyPositionList(list, min_distance)
157 min_distance = min_distance or 0.03
158 while true do
159 if #list == 0 then return end
160 local changed = false
161 local i = 1
162 while i <= #list do
163 local nearest, distance = nil, 0
164 for j = i+1, #list do
165 local d = Distance(list[i], list[j])
166 if not nearest or d < distance then
167 nearest, distance = j, d
170 if nearest and distance < min_distance then
171 local a, b = list[i], list[nearest]
172 a[2] = (a[2]*a[4]+b[2]*b[4])/(a[4]+b[4])
173 a[3] = (a[3]*a[4]+b[3]*b[4])/(a[4]+b[4])
174 a[4] = a[4]+b[4]
175 table.remove(list, nearest)
176 changed = true
177 else
178 i = i + 1
181 if not changed then
182 -- Because we moved nodes around, we'll check again to make sure we didn't move too close together
183 break
187 local highest = 0
189 for i, data in ipairs(list) do
190 data[4] = math.pow(data[4], 0.73575888234288) -- Raising it to this number to make huge numbers seem closer together, the positions are probably correct and not some mistake.
191 highest = math.max(highest, data[4])
194 local i = 1 -- Remove anything that doesn't seem very likely.
195 while i <= #list do
196 if list[i][4] < highest*0.2 then
197 table.remove(list, i)
198 else
199 list[i][4] = math.max(1, math.floor(list[i][4]*100/highest+0.5))
200 i = i + 1
204 for i, j in ipairs(list) do
205 j[2] = math.floor(j[2]*10000+0.5)/10000
206 j[3] = math.floor(j[3]*10000+0.5)/10000
209 table.sort(list, function(a, b)
210 if a[4] > b[4] then return true end
211 if a[4] < b[4] then return false end
212 if a[1] < b[1] then return true end
213 if a[1] > b[1] then return false end
214 if a[2] < b[2] then return true end
215 if a[2] > b[2] then return false end
216 return a[3] < b[3]
217 end)
220 local function DropListMass(list)
221 local mass = 0
222 for item, count in pairs(list) do
223 mass = mass + count
225 return mass
228 local function PositionListMass(list)
229 local mass = 0
230 for _, pos in ipairs(list) do
231 mass = mass + pos[4]
233 return mass
236 local function CollapseDropList(list)
237 local result, c = nil, 0
238 for item, count in pairs(list) do
239 if not result or count > c then
240 result, c = item, count
243 return result
246 local function MergeDropLists(list, add)
247 for item, count in pairs(add) do
248 list[item] = (list[item] or 0) + count
252 local function MergePositionLists(list, add)
253 for _, pos in ipairs(add) do
254 local index, x, y, w = pos[1], pos[2], pos[3], pos[4]
255 if type(index) == "number" and QuestHelper_NameLookup[index] and
256 type(w) == "number" and w > 0 then
257 local bp, distance = nil, 0
258 for i, pos2 in ipairs(list) do
259 if index == pos2[1] then
260 local d = math.sqrt((x-pos2[2])*(x-pos2[2])+(y-pos2[3])*(y-pos2[3]))
261 if not nearest or d < distance then
262 bp, distance = pos2, d
266 if bp and distance < 0.03 then
267 bp[2] = (bp[2]*bp[4]+x*w)/(bp[4]+w)
268 bp[3] = (bp[3]*bp[4]+y*w)/(bp[4]+w)
269 bp[4] = bp[4]+w
270 else
271 table.insert(list, {index,x,y,w})
277 local function AddQuestEnd(quest, npc)
278 if not quest.finish then quest.finish = {} end
279 quest.finish[npc] = (quest.finish[npc] or 0) + 1
282 local function AddQuestPos(quest, pos)
283 if not quest.pos then quest.pos = {} end
284 MergePositionLists(quest.pos, pos)
287 local function AddQuestItems(quest, list)
288 for item, data in pairs(list) do
289 if type(data.drop) == "table" then
290 if not quest.item then quest.item = {} end
291 if not quest.item[item] then quest.item[item] = {} end
292 if not quest.item[item].drop then quest.item[item].drop = {} end
293 MergeDropLists(quest.item[item].drop, data.drop)
294 elseif type(data.pos) == "table" then
295 if not quest.item then quest.item = {} end
296 if not quest.item[item] then quest.item[item] = {} end
297 if not quest.item[item].pos then quest.item[item].pos = {} end
298 MergePositionLists(quest.item[item].pos, data.pos)
303 local function ValidFaction(faction)
304 return faction == 1 or faction == 2
307 local function AddQuest(locale, faction, level, name, data)
308 if ValidFaction(faction)
309 and type(level) == "number" and level >= 1 and level <= 100
310 and type(name) == "string" and type(data) == "table" then
312 local _, _, real_name = string.find(name, "^%["..level.."[^%s]-%]%s?(.+)$")
314 if real_name then
315 -- The Quest Level AddOn mangles level names.
316 name = real_name
319 local q = GetQuest(locale, faction, level, name, type(data.hash) == "number" and data.hash or nil)
321 if type(data.id) == "number" then
322 local wdq = WoWData.quest[data.id]
323 if not wdq then
324 wdq = {name={},hash={},faction={}}
325 WoWData.quest[data.id] = wdq
327 wdq.name[locale] = name
328 wdq.hash[locale] = data.hash or wdq.hash[locale]
329 wdq.level = level
332 if type(data.finish) == "string" then
333 AddQuestEnd(q, data.finish)
334 elseif type(data.pos) == "table" then
335 AddQuestPos(q, data.pos)
338 if type(data.item) == "table" then
339 AddQuestItems(q, data.item)
342 if type(data.hash) == "number" and type(data.alt) == "table" then
343 for hash, quest in pairs(data.alt) do
344 quest.hash = hash
345 AddQuest(locale, faction, level, name, quest)
351 local function AddFlightInstructor(locale, faction, location, npc)
352 if ValidFaction(faction) and type(location) == "string" and type(npc) == "string" then
353 local l = StaticData[locale]
354 if not l then
355 l = {}
356 StaticData[locale] = l
358 local faction_list = l.flight_instructors
359 if not faction_list then
360 faction_list = {}
361 l.flight_instructors = faction_list
364 local location_list = faction_list[faction]
365 if not location_list then
366 location_list = {}
367 faction_list[faction] = location_list
369 location_list[location] = npc
373 local function AddFlightRoute(locale, faction, start, destination, hash, value)
374 if ValidFaction(faction) and
375 type(start) == "string" and
376 type(destination) == "string" and
377 type(hash) == "number" and
378 ((value == true and hash == 0) or (type(value) == "number" and value > 0)) then
379 local l = StaticData[locale]
380 if not l then
381 l = {}
382 StaticData[locale] = l
384 local faction_list = l.flight_routes
385 if not faction_list then
386 faction_list = {}
387 l.flight_routes = faction_list
389 local start_list = faction_list[faction]
390 if not start_list then
391 start_list = {}
392 faction_list[faction] = start_list
394 local end_list = start_list[start]
395 if not end_list then
396 end_list = {}
397 start_list[start] = end_list
399 local hash_list = end_list[destination]
400 if not hash_list then
401 hash_list = {}
402 end_list[destination] = hash_list
404 if value == true then
405 hash_list[hash] = hash_list[hash] or true
406 else
407 local average = hash_list[hash]
408 if type(average) ~= "table" then
409 average = CreateAverage()
410 hash_list[hash] = average
412 AppendToAverage(average, value)
417 local function addVendor(list, npc)
418 for _, existing in ipairs(list) do
419 if existing == npc then
420 return
424 table.insert(list, npc)
427 local function addVendors(list, to_add)
428 if not list then list = {} end
430 for _, npc in ipairs(to_add) do
431 addVendor(list, npc)
432 local known = false
435 return list
438 local function AddObjective(locale, category, name, objective)
439 if type(category) == "string"
440 and type(name) == "string"
441 and type(objective) == "table" then
442 local o = GetObjective(locale, category, name)
444 if objective.quest == true then o.quest = true end
446 if type(objective.pos) == "table" then
447 if not o.pos then o.pos = {} end
448 MergePositionLists(o.pos, objective.pos)
451 if category == "monster" then
452 if type(objective.id) == "number" then
453 local wdm = WoWData.npc[objective.id]
454 if not wdm then
455 wdm = {name={}}
456 WoWData.npc[objective.id] = wdm
458 wdm.name[locale] = name
461 if type(objective.looted) == "number" and objective.looted >= 1 then
462 o.looted = (o.looted or 0) + objective.looted
464 if ValidFaction(objective.faction) then
465 o.faction = objective.faction
467 elseif category == "item" then
468 if type(objective.id) == "number" then
469 local wdi = WoWData.item[objective.id]
470 if not wdi then
471 wdi = {name={}}
472 WoWData.item[objective.id] = wdi
474 wdi.name[locale] = name
477 if type(objective.opened) == "number" and objective.opened >= 1 then
478 o.opened = (o.opened or 0) + objective.opened
480 if type(objective.vendor) == "table" then
481 o.vendor = addVendors(o.vendor, objective.vendor)
483 if type(objective.drop) == "table" then
484 if not o.drop then o.drop = {} end
485 for monster, count in pairs(objective.drop) do
486 if type(monster) == "string" and type(count) == "number" then
487 o.drop[monster] = (o.drop[monster] or 0) + count
491 if type(objective.contained) == "table" then
492 if not o.contained then o.contained = {} end
493 for item, count in pairs(objective.contained) do
494 if type(item) == "string" and type(count) == "number" then
495 o.contained[item] = (o.contained[item] or 0) + count
503 local function CollapseQuest(locale, quest)
504 local name_score = quest.finish and DropListMass(quest.finish) or 0
505 local pos_score = quest.pos and PositionListMass(quest.pos)*0.25 or 0
507 if name_score > pos_score then
508 quest.finish = CollapseDropList(quest.finish)
509 quest.pos = nil
510 else
511 quest.finish = nil
512 if quest.pos then
513 TidyPositionList(quest.pos)
517 if quest.item and not next(quest.item) then
518 quest.item = nil
521 if quest.finish then
522 -- This NPC is for a quest. Need to know them.
523 GetObjective(locale, "monster", quest.finish).quest = true
526 return quest.pos == nil and quest.finish == nil and quest.item == nil
529 local function CollapseObjective(locale, objective)
530 if not objective.quest then return true end
531 objective.quest = nil
533 if objective.vendor and not next(objective.vendor, nil) then objective.vendor = nil end
535 if objective.pos and (PositionListMass(objective.pos) >
536 ((objective.drop and DropListMass(objective.drop) or 0) +
537 (objective.contained and DropListMass(objective.contained) or 0))) then
538 objective.drop = nil
539 objective.contained = nil
541 TidyPositionList(objective.pos)
543 if not next(objective.pos, nil) then
544 objective.pos = nil
546 else
547 objective.pos = nil
549 if objective.drop and not next(objective.drop) then objective.drop = nil end
550 if objective.contained and not next(objective.contained) then objective.contained = nil end
553 if objective.looted then
554 objective.looted = math.max(1, math.ceil(objective.looted))
557 if objective.opened then
558 objective.opened = math.max(1, math.ceil(objective.opened))
561 if objective.vendor and next(objective.vendor) then
562 table.sort(objective.vendor)
563 else
564 objective.vendor = nil
567 return objective.drop == nil and objective.contained == nil and objective.pos == nil and objective.vendor == nil
570 local function AddInputData(data)
571 if data.QuestHelper_StaticData then
572 -- Importing a static data file.
573 local static = data.QuestHelper_StaticData
574 data.QuestHelper_StaticData = nil
576 for locale, info in pairs(static) do
577 data.QuestHelper_Locale = locale
578 data.QuestHelper_Quests = info.quest
579 data.QuestHelper_Objectives = info.objective
580 data.QuestHelper_FlightRoutes = info.flight_routes
581 data.QuestHelper_FlightInstructors = info.flight_instructors
583 for cat, list in pairs(data.QuestHelper_Objectives) do
584 for name, info in pairs(list) do
585 info.quest = true
589 AddInputData(data)
592 return
595 QuestHelper_UpgradeDatabase(data)
597 if type(data.QuestHelper_Locale) == "string" then
598 local locale = data.QuestHelper_Locale
600 if type(data.QuestHelper_Quests) == "table" then for faction, levels in pairs(data.QuestHelper_Quests) do
601 if type(levels) == "table" then for level, quest_list in pairs(levels) do
602 if type(quest_list) == "table" then for quest_name, quest_data in pairs(quest_list) do
603 AddQuest(locale, faction, level, quest_name, quest_data)
604 end end
605 end end
606 end end
608 if type(data.QuestHelper_Objectives) == "table" then for category, objectives in pairs(data.QuestHelper_Objectives) do
609 if type(objectives) == "table" then for name, objective in pairs(objectives) do
610 AddObjective(locale, category, name, objective)
611 end end
612 end end
614 if type(data.QuestHelper_FlightInstructors) == "table" then for faction, list in pairs(data.QuestHelper_FlightInstructors) do
615 if type(list) == "table" then for location, npc in pairs(list) do
616 AddFlightInstructor(locale, faction, location, npc)
617 end end
618 end end
620 if type(data.QuestHelper_FlightRoutes) == "table" then for faction, start_list in pairs(data.QuestHelper_FlightRoutes) do
621 if type(start_list) == "table" then for start, destination_list in pairs(start_list) do
622 if type(destination_list) == "table" then for destination, hash_list in pairs(destination_list) do
623 if type(hash_list) == "table" then for hash, value in pairs(hash_list) do
624 AddFlightRoute(locale, faction, start, destination, hash, value)
625 end end
626 end end
627 end end
628 end end
632 local function QuestItemsAreSimilar(item, quest_list)
633 -- TODO: Write this function. Should make sure all the quests get item from the same place.
634 return #quest_list <= 1
637 local function RemoveQuestByData(data)
638 for locale, l in pairs(StaticData) do
639 for faction, levels in pairs(l.quest) do
640 for level, quest_list in pairs(levels) do
641 for quest, quest_data in pairs(quest_list) do
642 if data == quest_data then
643 if quest_data.alt then
644 local alt = quest_data.alt
645 local hash = next(alt, nil)
646 local quest_data2 = alt[hash]
647 alt[hash] = nil
648 quest_list[quest] = quest_data2
649 if next(alt, nil) then
650 quest_data2.alt = alt
651 quest_data2.hash = hash
653 else
654 quest_list[quest] = nil
656 elseif quest_data.alt then
657 for hash, quest_data2 in pairs(quest_data.alt) do
658 if data == quest_data2 then
659 quest_data.alt[hash] = nil
660 break
663 if not next(quest_data.alt) then
664 quest_data.alt = nil
665 quest_data.hash = nil
669 if not next(levels[level], nil) then levels[level] = nil end
671 if not next(l.quest[faction], nil) then l.quest[faction] = nil end
676 function CompileInputFile(filename)
677 local data_loader = loadfile(filename)
678 if data_loader then
679 local data = {}
680 setfenv(data_loader, data)
681 data_loader()
682 AddInputData(data)
683 else
684 print("'"..filename.."' couldn't be loaded!")
688 function handleTranslations()
689 for locale, l in pairs(StaticData) do
690 if l.objective then
691 local item_map = {}
692 local monster_map = {}
693 local quest_map = {}
695 for id, data in pairs(WoWData.item) do
696 if data.name[locale] then
697 item_map[data.name[locale]] = id
701 for id, data in pairs(WoWData.npc) do
702 if data.name[locale] then
703 monster_map[data.name[locale]] = id
707 for id, data in pairs(WoWData.quest) do
708 if data.name[locale] then
709 quest_map[data.level.."/"..data.hash[locale].."/"..data.name[locale]] = id
713 local function item2dat(data, item)
714 if not item then item = {} end
716 item.quest = item.quest or data.quest
718 if data.opened then
719 item.opened = (item.opened or 0) + data.opened
720 data.opened = nil
723 if data.pos then
724 if not item.pos then
725 item.pos = data.pos
726 else
727 MergePositionLists(item.pos, data.pos)
730 data.pos = nil
733 if data.vendor then
734 if not item.vendor then
735 item.vendor = {}
738 for i, npc in ipairs(data.vendor) do
739 local id = monster_map[npc]
740 if id then
741 addVendor(item.vendor, id)
746 if data.drop then
747 if not item.drop then
748 item.drop = {}
751 for name, count in pairs(data.drop) do
752 local id = monster_map[name]
753 if id then
754 item.drop[id] = (item.drop[id] or 0) + count
755 data.drop[name] = nil
760 if data.contained then
761 if not item.contained then
762 item.contained = {}
765 for name, count in pairs(data.contained) do
766 local id = item_map[name]
767 if id then
768 item.contained[id] = (item.contained[id] or 0) + count
769 data.contained[name] = nil
774 return item
777 if l.objective.item then for name, data in pairs(l.objective.item) do
778 local id = item_map[name]
779 if id then
780 item2dat(data, WoWData.item[id])
781 local item = WoWData.item[id]
783 end end
785 if l.objective.monster then for name, data in pairs(l.objective.monster) do
786 local id = monster_map[name]
787 if id then
788 local npc = WoWData.npc[id]
790 npc.quest = npc.quest or data.quest
791 npc.faction = npc.faction or data.faction
793 if data.looted then
794 npc.looted = (npc.looted or 0) + data.looted
795 data.looted = nil
798 if data.pos then
799 if not npc.pos then
800 npc.pos = data.pos
801 else
802 MergePositionLists(npc.pos, data.pos)
805 data.pos = nil
808 end end
810 local function q2static(faction, name, data)
811 local id = quest_map[name]
812 if id then
813 local quest = WoWData.quest[id]
814 quest.faction[faction] = true
816 print("Copying Quest", faction, name)
818 if data.finish and next(data.finish) then
819 quest.finish = monster_map[CollapseDropList(data.finish)] or quest.finish
822 if data.item then
823 quest.item = quest.item or {}
825 for name, idata in pairs(data.item) do
826 local id = item_map[name]
827 if id then
828 quest.item[id] = item2dat(idata, quest.item[id])
835 if l.quest then for faction, fqlist in pairs(l.quest) do
836 for level, qlist in pairs(fqlist) do
837 for name, qdata in pairs(qlist) do
838 if qdata.hash then
839 q2static(faction, level.."/"..qdata.hash.."/"..name, qdata)
842 if qdata.alt then for hash, qdata2 in pairs(qdata.alt) do
843 q2static(faction, level.."/"..hash.."/"..name, qdata2)
844 end end
847 end end
851 for id, item in pairs(WoWData.item) do
852 for locale, name in pairs(item.name) do
853 print("Adding item ", locale, name)
854 local data = GetObjective(locale, "item", name)
856 data.quest = data.quest or item.quest
858 if item.opened then
859 data.opened = item.opened
862 if item.pos then
863 data.pos = data.pos or {}
864 MergePositionLists(data.pos, item.pos)
867 if item.vendor then
868 data.vendor = data.vendor or {}
870 for i, id in ipairs(item.vendor) do
871 local name = WoWData.npc[id] and WoWData.npc[id].name[locale]
872 if name then
873 addVendor(data.vendor, name)
878 if item.drop then
879 data.drop = data.drop or {}
880 for id, count in pairs(item.drop) do
881 local name = WoWData.npc[id] and WoWData.npc[id].name[locale]
882 if name then
883 data.drop[name] = (data.drop[name] or 0) + count
888 if item.contained then
889 data.contained = data.contained or {}
890 for id, count in pairs(item.contained) do
891 local name = WoWData.item[id] and WoWData.item[id].name[locale]
892 if name then
893 data.contained[name] = (data.contained[name] or 0) + count
900 for id, npc in pairs(WoWData.npc) do
901 for locale, name in pairs(npc.name) do
902 print("Adding NPC ", locale, name)
903 local data = GetObjective(locale, "monster", name)
905 data.quest = data.quest or npc.quest
906 data.faction = data.faction or npc.faction
908 if npc.looted then
909 data.looted = npc.looted
912 if npc.pos then
913 data.pos = data.pos or {}
914 MergePositionLists(data.pos, npc.pos)
919 for id, quest in pairs(WoWData.quest) do
920 for faction in pairs(quest.faction) do
921 for locale, name in pairs(quest.name) do
922 print("Adding Quest ", locale, faction, quest.level, quest.hash[locale], name)
923 local data = GetQuest(locale, faction, quest.level, name, quest.hash[locale])
925 if quest.finish then
926 local fname = WoWData.npc[quest.finish] and WoWData.npc[quest.finish].name[locale]
927 if fname then
928 data.finish = {[fname] = 1}
932 if quest.item then
933 for id, item in pairs(quest.item) do
934 local iname = WoWData.item[id] and WoWData.item[id].name[locale]
935 if iname then
936 local qdata = data
938 if not qdata.item then qdata.item = {} end
939 local data = qdata.item[iname] or {}
940 qdata.item[iname] = data
942 if item.pos then
943 data.pos = data.pos or {}
944 MergePositionLists(data.pos, item.pos)
947 if item.drop then
948 data.drop = data.drop or {}
949 for id, count in pairs(item.drop) do
950 local name = WoWData.npc[id] and WoWData.npc[id].name[locale]
951 if name then
952 data.drop[name] = (data.drop[name] or 0) + count
957 if item.contained then
958 data.contained = data.contained or {}
959 for id, count in pairs(item.contained) do
960 local name = WoWData.item[id] and WoWData.item[id].name[locale]
961 if name then
962 data.contained[name] = (data.contained[name] or 0) + count
973 -- TODO: quests.
976 function CompileFinish()
977 handleTranslations()
979 for locale, l in pairs(StaticData) do
980 local quest_item_mass = {}
981 local quest_item_quests = {}
983 local function WatchQuestItems(quest)
984 if quest.finish then GetObjective(locale, "monster", quest.finish).quest = true end
986 if quest.item then
987 for item, data in pairs(quest.item) do
988 quest_item_mass[item] = (quest_item_mass[item] or 0)+
989 (data.drop and DropListMass(data.drop) or 0)+
990 (data.contained and DropListMass(data.contained) or 0)+
991 (data.pos and PositionListMass(data.pos) or 0)
993 quest_item_quests[item] = quest_item_quests[item] or {}
994 table.insert(quest_item_quests[item], quest)
999 print("Processing quests ", locale)
1001 for faction, levels in pairs(l.quest) do
1002 local delete_faction = true
1003 for level, quest_list in pairs(levels) do
1004 local delete_level = true
1005 for quest, quest_data in pairs(quest_list) do
1006 if quest_data.alt then
1007 for hash, quest_data2 in pairs(quest_data.alt) do
1008 if CollapseQuest(locale, quest_data2) then
1009 quest_data.alt[hash] = nil
1010 else
1011 quest_data2.hash = nil
1012 WatchQuestItems(quest_data2)
1016 if not next(quest_data.alt, nil) then
1017 quest_data.alt = nil
1018 quest_data.hash = nil
1022 if CollapseQuest(locale, quest_data) then
1023 if quest_data.alt then
1024 local alt = quest_data.alt
1025 local hash = next(alt, nil)
1026 local quest_data2 = alt[hash]
1027 alt[hash] = nil
1028 quest_list[quest] = quest_data2
1029 if next(alt, nil) then
1030 quest_data2.alt = alt
1031 quest_data2.hash = hash
1034 delete_level = false
1035 else
1036 quest_list[quest] = nil
1038 else
1039 WatchQuestItems(quest_data)
1040 delete_level = false
1044 if delete_level then levels[level] = nil else delete_faction = false end
1046 if delete_faction then l.quest[faction] = nil end
1049 if l.flight_instructors then for faction, list in pairs(l.flight_instructors) do
1050 for area, npc in pairs(list) do
1051 -- Need to remember the flight instructors, for use in routing.
1052 GetObjective(locale, "monster", npc).quest = true
1054 end end
1056 for item, quest_list in pairs(quest_item_quests) do
1057 -- If all the items are similar, then we don't want quest item entries for them,
1058 -- we want to use the gobal item objective instead.
1059 if QuestItemsAreSimilar(item, quest_list) then
1060 quest_item_mass[item] = 0
1064 print("Processing quest items ", locale)
1065 -- Will go through the items and either delete them, or merge the quest items into them, and then
1066 -- mark the relevent monsters as being quest objectives.
1067 if l.objective["item"] then
1068 for name, objective in pairs(l.objective["item"]) do
1069 -- If this is a quest item, mark anything that drops it as being a quest monster.
1070 local quest_mass = quest_item_mass[name] or 0
1072 if objective.vendor and next(objective.vendor, nil) then
1073 quest_mass = 0 -- If the item can be bought, then it shouldn't be quest specific.
1076 local item_mass = (objective.pos and PositionListMass(objective.pos) or 0)+
1077 (objective.drop and DropListMass(objective.drop) or 0)+
1078 (objective.contained and DropListMass(objective.contained) or 0)
1080 if quest_mass > item_mass then
1081 -- Delete this item, we'll deal with the the quests using the items after.
1082 l.objective["item"][name] = nil
1083 else
1084 if quest_item_quests[name] then
1085 for i, quest_data in pairs(quest_item_quests[name]) do
1086 local quest_item = quest_data.item and quest_data.item[name]
1087 if quest_item then
1088 if quest_item.drop then
1089 if not objective.drop then objective.drop = {} end
1090 MergeDropLists(objective.drop, quest_item.drop)
1093 if quest_item.contained then
1094 if not objective.contained then objective.contained = {} end
1095 MergeDropLists(objective.contained, quest_item.contained)
1098 if quest_item.pos then
1099 if not objective.pos then objective.pos = {} end
1100 MergePositionLists(objective.pos, quest_item.pos)
1103 quest_data.item[name] = nil
1104 if not next(quest_data.item, nil) then
1105 quest_data.item = nil
1107 if not quest_data.finish and not quest_data.pos then
1108 RemoveQuestByData(quest_data)
1114 quest_item_quests[name] = nil
1115 objective.quest = true
1118 if objective.quest then
1119 if objective.drop then
1120 TidyDropList(locale, objective.drop)
1121 for monster, count in pairs(objective.drop) do
1122 GetObjective(locale, "monster", monster).quest = true
1126 if objective.contained then
1127 TidyContainedList(locale, objective.contained)
1128 for item, count in pairs(objective.contained) do
1129 GetObjective(locale, "item", item).quest = true
1133 if objective.vendor then
1134 for i, npc in ipairs(objective.vendor) do
1135 GetObjective(locale, "monster", npc).quest = true
1143 -- For any quest items that didn't get handled above, we'll clean them up and leave them be.
1144 for item, quest_list in pairs(quest_item_quests) do
1145 for _, quest_data in ipairs(quest_list) do
1146 -- Item should already exist in quest, not going to check.
1147 local item_data = quest_data.item[item]
1149 local pos_mass = 0
1150 if item_data.pos then
1151 pos_mass = PositionListMass(item_data.pos)
1152 TidyPositionList(item_data.pos)
1155 local drop_mass = 0
1156 if item_data.drop then
1157 drop_mass = DropListMass(item_data.drop)
1158 TidyDropList(locale, item_data.drop)
1161 local contained_mass = 0
1162 if item_data.contained then
1163 contained_mass = DropListMass(item_data.contained)
1164 TidyContainedList(locale, item_data.contained)
1167 if drop_mass+contained_mass > pos_mass then
1168 item_data.pos = nil
1169 if item_data.drop then
1170 for monster, count in pairs(item_data.drop) do
1171 GetObjective(locale, "monster", monster).quest = true
1175 if item_data.contained then
1176 for item, count in pairs(item_data.contained) do
1177 GetObjective(locale, "item", item).quest = true
1180 else
1181 item_data.drop = nil
1182 item_data.contained = nil
1185 if not item_data.pos and not item_data.drop then
1186 quest_data.item[item] = nil
1187 if not next(quest_data.item, nil) then
1188 quest_data.item = nil
1190 if not quest_data.finish and not quest_data.pos then
1191 RemoveQuestByData(quest_data)
1198 print("Processing objectives ", locale)
1200 for category, objectives in pairs(l.objective) do
1201 local delete_category = true
1202 for name, objective in pairs(objectives) do
1203 if CollapseObjective(locale, objective) then
1204 objectives[name] = nil
1205 else
1206 delete_category = false
1209 if delete_category then l.objective[category] = nil end
1212 if l.flight_routes then
1213 for faction, start_list in pairs(l.flight_routes) do
1214 local delete_faction = true
1215 for start, dest_list in pairs(start_list) do
1216 local delete_start = true
1217 for dest, hash_list in pairs(dest_list) do
1218 local delete_dest = true
1219 for hash, value in pairs(hash_list) do
1220 if type(value) == "table" then
1221 hash_list[hash] = CollapseAverage(value)
1222 delete_dest = false
1223 elseif value == true and hash == 0 then
1224 delete_dest = false
1227 if delete_dest then
1228 dest_list[dest] = nil
1229 else
1230 delete_start = false
1233 if delete_start then
1234 start_list[start] = nil
1235 else
1236 delete_faction = false
1239 if delete_faction then
1240 l.flight_routes[faction] = nil
1246 local old_data = StaticData
1247 StaticData = {}
1248 return old_data