chore: flatten Altoholic-Addon/ wrapper + add standard .gitignore/.gitattributes
Each DataStore_* / Altoholic_* addon now lives at the repo root, matching the Exiles fork-layout convention (one folder per addon, no wrapper dir).
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
--[[ *** DataStore_Quests ***
|
||||
Written by : Thaoky, EU-Marécages de Zangar
|
||||
July 8th, 2009
|
||||
--]]
|
||||
if not DataStore then return end
|
||||
|
||||
local addonName = "DataStore_Quests"
|
||||
|
||||
_G[addonName] = LibStub("AceAddon-3.0"):NewAddon(addonName, "AceConsole-3.0", "AceEvent-3.0", "AceTimer-3.0")
|
||||
|
||||
local addon = _G[addonName]
|
||||
|
||||
local THIS_ACCOUNT = "Default"
|
||||
|
||||
local AddonDB_Defaults = {
|
||||
global = {
|
||||
Options = {
|
||||
TrackTurnIns = 1, -- by default, save the ids of completed quests in the history
|
||||
AutoUpdateHistory = 1, -- if history has been queried at least once, auto update it at logon (fast operation - already in the game's cache)
|
||||
},
|
||||
Characters = {
|
||||
['*'] = { -- ["Account.Realm.Name"]
|
||||
lastUpdate = nil,
|
||||
Quests = {},
|
||||
QuestLinks = {},
|
||||
Rewards = {},
|
||||
History = {}, -- a list of completed quests, hash table ( [questID] = true )
|
||||
HistoryBuild = nil, -- build version under which the history has been saved
|
||||
HistorySize = 0,
|
||||
HistoryLastUpdate = nil,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- *** Utility functions ***
|
||||
local function GetOption(option)
|
||||
return addon.db.global.Options[option]
|
||||
end
|
||||
|
||||
local function GetQuestLogIndexByName(name)
|
||||
-- helper function taken from QuestGuru
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local title = GetQuestLogTitle(i);
|
||||
if title == strtrim(name) then
|
||||
return i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- *** Scanning functions ***
|
||||
local headersState = {}
|
||||
|
||||
local function SaveHeaders()
|
||||
local headerCount = 0 -- use a counter to avoid being bound to header names, which might not be unique.
|
||||
|
||||
for i = GetNumQuestLogEntries(), 1, -1 do -- 1st pass, expand all categories
|
||||
local _, _, _, _, isHeader, isCollapsed = GetQuestLogTitle(i)
|
||||
if isHeader then
|
||||
headerCount = headerCount + 1
|
||||
if isCollapsed then
|
||||
ExpandQuestHeader(i)
|
||||
headersState[headerCount] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function RestoreHeaders()
|
||||
local headerCount = 0
|
||||
for i = GetNumQuestLogEntries(), 1, -1 do
|
||||
local _, _, _, _, isHeader = GetQuestLogTitle(i)
|
||||
if isHeader then
|
||||
headerCount = headerCount + 1
|
||||
if headersState[headerCount] then
|
||||
CollapseQuestHeader(i)
|
||||
end
|
||||
end
|
||||
end
|
||||
wipe(headersState)
|
||||
end
|
||||
|
||||
local REWARD_TYPE_CHOICE = "c"
|
||||
local REWARD_TYPE_REWARD = "r"
|
||||
local REWARD_TYPE_SPELL = "s"
|
||||
|
||||
local function ScanQuests()
|
||||
local char = addon.ThisCharacter
|
||||
local quests = char.Quests
|
||||
local links = char.QuestLinks
|
||||
local rewards = char.Rewards
|
||||
|
||||
wipe(quests)
|
||||
wipe(links)
|
||||
wipe(rewards)
|
||||
|
||||
local currentSelection = GetQuestLogSelection() -- save the currently selected quest
|
||||
SaveHeaders()
|
||||
|
||||
local RewardsCache = {}
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local title, _, questTag, groupSize, isHeader, _, isComplete = GetQuestLogTitle(i);
|
||||
|
||||
if isHeader then
|
||||
quests[i] = "0|" .. (title or "")
|
||||
else
|
||||
SelectQuestLogEntry(i)
|
||||
local money = GetQuestLogRewardMoney()
|
||||
quests[i] = format("1|%s|%d|%d|%d", questTag or "", groupSize, money, isComplete or 0)
|
||||
links[i] = GetQuestLink(i)
|
||||
|
||||
wipe(RewardsCache)
|
||||
local num = GetNumQuestLogChoices() -- these are the actual item choices proposed to the player
|
||||
if num > 0 then
|
||||
for i = 1, num do
|
||||
local _, _, numItems, _, isUsable = GetQuestLogChoiceInfo(i)
|
||||
local link = GetQuestLogItemLink("choice", i)
|
||||
if link then
|
||||
local id = tonumber(link:match("item:(%d+)"))
|
||||
if id then
|
||||
table.insert(RewardsCache, REWARD_TYPE_CHOICE .."|"..id.."|"..numItems.."|"..(isUsable or 0))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
num = GetNumQuestLogRewards() -- these are the rewards given anyway
|
||||
if num > 0 then
|
||||
for i = 1, num do
|
||||
local _, _, numItems, _, isUsable = GetQuestLogRewardInfo(i)
|
||||
local link = GetQuestLogItemLink("reward", i)
|
||||
if link then
|
||||
local id = tonumber(link:match("item:(%d+)"))
|
||||
if id then
|
||||
table.insert(RewardsCache, REWARD_TYPE_REWARD .. "|"..id.."|"..numItems.."|"..(isUsable or 0))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if GetQuestLogRewardSpell() then -- apparently, there is only one spell as reward
|
||||
local _, _, isTradeskillSpell, isSpellLearned = GetQuestLogRewardSpell()
|
||||
if isTradeskillSpell or isSpellLearned then
|
||||
local link = GetQuestLogSpellLink()
|
||||
if link then
|
||||
local id = tonumber(link:match("spell:(%d+)"))
|
||||
if id then
|
||||
table.insert(RewardsCache, REWARD_TYPE_SPELL .. "|"..id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #RewardsCache > 0 then
|
||||
rewards[i] = table.concat(RewardsCache, ",")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
RestoreHeaders()
|
||||
SelectQuestLogEntry(currentSelection) -- restore the selection to match the cursor, must be properly set if a user abandons a quest
|
||||
|
||||
addon.ThisCharacter.lastUpdate = time()
|
||||
end
|
||||
|
||||
local function RefreshQuestHistory()
|
||||
-- called 5 seconds after login, if the current character already has an history, one that was saved in the same build, then it's safe to refresh it automatically
|
||||
local thisChar = addon.ThisCharacter
|
||||
if not thisChar.HistoryLastUpdate then return end -- never scanned the history before ? exit
|
||||
|
||||
local _, version = GetBuildInfo()
|
||||
if version and thisChar.HistoryBuild and version == thisChar.HistoryBuild then -- proceed if version is the same as the one saved in the db
|
||||
QueryQuestsCompleted()
|
||||
end
|
||||
end
|
||||
|
||||
-- *** Event Handlers ***
|
||||
local function OnPlayerAlive()
|
||||
-- print("DataStore_Quests.lua") -- DEBUG 2025 07 21
|
||||
if not UnitIsGhost("player") then return end -- only scan if player released spirit and went to graveyard
|
||||
|
||||
ScanQuests()
|
||||
end
|
||||
|
||||
local function OnQuestLogUpdate()
|
||||
addon:UnregisterEvent("QUEST_LOG_UPDATE") -- .. and unregister it right away, since we only want it to be processed once (and it's triggered way too often otherwise)
|
||||
ScanQuests()
|
||||
end
|
||||
|
||||
local function OnUnitQuestLogChanged() -- triggered when accepting/validating a quest .. but too soon to refresh data
|
||||
addon:RegisterEvent("QUEST_LOG_UPDATE", OnQuestLogUpdate) -- so register for this one ..
|
||||
end
|
||||
|
||||
local lastCompleteQuestLink
|
||||
|
||||
local function OnQuestComplete()
|
||||
if GetOption("TrackTurnIns") ~= 1 then return end
|
||||
|
||||
-- "QUEST_COMPLETE" is triggered when the UI reaches the page where a player can click on the "Complete" Button.
|
||||
-- At this point, only detect which quest we're dealing with, and save its link
|
||||
local num = GetQuestLogIndexByName(GetTitleText());
|
||||
if num then
|
||||
lastCompleteQuestLink = GetQuestLink(num) -- or save quest id
|
||||
end
|
||||
end
|
||||
|
||||
local queryVerbose
|
||||
|
||||
local function OnQuestQueryComplete()
|
||||
local thisChar = addon.ThisCharacter
|
||||
local history = thisChar.History
|
||||
local quests = {}
|
||||
GetQuestsCompleted(quests)
|
||||
|
||||
local count = 0
|
||||
for questID in pairs(quests) do
|
||||
history[questID] = true
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
local _, version = GetBuildInfo() -- save the current build, to know if we can requery and expect immediate execution
|
||||
thisChar.HistoryBuild = version
|
||||
thisChar.HistorySize = count
|
||||
thisChar.HistoryLastUpdate = time()
|
||||
|
||||
if queryVerbose then
|
||||
addon:Print("Quest history successfully retrieved!")
|
||||
queryVerbose = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- ** Mixins **
|
||||
local function _GetQuestLogSize(character)
|
||||
return #character.Quests
|
||||
end
|
||||
|
||||
local function _GetQuestLogInfo(character, index)
|
||||
local quest = character.Quests[index]
|
||||
local link = character.QuestLinks[index]
|
||||
local isHeader, questTag, groupSize, money, isComplete = strsplit("|", quest)
|
||||
|
||||
if isHeader == "0" then
|
||||
return true, questTag -- questTag contains the title in a header line
|
||||
end
|
||||
|
||||
isComplete = tonumber(isComplete)
|
||||
return nil, link, questTag, tonumber(groupSize), tonumber(money), tonumber(isComplete)
|
||||
end
|
||||
|
||||
local function _GetQuestLogNumRewards(character, index)
|
||||
local reward = character.Rewards[index]
|
||||
if reward then
|
||||
return select(2, gsub(reward, ",", ",")) + 1 -- returns the number of rewards (=count of ^ +1)
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function _GetQuestLogRewardInfo(character, index, rewardIndex)
|
||||
local reward = character.Rewards[index]
|
||||
if not reward then return end
|
||||
|
||||
local i = 1
|
||||
for v in reward:gmatch("([^,]+)") do
|
||||
if rewardIndex == i then
|
||||
local rewardType, id, numItems, isUsable = strsplit("|", v)
|
||||
|
||||
numItems = tonumber(numItems) or 0
|
||||
isUsable = (isUsable and isUsable == 1) and true or nil
|
||||
|
||||
return rewardType, tonumber(id), numItems, isUsable
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function _GetQuestInfo(link)
|
||||
assert(type(link) == "string")
|
||||
|
||||
local questID, questLevel = link:match("quest:(%d+):(-?%d+)")
|
||||
local questName = link:match("%[(.+)%]")
|
||||
|
||||
return questName, tonumber(questID), tonumber(questLevel)
|
||||
end
|
||||
|
||||
local function _QueryQuestHistory()
|
||||
QueryQuestsCompleted() -- this call triggers "QUEST_QUERY_COMPLETE"
|
||||
queryVerbose = true
|
||||
end
|
||||
|
||||
local function _GetQuestHistory(character)
|
||||
return character.History
|
||||
end
|
||||
|
||||
local function _GetQuestHistoryInfo(character)
|
||||
-- return the size of the history, the timestamp, and the build under which it was saved
|
||||
return character.HistorySize, character.HistoryLastUpdate, character.HistoryBuild
|
||||
end
|
||||
|
||||
local function _IsQuestCompletedBy(character, questID)
|
||||
return character.History[questID] -- nil = not completed (not in the table), true = completed
|
||||
end
|
||||
|
||||
local PublicMethods = {
|
||||
GetQuestLogSize = _GetQuestLogSize,
|
||||
GetQuestLogInfo = _GetQuestLogInfo,
|
||||
GetQuestLogNumRewards = _GetQuestLogNumRewards,
|
||||
GetQuestLogRewardInfo = _GetQuestLogRewardInfo,
|
||||
GetQuestInfo = _GetQuestInfo,
|
||||
QueryQuestHistory = _QueryQuestHistory,
|
||||
GetQuestHistory = _GetQuestHistory,
|
||||
GetQuestHistoryInfo = _GetQuestHistoryInfo,
|
||||
IsQuestCompletedBy = _IsQuestCompletedBy,
|
||||
}
|
||||
|
||||
function addon:OnInitialize()
|
||||
addon.db = LibStub("AceDB-3.0"):New(addonName .. "DB", AddonDB_Defaults)
|
||||
|
||||
DataStore:RegisterModule(addonName, addon, PublicMethods)
|
||||
DataStore:SetCharacterBasedMethod("GetQuestLogSize")
|
||||
DataStore:SetCharacterBasedMethod("GetQuestLogInfo")
|
||||
DataStore:SetCharacterBasedMethod("GetQuestLogNumRewards")
|
||||
DataStore:SetCharacterBasedMethod("GetQuestLogRewardInfo")
|
||||
DataStore:SetCharacterBasedMethod("GetQuestHistory")
|
||||
DataStore:SetCharacterBasedMethod("GetQuestHistoryInfo")
|
||||
DataStore:SetCharacterBasedMethod("IsQuestCompletedBy")
|
||||
end
|
||||
|
||||
function addon:OnEnable()
|
||||
addon:RegisterEvent("PLAYER_ALIVE", OnPlayerAlive)
|
||||
addon:RegisterEvent("UNIT_QUEST_LOG_CHANGED", OnUnitQuestLogChanged)
|
||||
addon:RegisterEvent("QUEST_COMPLETE", OnQuestComplete)
|
||||
addon:RegisterEvent("QUEST_QUERY_COMPLETE", OnQuestQueryComplete)
|
||||
|
||||
addon:SetupOptions()
|
||||
|
||||
if GetOption("AutoUpdateHistory") == 1 then -- if history has been queried at least once, auto update it at logon (fast operation - already in the game's cache)
|
||||
addon:ScheduleTimer(RefreshQuestHistory, 5) -- refresh quest history 5 seconds later, to decrease the load at startup
|
||||
end
|
||||
end
|
||||
|
||||
function addon:OnDisable()
|
||||
addon:UnregisterEvent("PLAYER_ALIVE")
|
||||
addon:UnregisterEvent("UNIT_QUEST_LOG_CHANGED")
|
||||
addon:UnregisterEvent("QUEST_QUERY_COMPLETE")
|
||||
end
|
||||
|
||||
-- *** Hooks ***
|
||||
|
||||
local Orig_QuestRewardCompleteButton_OnClick = QuestRewardCompleteButton_OnClick;
|
||||
|
||||
function QuestRewardCompleteButton_OnClick()
|
||||
if lastCompleteQuestLink then -- if there's a valid link
|
||||
local questID = lastCompleteQuestLink:match("quest:(%d+):")
|
||||
questID = tonumber(questID)
|
||||
if questID then
|
||||
addon.ThisCharacter.History[questID] = true -- mark the current quest ID as completed
|
||||
addon:SendMessage("DATASTORE_QUEST_TURNED_IN", questID) -- trigger the DS event
|
||||
end
|
||||
lastCompleteQuestLink = nil
|
||||
end
|
||||
Orig_QuestRewardCompleteButton_OnClick();
|
||||
end
|
||||
|
||||
QuestFrameCompleteQuestButton:SetScript("OnClick", QuestRewardCompleteButton_OnClick);
|
||||
Reference in New Issue
Block a user