Add OctoQuestRewards 1.0.0 addon source
This commit is contained in:
+189
@@ -0,0 +1,189 @@
|
||||
OctoQuestRewards = {}
|
||||
local A = OctoQuestRewards
|
||||
local G = getfenv(0)
|
||||
-- A raw custom base XP cross-check, not the website's potentially scaled Gains.
|
||||
-- https://octowow.st/db/?quest=41677 (RewXP=2150, level=32, checked 2026-10-05)
|
||||
local octoBase = { [41677] = {32, 2150} }
|
||||
|
||||
-- Only documented, present functions are called. No modern XP API is assumed.
|
||||
function A.Call(fn, a)
|
||||
if type(fn) ~= "function" then return nil end
|
||||
local ok, value
|
||||
if a == nil then ok, value = pcall(fn) else ok, value = pcall(fn, a) end
|
||||
if ok then return value end
|
||||
end
|
||||
|
||||
local function QuestTables()
|
||||
local q = pfDB and pfDB.quests
|
||||
return q and q.data, q and (q.loc or q[GetLocale()] or q.enUS)
|
||||
end
|
||||
|
||||
local titleIndex, indexedLocale
|
||||
local function MatchTitle(title, level, description, objectives)
|
||||
local data, loc = QuestTables()
|
||||
if not data or not loc or not title then return nil end
|
||||
if indexedLocale ~= loc then
|
||||
titleIndex, indexedLocale = {}, loc
|
||||
for id, row in pairs(loc) do
|
||||
if type(row) == "table" and row.T and data[id] then
|
||||
titleIndex[row.T] = titleIndex[row.T] or {}
|
||||
table.insert(titleIndex[row.T], id)
|
||||
end
|
||||
end
|
||||
end
|
||||
local found, count = nil, 0
|
||||
for _, id in ipairs(titleIndex[title] or {}) do
|
||||
local row = data[id]
|
||||
if not level or level == row.lvl or row.lvl == -1 then
|
||||
found, count = id, count + 1
|
||||
end
|
||||
end
|
||||
if count == 1 then return found, "pfQuest title/level match (inferred ID)" end
|
||||
-- Duplicate titles need exact quest text; never pick the first guessed ID.
|
||||
found, count = nil, 0
|
||||
if pfDatabase and pfDatabase.FormatQuestText then
|
||||
for _, id in ipairs(titleIndex[title] or {}) do
|
||||
local row = loc[id]
|
||||
local d = row.D and pfDatabase:FormatQuestText(row.D)
|
||||
local o = row.O and pfDatabase:FormatQuestText(row.O)
|
||||
if (not level or level == data[id].lvl or data[id].lvl == -1)
|
||||
and ((description and description ~= "" and description == d)
|
||||
or (objectives and objectives ~= "" and objectives == o)) then
|
||||
found, count = id, count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
if count == 1 then return found, "pfQuest exact text match (inferred ID)" end
|
||||
end
|
||||
|
||||
local function PositiveID(id)
|
||||
id = tonumber(id)
|
||||
if id and id > 0 then return id end
|
||||
end
|
||||
|
||||
function A.LogTitle(index)
|
||||
if pfQuestCompat and pfQuestCompat.GetQuestLogTitle then
|
||||
return pfQuestCompat.GetQuestLogTitle(index)
|
||||
end
|
||||
return GetQuestLogTitle(index)
|
||||
end
|
||||
|
||||
function A.LogID(index, title, level, description, objectives)
|
||||
if C_QuestLog and C_QuestLog.GetQuestIDForLogIndex then
|
||||
local id = PositiveID(A.Call(C_QuestLog.GetQuestIDForLogIndex, index))
|
||||
if id then return id, "C_QuestLog.GetQuestIDForLogIndex" end
|
||||
end
|
||||
local link = A.Call(G.GetQuestLink, index)
|
||||
if type(link) == "string" then
|
||||
local _, _, id = string.find(link, "quest:(%d+)")
|
||||
if PositiveID(id) then return tonumber(id), "GetQuestLink" end
|
||||
end
|
||||
return MatchTitle(title, level, description, objectives)
|
||||
end
|
||||
|
||||
-- Vanilla degradation. Octo's server rate/scaling is not verified.
|
||||
-- Level alone cannot determine base XP: quests at the same level vary widely.
|
||||
function A.EstimateXP(base, questLevel, playerLevel)
|
||||
if type(base) ~= "number" or not questLevel then return nil end
|
||||
if playerLevel >= 60 then return 0 end
|
||||
if questLevel < 1 then questLevel = playerLevel end
|
||||
local difference = playerLevel - questLevel
|
||||
local factor = 1
|
||||
if A.db.scaling == "full25" then
|
||||
-- Optional explicit assumption for Turtle-like custom scaling.
|
||||
if difference > 25 then return nil end
|
||||
elseif difference >= 10 then factor = 0.1
|
||||
elseif difference > 5 then factor = (10 - difference) * 0.2 end
|
||||
return math.ceil(base * factor - 0.000001)
|
||||
end
|
||||
|
||||
function A.GetRecord(kind)
|
||||
local r = { playerLevel = UnitLevel("player"), kind = kind }
|
||||
local index
|
||||
if kind == "log" then
|
||||
index = GetQuestLogSelection()
|
||||
local header
|
||||
local tag
|
||||
r.title, r.level, tag, header = A.LogTitle(index)
|
||||
if not r.title or header then return nil end
|
||||
local description, objectives = GetQuestLogQuestText()
|
||||
r.id, r.idSource = A.LogID(index, r.title, r.level, description, objectives)
|
||||
r.money = A.Call(G.GetQuestLogRewardMoney)
|
||||
r.moneySource = "GetQuestLogRewardMoney (server cache)"
|
||||
else
|
||||
r.title = GetTitleText()
|
||||
if not r.title or r.title == "" then return nil end
|
||||
-- VoiceOver installs GetQuestID inside its own namespace, not the engine.
|
||||
r.id = PositiveID(A.Call(G.GetQuestID))
|
||||
if r.id then r.idSource = "GetQuestID (provider unverified)" end
|
||||
if not r.id then
|
||||
r.id, r.idSource = MatchTitle(r.title, nil,
|
||||
kind == "detail" and GetQuestText() or nil,
|
||||
kind == "detail" and GetObjectiveText() or nil)
|
||||
end
|
||||
if kind == "progress" then
|
||||
-- REQUEST_ITEMS packets don't supply reward money: GetRewardMoney here
|
||||
-- can be stale. Read only a uniquely matching accepted quest instead.
|
||||
local match, count = nil, 0
|
||||
for i = 1, GetNumQuestLogEntries() do
|
||||
local title, level, tag, header = A.LogTitle(i)
|
||||
if title == r.title and not header then match, count = i, count + 1 end
|
||||
end
|
||||
if count == 1 then
|
||||
local old = GetQuestLogSelection()
|
||||
SelectQuestLogEntry(match)
|
||||
local log = A.GetRecord("log")
|
||||
SelectQuestLogEntry(old)
|
||||
if log then
|
||||
r.id, r.idSource, r.level = log.id, log.idSource, log.level
|
||||
r.money, r.moneySource = log.money, log.moneySource
|
||||
end
|
||||
end
|
||||
else
|
||||
r.money = A.Call(G.GetRewardMoney)
|
||||
r.moneySource = "GetRewardMoney (NPC server packet)"
|
||||
end
|
||||
end
|
||||
local data = QuestTables()
|
||||
if not r.level and data and r.id and data[r.id] then r.level = data[r.id].lvl end
|
||||
local cache
|
||||
if r.id and C_QuestLog and C_QuestLog.GetQuestDetails then
|
||||
cache = A.Call(C_QuestLog.GetQuestDetails, r.id)
|
||||
if type(cache) == "table" then
|
||||
r.level = cache.level or r.level
|
||||
if r.money == nil and r.playerLevel < 60 then
|
||||
r.money, r.moneySource = cache.rewardMoney, "C_QuestLog.GetQuestDetails.rewardMoney"
|
||||
end
|
||||
end
|
||||
end
|
||||
if r.money and r.money < 0 then r.money = 0 end -- A cost isn't a reward.
|
||||
local reward = r.id and PfExtend_QuestRewards and PfExtend_QuestRewards[r.id]
|
||||
local baseline = r.id and (octoBase[r.id] or (A.VanillaXP and A.VanillaXP[r.id]))
|
||||
local base
|
||||
if baseline and (baseline[1] == r.level or baseline[1] == -1) then
|
||||
base = baseline[2]
|
||||
r.xpSource = (octoBase[r.id] and "Octo DB raw RewXP" or "LibQuestXP Vanilla baseline")
|
||||
.. " + " .. A.db.scaling .. " scaling; rate assumed 1x; Octo match unverified"
|
||||
elseif not reward and type(cache) == "table" and type(cache.rewardMoneyAtMaxLevel) == "number" then
|
||||
-- Classic cache has no XP field. This is the historical Vanilla conversion,
|
||||
-- NOT a directly supplied XP value, and may differ on custom quests.
|
||||
if r.level and r.level <= 60 then
|
||||
base = cache.rewardMoneyAtMaxLevel / 0.6
|
||||
r.xpSource = "ClassicAPI max-level money / 0.6 + " .. A.db.scaling .. " scaling (estimated)"
|
||||
end
|
||||
end
|
||||
r.baseXP = base
|
||||
r.xp = A.EstimateXP(base, r.level, r.playerLevel)
|
||||
if base == nil and reward and type(reward.xp) == "number" then
|
||||
-- The installed pfExtend scraper copies displayed Gains, not raw RewXP.
|
||||
-- We cannot undo unknown website scaling. Use it only as a near-level
|
||||
-- reference, with no second scaling; suppress it on overlevelled quests.
|
||||
r.xpSource = "pfExtend website Gains reference; base XP/scaling unverified"
|
||||
if r.playerLevel >= 60 then r.xp = 0
|
||||
elseif r.level and (r.level < 1 or r.playerLevel <= r.level + 5) then r.xp = reward.xp end
|
||||
end
|
||||
r.idSource = r.idSource or "unresolved/ambiguous ID"
|
||||
r.xpSource = r.xpSource or "unavailable"
|
||||
if r.id then A.predictions[r.id] = r end
|
||||
return r
|
||||
end
|
||||
Reference in New Issue
Block a user