400 lines
18 KiB
Lua
400 lines
18 KiB
Lua
---@class QuestieCorrections
|
|
local QuestieCorrections = QuestieLoader:CreateModule("QuestieCorrections")
|
|
|
|
---@type QuestieDB
|
|
local QuestieDB = QuestieLoader:ImportModule("QuestieDB")
|
|
---@type ZoneDB
|
|
local ZoneDB = QuestieLoader:ImportModule("ZoneDB")
|
|
---@type QuestieLib
|
|
local QuestieLib = QuestieLoader:ImportModule("QuestieLib")
|
|
---@type RamerDouglasPeucker
|
|
local RamerDouglasPeucker = QuestieLoader:ImportModule("RamerDouglasPeucker")
|
|
---@type QuestieEvent
|
|
local QuestieEvent = QuestieLoader:ImportModule("QuestieEvent")
|
|
---@type QuestieQuestBlacklist
|
|
local QuestieQuestBlacklist = QuestieLoader:ImportModule("QuestieQuestBlacklist")
|
|
---@type QuestieNPCBlacklist
|
|
local QuestieNPCBlacklist = QuestieLoader:ImportModule("QuestieNPCBlacklist")
|
|
---@type QuestieItemBlacklist
|
|
local QuestieItemBlacklist = QuestieLoader:ImportModule("QuestieItemBlacklist")
|
|
|
|
---@type QuestieQuestFixes
|
|
local QuestieQuestFixes = QuestieLoader:ImportModule("QuestieQuestFixes")
|
|
---@type QuestieClassicQuestReputationFixes
|
|
local QuestieClassicQuestReputationFixes = QuestieLoader:ImportModule("QuestieClassicQuestReputationFixes")
|
|
---@type QuestieNPCFixes
|
|
local QuestieNPCFixes = QuestieLoader:ImportModule("QuestieNPCFixes")
|
|
---@type QuestieItemFixes
|
|
local QuestieItemFixes = QuestieLoader:ImportModule("QuestieItemFixes")
|
|
---@type QuestieObjectFixes
|
|
local QuestieObjectFixes = QuestieLoader:ImportModule("QuestieObjectFixes")
|
|
|
|
---@type QuestieTBCQuestFixes
|
|
local QuestieTBCQuestFixes = QuestieLoader:ImportModule("QuestieTBCQuestFixes")
|
|
---@type QuestieTBCNpcFixes
|
|
local QuestieTBCNpcFixes = QuestieLoader:ImportModule("QuestieTBCNpcFixes")
|
|
---@type QuestieTBCItemFixes
|
|
local QuestieTBCItemFixes = QuestieLoader:ImportModule("QuestieTBCItemFixes")
|
|
---@type QuestieTBCObjectFixes
|
|
local QuestieTBCObjectFixes = QuestieLoader:ImportModule("QuestieTBCObjectFixes")
|
|
|
|
---@type QuestieWotlkQuestFixes
|
|
local QuestieWotlkQuestFixes = QuestieLoader:ImportModule("QuestieWotlkQuestFixes")
|
|
---@type QuestieWotlkNpcFixes
|
|
local QuestieWotlkNpcFixes = QuestieLoader:ImportModule("QuestieWotlkNpcFixes")
|
|
---@type QuestieWotlkItemFixes
|
|
local QuestieWotlkItemFixes = QuestieLoader:ImportModule("QuestieWotlkItemFixes")
|
|
---@type QuestieWotlkObjectFixes
|
|
local QuestieWotlkObjectFixes = QuestieLoader:ImportModule("QuestieWotlkObjectFixes")
|
|
|
|
---@type IsleOfQuelDanas
|
|
local IsleOfQuelDanas = QuestieLoader:ImportModule("IsleOfQuelDanas")
|
|
|
|
--- Automatic corrections
|
|
local QuestieItemStartFixes = QuestieLoader:ImportModule("QuestieItemStartFixes")
|
|
|
|
--- COMPATIBILITY ---
|
|
local C_Timer = QuestieCompat.C_Timer
|
|
|
|
--[[
|
|
This file load the corrections of the database files.
|
|
|
|
It is a separate file so we can upstream those changes easier to cmangos and can still
|
|
update the database files with a script.
|
|
|
|
Most of the corrections can be done by accessing a specific key instead of copying the
|
|
whole object over and change it.
|
|
You can find the keys at the beginning of each file (e.g. 'questKeys' are at the beginning of 'questDB.lua').
|
|
|
|
Further information on how to use this can be found at the wiki
|
|
https://github.com/Questie/Questie/wiki/Corrections
|
|
--]]
|
|
|
|
-- flags that can be used in corrections (currently only blacklists)
|
|
QuestieCorrections.TBC_ONLY = 1 -- Hide only in TBC
|
|
QuestieCorrections.CLASSIC_ONLY = 2 -- Hide only in Classic
|
|
QuestieCorrections.WOTLK_ONLY = 3 -- Hide only in Wotlk
|
|
QuestieCorrections.TBC_AND_WOTLK = 4 -- Hide in TBC and Wotlk
|
|
QuestieCorrections.CLASSIC_AND_TBC = 7 -- Hide in both Classic and TBC
|
|
|
|
QuestieCorrections.killCreditObjectiveFirst = {} -- Only used for TBC quests
|
|
|
|
-- this function filters a table of values, if the value is TBC_ONLY or CLASSIC_ONLY, set it to true or nil if that case is met
|
|
---@generic T
|
|
---@param values T
|
|
---@return T
|
|
local function filterExpansion(values)
|
|
local isClassic = Questie.IsClassic
|
|
local isTBC = Questie.IsTBC
|
|
local isWotlk = Questie.IsWotlk
|
|
for k, v in pairs(values) do
|
|
if v == QuestieCorrections.WOTLK_ONLY then
|
|
if isWotlk then
|
|
values[k] = true
|
|
else
|
|
values[k] = nil
|
|
end
|
|
elseif v == QuestieCorrections.TBC_ONLY then
|
|
if isTBC then
|
|
values[k] = true
|
|
else
|
|
values[k] = nil
|
|
end
|
|
elseif v == QuestieCorrections.CLASSIC_ONLY then
|
|
if isTBC or isWotlk then
|
|
values[k] = nil
|
|
else
|
|
values[k] = true
|
|
end
|
|
elseif v == QuestieCorrections.TBC_AND_WOTLK then
|
|
if isTBC or isWotlk then
|
|
values[k] = true
|
|
else
|
|
values[k] = nil
|
|
end
|
|
elseif v == QuestieCorrections.CLASSIC_AND_TBC then
|
|
if isClassic or isTBC then
|
|
values[k] = true
|
|
else
|
|
values[k] = nil
|
|
end
|
|
end
|
|
end
|
|
return values
|
|
end
|
|
|
|
do
|
|
local type, assert = type, assert
|
|
--- Add runtime overrides for the database
|
|
---@param override_table table<number, table<number, string|table|number>>
|
|
---@param new_overrides table<number, table<number, string|table|number>>
|
|
local function addOverride(override_table, new_overrides)
|
|
assert(type(override_table) == "table", "Override table must be a table!")
|
|
assert(type(new_overrides) == "table", "New overrides must be a table!")
|
|
for id, data in pairs(new_overrides) do
|
|
assert(type(id) == "number", "Override id must be a number!")
|
|
assert(type(data) == "table", "Override data must be a table!")
|
|
-- If no override exist assign it
|
|
if not override_table[id] then
|
|
override_table[id] = data
|
|
else
|
|
-- Override already exists, merge the new data
|
|
for key, value in pairs(data) do
|
|
override_table[id][key] = value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function QuestieCorrections:MinimalInit() -- db already compiled
|
|
|
|
-- Classic Era Corrections
|
|
addOverride(QuestieDB.itemDataOverrides, QuestieItemFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.npcDataOverrides, QuestieNPCFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.objectDataOverrides, QuestieObjectFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.questDataOverrides, QuestieQuestFixes:LoadFactionFixes())
|
|
|
|
-- TBC Corrections
|
|
if (Questie.IsTBC or Questie.IsWotlk) then
|
|
addOverride(QuestieDB.itemDataOverrides, QuestieTBCItemFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.npcDataOverrides, QuestieTBCNpcFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.objectDataOverrides, QuestieTBCObjectFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.questDataOverrides, QuestieTBCQuestFixes:LoadFactionFixes())
|
|
end
|
|
|
|
-- WOTLK Corrections
|
|
if (Questie.IsWotlk) then
|
|
addOverride(QuestieDB.npcDataOverrides, QuestieWotlkNpcFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.itemDataOverrides, QuestieWotlkItemFixes:LoadFactionFixes())
|
|
addOverride(QuestieDB.objectDataOverrides, QuestieWotlkObjectFixes:LoadFactionFixes())
|
|
end
|
|
|
|
QuestieCorrections.questItemBlacklist = filterExpansion(QuestieItemBlacklist:Load())
|
|
QuestieCorrections.questNPCBlacklist = filterExpansion(QuestieNPCBlacklist:Load())
|
|
QuestieCorrections.hiddenQuests = filterExpansion(QuestieQuestBlacklist:Load())
|
|
|
|
-- TBC Quel Danas Blacklist
|
|
if Questie.db.global.isleOfQuelDanasPhase == IsleOfQuelDanas.MAX_ISLE_OF_QUEL_DANAS_PHASES then
|
|
for id, hide in pairs(IsleOfQuelDanas.quests[Questie.db.global.isleOfQuelDanasPhase]) do
|
|
-- This has to be a nil-check, because the value could be false
|
|
if (QuestieCorrections.hiddenQuests[id] == nil) then
|
|
QuestieCorrections.hiddenQuests[id] = hide
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Wotlk Blacklist
|
|
if (Questie.IsWotlk) then
|
|
-- We only add blacklist if no blacklist entry for the quest already exists
|
|
for id, hide in pairs(QuestieQuestBlacklist.LoadAutoBlacklistWotlk()) do
|
|
-- This has to be a nil-check, because the value could be false
|
|
if (QuestieCorrections.hiddenQuests[id] == nil) then
|
|
QuestieCorrections.hiddenQuests[id] = hide
|
|
end
|
|
end
|
|
end
|
|
|
|
if QuestieCompat.Is335 then QuestieCompat.LoadBlacklists() end
|
|
|
|
if Questie.db.profile.showEventQuests then
|
|
C_Timer.After(1, function()
|
|
-- This is done with a delay because on startup the Blizzard API seems to be
|
|
-- very slow and therefore the date calculation in QuestieEvents isn't done
|
|
-- correctly.
|
|
QuestieEvent:Load()
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param databaseTableName string The name of the QuestieDB field that should be manipulated (e.g. "itemData", "questData")
|
|
---@param corrections table All corrections for the given databaseTableName (e.g. all quest corrections)
|
|
---@param reversedKeys table The reverted QuestieDB keys for the given databaseTableName (e.g. QuestieDB.questKeys)
|
|
---@param validationTables table Only used by the CI validation scripts to validate the corrections against the original database values and find irrelevant corrections
|
|
---@param noOverwrites true? Do not overwrite existing values
|
|
---@param noNewEntries true? Do not create new entries in the database
|
|
local _LoadCorrections = function(databaseTableName, corrections, reversedKeys, validationTables, noOverwrites, noNewEntries)
|
|
if not QuestieDB[databaseTableName] then return end
|
|
for id, data in pairs(corrections) do
|
|
for key, value in pairs(data) do
|
|
-- Create the id if missing unless noNewEntries is set
|
|
if not QuestieDB[databaseTableName][id] and not noNewEntries then
|
|
QuestieDB[databaseTableName][id] = {}
|
|
end
|
|
if validationTables and QuestieDB[databaseTableName][id] then
|
|
if value and QuestieLib.equals(QuestieDB[databaseTableName][id][key], value) and validationTables[databaseTableName][id] and
|
|
QuestieLib.equals(validationTables[databaseTableName][id][key], value) then
|
|
Questie:Warning("Correction of " ..
|
|
databaseTableName .. " " .. tostring(id) .. "." .. reversedKeys[key] .. " matches base DB! Value:" .. tostring(value))
|
|
end
|
|
end
|
|
if QuestieDB[databaseTableName][id] then
|
|
if noOverwrites and QuestieDB[databaseTableName][id][key] == nil then
|
|
QuestieDB[databaseTableName][id][key] = value
|
|
elseif not noOverwrites then
|
|
QuestieDB[databaseTableName][id][key] = value
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param validationTables table? Only used by the CI validation scripts to validate the corrections against the original database values and find irrelevant corrections
|
|
function QuestieCorrections:Initialize(validationTables)
|
|
if QuestieQuestFixes and QuestieQuestFixes.LoadMissingQuests then
|
|
QuestieQuestFixes:LoadMissingQuests()
|
|
end
|
|
|
|
-- Classic Corrections
|
|
if QuestieClassicQuestReputationFixes then _LoadCorrections("questData", QuestieClassicQuestReputationFixes:Load(), QuestieDB.questKeysReversed, validationTables) end
|
|
if QuestieQuestFixes then _LoadCorrections("questData", QuestieQuestFixes:Load(), QuestieDB.questKeysReversed, validationTables) end
|
|
if QuestieNPCFixes then _LoadCorrections("npcData", QuestieNPCFixes:Load(), QuestieDB.npcKeysReversed, validationTables) end
|
|
if QuestieItemFixes then _LoadCorrections("itemData", QuestieItemFixes:Load(), QuestieDB.itemKeysReversed, validationTables) end
|
|
if QuestieObjectFixes then _LoadCorrections("objectData", QuestieObjectFixes:Load(), QuestieDB.objectKeysReversed, validationTables) end
|
|
|
|
if Questie.IsTBC or Questie.IsWotlk or QuestieCompat.Is335 then
|
|
if QuestieTBCQuestFixes then _LoadCorrections("questData", QuestieTBCQuestFixes:Load(), QuestieDB.questKeysReversed, validationTables) end
|
|
if QuestieTBCNpcFixes then _LoadCorrections("npcData", QuestieTBCNpcFixes:Load(), QuestieDB.npcKeysReversed, validationTables) end
|
|
if QuestieTBCItemFixes then _LoadCorrections("itemData", QuestieTBCItemFixes:Load(), QuestieDB.itemKeysReversed, validationTables) end
|
|
if QuestieTBCObjectFixes then _LoadCorrections("objectData", QuestieTBCObjectFixes:Load(), QuestieDB.objectKeysReversed, validationTables) end
|
|
end
|
|
|
|
if Questie.IsWotlk or QuestieCompat.Is335 then
|
|
if QuestieWotlkQuestFixes then _LoadCorrections("questData", QuestieWotlkQuestFixes:Load(), QuestieDB.questKeysReversed, validationTables) end
|
|
if QuestieWotlkNpcFixes then _LoadCorrections("npcData", QuestieWotlkNpcFixes:LoadAutomatics(), QuestieDB.npcKeysReversed, validationTables) end
|
|
if QuestieWotlkNpcFixes then _LoadCorrections("npcData", QuestieWotlkNpcFixes:Load(), QuestieDB.npcKeysReversed, validationTables) end
|
|
if QuestieWotlkItemFixes then _LoadCorrections("itemData", QuestieWotlkItemFixes:Load(), QuestieDB.itemKeysReversed, validationTables) end
|
|
if QuestieWotlkObjectFixes then _LoadCorrections("objectData", QuestieWotlkObjectFixes:Load(), QuestieDB.objectKeysReversed, validationTables) end
|
|
end
|
|
|
|
--- Corrections that apply to all versions
|
|
_LoadCorrections("itemData", QuestieItemStartFixes:LoadAutomaticQuestStarts(), QuestieDB.itemKeysReversed, validationTables, true, true)
|
|
|
|
if QuestieCompat.Is335 then QuestieCompat.LoadCorrections(_LoadCorrections, validationTables) end
|
|
|
|
local patchCount = 0
|
|
if type(QuestieDB.questData) == "table" then
|
|
for _, quest in pairs(QuestieDB.questData) do
|
|
if (not quest[QuestieDB.questKeys.requiredRaces]) or quest[QuestieDB.questKeys.requiredRaces] == 0 then
|
|
-- check against questgiver
|
|
local canHorde = false
|
|
local canAlliance = false
|
|
local starts = quest[QuestieDB.questKeys.startedBy]
|
|
if starts then
|
|
starts = starts[1]
|
|
if starts then
|
|
for _, id in pairs(starts) do
|
|
local npc = QuestieDB.npcData[id]
|
|
if npc then
|
|
local friendly = npc[QuestieDB.npcKeys.friendlyToFaction]
|
|
if friendly then
|
|
if friendly == "H" then
|
|
canHorde = true
|
|
elseif friendly == "A" then
|
|
canAlliance = true
|
|
elseif friendly == "AH" then
|
|
canAlliance = true
|
|
canHorde = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if canAlliance ~= canHorde then
|
|
patchCount = patchCount + 1
|
|
if canAlliance then
|
|
quest[QuestieDB.questKeys.requiredRaces] = QuestieDB.raceKeys.ALL_ALLIANCE
|
|
else
|
|
quest[QuestieDB.questKeys.requiredRaces] = QuestieDB.raceKeys.ALL_HORDE
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end -- type(questData) == "table"
|
|
|
|
QuestieCorrections:MinimalInit()
|
|
|
|
end
|
|
|
|
local WAYPOINT_MIN_DISTANCE = 1.5 -- todo: make this a config value maybe?
|
|
local ZONE_SCALES = {
|
|
[ZoneDB.zoneIDs.STORMWIND_CITY] = 0.5,
|
|
[ZoneDB.zoneIDs.IRONFORGE] = 0.5,
|
|
[ZoneDB.zoneIDs.TELDRASSIL] = 0.5,
|
|
|
|
[ZoneDB.zoneIDs.ORGRIMMAR] = 0.5,
|
|
[ZoneDB.zoneIDs.THUNDER_BLUFF] = 0.5,
|
|
[ZoneDB.zoneIDs.UNDERCITY] = 0.5,
|
|
}
|
|
|
|
|
|
local abs, sqrt = math.abs, math.sqrt
|
|
local function euclid(x, y, i, e)
|
|
local xd = abs(x - i)
|
|
local yd = abs(y - e)
|
|
return sqrt(xd * xd + yd * yd)
|
|
end
|
|
|
|
function QuestieCorrections:OptimizeWaypoints(waypointData)
|
|
local newWaypointZones = {}
|
|
for zone, waypointList in pairs(waypointData) do
|
|
local newWaypointList = {}
|
|
if waypointList[1] and type(waypointList[1][1]) == "number" then
|
|
waypointList = {waypointList} -- corrections support both {{x,y}, ...} and {{{x,y}, ...}, {{x,y}, ...}, ...}
|
|
end
|
|
for _, waypoints in pairs(waypointList) do
|
|
-- apply RDP algorithm
|
|
local minDist = WAYPOINT_MIN_DISTANCE * (ZONE_SCALES[zone] or 1)
|
|
local newWaypoints = RamerDouglasPeucker(waypoints, 0.1, true)
|
|
|
|
waypoints = newWaypoints
|
|
newWaypoints = {}
|
|
|
|
-- subdivide waypoints where needed
|
|
-- We do this because the clickable area of waypoint lines can only be a square, so lines need to be broken up in some places
|
|
local lastWay
|
|
for _, way in pairs(waypoints) do
|
|
if lastWay then
|
|
local dist = euclid(way[1], way[2], lastWay[1], lastWay[2])
|
|
if dist > minDist then
|
|
local divs = math.ceil(dist/minDist)
|
|
for i=1,divs do
|
|
local mul0 = i/divs
|
|
local mul1 = 1-mul0
|
|
newWaypoints[#newWaypoints+1] = {way[1] * mul0 + lastWay[1] * mul1, way[2] * mul0 + lastWay[2] * mul1}
|
|
end
|
|
else
|
|
newWaypoints[#newWaypoints+1] = way
|
|
end
|
|
else
|
|
newWaypoints[#newWaypoints+1] = way
|
|
end
|
|
lastWay = way
|
|
end
|
|
newWaypointList[#newWaypointList+1] = newWaypoints
|
|
end
|
|
newWaypointZones[zone] = newWaypointList
|
|
end
|
|
return newWaypointZones
|
|
end
|
|
|
|
function QuestieCorrections:PreCompile() -- this happens only if we are about to compile the database. Run intensive preprocessing tasks here (like ramer-douglas-peucker)
|
|
local waypointKey = QuestieDB.npcKeys["waypoints"]
|
|
local npcData = QuestieDB.npcData
|
|
|
|
local count = 0
|
|
for id, data in pairs(npcData) do
|
|
local way = data[waypointKey]
|
|
if way then
|
|
npcData[id][waypointKey] = QuestieCorrections:OptimizeWaypoints(way)
|
|
end
|
|
|
|
if count > 500 then -- 500 seems like a good number
|
|
count = 0
|
|
coroutine.yield()
|
|
end
|
|
count = count + 1
|
|
end
|
|
end
|