v9.7.2: Ebonhold Database integration and core logic refinements
This commit is contained in:
@@ -0,0 +1,436 @@
|
||||
---@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 HardcoreBlacklist
|
||||
local HardcoreBlacklist = QuestieLoader:ImportModule("HardcoreBlacklist")
|
||||
---@type SeasonOfDiscovery
|
||||
local SeasonOfDiscovery = QuestieLoader:ImportModule("SeasonOfDiscovery")
|
||||
|
||||
---@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.SOD_ONLY = 5 -- Hide when *not* Season of Discovery; use for SoD-only quests
|
||||
QuestieCorrections.HIDE_SOD = 6 -- Hide when Season of Discovery; use to hide quests that are not available in SoD
|
||||
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
|
||||
local isSoD = Questie.IsSoD
|
||||
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.SOD_ONLY then
|
||||
if not isSoD then
|
||||
values[k] = true
|
||||
else
|
||||
values[k] = nil
|
||||
end
|
||||
elseif v == QuestieCorrections.HIDE_SOD then
|
||||
if isSoD 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
|
||||
|
||||
-- Season of Discovery Corrections
|
||||
if Questie.IsSoD then
|
||||
addOverride(QuestieDB.questDataOverrides, SeasonOfDiscovery:LoadFactionQuestFixes())
|
||||
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
|
||||
|
||||
-- Hardcore Blacklist
|
||||
if (Questie.IsHardcore) then
|
||||
for id, _ in pairs(HardcoreBlacklist:Load()) do
|
||||
QuestieCorrections.hiddenQuests[id] = true
|
||||
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)
|
||||
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)
|
||||
QuestieQuestFixes:LoadMissingQuests()
|
||||
|
||||
-- Classic Corrections
|
||||
_LoadCorrections("questData", QuestieClassicQuestReputationFixes:Load(), QuestieDB.questKeysReversed, validationTables)
|
||||
_LoadCorrections("questData", QuestieQuestFixes:Load(), QuestieDB.questKeysReversed, validationTables)
|
||||
_LoadCorrections("npcData", QuestieNPCFixes:Load(), QuestieDB.npcKeysReversed, validationTables)
|
||||
_LoadCorrections("itemData", QuestieItemFixes:Load(), QuestieDB.itemKeysReversed, validationTables)
|
||||
_LoadCorrections("objectData", QuestieObjectFixes:Load(), QuestieDB.objectKeysReversed, validationTables)
|
||||
|
||||
if Questie.IsTBC or Questie.IsWotlk then
|
||||
_LoadCorrections("questData", QuestieTBCQuestFixes:Load(), QuestieDB.questKeysReversed, validationTables)
|
||||
_LoadCorrections("npcData", QuestieTBCNpcFixes:Load(), QuestieDB.npcKeysReversed, validationTables)
|
||||
_LoadCorrections("itemData", QuestieTBCItemFixes:Load(), QuestieDB.itemKeysReversed, validationTables)
|
||||
_LoadCorrections("objectData", QuestieTBCObjectFixes:Load(), QuestieDB.objectKeysReversed, validationTables)
|
||||
end
|
||||
|
||||
if Questie.IsWotlk then
|
||||
_LoadCorrections("questData", QuestieWotlkQuestFixes:Load(), QuestieDB.questKeysReversed, validationTables)
|
||||
_LoadCorrections("npcData", QuestieWotlkNpcFixes:LoadAutomatics(), QuestieDB.npcKeysReversed, validationTables)
|
||||
_LoadCorrections("npcData", QuestieWotlkNpcFixes:Load(), QuestieDB.npcKeysReversed, validationTables)
|
||||
_LoadCorrections("itemData", QuestieWotlkItemFixes:Load(), QuestieDB.itemKeysReversed, validationTables)
|
||||
_LoadCorrections("objectData", QuestieWotlkObjectFixes:Load(), QuestieDB.objectKeysReversed, validationTables)
|
||||
end
|
||||
|
||||
if Questie.IsSoD then
|
||||
_LoadCorrections("questData", SeasonOfDiscovery:LoadBaseQuests(), QuestieDB.questKeysReversed, validationTables)
|
||||
_LoadCorrections("questData", SeasonOfDiscovery:LoadQuests(), QuestieDB.questKeysReversed, validationTables)
|
||||
_LoadCorrections("npcData", SeasonOfDiscovery:LoadBaseNPCs(), QuestieDB.npcKeysReversed, validationTables)
|
||||
_LoadCorrections("npcData", SeasonOfDiscovery:LoadNPCs(), QuestieDB.npcKeysReversed, validationTables)
|
||||
_LoadCorrections("itemData", SeasonOfDiscovery:LoadBaseItems(), QuestieDB.itemKeysReversed, validationTables)
|
||||
_LoadCorrections("itemData", SeasonOfDiscovery:LoadItems(), QuestieDB.itemKeysReversed, validationTables)
|
||||
_LoadCorrections("objectData", SeasonOfDiscovery:LoadBaseObjects(), QuestieDB.objectKeysReversed, validationTables)
|
||||
_LoadCorrections("objectData", SeasonOfDiscovery:LoadObjects(), QuestieDB.objectKeysReversed, validationTables)
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user