The quest log hears it from Questie too #53

Manually merged
mdoorn merged 6 commits from feat/053-the-quest-log-hears-it-from-questie-too into master 2026-08-05 15:54:23 +02:00
Owner

The quest log hears it from Questie too

Along the way:

  • Where you are standing is asked once
  • A feed row draws a whole word or none of it
  • A list row knows which button and which modifier
  • Questie's tracker goes off, by its own hand
  • Somebody nearby has a quest you have never taken

Touches 26 files: docs/CHANGELOG.md, scripts/harness/client/05-quests.lua, scripts/harness/client/12-questlog.lua, scripts/harness/client/16-dungeons.lua, scripts/harness/client/23-quest-near.lua, scripts/harness/client/init.lua and 20 more.

The quest log hears it from Questie too Along the way: - Where you are standing is asked once - A feed row draws a whole word or none of it - A list row knows which button and which modifier - Questie's tracker goes off, by its own hand - Somebody nearby has a quest you have never taken Touches 26 files: `docs/CHANGELOG.md`, `scripts/harness/client/05-quests.lua`, `scripts/harness/client/12-questlog.lua`, `scripts/harness/client/16-dungeons.lua`, `scripts/harness/client/23-quest-near.lua`, `scripts/harness/client/init.lua` and 20 more.
praghunathan left a comment

Reading the Questie that is installed rather than the one we tested against is the right instinct. Three notes.

Reading the Questie that is installed rather than the one we tested against is the right instinct. Three notes.
@ -0,0 +304,4 @@
settle()
check(Off.Apply() == false, "the section left the two ends disagreeing")
print("quests Questie's tracker is " .. Off.Describe())
Owner

A print at the end of the section with no check after it. It reads like the summary line the other sections end on, so nobody will notice it asserts nothing.

A print at the end of the section with no check after it. It reads like the summary line the other sections end on, so nobody will notice it asserts nothing.
@ -0,0 +265,4 @@
--
-- Exported rather than local because ns.UI.Ticker takes a named function and
-- scripts/hot.lua walks out from the argument it is handed.
function Near.Beat()
Owner

Exported so the ticker can take it by name and hot.lua can walk out from it. Worth saying that on the line, because a function exported for a tool looks like public API to the next reader.

Exported so the ticker can take it by name and hot.lua can walk out from it. Worth saying that on the line, because a function exported for a tool looks like public API to the next reader.
@ -0,0 +173,4 @@
-- Login rather than load, for the reason Quests/Window.lua gives about the
-- function swap beside this one: the addon being reached into may not have
-- loaded yet, and Questie fills its settings table in at ADDON_LOADED.
local events = CreateFrame("Frame")
Owner

Login rather than load, because Questie fills its settings in at ADDON_LOADED. Right, and the sort of ordering that costs an evening when you get it wrong.

Login rather than load, because Questie fills its settings in at ADDON_LOADED. Right, and the sort of ordering that costs an evening when you get it wrong.
Author
Owner

Comment added, naming hot.lua.

Comment added, naming hot.lua.
Author
Owner

There is a check above it now; the print stays as the summary.

There is a check above it now; the print stays as the summary.
Author
Owner

It did. Two evenings, and one of them was spent on the wrong file.

It did. Two evenings, and one of them was spent on the wrong file.
tvidal approved these changes 2026-08-05 09:30:00 +02:00
tvidal left a comment

Read the swap. Nothing is replaced that Questie has not already installed, and the refusal says which of the two we found.

Read the swap. Nothing is replaced that Questie has not already installed, and the refusal says which of the two we found.
Questie v11 ships a Public/ folder whose README declares four things stable and
safe to call: isReady, RegisterOnReady, RegisterForQuestUpdates and
GetQuestObjectiveIconForUnit. ns.Questie could reach none of them. It asks
QuestieLoader for a module by name, and Questie.API is a plain table written
onto the Questie global by Modules/VersionCheck.lua, which the loader has never
heard of.

ns.QuestieAPI(name) sits beside it in Core/Core.lua and asks the same three
questions of the other half of that addon: the global, the API field, the named
call, nil for each and nothing said. It hands back the table rather than the
function, because isReady is a field and the caller that wants it reads it off
what comes back, next to the read. The name argument is what tells v11 from v6,
which has the global and no Public/ at all.

Quests/Window.lua registers for quest updates through that door at
PLAYER_LOGIN, behind RegisterOnReady because Questie's database compiles
minutes later and a callback is the database saying so itself. The client's
four events stay. What the second source buys is order: Questie recomputes the
two answers in the right column off the same client events this file listens
to, so the client's redraw is taken before that work and this one after it.

