You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

517 lines
17 KiB

5 years ago
-- ========================================================================= --
-- SylingTracker --
-- https://www.curseforge.com/wow/addons/sylingtracker --
-- --
-- Repository: --
-- https://github.com/Skamer/SylingTracker --
-- --
-- ========================================================================= --
Syling "SylingTracker.Quests" ""
-- ========================================================================= --
import "SLT"
-- ========================================================================= --
_Active = false
-- ========================================================================= --
export {
-- Syling API
ItemBar_AddItemData = API.ItemBar_AddItemData,
ItemBar_RemoveItemData = API.ItemBar_RemoveItemData,
ItemBar_Update = API.ItemBar_Update,
RegisterContentType = API.RegisterContentType,
RegisterModel = API.RegisterModel,
-- WoW API & Utils
AddQuestWatch = C_QuestLog.AddQuestWatch,
EnumQuestWatchType = _G.Enum.QuestWatchType,
GetCampaignID = C_CampaignInfo.GetCampaignID,
GetDistanceSqToQuest = C_QuestLog.GetDistanceSqToQuest,
GetInfo = C_QuestLog.GetInfo,
GetLogIndexForQuestID = C_QuestLog.GetLogIndexForQuestID,
GetNumQuestLogEntries = C_QuestLog.GetNumQuestLogEntries,
GetNumQuestObjectives = C_QuestLog.GetNumQuestObjectives,
GetNumQuestWatches = C_QuestLog.GetNumQuestWatches,
GetQuestDifficultyLevel = C_QuestLog.GetQuestDifficultyLevel,
GetQuestLogCompletionText = GetQuestLogCompletionText,
GetQuestLogSpecialItemInfo = GetQuestLogSpecialItemInfo,
GetQuestName = QuestUtils_GetQuestName,
GetQuestObjectiveInfo = GetQuestObjectiveInfo,
GetQuestProgressBarPercent = GetQuestProgressBarPercent,
GetQuestTagInfo = C_QuestLog.GetQuestTagInfo,
GetRequiredMoney = C_QuestLog.GetRequiredMoney,
GetSuggestedGroupSize = C_QuestLog.GetSuggestedGroupSize,
GetTimeAllowed = C_QuestLog.GetTimeAllowed,
IsCampaignQuest = Utils.Quest.IsCampaignQuest,
IsDungeonQuest = Utils.Quest.IsDungeonQuest,
IsLegendaryQuest = C_QuestLog.IsLegendaryQuest,
IsOnMap = C_QuestLog.IsOnMap,
IsQuestBounty = C_QuestLog.IsQuestBounty,
IsQuestComplete = C_QuestLog.IsComplete,
IsQuestTask = C_QuestLog.IsQuestTask,
IsQuestTrivial = C_QuestLog.IsQuestTrivial,
IsQuestWatched = QuestUtils_IsQuestWatched,
IsRaidQuest = Utils.Quest.IsRaidQuest,
IsWorldQuest = QuestUtils_IsQuestWorldQuest,
RequestLoadQuestByID = C_QuestLog.RequestLoadQuestByID,
SelectQuestLogEntry = SelectQuestLogEntry,
SelectQuestLogEntry = SelectQuestLogEntry,
SetSelectedQuest = C_QuestLog.SetSelectedQuest
}
-- ========================================================================= --
_QuestModel = RegisterModel(QuestModel, "quests-data")
-- ========================================================================= --
-- Register the achievements content type
-- ========================================================================= --
RegisterContentType({
ID = "quests",
DisplayName = "Quests",
Description = "Track the watched quests",
DefaultOrder = 100,
DefaultModel = _QuestModel,
DefaultViewClass = QuestsContentView,
Events = { "PLAYER_ENTERING_WORLD", "QUEST_WATCH_LIST_CHANGED"},
Status = function() return GetNumQuestWatches() > 0 end
})
RegisterContentType({
ID = "campaign",
DisplayName = "Campaign",
Description = "Track the camapign quests",
DefaultOrder = 90,
DefaultModel = _QuestModel,
DefaultViewClass = CampaignContentView,
Events = { "PLAYER_ENTERING_WORLD", "QUEST_WATCH_LIST_CHANGED"},
Status = function() return GetNumQuestWatches() > 0 end
})
-- ========================================================================= --
local DISTANCE_UPDATER_ENABLED = false
local QUESTS_CACHE = {}
local QUEST_HEADERS_CACHE = {}
local QUESTS_WITH_PROGRESS = {}
local QUESTS_WITH_ITEMS = {}
local QUESTS_REQUESTED = {}
-- ========================================================================= --
__ActiveOnEvents__ "PLAYER_ENTERING_WORLD" "QUEST_WATCH_LIST_CHANGED" "QUEST_ACCEPTED"
function BecomeActiveOn(self, event, ...)
if event == "QUEST_ACCEPTED" then
-- Caling QUEST_ACCEPTED will watch the quest if matching the requirements,
-- so going to trigger the checking on "QUEST_WATCH_LIST_CHANGED".
-- As QUEST_ACCEPTED returns nil, the system skip the event.
return QUEST_ACCEPTED(...)
end
return GetNumQuestWatches() > 0
end
__InactiveOnEvents__ "PLAYER_ENTERING_WORLD" "QUEST_WATCH_LIST_CHANGED"
function BecomeInactiveOn(self, event, ...)
return GetNumQuestWatches() == 0
end
-- ========================================================================= --
__Async__()
function OnActive(self)
if self:IsActivateByEvent("PLAYER_ENTERING_WORLD") then
local initialLogin = self:GetActivatingEventArgs()
if initialLogin then
-- If it's the first login, we need to wait "QUEST_LOG_UPDATE" is fired
-- to get valid informations about quests
Wait("QUEST_LOG_UPDATE")
end
end
_M:LoadQuests()
end
function OnInactive(self)
_QuestModel:ClearData()
wipe(QUESTS_CACHE)
wipe(QUESTS_WITH_PROGRESS)
for questID in pairs(QUESTS_WITH_ITEMS) do
ItemBar_RemoveItemData(questID)
end
ItemBar_Update()
wipe(QUESTS_WITH_ITEMS)
end
-- ========================================================================= --
function LoadQuests(self)
local numEntries, numQuests = GetNumQuestLogEntries()
local currentHeader = "Misc"
for i = 1, numEntries do
local questInfo = GetInfo(i)
-- local title, questLogIndex, questID, campaignID, level, difficultyLevel,
-- suggestedGroup, frequency, isHeader, isCollapsed, startEvent, isTask,
-- isBounty, isStory, isScaling, isOnMap, hasLocalPOI, isHidden,
-- isAutoComplete, overridesSortOrder, readyForTranslation = GetInfo(i)
local questID = questInfo.questID
local isHeader = questInfo.isHeader
local isHidden = questInfo.isHidden
local isBounty = questInfo.isBounty
local isTask = questInfo.isTask
if questInfo.isHeader then
currentHeader = questInfo.title
elseif IsQuestWatched(questID) and not isHidden and not isBounty and not isTask then
-- elseif IsQuestWatched(IsOnShadowlands() and questID or i) and not questInfo.isHidden and not isBounty and not isTask then
QUESTS_CACHE[questID] = true
QUEST_HEADERS_CACHE[questID] = currentHeader
local questData = {
title = questInfo.title,
name = questInfo.title,
questLogIndex = questInfo.questLogIndex,
questID = questInfo.questID,
campaignID = questInfo.campaignID,
level = questInfo.level,
difficultyLevel = questInfo.difficultyLevel,
suggestedGroup = questInfo.suggestedGroup,
frequency = questInfo.frequency,
isHeader = questInfo.isHeader,
isCollapsed = questInfo.isCollapsed,
startEvent = questInfo.startEvent,
isTask = questInfo.isTask,
isBounty = questInfo.isBounty,
isStory = questInfo.isStory,
isScaling = questInfo.isScaling,
isOnMap = questInfo.isOnMap,
hasLocalPOI = questInfo.hasLocalPOI,
isHidden = questInfo.isHidden,
isAutoComplete = questInfo.isAutoComplete,
overridesSortOrder = questInfo.overridesSortOrder,
readyForTranslation = questInfo.readyForTranslation,
header = currentHeader,
category = currentHeader
}
_QuestModel:SetQuestData(questID, questData)
QUESTS_REQUESTED[questID] = true
RequestLoadQuestByID(questID)
end
end
_QuestModel:Flush()
end
function UpdateQuest(self, questID)
-- Cross function & unchanged fonction
local title = GetQuestName(questID)
local level = GetQuestDifficultyLevel(questID)
local header = self:GetQuestHeader(questID)
local questLogIndex = GetLogIndexForQuestID(questID)
local numObjectives = GetNumQuestObjectives(questID)
local isComplete = IsQuestComplete(questID)
local isTask = IsQuestTask(questID)
local isBounty = IsQuestBounty(questID)
local distance = GetDistanceSqToQuest(questID)
local isDungeon = IsDungeonQuest(questID)
local isRaid = IsRaidQuest(questID)
local requiredMoney = GetRequiredMoney(questID)
local suggestedGroup = GetSuggestedGroupSize(questID)
local isLegendary = IsLegendaryQuest(questID)
local tag = GetQuestTagInfo(questID)
local campaignID = GetCampaignID(questID)
if not distance then
distance = 99999
else
distance = sqrt(distance)
end
local failureTime, timeElapsed = GetTimeAllowed(questID)
local isOnMap, hasLocalPOI = IsOnMap(questID)
-- local isStory
-- local startEvent
-- local isAutoComplete
local questData = {
questID = questID,
title = title,
name = title,
level = level,
header = header,
category = header,
campaignID = campaignID,
questLogIndex = questLogIndex,
numObjectives = numObjectives,
isComplete = isComplete,
isTask = isTask,
isBounty = isBounty,
requiredMoney = requiredMoney,
failureTime = failureTime,
isOnMap = isOnMap,
hasLocalPOI = hasLocalPOI,
questType = questType,
tag = questType,
isStory = isStory,
startEvent = startEvent,
isAutoComplete = isAutoComplete,
suggestedGroup = suggestedGroup,
distance = distance,
isDungeon = isDungeon,
isRaid = isRaid,
isLegendary = isLegendary,
tag = tag
}
-- Is the quest has an item quest ?
local itemLink, itemTexture
-- We check if the quest log index is valid before fetching as sometimes
-- for unknown reason this can be nil.
if questLogIndex then
itemLink, itemTexture = GetQuestLogSpecialItemInfo(questLogIndex)
end
if itemLink and itemTexture then
questData.item = {
link = itemLink,
texture = itemTexture
}
-- We check if the quest has already the item for avoiding useless data
-- update.
if not QUESTS_WITH_ITEMS[questID] then
ItemBar_AddItemData(questID, {
link = itemLink,
texture = itemTexture
})
ItemBar_Update()
QUESTS_WITH_ITEMS[questID] = true
end
else
QUESTS_WITH_ITEMS[questID] = nil
end
-- Fetch the objectives
if numObjectives > 0 then
local objectivesData = {}
for index = 1, numObjectives do
for index = 1, numObjectives do
local text, type, finished = GetQuestObjectiveInfo(questID, index, false)
local data = {
text = text,
type = type,
isCompleted = finished
}
if type == "progressbar" then
local progress = GetQuestProgressBarPercent(questID)
data.hasProgressBar = true
data.progress = progress
data.minProgress = 0
data.maxProgress = 100
data.progressText = PERCENTAGE_STRING:format(progress)
QUESTS_WITH_PROGRESS[questID] = true
else
QUESTS_WITH_PROGRESS[questID] = nil
end
objectivesData[index] = data
end
end
questData.objectives = objectivesData
else
SetSelectedQuest(questID)
local text = GetQuestLogCompletionText()
questData.objectives = {
[1] = {
text = text,
isCompleted = false
}
}
end
_QuestModel:AddQuestData(questID, questData)
end
__SystemEvent__ "QUEST_LOG_UPDATE"
function QUESTS_UPDATE()
for questID in pairs(QUESTS_CACHE) do
_M:UpdateQuest(questID)
end
_QuestModel:Flush()
end
__SystemEvent__()
function QUEST_POI_UPDATE()
QuestSuperTracking_OnPOIUpdate()
_M:UpdateDistance()
end
__SystemEvent__ "ZONE_CHANGED" "ZONE_CHANGED_NEW_AREA" "AREA_POIS_UPDATED"
function QUESTS_ON_MAP_UPDATE()
QUESTS_UPDATE()
_M:UpdateDistance()
end
__SystemEvent__()
function QUEST_ACCEPTED(questID)
-- Don't continue if the quest is a world quest or a emissary
if IsWorldQuest(questID) or IsQuestTask(questID) or IsQuestBounty(questID) then
return
end
Trace("The quest (id:%i) has been accepted", questID)
-- Add it in the quest watched
if AUTO_QUEST_WATCH == "1" and GetNumQuestWatches() < Constants.QuestWatchConsts.MAX_QUEST_WATCHES then
local wasWatched = AddQuestWatch(questID, EnumQuestWatchType.Automatic)
if wasWatched then
QuestSuperTracking_OnQuestTracked(questID)
Debug("The quest (id:%i) has been watched with success", questID)
else
Debug("The quest (id:%i) hasn't been watched for a unknown reason", questID)
end
else
Debug("The quest (id:%i) doesn't match the requirements for being watched", questID)
Trace("AUTO_QUEST_WATCH: %s", AUTO_QUEST_WATCH)
Trace("GetNumQuestWatches(): %i", GetNumQuestWatches())
Trace("MAX_QUEST_WATCHES: %i", Constants.QuestWatchConsts.MAX_QUEST_WATCHES)
end
end
__SystemEvent__()
function QUEST_WATCH_LIST_CHANGED(questID, isAdded)
if not questID then
return
end
-- Manually tracking the world quest trigger this event so we need to check
-- this isn't a world quest
if IsWorldQuest(questID) then
return
end
if isAdded then
Debug("The quest (id:%i) has been added in the watch list", questID)
QUESTS_CACHE[questID] = true
QUESTS_REQUESTED[questID] = true
RequestLoadQuestByID(questID)
-- _M:UpdateQuest(questID)
else
Debug("The quest (id:%i) has been removed from the watch list", questID)
QUESTS_CACHE[questID] = nil
QUESTS_REQUESTED[questID] = nil
if QUESTS_WITH_ITEMS[questID] then
ItemBar_RemoveItemData(questID)
ItemBar_Update()
QUESTS_WITH_ITEMS[questID] = nil
end
_QuestModel:RemoveQuestData(questID)
_QuestModel:Flush()
end
end
__SystemEvent__()
function QUEST_DATA_LOAD_RESULT(questID, success)
if success and QUESTS_REQUESTED[questID] then
QUESTS_REQUESTED[questID] = nil
_M:UpdateQuest(questID)
_QuestModel:Flush()
end
end
function GetQuestHeader(self, qID)
-- Check if the quest header is in the cache
if QUEST_HEADERS_CACHE[qID] then
return QUEST_HEADERS_CACHE[qID]
end
-- if no, find the quest header
local currentHeader = "Misc"
local numEntries, numQuests = GetNumQuestLogEntries()
for i = 1, numEntries do
local data = GetInfo(i)
if data.isHeader then
currentHeader = data.title
elseif data.questID == qID then
QUEST_HEADERS_CACHE[qID] = currentHeader
return currentHeader
end
end
return currentHeader
end
__Async__()
__SystemEvent__()
function PLAYER_STARTED_MOVING()
DISTANCE_UPDATER_ENABLED = true
while DISTANCE_UPDATER_ENABLED do
_M:UpdateDistance()
-- TODO: Create an option for changing the refresh rate.
Delay(1)
end
end
__SystemEvent__()
function PLAYER_STOPPED_MOVING()
DISTANCE_UPDATER_ENABLED = false
_M:UpdateDistance()
end
IN_TAXI = false
__Async__()
__SystemEvent__()
function VEHICLE_ANGLE_SHOW()
if IN_TAXI then
return
end
IN_TAXI = true
PLAYER_STARTED_MOVING()
NextEvent("VEHICLE_ANGLE_SHOW")
PLAYER_STOPPED_MOVING()
Delay(0.2)
IN_TAXI = false
end
function UpdateDistance()
for questID in pairs(QUESTS_CACHE) do
local distanceSq = GetDistanceSqToQuest(questID)
if distanceSq then
_QuestModel:AddQuestData(questID, { distance = math.sqrt(distanceSq) })
end
end
_QuestModel:Flush()
end
-- ========================================================================= --
-- Debug Utils Tools
-- ========================================================================= --
if ViragDevTool_AddData then
ViragDevTool_AddData(_QuestModel, "SLT Quest Model")
end