1 WoWData
= {item
={},quest
={},npc
={}} -- The build script will replace this with actual data if it can.
3 local ignored
= {"Unknown", "Field Repair Bot 74A", "Field Repair Bot 110G"}
7 function CreateAverage()
11 function AppendToAverage(average
, value
)
12 table.insert(average
, value
)
15 function CollapseAverage(average
)
17 local to_remove
= math
.floor(#average
*0.2)
18 for i
= 1,to_remove
do
19 table.remove(average
, 1)
26 for _
, v
in pairs(average
) do
32 function GetQuest(locale
, faction
, level
, name
, hash
)
33 local l
= StaticData
[locale
]
38 StaticData
[locale
] = l
40 local a
= l
.quest
[faction
]
58 if hash
and hash
~= q
.hash
then
64 if hash
and not q
.hash
then
65 -- If the old quest didn't have a hash, we'll assume this is it. If we're wrong, we'll
66 -- hopefully overwrite it with the correct data.
68 elseif not hash
and q
.hash
then
69 -- If the old quest had a hash, but this one doesn't, we'll return a dummy object
70 -- so that we don't overwrite it with the wrong quest data.
72 elseif hash
~= q
.hash
then
82 function GetObjective(locale
, category
, name
)
83 local l
= StaticData
[locale
]
88 StaticData
[locale
] = l
90 local list
= l
.objective
[category
]
93 l
.objective
[category
] = list
95 local obj
= list
[name
]
103 local function Distance(a
, b
)
104 -- Doing this, because the distances are going to be rounded later, and I don't want it to create
105 -- a situation where if you ran TidyPositionList twice, it would have a different result.
107 local x
, y
= math
.floor(a
[2]*10000+0.5)/10000-math
.floor(b
[2]*10000+0.5)/10000,
108 math
.floor(a
[3]*10000+0.5)/10000-math
.floor(b
[3]*10000+0.5)/10000
110 return math
.sqrt(x
*x
+y
*y
)
113 local function TidyDropList(locale
, list
)
116 for monster
, count
in pairs(list
) do
117 local monster_obj
= GetObjective(locale
, "monster", monster
)
118 if monster_obj
.looted
and monster_obj
.looted
> 0 then
119 high
= math
.max(high
, math
.max(1, math
.floor(count
))/math
.ceil(monster_obj
.looted
))
123 for monster
, count
in pairs(list
) do
124 local monster_obj
= GetObjective(locale
, "monster", monster
)
125 count
= math
.max(1, math
.floor(count
))
127 if monster_obj
.looted
and monster_obj
.looted
> 0 and count
/math
.ceil(monster_obj
.looted
) > high
*0.2 then
128 list
[monster
] = count
136 local function TidyContainedList(locale
, list
)
139 for item
, count
in pairs(list
) do
140 local item_obj
= GetObjective(locale
, "item", item
)
141 if item_obj
.opened
and item_obj
.opened
> 0 then
142 high
= math
.max(high
, math
.max(1, math
.floor(count
))/math
.ceil(item_obj
.opened
))
146 for item
, count
in pairs(list
) do
147 local item_obj
= GetObjective(locale
, "item", item
)
148 count
= math
.max(1, math
.floor(count
))
150 if item_obj
.opened
and item_obj
.opened
> 0 and count
/math
.ceil(item_obj
.opened
) > high
*0.2 then
158 local function TidyPositionList(list
, min_distance
)
159 min_distance
= min_distance
or 0.03
161 if #list
== 0 then return end
162 local changed
= false
165 local nearest
, distance
= nil, 0
166 for j
= i
+1, #list
do
167 local d
= Distance(list
[i
], list
[j
])
168 if not nearest
or d
< distance
then
169 nearest
, distance
= j
, d
172 if nearest
and distance
< min_distance
then
173 local a
, b
= list
[i
], list
[nearest
]
174 a
[2] = (a
[2]*a
[4]+b
[2]*b
[4])/(a
[4]+b
[4])
175 a
[3] = (a
[3]*a
[4]+b
[3]*b
[4])/(a
[4]+b
[4])
177 table.remove(list
, nearest
)
184 -- Because we moved nodes around, we'll check again to make sure we didn't move too close together
191 for i
, data
in ipairs(list
) do
192 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.
193 highest
= math
.max(highest
, data
[4])
196 local i
= 1 -- Remove anything that doesn't seem very likely.
198 if list
[i
][4] < highest
*0.2 then
199 table.remove(list
, i
)
201 list
[i
][4] = math
.max(1, math
.floor(list
[i
][4]*100/highest
+0.5))
206 for i
, j
in ipairs(list
) do
207 j
[2] = math
.floor(j
[2]*10000+0.5)/10000
208 j
[3] = math
.floor(j
[3]*10000+0.5)/10000
211 table.sort(list
, function(a
, b
)
212 if a
[4] > b
[4] then return true end
213 if a
[4] < b
[4] then return false end
214 if a
[1] < b
[1] then return true end
215 if a
[1] > b
[1] then return false end
216 if a
[2] < b
[2] then return true end
217 if a
[2] > b
[2] then return false end
221 -- Only use the first 5 positions.
222 for i
= 6,#list
do table.remove(list
) end
225 local function DropListMass(list
)
227 for item
, count
in pairs(list
) do
233 local function PositionListMass(list
)
235 for _
, pos
in ipairs(list
) do
241 local function CollapseDropList(list
)
242 local result
, c
= nil, 0
243 for item
, count
in pairs(list
) do
244 if not result
or count
> c
then
245 result
, c
= item
, count
251 local function MergeDropLists(list
, add
)
252 for item
, count
in pairs(add
) do
253 list
[item
] = (list
[item
] or 0) + count
257 local function MergePositionLists(list
, add
)
258 for _
, pos
in ipairs(add
) do
259 local index
, x
, y
, w
= pos
[1], pos
[2], pos
[3], pos
[4]
260 if type(index
) == "number" and QuestHelper_NameLookup
[index
] and
261 type(w
) == "number" and w
> 0 then
262 local bp
, distance
= nil, 0
263 for i
, pos2
in ipairs(list
) do
264 if index
== pos2
[1] then
265 local d
= math
.sqrt((x
-pos2
[2])*(x
-pos2
[2])+(y
-pos2
[3])*(y
-pos2
[3]))
266 if not bp
or d
< distance
then
267 bp
, distance
= pos2
, d
271 if bp
and distance
< 0.03 then
272 bp
[2] = (bp
[2]*bp
[4]+x
*w
)/(bp
[4]+w
)
273 bp
[3] = (bp
[3]*bp
[4]+y
*w
)/(bp
[4]+w
)
276 table.insert(list
, {index
,x
,y
,w
})
282 local function AddQuestEnd(quest
, npc
)
283 if not quest
.finish
then quest
.finish
= {} end
284 quest
.finish
[npc
] = (quest
.finish
[npc
] or 0) + 1
287 local function AddQuestPos(quest
, pos
)
288 if not quest
.pos
then quest
.pos
= {} end
289 MergePositionLists(quest
.pos
, pos
)
292 local function AddQuestItems(quest
, list
)
293 for item
, data
in pairs(list
) do
294 if type(data
.drop
) == "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
].drop
then quest
.item
[item
].drop
= {} end
298 MergeDropLists(quest
.item
[item
].drop
, data
.drop
)
299 elseif type(data
.pos
) == "table" then
300 if not quest
.item
then quest
.item
= {} end
301 if not quest
.item
[item
] then quest
.item
[item
] = {} end
302 if not quest
.item
[item
].pos
then quest
.item
[item
].pos
= {} end
303 MergePositionLists(quest
.item
[item
].pos
, data
.pos
)
308 local function ValidFaction(faction
)
309 return faction
== 1 or faction
== 2
312 local function AddQuest(locale
, faction
, level
, name
, data
)
313 if ValidFaction(faction
)
314 and type(level
) == "number" and level
>= 1 and level
<= 100
315 and type(name
) == "string" and type(data
) == "table" then
317 local _
, _
, real_name
= string.find(name
, "^%["..level
.."[^%s]-%]%s?(.+)$")
320 -- The Quest Level AddOn mangles level names.
324 local q
= GetQuest(locale
, faction
, level
, name
, type(data
.hash
) == "number" and data
.hash
or nil)
326 if type(data
.id
) == "number" then
327 local wdq
= WoWData
.quest
[data
.id
]
329 wdq
= {name
={},hash
={},faction
={}}
330 WoWData
.quest
[data
.id
] = wdq
332 wdq
.name
[locale
] = name
333 wdq
.hash
[locale
] = data
.hash
or wdq
.hash
[locale
]
337 if type(data
.finish
) == "string" then
338 AddQuestEnd(q
, data
.finish
)
339 elseif type(data
.pos
) == "table" then
340 AddQuestPos(q
, data
.pos
)
343 if type(data
.item
) == "table" then
344 AddQuestItems(q
, data
.item
)
347 if type(data
.hash
) == "number" and type(data
.alt
) == "table" then
348 for hash
, quest
in pairs(data
.alt
) do
350 AddQuest(locale
, faction
, level
, name
, quest
)
356 local function AddFlightInstructor(locale
, faction
, location
, npc
)
357 if ValidFaction(faction
) and type(location
) == "string" and type(npc
) == "string" then
358 local l
= StaticData
[locale
]
361 StaticData
[locale
] = l
363 local faction_list
= l
.flight_instructors
364 if not faction_list
then
366 l
.flight_instructors
= faction_list
369 local location_list
= faction_list
[faction
]
370 if not location_list
then
372 faction_list
[faction
] = location_list
374 location_list
[location
] = npc
378 local function AddFlightRoute(locale
, faction
, start
, destination
, hash
, value
)
379 if ValidFaction(faction
) and
380 type(start
) == "string" and
381 type(destination
) == "string" and
382 type(hash
) == "number" and
383 ((value
== true and hash
== 0) or (type(value
) == "number" and value
> 0)) then
384 local l
= StaticData
[locale
]
387 StaticData
[locale
] = l
389 local faction_list
= l
.flight_routes
390 if not faction_list
then
392 l
.flight_routes
= faction_list
394 local start_list
= faction_list
[faction
]
395 if not start_list
then
397 faction_list
[faction
] = start_list
399 local end_list
= start_list
[start
]
402 start_list
[start
] = end_list
404 local hash_list
= end_list
[destination
]
405 if not hash_list
then
407 end_list
[destination
] = hash_list
409 if value
== true then
410 hash_list
[hash
] = hash_list
[hash
] or true
412 local average
= hash_list
[hash
]
413 if type(average
) ~= "table" then
414 average
= CreateAverage()
415 hash_list
[hash
] = average
417 AppendToAverage(average
, value
)
422 local function addVendor(list
, npc
)
423 for _
, existing
in ipairs(list
) do
424 if existing
== npc
then
429 table.insert(list
, npc
)
432 local function addVendors(list
, to_add
)
433 if not list
then list
= {} end
435 for _
, npc
in ipairs(to_add
) do
436 if not ignored
[npc
] then
444 local function AddObjective(locale
, category
, name
, objective
)
445 if type(category
) == "string"
446 and type(name
) == "string"
447 and not ignored
[name
]
448 and type(objective
) == "table" then
449 local o
= GetObjective(locale
, category
, name
)
451 if objective
.quest
== true then o
.quest
= true end
453 if type(objective
.pos
) == "table" then
454 if not o
.pos
then o
.pos
= {} end
455 MergePositionLists(o
.pos
, objective
.pos
)
458 if category
== "monster" then
459 if type(objective
.id
) == "number" then
460 local wdm
= WoWData
.npc
[objective
.id
]
463 WoWData
.npc
[objective
.id
] = wdm
465 wdm
.name
[locale
] = name
468 if type(objective
.looted
) == "number" and objective
.looted
>= 1 then
469 o
.looted
= (o
.looted
or 0) + objective
.looted
471 if ValidFaction(objective
.faction
) then
472 o
.faction
= objective
.faction
474 elseif category
== "item" then
475 if type(objective
.id
) == "number" then
476 local wdi
= WoWData
.item
[objective
.id
]
479 WoWData
.item
[objective
.id
] = wdi
481 wdi
.name
[locale
] = name
484 if type(objective
.opened
) == "number" and objective
.opened
>= 1 then
485 o
.opened
= (o
.opened
or 0) + objective
.opened
487 if type(objective
.vendor
) == "table" then
488 o
.vendor
= addVendors(o
.vendor
, objective
.vendor
)
490 if type(objective
.drop
) == "table" then
491 if not o
.drop
then o
.drop
= {} end
492 for monster
, count
in pairs(objective
.drop
) do
493 if type(monster
) == "string" and not ignored
[monster
] and type(count
) == "number" then
494 o
.drop
[monster
] = (o
.drop
[monster
] or 0) + count
498 if type(objective
.contained
) == "table" then
499 if not o
.contained
then o
.contained
= {} end
500 for item
, count
in pairs(objective
.contained
) do
501 if type(item
) == "string" and type(count
) == "number" then
502 o
.contained
[item
] = (o
.contained
[item
] or 0) + count
510 local function CollapseQuest(locale
, quest
)
511 local name_score
= quest
.finish
and DropListMass(quest
.finish
) or 0
512 local pos_score
= quest
.pos
and PositionListMass(quest
.pos
)*0.25 or 0
514 if name_score
> pos_score
then
515 quest
.finish
= CollapseDropList(quest
.finish
)
520 TidyPositionList(quest
.pos
)
524 if quest
.item
and not next(quest
.item
) then
529 -- This NPC is for a quest. Need to know them.
530 GetObjective(locale
, "monster", quest
.finish
).quest
= true
533 return quest
.pos
== nil and quest
.finish
== nil and quest
.item
== nil
536 local function CollapseObjective(locale
, objective
)
537 if not objective
.quest
then return true end
538 objective
.quest
= nil
540 if objective
.vendor
and not next(objective
.vendor
, nil) then objective
.vendor
= nil end
542 if objective
.pos
and (PositionListMass(objective
.pos
) >
543 ((objective
.drop
and DropListMass(objective
.drop
) or 0) +
544 (objective
.contained
and DropListMass(objective
.contained
) or 0))) then
546 objective
.contained
= nil
548 TidyPositionList(objective
.pos
)
550 if not next(objective
.pos
, nil) then
556 if objective
.drop
and not next(objective
.drop
) then objective
.drop
= nil end
557 if objective
.contained
and not next(objective
.contained
) then objective
.contained
= nil end
560 if objective
.looted
then
561 objective
.looted
= math
.max(1, math
.ceil(objective
.looted
))
564 if objective
.opened
then
565 objective
.opened
= math
.max(1, math
.ceil(objective
.opened
))
568 if objective
.vendor
and next(objective
.vendor
) then
569 table.sort(objective
.vendor
)
571 objective
.vendor
= nil
574 return objective
.drop
== nil and objective
.contained
== nil and objective
.pos
== nil and objective
.vendor
== nil
577 local function AddInputData(data
, pairfrequencies
)
578 if data
.QuestHelper_StaticData
then
579 -- Importing a static data file.
580 local static
= data
.QuestHelper_StaticData
581 data
.QuestHelper_StaticData
= nil
583 for locale
, info
in pairs(static
) do
584 data
.QuestHelper_Locale
= locale
585 data
.QuestHelper_Quests
= info
.quest
586 data
.QuestHelper_Objectives
= info
.objective
587 data
.QuestHelper_FlightRoutes
= info
.flight_routes
588 data
.QuestHelper_FlightInstructors
= info
.flight_instructors
590 for cat
, list
in pairs(data
.QuestHelper_Objectives
) do
591 for name
, info
in pairs(list
) do
602 QuestHelper_UpgradeDatabase(data
)
604 if type(data
.QuestHelper_Locale
) == "string" then
605 local locale
= data
.QuestHelper_Locale
607 local seen_pairs
= {}
609 if type(data
.QuestHelper_Quests
) == "table" then for version
, package
in pairs(data
.QuestHelper_Quests
) do
610 seen_pairs
[version
] = true
611 if AuthorizedVersion(version
) and type(package
) == "table" then for faction
, levels
in pairs(package
) do
612 if type(levels
) == "table" then for level
, quest_list
in pairs(levels
) do
613 if type(quest_list
) == "table" then for quest_name
, quest_data
in pairs(quest_list
) do
614 AddQuest(locale
, faction
, level
, quest_name
, quest_data
)
620 local function PreWrath(ver
)
621 return ver
:sub(1,1) ~= '3'
624 if type(data
.QuestHelper_Objectives
) == "table" then for version
, package
in pairs(data
.QuestHelper_Objectives
) do
625 seen_pairs
[version
] = true
626 if AuthorizedVersion(version
) and type(package
) == "table" then for category
, objectives
in pairs(package
) do
627 if type(objectives
) == "table" and PreWrath(version
) then for name
, objective
in pairs(objectives
) do
628 if type(objective
) == "table" and objective
.pos
and type(objective
.pos
) == "table" then
629 for i
, pos
in pairs(objective
.pos
) do
630 QuestHelper_ConvertCoordsToWrath(pos
, true)
634 if type(objectives
) == "table" then for name
, objective
in pairs(objectives
) do
635 AddObjective(locale
, category
, name
, objective
)
640 if type(data
.QuestHelper_FlightInstructors
) == "table" then for version
, package
in pairs(data
.QuestHelper_FlightInstructors
) do
641 seen_pairs
[version
] = true
642 if AuthorizedVersion(version
) and type(package
) == "table" then for faction
, list
in pairs(package
) do
643 if type(list
) == "table" then for location
, npc
in pairs(list
) do
644 AddFlightInstructor(locale
, faction
, location
, npc
)
649 if type(data
.QuestHelper_FlightRoutes
) == "table" then for version
, package
in pairs(data
.QuestHelper_FlightRoutes
) do
650 seen_pairs
[version
] = true
651 if AuthorizedVersion(version
) and type(package
) == "table" then for faction
, start_list
in pairs(package
) do
652 if type(start_list
) == "table" then for start
, destination_list
in pairs(start_list
) do
653 if type(destination_list
) == "table" then for destination
, hash_list
in pairs(destination_list
) do
654 if type(hash_list
) == "table" then for hash
, value
in pairs(hash_list
) do
655 AddFlightRoute(locale
, faction
, start
, destination
, hash
, value
)
662 for k
in pairs(seen_pairs
) do
663 pairfrequencies
[k
] = (pairfrequencies
[k
] or 0) + 1
668 local function QuestItemsAreSimilar(item
, quest_list
)
669 -- TODO: Write this function. Should make sure all the quests get item from the same place.
670 return #quest_list
<= 1
673 local function RemoveQuestByData(data
)
674 for locale
, l
in pairs(StaticData
) do
675 for faction
, levels
in pairs(l
.quest
) do
676 for level
, quest_list
in pairs(levels
) do
677 for quest
, quest_data
in pairs(quest_list
) do
678 if data
== quest_data
then
679 if quest_data
.alt
then
680 local alt
= quest_data
.alt
681 local hash
= next(alt
, nil)
682 local quest_data2
= alt
[hash
]
684 quest_list
[quest
] = quest_data2
685 if next(alt
, nil) then
686 quest_data2
.alt
= alt
687 quest_data2
.hash
= hash
690 quest_list
[quest
] = nil
692 elseif quest_data
.alt
then
693 for hash
, quest_data2
in pairs(quest_data
.alt
) do
694 if data
== quest_data2
then
695 quest_data
.alt
[hash
] = nil
699 if not next(quest_data
.alt
) then
701 quest_data
.hash
= nil
705 if not next(levels
[level
], nil) then levels
[level
] = nil end
707 if not next(l
.quest
[faction
], nil) then l
.quest
[faction
] = nil end
712 function CompileInputFile(filename
, pairfrequencies
)
713 local data_loader
= loadfile(filename
)
716 setfenv(data_loader
, data
)
718 AddInputData(data
, pairfrequencies
)
720 print("'"..filename
.."' couldn't be loaded!")
724 function handleTranslations()
725 for locale
, l
in pairs(StaticData
) do
728 local monster_map
= {}
731 for id
, data
in pairs(WoWData
.item
) do
732 if data
.name
[locale
] then
733 item_map
[data
.name
[locale]]
= id
737 for id
, data
in pairs(WoWData
.npc
) do
738 if data
.name
[locale
] then
739 monster_map
[data
.name
[locale]]
= id
743 for id
, data
in pairs(WoWData
.quest
) do
744 if data
.name
[locale
] then
745 quest_map
[data
.level
.."/"..data
.hash
[locale
].."/"..data
.name
[locale]]
= id
749 local function item2dat(data
, item
)
750 if not item
then item
= {} end
752 item
.quest
= item
.quest
or data
.quest
755 item
.opened
= (item
.opened
or 0) + data
.opened
763 MergePositionLists(item
.pos
, data
.pos
)
770 if not item
.vendor
then
774 for i
, npc
in ipairs(data
.vendor
) do
775 local id
= monster_map
[npc
]
777 addVendor(item
.vendor
, id
)
783 if not item
.drop
then
787 for name
, count
in pairs(data
.drop
) do
788 local id
= monster_map
[name
]
790 item
.drop
[id
] = (item
.drop
[id
] or 0) + count
791 data
.drop
[name
] = nil
796 if data
.contained
then
797 if not item
.contained
then
801 for name
, count
in pairs(data
.contained
) do
802 local id
= item_map
[name
]
804 item
.contained
[id
] = (item
.contained
[id
] or 0) + count
805 data
.contained
[name
] = nil
813 if l
.objective
.item
then for name
, data
in pairs(l
.objective
.item
) do
814 local id
= item_map
[name
]
816 item2dat(data
, WoWData
.item
[id
])
817 local item
= WoWData
.item
[id
]
821 if l
.objective
.monster
then for name
, data
in pairs(l
.objective
.monster
) do
822 local id
= monster_map
[name
]
824 local npc
= WoWData
.npc
[id
]
826 npc
.quest
= npc
.quest
or data
.quest
827 npc
.faction
= npc
.faction
or data
.faction
830 npc
.looted
= (npc
.looted
or 0) + data
.looted
838 MergePositionLists(npc
.pos
, data
.pos
)
846 local function q2static(faction
, name
, data
)
847 local id
= quest_map
[name
]
849 local quest
= WoWData
.quest
[id
]
850 quest
.faction
[faction
] = true
852 print("Copying Quest", faction
, name
)
854 if data
.finish
and next(data
.finish
) then
855 quest
.finish
= monster_map
[CollapseDropList(data
.finish
)] or quest
.finish
859 quest
.item
= quest
.item
or {}
861 for name
, idata
in pairs(data
.item
) do
862 local id
= item_map
[name
]
864 quest
.item
[id
] = item2dat(idata
, quest
.item
[id
])
871 if l
.quest
then for faction
, fqlist
in pairs(l
.quest
) do
872 for level
, qlist
in pairs(fqlist
) do
873 for name
, qdata
in pairs(qlist
) do
875 q2static(faction
, level
.."/"..qdata
.hash
.."/"..name
, qdata
)
878 if qdata
.alt
then for hash
, qdata2
in pairs(qdata
.alt
) do
879 q2static(faction
, level
.."/"..hash
.."/"..name
, qdata2
)
887 for id
, item
in pairs(WoWData
.item
) do
888 for locale
, name
in pairs(item
.name
) do
889 print("Adding item ", locale
, name
)
890 local data
= GetObjective(locale
, "item", name
)
892 data
.quest
= data
.quest
or item
.quest
895 data
.opened
= item
.opened
899 data
.pos
= data
.pos
or {}
900 MergePositionLists(data
.pos
, item
.pos
)
904 data
.vendor
= data
.vendor
or {}
906 for i
, id
in ipairs(item
.vendor
) do
907 local name
= WoWData
.npc
[id
] and WoWData
.npc
[id
].name
[locale
]
909 addVendor(data
.vendor
, name
)
915 data
.drop
= data
.drop
or {}
916 for id
, count
in pairs(item
.drop
) do
917 local name
= WoWData
.npc
[id
] and WoWData
.npc
[id
].name
[locale
]
919 data
.drop
[name
] = (data
.drop
[name
] or 0) + count
924 if item
.contained
then
925 data
.contained
= data
.contained
or {}
926 for id
, count
in pairs(item
.contained
) do
927 local name
= WoWData
.item
[id
] and WoWData
.item
[id
].name
[locale
]
929 data
.contained
[name
] = (data
.contained
[name
] or 0) + count
936 for id
, npc
in pairs(WoWData
.npc
) do
937 for locale
, name
in pairs(npc
.name
) do
938 print("Adding NPC ", locale
, name
)
939 local data
= GetObjective(locale
, "monster", name
)
941 data
.quest
= data
.quest
or npc
.quest
942 data
.faction
= data
.faction
or npc
.faction
945 data
.looted
= npc
.looted
949 data
.pos
= data
.pos
or {}
950 MergePositionLists(data
.pos
, npc
.pos
)
955 for id
, quest
in pairs(WoWData
.quest
) do
956 for faction
in pairs(quest
.faction
) do
957 for locale
, name
in pairs(quest
.name
) do
958 print("Adding Quest ", locale
, faction
, quest
.level
, quest
.hash
[locale
], name
)
959 local data
= GetQuest(locale
, faction
, quest
.level
, name
, quest
.hash
[locale
])
962 local fname
= WoWData
.npc
[quest
.finish
] and WoWData
.npc
[quest
.finish
].name
[locale
]
964 data
.finish
= {[fname
] = 1}
969 for id
, item
in pairs(quest
.item
) do
970 local iname
= WoWData
.item
[id
] and WoWData
.item
[id
].name
[locale
]
974 if not qdata
.item
then qdata
.item
= {} end
975 local data
= qdata
.item
[iname
] or {}
976 qdata
.item
[iname
] = data
979 data
.pos
= data
.pos
or {}
980 MergePositionLists(data
.pos
, item
.pos
)
984 data
.drop
= data
.drop
or {}
985 for id
, count
in pairs(item
.drop
) do
986 local name
= WoWData
.npc
[id
] and WoWData
.npc
[id
].name
[locale
]
988 data
.drop
[name
] = (data
.drop
[name
] or 0) + count
993 if item
.contained
then
994 data
.contained
= data
.contained
or {}
995 for id
, count
in pairs(item
.contained
) do
996 local name
= WoWData
.item
[id
] and WoWData
.item
[id
].name
[locale
]
998 data
.contained
[name
] = (data
.contained
[name
] or 0) + count
1012 function CompileFinish()
1013 handleTranslations()
1014 print("Finished translations")
1016 for locale
, l
in pairs(StaticData
) do
1017 local quest_item_mass
= {}
1018 local quest_item_quests
= {}
1020 local function WatchQuestItems(quest
)
1021 if quest
.finish
then GetObjective(locale
, "monster", quest
.finish
).quest
= true end
1024 for item
, data
in pairs(quest
.item
) do
1025 quest_item_mass
[item
] = (quest_item_mass
[item
] or 0)+
1026 (data
.drop
and DropListMass(data
.drop
) or 0)+
1027 (data
.contained
and DropListMass(data
.contained
) or 0)+
1028 (data
.pos
and PositionListMass(data
.pos
) or 0)
1030 quest_item_quests
[item
] = quest_item_quests
[item
] or {}
1031 table.insert(quest_item_quests
[item
], quest
)
1036 print("Processing quests ", locale
)
1038 for faction
, levels
in pairs(l
.quest
) do
1039 local delete_faction
= true
1040 for level
, quest_list
in pairs(levels
) do
1041 local delete_level
= true
1042 for quest
, quest_data
in pairs(quest_list
) do
1043 if quest_data
.alt
then
1044 for hash
, quest_data2
in pairs(quest_data
.alt
) do
1045 if CollapseQuest(locale
, quest_data2
) then
1046 quest_data
.alt
[hash
] = nil
1048 quest_data2
.hash
= nil
1049 WatchQuestItems(quest_data2
)
1053 if not next(quest_data
.alt
, nil) then
1054 quest_data
.alt
= nil
1055 quest_data
.hash
= nil
1059 if CollapseQuest(locale
, quest_data
) then
1060 if quest_data
.alt
then
1061 local alt
= quest_data
.alt
1062 local hash
= next(alt
, nil)
1063 local quest_data2
= alt
[hash
]
1065 quest_list
[quest
] = quest_data2
1066 if next(alt
, nil) then
1067 quest_data2
.alt
= alt
1068 quest_data2
.hash
= hash
1071 delete_level
= false
1073 quest_list
[quest
] = nil
1076 WatchQuestItems(quest_data
)
1077 delete_level
= false
1081 if delete_level
then levels
[level
] = nil else delete_faction
= false end
1083 if delete_faction
then l
.quest
[faction
] = nil end
1086 if l
.flight_instructors
then for faction
, list
in pairs(l
.flight_instructors
) do
1087 for area
, npc
in pairs(list
) do
1088 -- Need to remember the flight instructors, for use in routing.
1089 GetObjective(locale
, "monster", npc
).quest
= true
1093 for item
, quest_list
in pairs(quest_item_quests
) do
1094 -- If all the items are similar, then we don't want quest item entries for them,
1095 -- we want to use the gobal item objective instead.
1096 if QuestItemsAreSimilar(item
, quest_list
) then
1097 quest_item_mass
[item
] = 0
1101 print("Processing quest items ", locale
)
1102 -- Will go through the items and either delete them, or merge the quest items into them, and then
1103 -- mark the relevent monsters as being quest objectives.
1104 if l
.objective
["item"] then
1105 for name
, objective
in pairs(l
.objective
["item"]) do
1106 -- If this is a quest item, mark anything that drops it as being a quest monster.
1107 local quest_mass
= quest_item_mass
[name
] or 0
1109 if objective
.vendor
and next(objective
.vendor
, nil) then
1110 quest_mass
= 0 -- If the item can be bought, then it shouldn't be quest specific.
1113 local item_mass
= (objective
.pos
and PositionListMass(objective
.pos
) or 0)+
1114 (objective
.drop
and DropListMass(objective
.drop
) or 0)+
1115 (objective
.contained
and DropListMass(objective
.contained
) or 0)
1117 if quest_mass
> item_mass
then
1118 -- Delete this item, we'll deal with the the quests using the items after.
1119 l
.objective
["item"][name
] = nil
1121 if quest_item_quests
[name
] then
1122 for i
, quest_data
in pairs(quest_item_quests
[name
]) do
1123 local quest_item
= quest_data
.item
and quest_data
.item
[name
]
1125 if quest_item
.drop
then
1126 if not objective
.drop
then objective
.drop
= {} end
1127 MergeDropLists(objective
.drop
, quest_item
.drop
)
1130 if quest_item
.contained
then
1131 if not objective
.contained
then objective
.contained
= {} end
1132 MergeDropLists(objective
.contained
, quest_item
.contained
)
1135 if quest_item
.pos
then
1136 if not objective
.pos
then objective
.pos
= {} end
1137 MergePositionLists(objective
.pos
, quest_item
.pos
)
1140 quest_data
.item
[name
] = nil
1141 if not next(quest_data
.item
, nil) then
1142 quest_data
.item
= nil
1144 if not quest_data
.finish
and not quest_data
.pos
then
1145 RemoveQuestByData(quest_data
)
1151 quest_item_quests
[name
] = nil
1152 objective
.quest
= true
1155 if objective
.quest
then
1156 if objective
.drop
then
1157 TidyDropList(locale
, objective
.drop
)
1158 for monster
, count
in pairs(objective
.drop
) do
1159 GetObjective(locale
, "monster", monster
).quest
= true
1163 if objective
.contained
then
1164 TidyContainedList(locale
, objective
.contained
)
1165 for item
, count
in pairs(objective
.contained
) do
1166 GetObjective(locale
, "item", item
).quest
= true
1170 if objective
.vendor
then
1171 for i
, npc
in ipairs(objective
.vendor
) do
1172 GetObjective(locale
, "monster", npc
).quest
= true
1180 -- For any quest items that didn't get handled above, we'll clean them up and leave them be.
1181 for item
, quest_list
in pairs(quest_item_quests
) do
1182 for _
, quest_data
in ipairs(quest_list
) do
1183 -- Item should already exist in quest, not going to check.
1184 local item_data
= quest_data
.item
[item
]
1187 if item_data
.pos
then
1188 pos_mass
= PositionListMass(item_data
.pos
)
1189 TidyPositionList(item_data
.pos
)
1193 if item_data
.drop
then
1194 drop_mass
= DropListMass(item_data
.drop
)
1195 TidyDropList(locale
, item_data
.drop
)
1198 local contained_mass
= 0
1199 if item_data
.contained
then
1200 contained_mass
= DropListMass(item_data
.contained
)
1201 TidyContainedList(locale
, item_data
.contained
)
1204 if drop_mass
+contained_mass
> pos_mass
then
1206 if item_data
.drop
then
1207 for monster
, count
in pairs(item_data
.drop
) do
1208 GetObjective(locale
, "monster", monster
).quest
= true
1212 if item_data
.contained
then
1213 for item
, count
in pairs(item_data
.contained
) do
1214 GetObjective(locale
, "item", item
).quest
= true
1218 item_data
.drop
= nil
1219 item_data
.contained
= nil
1222 if not item_data
.pos
and not item_data
.drop
then
1223 quest_data
.item
[item
] = nil
1224 if not next(quest_data
.item
, nil) then
1225 quest_data
.item
= nil
1227 if not quest_data
.finish
and not quest_data
.pos
then
1228 RemoveQuestByData(quest_data
)
1235 print("Processing objectives ", locale
)
1237 for category
, objectives
in pairs(l
.objective
) do
1238 local delete_category
= true
1239 for name
, objective
in pairs(objectives
) do
1240 if CollapseObjective(locale
, objective
) then
1241 objectives
[name
] = nil
1243 delete_category
= false
1246 if delete_category
then l
.objective
[category
] = nil end
1249 local function IgnoreItem(start
, dest
, hash_list
, distance
, count
)
1250 return dest
== "Shattered Sun Staging Area" and (start
== "Light's Hope Chapel, Eastern Plaguelands" or start
== "Menethil Harbor, Wetlands" or start
== "Ironforge, Dun Morogh" or start
== "Stormwind, Elwynn")
1253 if l
.flight_routes
then
1254 for faction
, start_list
in pairs(l
.flight_routes
) do
1255 local delete_faction
= true
1256 for start
, dest_list
in pairs(start_list
) do
1257 local delete_start
= true
1258 for dest
, hash_list
in pairs(dest_list
) do
1259 local delete_dest
= true
1261 local delete_hashes
= {}
1263 for hash
, value
in pairs(hash_list
) do
1264 if type(value
) == "table" and hash
~= "interrupt_count" and hash
~= "no_interrupt_count" then
1265 local count
= #value
1267 hash_list
[hash
] = CollapseAverage(value
)
1269 if IgnoreItem(start
, dest
, hash_list
, hash_list
[hash
], count
) then
1270 print(string.format("Deleting path: %s to %s, value %f, with interrupt/nointerrupt %d %d", start
, dest
, hash_list
[hash
], hash_list
.interrupt_count
or 0, hash_list
.no_interrupt_count
or 0))
1271 table.insert(delete_hashes
, hash
)
1274 delete_dest
= false -- We leave this out here because it *is* a valid flight path, we just don't have valid information for it
1275 elseif value
== true and hash
== 0 then
1280 for k
, v
in pairs(delete_hashes
) do
1284 hash_list
.interrupt_count
= nil
1285 hash_list
.no_interrupt_count
= nil
1288 dest_list
[dest
] = nil
1290 delete_start
= false
1293 if delete_start
then
1294 start_list
[start
] = nil
1296 delete_faction
= false
1299 if delete_faction
then
1300 l
.flight_routes
[faction
] = nil
1306 local old_data
= StaticData