Item 97 wants AvailableQuests.__availableQuestsByNpc and
QuestieDB:GetNPC(npcId).spawns. Both are fields on loader modules, so this is
the wrong door for them; they come through ns.Questie, whose header already
says a caller reading a field checks that field next to the read.
Two trees were about to work out the same thing. The quest tracker needs the
place around you to scope itself on, the map window will want the same answer,
and a part may not name a file outside its own tree, so the honest choices were
one shared file or two readings drifting apart. ns.QuestHere is the shared file
and it lives in Core because Quests and Map both read it.

It answers a table: map is the ui map id and is always a number, name is what
this client calls that map in the player's own language, dungeon is a boolean,
area is Questie's area id for that map and parent is the area above it. The last
three degrade one at a time rather than all at once, so a client with no Questie
still gets the map id and the name. The table is held until you walk onto a
different map, and Here.Forget drops it. An answer that came back with no area
id is held for five seconds and not longer, because Questie compiles its
database minutes after login and standing still through that would otherwise
leave the area missing for the session.

Two things the item asked for turned out not to hold. The dungeon half does not
delegate to ns.DungeonHere: scripts/trees.lua closes the base, so Core may not
name a feature back, and Core -> ns.DungeonHere fails with no allow-list entry
able to excuse it. IsInInstance is the better answer anyway, because a caller
that goes quiet indoors wants raids and battlegrounds and the book has forty
dungeons and neither; Questie's ZoneDB.IsDungeonZone is the fallback for a
client with no such call. And ZoneDB:GetAreaIdByUiMapId calls error() outright
for a map it has no row for rather than returning nil, so the pcall around it is
load-bearing.

Item 91 takes name off this and matches it against the client's quest log
header, which is the client's own string in the same language on the same tick.
Item 93 takes area. Item 99 takes dungeon. Nothing writes to the table it gets
back: it is the held one and not a copy.

The client stub gains IsInInstance and the other three ZoneDB calls, in
scripts/harness/client/16-dungeons.lua because the join has to answer for
15-worldmap.lua's zones and that file's dungeons out of one table. Every area id
in it is read off the installed Questie's generated tables. Section
83-quest-here drives four zones, three dungeons, a client with no IsInInstance,
a Questie that is absent, a Questie with none of the calls on it and a client
that will not say which map you are on.
A row's dim middle column had never been measured against what was written
into it. Feeds/Combat.lua asks for 96 units of it and its widest string,
"from Plains Creeper", measures 131 in the shipped face at a row's text size,
so that row has drawn "from Plains Cree" since the column was written. I
measured it through Media/Sans.ttf's own advances at ROW_TEXT rather than
trusting the number in the item: 131.05, not the 123 item 100 quoted, which is
Lederverarbeitung's number from item 86 carried across. The method reproduces
that commit's other four to two decimals, so it is the same ruler.

UI/Feed.lua now decides it. Noted compares the string against the width the
column actually got and hides the note that does not fit, and it is asked on
the three moves that change either half: the resize, a new note written to a
row, and the swap back out of a marker. The rule is here rather than a shorter
phrase in the two streams because geom.note is clamped to half the free width
in Feed:Resize, so a column that holds its string at 280 units of feed loses it
at 200, and a number a stream writes once cannot answer that. The room does not
go back to the name: the columns line up down the feed, and a name that grew on
the rows whose note went missing is a ragged number column.

That deletes the combat feed's note column at the width it ships at, and at
every other width the panel offers. Dragged to 520 the column gets the whole 96
it asked for and 96 is short of 131. This is a feature going quiet rather than
a fix nobody sees, and it is the honest reading of what that column was doing:
the mob's name is on the hover under From and To, where it is whole. A stream
that wants a column back asks for a width its strings fit in. Feeds/Loot.lua
keeps its 48, because item 86 sized that against the widest quest count an
objective carries, 37, and 40-loot-feed.lua:828 was already asserting the row
draws it. That check was free before this and is now the thing standing between
the loot feed and the same silence.

31-feed-note.lua is a file of its own because 31-feeds.lua is at the 800 line
ceiling every section shares and 40-loot-feed.lua is at its own 908. It reads
both feeds: the widest combat string at the shipped width, at 520 and at 200, a
short one that still draws, the same short one at two text sizes, and the loot
column against a four digit count at both ends of its slider. Without the
comparison in Noted four of its checks fail.
UI.List handed onSelect an id and nothing else, so every column in the addon
could tell you which row was clicked and never how. Worse, List:Select refuses a
move to the selection it already holds, which is the right answer for a repaint
and the wrong one for a hand: a gesture aimed at the row you are already reading
told nobody, while the same gesture on every other row worked. A shift click to
pin the quest you are looking at would have shipped doing nothing.

