Quests
Contributor4 min readUpdated
Quest database, markers, tooltips, quest levels
addon/Polestar/Quests/ loads the load-on-demand data addonA companion addon that carries data, not code, mostly loaded on demand: Polestar_QuestDB, Polestar_ItemDB, Polestar_Codex, Polestar_Travel.
Polestar_QuestDB through Data.Pack (≈ 1.3 MB of Lua, ≈ 6.4 MB resident)
at login, and only while qol.questDB is on and at least one feature that
needs it is (QuestDB.IsWanted(): map/minimap objectives, available quests,
turn-ins, tooltips, nameplates); turning the first one on later loads it then,
and a load refused in combat is retried when combat ends. It fires
QUESTDB_LOADED, then QUESTDB_STARTED; QUESTDB_UPDATED follows every
change to the tracked quest log. Record layouts: tools/lib/questdb/build.js.
An entity's spawn clusters (70 yd) cover every known spawn on each map (the
reference dataThe game facts in data/forever/*.json (quests, NPCs, objects, items, zones, map frames and more) that the compiler and the data builds read; built locally by data build.'s pts and more), with no cap on how many.
Polestar.QuestDB.IsLoaded() / IsEnabled() / IsRunning() / IsWanted()
Polestar.QuestDB.GetQuest(id) -> { id, title, level, minLevel, races, classes, faction, start = { npc, object, item },
finish = { npc, object }, pre (any-of), preAll, excl, zone, skill, holiday,
objectives = { { kind = "kill"|"item"|"object"|"event", id, count, map, x, y, f } } } | nil
Polestar.QuestDB.GetNpc(id) / GetObject(id) -> { id, name, spawns = { { map, x, y, r (yd), f } } } | nil
Polestar.QuestDB.GetItem(id) -> { id, name, droppers = { { id, name, chance } }, objects = { ... }, vendors = { { id, name } } } | nil
Polestar.QuestDB.GetItemDroppers(itemID) -> { { id, name, chance }, ... } | nil -- best chance first
Polestar.QuestDB.GetObjectiveMobs(questID, oi) -> { name, ... } | nil -- kill target or item droppers
Polestar.QuestDB.CheckAvailable(id, ctx, isDone, isOn, opts) -> ok, reason -- opts: lowLevel, levelAbove, skillRank
Polestar.QuestDB.EachAvailabilityRef(id, fn) -> needsSkill -- fn(q) per quest whose done/in-log state CheckAvailable reads
Polestar.QuestDB.GetAvailableQuests() -> { questID, ... } ; IsAvailable(q) ; WhyUnavailable(q) -> reason | nil
-- reasons: "eligibility" (faction, race incl. Skyborne high bits, class, holiday), "done", "active", "level"
-- (minLevel), "grey", "high" (beyond qol.questLevelWindow), "prerequisite", "exclusive", "skill"
Polestar.QuestDB.ObjectivesForNpc(id) / ObjectivesForItem(id) / ObjectivesForObject(id)
-> { { q, kind, id, name, cur, max, done, entry = { q, title, ready } }, ... } | nil -- quests in the log
Polestar.QuestDB.GetMarkers(map [, "objectives"|"turnin"|"available"]) -> { pin, ... }
Polestar.QuestDB.IsGuideQuest(q) -> bool -- in the active guide's current stepRead the rest of this section510 words
"Done" comes from C_QuestLog.GetAllCompletedQuestIDs (one call per available
scan), else C_QuestLog.IsQuestFlaggedCompleted per quest. A scan rechecks every
candidate only after a level-up, a skill change, a filter setting or without the
bulk call; otherwise only the candidates that read a quest whose done or in-log
state moved since the last scan (EachAvailabilityRef), plus those needing a
profession skill.
Pins layers: quests.objectives, quests.turnin, quests.available, one pin
per entity spawn cluster and layer (a mob two objectives need carries both
under the higher-ranked glyph: kill > interact > collect > vendor). Objective
clusters at least MIN_AREA (12 yd) wide carry their radius as area. Markers
and the tooltip/nameplate indexes are rebuilt only when what they show
changed (a quest came or went, got ready or changed title, an objective was
done or undone); a progress count alone only fires QUESTDB_UPDATED, since
tooltips and nameplates read counts live. A rebuild reuses the pin tables
the last one kept for the same cluster, with their world position (c,
wx, wy) worked out once, so Pins.SetLayer converts only new pins. Look is
applied at publish: while a Polestar guideOne route, written as a .guide file and compiled to Lua; its id reads like forever/alliance/human/01-elwynn-forest. runs, pins of quests in its current
stepOne stop in a guide: a title, optional conditions and tip, and at least one goal. (Engine.GetGuide().steps[Engine.GetStepIndex()].goals[*].quest, re-read on
STEP_CHANGED/GUIDE_CHANGED) are 1.3× inside an accent ring and on top;
with qol.guideDim every other quest pin drops to 0.6 alpha (Q.DIM_ALPHA).
Tooltips go through Polestar.Hooks (owner "quests": UnitLines, ItemLines,
ObjectLines, registered only while running) with the suite's quest line,
Hooks.AddLine(tt, "<Quest>:", "<what> cur/max", quest, textDim | success):
"Kill Kobold Vermin 3/10" on the target, "Large Candle 3/8" on a dropper, "3/8"
on the item; quest-starting items get "Starts a quest: <title>".
Quests/QuestLevels.lua prefixes "[12]" (and "+" for elite) to quest titles
the client lists without one (gossip, greeting, details; camelot's own log and
tracker already show it, so it adds there only when missing).
(camelot is Forever's game type in Blizzard's UI source: the files the Forever client loads carry AllowLoadGameType camelot, and its own overrides sit in Camelot/ folders.)
Nameplate icons (qol.nameplateIcons): a mob an open objective still needs
gets a 16px objective icon (14–20) 3px left of the health bar the player
sees: EllesmereUI Nameplates' unit frame, Plater/ElvUI/oUF, KuiNameplates or
Threat Plates when present, else the shown child of the base plate holding
the largest status bar, else Blizzard's health bar, else the plate itself;
parented to that frame so it follows its fading and scaling. Beside it, left,
a font string of the icon frame (.count) shows the progress of the open
objectives with a count above 1 (Q.ProgressText, by quest, joined with
" · "), re-read on every QUESTDB_UPDATED; hidden when none has one. Every
access is guarded; Quests.NameplateIcon(unit) returns the icon and
Quests.NameplateCount(unit) the progress shown (tests).
Settings (qol.*, Questing → Quests; the markers under Map → Guide & quests →
Quest markers): questDB, availableQuests,
questLevelWindow (0–10, 10 = any; default 5), lowLevelQuests,
mapObjectives, objectiveAreas, turnInMarkers, minimapObjectives,
guideHighlight, guideDim, questTooltips, nameplateIcons, questLevels.
The four marker layers (availableQuests, mapObjectives, turnInMarkers,
minimapObjectives) and objectiveAreas default to off (Map → Guide & quests →
Quest markers); guideHighlight only restyles markers those layers draw.