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 CreateFlightTable(locale
, faction
, start
, destination
, new_style
)
379 if ValidFaction(faction
) and
380 type(start
) == "string" and
381 type(destination
) == "string" then
382 local l
= StaticData
[locale
]
385 StaticData
[locale
] = l
387 local faction_list
= l
.flight_routes
388 if not faction_list
then
390 l
.flight_routes
= faction_list
392 local start_list
= faction_list
[faction
]
393 if not start_list
then
395 faction_list
[faction
] = start_list
397 local end_list
= start_list
[start
]
400 start_list
[start
] = end_list
402 local hash_list
= end_list
[destination
]
403 if not hash_list
then
405 end_list
[destination
] = hash_list
408 -- Check to see if the new_style thing is correct
409 local dest_new_style
= hash_list
.no_interrupt_count
or hash_list
.interrupt_count
411 -- If we have new style, and the destination is old style, we reset the destination
412 if (not dest_new_style
) and new_style
then
414 end_list
[destination
] = hash_list
415 dest_new_style
= true
418 if (not dest_new_style
) ~= (not new_style
) then
428 local function AddFlightRoute(hash_list
, hash
, value
, new_style
)
429 if type(hash
) == "number" and
430 ((value
== true and hash
== 0) or (type(value
) == "number" and value
> 0)) then
433 if value
== true then
434 hash_list
[hash
] = hash_list
[hash
] or true
436 local average
= hash_list
[hash
]
437 if type(average
) ~= "table" then
438 average
= CreateAverage()
439 hash_list
[hash
] = average
441 AppendToAverage(average
, value
)
446 local function addVendor(list
, npc
)
447 for _
, existing
in ipairs(list
) do
448 if existing
== npc
then
453 table.insert(list
, npc
)
456 local function addVendors(list
, to_add
)
457 if not list
then list
= {} end
459 for _
, npc
in ipairs(to_add
) do
460 if not ignored
[npc
] then
468 local function AddObjective(locale
, category
, name
, objective
)
469 if type(category
) == "string"
470 and type(name
) == "string"
471 and not ignored
[name
]
472 and type(objective
) == "table" then
473 local o
= GetObjective(locale
, category
, name
)
475 if objective
.quest
== true then o
.quest
= true end
477 if type(objective
.pos
) == "table" then
478 if not o
.pos
then o
.pos
= {} end
479 MergePositionLists(o
.pos
, objective
.pos
)
482 if category
== "monster" then
483 if type(objective
.id
) == "number" then
484 local wdm
= WoWData
.npc
[objective
.id
]
487 WoWData
.npc
[objective
.id
] = wdm
489 wdm
.name
[locale
] = name
492 if type(objective
.looted
) == "number" and objective
.looted
>= 1 then
493 o
.looted
= (o
.looted
or 0) + objective
.looted
495 if ValidFaction(objective
.faction
) then
496 o
.faction
= objective
.faction
498 elseif category
== "item" then
499 if type(objective
.id
) == "number" then
500 local wdi
= WoWData
.item
[objective
.id
]
503 WoWData
.item
[objective
.id
] = wdi
505 wdi
.name
[locale
] = name
508 if type(objective
.opened
) == "number" and objective
.opened
>= 1 then
509 o
.opened
= (o
.opened
or 0) + objective
.opened
511 if type(objective
.vendor
) == "table" then
512 o
.vendor
= addVendors(o
.vendor
, objective
.vendor
)
514 if type(objective
.drop
) == "table" then
515 if not o
.drop
then o
.drop
= {} end
516 for monster
, count
in pairs(objective
.drop
) do
517 if type(monster
) == "string" and not ignored
[monster
] and type(count
) == "number" then
518 o
.drop
[monster
] = (o
.drop
[monster
] or 0) + count
522 if type(objective
.contained
) == "table" then
523 if not o
.contained
then o
.contained
= {} end
524 for item
, count
in pairs(objective
.contained
) do
525 if type(item
) == "string" and type(count
) == "number" then
526 o
.contained
[item
] = (o
.contained
[item
] or 0) + count
534 local function CollapseQuest(locale
, quest
)
535 local name_score
= quest
.finish
and DropListMass(quest
.finish
) or 0
536 local pos_score
= quest
.pos
and PositionListMass(quest
.pos
)*0.25 or 0
538 if name_score
> pos_score
then
539 quest
.finish
= CollapseDropList(quest
.finish
)
544 TidyPositionList(quest
.pos
)
548 if quest
.item
and not next(quest
.item
) then
553 -- This NPC is for a quest. Need to know them.
554 GetObjective(locale
, "monster", quest
.finish
).quest
= true
557 return quest
.pos
== nil and quest
.finish
== nil and quest
.item
== nil
560 local function CollapseObjective(locale
, objective
)
561 if not objective
.quest
then return true end
562 objective
.quest
= nil
564 if objective
.vendor
and not next(objective
.vendor
, nil) then objective
.vendor
= nil end
566 if objective
.pos
and (PositionListMass(objective
.pos
) >
567 ((objective
.drop
and DropListMass(objective
.drop
) or 0) +
568 (objective
.contained
and DropListMass(objective
.contained
) or 0))) then
570 objective
.contained
= nil
572 TidyPositionList(objective
.pos
)
574 if not next(objective
.pos
, nil) then
580 if objective
.drop
and not next(objective
.drop
) then objective
.drop
= nil end
581 if objective
.contained
and not next(objective
.contained
) then objective
.contained
= nil end
584 if objective
.looted
then
585 objective
.looted
= math
.max(1, math
.ceil(objective
.looted
))
588 if objective
.opened
then
589 objective
.opened
= math
.max(1, math
.ceil(objective
.opened
))
592 if objective
.vendor
and next(objective
.vendor
) then
593 table.sort(objective
.vendor
)
595 objective
.vendor
= nil
598 return objective
.drop
== nil and objective
.contained
== nil and objective
.pos
== nil and objective
.vendor
== nil
601 local function AddInputData(data
, pairfrequencies
)
602 if data
.QuestHelper_StaticData
then
603 -- Importing a static data file.
604 local static
= data
.QuestHelper_StaticData
605 data
.QuestHelper_StaticData
= nil
607 for locale
, info
in pairs(static
) do
608 data
.QuestHelper_Locale
= locale
609 data
.QuestHelper_Quests
= info
.quest
610 data
.QuestHelper_Objectives
= info
.objective
611 data
.QuestHelper_FlightRoutes
= info
.flight_routes
612 data
.QuestHelper_FlightInstructors
= info
.flight_instructors
614 for cat
, list
in pairs(data
.QuestHelper_Objectives
) do
615 for name
, info
in pairs(list
) do
626 QuestHelper_UpgradeDatabase(data
)
628 if type(data
.QuestHelper_Locale
) == "string" then
629 local locale
= data
.QuestHelper_Locale
631 local seen_pairs
= {}
633 if type(data
.QuestHelper_Quests
) == "table" then for version
, package
in pairs(data
.QuestHelper_Quests
) do
634 seen_pairs
[version
] = true
635 if AuthorizedVersion(version
) and type(package
) == "table" then for faction
, levels
in pairs(package
) do
636 if type(levels
) == "table" and PreWrath(version
) then for level
, quest_list
in pairs(levels
) do
637 if type(quest_list
) == "table" then for quest_name
, quest_data
in pairs(quest_list
) do
638 if type(quest_data
) == "table" and quest_data
.item
and type(quest_data
.item
) == "table" then for qiname
, qidata
in pairs(quest_data
.item
) do
639 if type(qidata
) == "table" and qidata
.pos
and type(qidata
.pos
) == "table" then for i
, pos
in pairs(qidata
.pos
) do
640 QuestHelper_ConvertCoordsToWrath(pos
, true)
646 if type(levels
) == "table" then for level
, quest_list
in pairs(levels
) do
647 if type(quest_list
) == "table" then for quest_name
, quest_data
in pairs(quest_list
) do
648 AddQuest(locale
, faction
, level
, quest_name
, quest_data
)
654 if type(data
.QuestHelper_Objectives
) == "table" then for version
, package
in pairs(data
.QuestHelper_Objectives
) do
655 seen_pairs
[version
] = true
656 if AuthorizedVersion(version
) and type(package
) == "table" then for category
, objectives
in pairs(package
) do
657 if type(objectives
) == "table" and PreWrath(version
) then for name
, objective
in pairs(objectives
) do
658 if type(objective
) == "table" and objective
.pos
and type(objective
.pos
) == "table" then
659 for i
, pos
in pairs(objective
.pos
) do
660 QuestHelper_ConvertCoordsToWrath(pos
, true)
664 if type(objectives
) == "table" then for name
, objective
in pairs(objectives
) do
665 AddObjective(locale
, category
, name
, objective
)
670 if type(data
.QuestHelper_FlightInstructors
) == "table" then for version
, package
in pairs(data
.QuestHelper_FlightInstructors
) do
671 seen_pairs
[version
] = true
672 if AuthorizedVersion(version
) and type(package
) == "table" then for faction
, list
in pairs(package
) do
673 if type(list
) == "table" then for location
, npc
in pairs(list
) do
674 AddFlightInstructor(locale
, faction
, location
, npc
)
679 if type(data
.QuestHelper_FlightRoutes
) == "table" then for version
, package
in pairs(data
.QuestHelper_FlightRoutes
) do
680 seen_pairs
[version
] = true
681 if AuthorizedVersion(version
) and type(package
) == "table" then for faction
, start_list
in pairs(package
) do
682 if type(start_list
) == "table" then for start
, destination_list
in pairs(start_list
) do
683 if type(destination_list
) == "table" then for destination
, hash_list
in pairs(destination_list
) do
684 local tab
= CreateFlightTable(locale
, faction
, start
, destination
, (hash_list
.no_interrupt_count
or hash_list
.interrupt_count
))
686 if hash_list
.no_interrupt_count
then tab
.no_interrupt_count
= (tab
.no_interrupt_count
or 0) + hash_list
.no_interrupt_count
end
687 if hash_list
.interrupt_count
then tab
.interrupt_count
= (tab
.interrupt_count
or 0) + hash_list
.interrupt_count
end
689 if type(hash_list
) == "table" then for hash
, value
in pairs(hash_list
) do
690 AddFlightRoute(tab
, hash
, value
)
698 for k
in pairs(seen_pairs
) do
699 pairfrequencies
[k
] = (pairfrequencies
[k
] or 0) + 1
704 local function QuestItemsAreSimilar(item
, quest_list
)
705 -- TODO: Write this function. Should make sure all the quests get item from the same place.
706 return #quest_list
<= 1
709 local function RemoveQuestByData(data
)
710 for locale
, l
in pairs(StaticData
) do
711 for faction
, levels
in pairs(l
.quest
) do
712 for level
, quest_list
in pairs(levels
) do
713 for quest
, quest_data
in pairs(quest_list
) do
714 if data
== quest_data
then
715 if quest_data
.alt
then
716 local alt
= quest_data
.alt
717 local hash
= next(alt
, nil)
718 local quest_data2
= alt
[hash
]
720 quest_list
[quest
] = quest_data2
721 if next(alt
, nil) then
722 quest_data2
.alt
= alt
723 quest_data2
.hash
= hash
726 quest_list
[quest
] = nil
728 elseif quest_data
.alt
then
729 for hash
, quest_data2
in pairs(quest_data
.alt
) do
730 if data
== quest_data2
then
731 quest_data
.alt
[hash
] = nil
735 if not next(quest_data
.alt
) then
737 quest_data
.hash
= nil
741 if not next(levels
[level
], nil) then levels
[level
] = nil end
743 if not next(l
.quest
[faction
], nil) then l
.quest
[faction
] = nil end
748 function CompileInputFile(filename
, pairfrequencies
)
749 local data_loader
= loadfile(filename
)
752 setfenv(data_loader
, data
)
754 AddInputData(data
, pairfrequencies
)
756 print("'"..filename
.."' couldn't be loaded!")
760 function handleTranslations()
761 for locale
, l
in pairs(StaticData
) do
764 local monster_map
= {}
767 for id
, data
in pairs(WoWData
.item
) do
768 if data
.name
[locale
] then
769 item_map
[data
.name
[locale]]
= id
773 for id
, data
in pairs(WoWData
.npc
) do
774 if data
.name
[locale
] then
775 monster_map
[data
.name
[locale]]
= id
779 for id
, data
in pairs(WoWData
.quest
) do
780 if data
.name
[locale
] then
781 quest_map
[data
.level
.."/"..data
.hash
[locale
].."/"..data
.name
[locale]]
= id
785 local function item2dat(data
, item
)
786 if not item
then item
= {} end
788 item
.quest
= item
.quest
or data
.quest
791 item
.opened
= (item
.opened
or 0) + data
.opened
799 MergePositionLists(item
.pos
, data
.pos
)
806 if not item
.vendor
then
810 for i
, npc
in ipairs(data
.vendor
) do
811 local id
= monster_map
[npc
]
813 addVendor(item
.vendor
, id
)
819 if not item
.drop
then
823 for name
, count
in pairs(data
.drop
) do
824 local id
= monster_map
[name
]
826 item
.drop
[id
] = (item
.drop
[id
] or 0) + count
827 data
.drop
[name
] = nil
832 if data
.contained
then
833 if not item
.contained
then
837 for name
, count
in pairs(data
.contained
) do
838 local id
= item_map
[name
]
840 item
.contained
[id
] = (item
.contained
[id
] or 0) + count
841 data
.contained
[name
] = nil
849 if l
.objective
.item
then for name
, data
in pairs(l
.objective
.item
) do
850 local id
= item_map
[name
]
852 item2dat(data
, WoWData
.item
[id
])
853 local item
= WoWData
.item
[id
]
857 if l
.objective
.monster
then for name
, data
in pairs(l
.objective
.monster
) do
858 local id
= monster_map
[name
]
860 local npc
= WoWData
.npc
[id
]
862 npc
.quest
= npc
.quest
or data
.quest
863 npc
.faction
= npc
.faction
or data
.faction
866 npc
.looted
= (npc
.looted
or 0) + data
.looted
874 MergePositionLists(npc
.pos
, data
.pos
)
882 local function q2static(faction
, name
, data
)
883 local id
= quest_map
[name
]
885 local quest
= WoWData
.quest
[id
]
886 quest
.faction
[faction
] = true
888 print("Copying Quest", faction
, name
)
890 if data
.finish
and next(data
.finish
) then
891 quest
.finish
= monster_map
[CollapseDropList(data
.finish
)] or quest
.finish
895 quest
.item
= quest
.item
or {}
897 for name
, idata
in pairs(data
.item
) do
898 local id
= item_map
[name
]
900 quest
.item
[id
] = item2dat(idata
, quest
.item
[id
])
907 if l
.quest
then for faction
, fqlist
in pairs(l
.quest
) do
908 for level
, qlist
in pairs(fqlist
) do
909 for name
, qdata
in pairs(qlist
) do
911 q2static(faction
, level
.."/"..qdata
.hash
.."/"..name
, qdata
)
914 if qdata
.alt
then for hash
, qdata2
in pairs(qdata
.alt
) do
915 q2static(faction
, level
.."/"..hash
.."/"..name
, qdata2
)
923 for id
, item
in pairs(WoWData
.item
) do
924 for locale
, name
in pairs(item
.name
) do
925 print("Adding item ", locale
, name
)
926 local data
= GetObjective(locale
, "item", name
)
928 data
.quest
= data
.quest
or item
.quest
931 data
.opened
= item
.opened
935 data
.pos
= data
.pos
or {}
936 MergePositionLists(data
.pos
, item
.pos
)
940 data
.vendor
= data
.vendor
or {}
942 for i
, id
in ipairs(item
.vendor
) do
943 local name
= WoWData
.npc
[id
] and WoWData
.npc
[id
].name
[locale
]
945 addVendor(data
.vendor
, name
)
951 data
.drop
= data
.drop
or {}
952 for id
, count
in pairs(item
.drop
) do
953 local name
= WoWData
.npc
[id
] and WoWData
.npc
[id
].name
[locale
]
955 data
.drop
[name
] = (data
.drop
[name
] or 0) + count
960 if item
.contained
then
961 data
.contained
= data
.contained
or {}
962 for id
, count
in pairs(item
.contained
) do
963 local name
= WoWData
.item
[id
] and WoWData
.item
[id
].name
[locale
]
965 data
.contained
[name
] = (data
.contained
[name
] or 0) + count
972 for id
, npc
in pairs(WoWData
.npc
) do
973 for locale
, name
in pairs(npc
.name
) do
974 print("Adding NPC ", locale
, name
)
975 local data
= GetObjective(locale
, "monster", name
)
977 data
.quest
= data
.quest
or npc
.quest
978 data
.faction
= data
.faction
or npc
.faction
981 data
.looted
= npc
.looted
985 data
.pos
= data
.pos
or {}
986 MergePositionLists(data
.pos
, npc
.pos
)
991 for id
, quest
in pairs(WoWData
.quest
) do
992 for faction
in pairs(quest
.faction
) do
993 for locale
, name
in pairs(quest
.name
) do
994 print("Adding Quest ", locale
, faction
, quest
.level
, quest
.hash
[locale
], name
)
995 local data
= GetQuest(locale
, faction
, quest
.level
, name
, quest
.hash
[locale
])
998 local fname
= WoWData
.npc
[quest
.finish
] and WoWData
.npc
[quest
.finish
].name
[locale
]
1000 data
.finish
= {[fname
] = 1}
1005 for id
, item
in pairs(quest
.item
) do
1006 local iname
= WoWData
.item
[id
] and WoWData
.item
[id
].name
[locale
]
1010 if not qdata
.item
then qdata
.item
= {} end
1011 local data
= qdata
.item
[iname
] or {}
1012 qdata
.item
[iname
] = data
1015 data
.pos
= data
.pos
or {}
1016 MergePositionLists(data
.pos
, item
.pos
)
1020 data
.drop
= data
.drop
or {}
1021 for id
, count
in pairs(item
.drop
) do
1022 local name
= WoWData
.npc
[id
] and WoWData
.npc
[id
].name
[locale
]
1024 data
.drop
[name
] = (data
.drop
[name
] or 0) + count
1029 if item
.contained
then
1030 data
.contained
= data
.contained
or {}
1031 for id
, count
in pairs(item
.contained
) do
1032 local name
= WoWData
.item
[id
] and WoWData
.item
[id
].name
[locale
]
1034 data
.contained
[name
] = (data
.contained
[name
] or 0) + count
1048 function CompileFinish()
1049 handleTranslations()
1050 print("Finished translations")
1052 for locale
, l
in pairs(StaticData
) do
1053 local quest_item_mass
= {}
1054 local quest_item_quests
= {}
1056 local function WatchQuestItems(quest
)
1057 if quest
.finish
then GetObjective(locale
, "monster", quest
.finish
).quest
= true end
1060 for item
, data
in pairs(quest
.item
) do
1061 quest_item_mass
[item
] = (quest_item_mass
[item
] or 0)+
1062 (data
.drop
and DropListMass(data
.drop
) or 0)+
1063 (data
.contained
and DropListMass(data
.contained
) or 0)+
1064 (data
.pos
and PositionListMass(data
.pos
) or 0)
1066 quest_item_quests
[item
] = quest_item_quests
[item
] or {}
1067 table.insert(quest_item_quests
[item
], quest
)
1072 --if locale == "enUS" then -- I'm hoping the other locales aren't corrupted, as this method of fixing really won't work for any locale without a lot of data
1073 print("Culling opened items ", locale
)
1075 local contained_preserved
= 0
1076 local contained_rejected
= 0
1078 local openablect
= {}
1080 for name
, item
in pairs(l
.objective
.item
) do
1081 table.insert(openablect
, item
.opened
)
1083 table.sort(openablect
, function (a
,b
) return a
< b
end)
1084 local thresh
= openablect
[math
.floor(#openablect
* 0.95)]
1086 for name
, item
in pairs(l
.objective
.item
) do
1087 item
.openable
= item
.opened
and (item
.opened
>= thresh
)
1090 for name
, item
in pairs(l
.objective
.item
) do
1091 if item
.contained
then
1092 local tempcontained
= {}
1093 for it
, itv
in pairs(item
.contained
) do
1094 if l
.objective
.item
[it
] and l
.objective
.item
[it
].openable
then
1095 tempcontained
[it
] = itv
1096 contained_preserved
= contained_preserved
+ 1
1098 contained_rejected
= contained_rejected
+ 1
1101 item
.contained
= tempcontained
1105 for name
, item
in pairs(l
.objective
.item
) do
1109 print(string.format("Containment cull pass done. %d preserved, %d rejected", contained_preserved
, contained_rejected
))
1112 print("Processing quests ", locale
)
1114 for faction
, levels
in pairs(l
.quest
) do
1115 local delete_faction
= true
1116 for level
, quest_list
in pairs(levels
) do
1117 local delete_level
= true
1118 for quest
, quest_data
in pairs(quest_list
) do
1119 if quest_data
.alt
then
1120 for hash
, quest_data2
in pairs(quest_data
.alt
) do
1121 if CollapseQuest(locale
, quest_data2
) then
1122 quest_data
.alt
[hash
] = nil
1124 quest_data2
.hash
= nil
1125 WatchQuestItems(quest_data2
)
1129 if not next(quest_data
.alt
, nil) then
1130 quest_data
.alt
= nil
1131 quest_data
.hash
= nil
1135 if CollapseQuest(locale
, quest_data
) then
1136 if quest_data
.alt
then
1137 local alt
= quest_data
.alt
1138 local hash
= next(alt
, nil)
1139 local quest_data2
= alt
[hash
]
1141 quest_list
[quest
] = quest_data2
1142 if next(alt
, nil) then
1143 quest_data2
.alt
= alt
1144 quest_data2
.hash
= hash
1147 delete_level
= false
1149 quest_list
[quest
] = nil
1152 WatchQuestItems(quest_data
)
1153 delete_level
= false
1157 if delete_level
then levels
[level
] = nil else delete_faction
= false end
1159 if delete_faction
then l
.quest
[faction
] = nil end
1162 if l
.flight_instructors
then for faction
, list
in pairs(l
.flight_instructors
) do
1163 for area
, npc
in pairs(list
) do
1164 -- Need to remember the flight instructors, for use in routing.
1165 GetObjective(locale
, "monster", npc
).quest
= true
1169 for item
, quest_list
in pairs(quest_item_quests
) do
1170 -- If all the items are similar, then we don't want quest item entries for them,
1171 -- we want to use the gobal item objective instead.
1172 if QuestItemsAreSimilar(item
, quest_list
) then
1173 quest_item_mass
[item
] = 0
1177 print("Processing quest items ", locale
)
1178 -- Will go through the items and either delete them, or merge the quest items into them, and then
1179 -- mark the relevent monsters as being quest objectives.
1180 if l
.objective
["item"] then
1181 for name
, objective
in pairs(l
.objective
["item"]) do
1182 -- If this is a quest item, mark anything that drops it as being a quest monster.
1183 local quest_mass
= quest_item_mass
[name
] or 0
1185 if objective
.vendor
and next(objective
.vendor
, nil) then
1186 quest_mass
= 0 -- If the item can be bought, then it shouldn't be quest specific.
1189 local item_mass
= (objective
.pos
and PositionListMass(objective
.pos
) or 0)+
1190 (objective
.drop
and DropListMass(objective
.drop
) or 0)+
1191 (objective
.contained
and DropListMass(objective
.contained
) or 0)
1193 if quest_mass
> item_mass
then
1194 -- Delete this item, we'll deal with the the quests using the items after.
1195 l
.objective
["item"][name
] = nil
1197 if quest_item_quests
[name
] then
1198 for i
, quest_data
in pairs(quest_item_quests
[name
]) do
1199 local quest_item
= quest_data
.item
and quest_data
.item
[name
]
1201 if quest_item
.drop
then
1202 if not objective
.drop
then objective
.drop
= {} end
1203 MergeDropLists(objective
.drop
, quest_item
.drop
)
1206 if quest_item
.contained
then
1207 if not objective
.contained
then objective
.contained
= {} end
1208 MergeDropLists(objective
.contained
, quest_item
.contained
)
1211 if quest_item
.pos
then
1212 if not objective
.pos
then objective
.pos
= {} end
1213 MergePositionLists(objective
.pos
, quest_item
.pos
)
1216 quest_data
.item
[name
] = nil
1217 if not next(quest_data
.item
, nil) then
1218 quest_data
.item
= nil
1220 if not quest_data
.finish
and not quest_data
.pos
then
1221 RemoveQuestByData(quest_data
)
1227 quest_item_quests
[name
] = nil
1228 objective
.quest
= true
1231 if objective
.quest
then
1232 if objective
.drop
then
1233 TidyDropList(locale
, objective
.drop
)
1234 for monster
, count
in pairs(objective
.drop
) do
1235 GetObjective(locale
, "monster", monster
).quest
= true
1239 if objective
.contained
then
1240 TidyContainedList(locale
, objective
.contained
)
1241 for item
, count
in pairs(objective
.contained
) do
1242 GetObjective(locale
, "item", item
).quest
= true
1246 if objective
.vendor
then
1247 for i
, npc
in ipairs(objective
.vendor
) do
1248 GetObjective(locale
, "monster", npc
).quest
= true
1256 -- For any quest items that didn't get handled above, we'll clean them up and leave them be.
1257 for item
, quest_list
in pairs(quest_item_quests
) do
1258 for _
, quest_data
in ipairs(quest_list
) do
1259 -- Item should already exist in quest, not going to check.
1260 local item_data
= quest_data
.item
[item
]
1263 if item_data
.pos
then
1264 pos_mass
= PositionListMass(item_data
.pos
)
1265 TidyPositionList(item_data
.pos
)
1269 if item_data
.drop
then
1270 drop_mass
= DropListMass(item_data
.drop
)
1271 TidyDropList(locale
, item_data
.drop
)
1274 local contained_mass
= 0
1275 if item_data
.contained
then
1276 contained_mass
= DropListMass(item_data
.contained
)
1277 TidyContainedList(locale
, item_data
.contained
)
1280 if drop_mass
+contained_mass
> pos_mass
then
1282 if item_data
.drop
then
1283 for monster
, count
in pairs(item_data
.drop
) do
1284 GetObjective(locale
, "monster", monster
).quest
= true
1288 if item_data
.contained
then
1289 for item
, count
in pairs(item_data
.contained
) do
1290 GetObjective(locale
, "item", item
).quest
= true
1294 item_data
.drop
= nil
1295 item_data
.contained
= nil
1298 if not item_data
.pos
and not item_data
.drop
then
1299 quest_data
.item
[item
] = nil
1300 if not next(quest_data
.item
, nil) then
1301 quest_data
.item
= nil
1303 if not quest_data
.finish
and not quest_data
.pos
then
1304 RemoveQuestByData(quest_data
)
1311 print("Processing objectives ", locale
)
1313 for category
, objectives
in pairs(l
.objective
) do
1314 local delete_category
= true
1315 for name
, objective
in pairs(objectives
) do
1316 if CollapseObjective(locale
, objective
) then
1317 objectives
[name
] = nil
1319 delete_category
= false
1322 if delete_category
then l
.objective
[category
] = nil end
1325 local function IgnoreItem(start
, dest
, hash_list
, distance
, count
)
1326 return hash_list
.interrupt_count
== 0 and 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")
1329 if l
.flight_routes
then
1330 for faction
, start_list
in pairs(l
.flight_routes
) do
1331 local delete_faction
= true
1332 for start
, dest_list
in pairs(start_list
) do
1333 local delete_start
= true
1334 for dest
, hash_list
in pairs(dest_list
) do
1335 local delete_dest
= true
1337 local delete_hashes
= {}
1339 for hash
, value
in pairs(hash_list
) do
1340 if type(value
) == "table" and hash
~= "interrupt_count" and hash
~= "no_interrupt_count" then
1341 local count
= #value
1343 hash_list
[hash
] = CollapseAverage(value
)
1345 if IgnoreItem(start
, dest
, hash_list
, hash_list
[hash
], count
) then
1346 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))
1347 table.insert(delete_hashes
, hash
)
1350 delete_dest
= false -- We leave this out here because it *is* a valid flight path, we just don't have valid information for it
1351 elseif value
== true and hash
== 0 then
1356 for k
, v
in pairs(delete_hashes
) do
1360 hash_list
.interrupt_count
= nil
1361 hash_list
.no_interrupt_count
= nil
1364 dest_list
[dest
] = nil
1366 delete_start
= false
1369 if delete_start
then
1370 start_list
[start
] = nil
1372 delete_faction
= false
1375 if delete_faction
then
1376 l
.flight_routes
[faction
] = nil
1382 local old_data
= StaticData