onSelect now takes (id, which, mods). The click path is List:Press, which moves
the cursor where the cursor moved and tells the caller either way; List:Select
stays what it was, the code path, and hands nil for both extra arguments. They
arrive together or not at all, so `which` is the guard a caller checks before
reading a modifier and a repaint can never look like a press. The modifier is
read off IsShiftKeyDown and friends inside the handler rather than cached, since
a key remembered from the last click is wrong for this one.

Which buttons a row registers for is unchanged. The right button still goes to
onRight and onBack, a list offering neither never registers it, and the columns
that do not take it still hand it to the camera. So `which` is "LeftButton" on
every click today and the argument exists to separate a press from a repaint.
Item 95 reads it, and the item text's claim that the rows are frames rather than
buttons is wrong: they are Buttons with an OnClick, and OnClick is where this
sits.
Two quest trackers on one screen is one too many, and the way to take one of
them off the screen is not to hide it. Questie re-shows its tracker on a dozen
of its own events, so a frame this addon hid would come back on the first quest
accepted and the answer to that would be to hide it again, forever, once per
event. Core/Attic.lua is for Blizzard's frames, which nobody else is holding.
Quests/TrackerOff.lua calls QuestieTracker:Disable(), which is the call
Questie's own Enable Tracker box makes, and Enable() is what puts it back.

That writes Questie.db.profile.trackerEnabled, which is another addon's saved
setting and a thing this addon has never done. So it has a switch of its own,
questsTrackerOff, rather than riding the one that cages Blizzard's window, and
/wk status carries a clause saying which way the setting is and who moved it
last. ns.QuestieProfile in Core/Core.lua is the third door onto that addon,
beside ns.Questie and ns.QuestieAPI, and it takes the setting's name for the
same reason ns.QuestieAPI does: Modules/VersionCheck.lua leaves a preinit
placeholder in Questie.db carrying one minimap flag, so a caller that checked
for the table would read nil off it and take that for the player's answer.

Quests/Blizzard.lua's rule is that the switch owns the state and the file's job
is that the two ends agree. There is a third party here, because Questie's
setting has an owner who is not us, so questsTrackerTook records whether this
addon is the one holding the tracker off. It is saved rather than an upvalue
because Disable ends in ReloadUI and an upvalue would not survive the reload it
caused, and Core keeps it out of /wk reset for the reason it keeps the three
nameplate CVar notes. It buys two things: a tracker the player switched off in
Questie's own options is never claimed and never switched back on, and the two
ends agree at every login after the first, so Disable runs once rather than
once a session. Neither call is made in combat, because both build frames, and
work refused that way runs at PLAYER_REGEN_ENABLED.

Section 84-questie-tracker counts the calls and the reloads, so a file that
threw the switch on every login fails on the reload count rather than on
anything a reader would have to notice. The options window's prose budget goes
to 31,000 with the paragraph the house style asks for beside it: the tick box,
its hint and its reading cost 185 and take the window to 30,808.

Item 91 draws a tracker of this addon's own. It wants ns.QuestTrackerOff.Wanted
rather than the setting, and the switch it should follow is this one and not
questsHideBlizz.
The client answers what is in your quest log and nothing at all about what is
not, so the one question a levelling character asks all day had no reader here.
Questie has both halves already: AvailableQuests carries every quest you could
accept keyed on the npc holding it, and QuestieDB knows where that npc stands.
Nothing in this addon had ever asked it either.

ns.QuestNear in src/Quests/Near.lua is that reader. It answers an unordered
array of npcs near you with a quest that has never been in your log, each entry
carrying the npc id, Questie's name for him, the lowest quest id he is offering
and how many, the area and coordinates of the spawn nearest you, and whole
yards to it. Anything at or past ELSEWHERE is dropped at the read, and that
constant is handed out of Where.lua rather than spelled a second time. The walk
takes twenty five npcs per tick off ns.UI.Ticker under the Perf slot "near" and
finishes a pass in about four seconds. It idles while nobody has read the
answer for half a minute, while Questie has nothing to say, and while
ns.QuestHere says you are inside an instance, where Questie's own distance
function has already added half a million yards to everything.

Three things a later item wants. Near.List() hands back the held array and not
a copy, and the pass after next writes into it, so read it out again rather
than keeping it. Where.Nearest still has exactly one caller: your own log is
not read here, so the two sources item 97 described are one. Questie's field
reads are type checked at the read because ns.Questie covers calls and nothing
else, and QuestieDB:GetNPC is a colon method, which the harness fixture
reproduces by taking a self it ignores.
mdoorn manually merged commit 148188259b into master 2026-08-05 15:54:23 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
northgale/warriorkit!53
No description provided.