Quester
On this page
Provides quest interaction and management functionality.
Functions
core.quester.get_quest_state(quest_id: number) -> number
Available on GMS and CW
Returns the quest state for the specified quest.
Quest States:
-1- Not available0- Available1- In progress2- Completed
core.quester.get_quest_progress(quest_id: number) -> table | nil
Available on GMS and CW
Returns a read-only view of a quest's completion requirements and your live progress. Returns nil for an unknown quest or one with no completion requirements.
Returned table:
id(number) — the quest id.name(string) — the quest's display name.state(string) —"available","in_progress","complete", or"not_available".objective(string) —"kill","collect","reach","talk","condition", or"unknown".start_npc(number | nil) — the NPC that offers the quest.target_npc(number | nil) — the turn-in / talk NPC.target_map(number | nil) — the destination map (for"reach"steps).min_level/max_level(number | nil) — the level range required to accept the quest.items(table) — item-collection requirements, each{ id, need, have }.mobs(table) — kill requirements, each{ id, need, have, order }(explained below).conditions(table) — record-based requirements, each{ key, need, have, cond }(explained below).
How kill requirements work
Kill quests carry a dedicated mob list. Each entry is one monster: id is the monster id, need is the required kill count, have is your live count (updated on every kill), and order is the quest step the requirement belongs to (multi-step quests gate later entries behind earlier ones; single-step quests use 1 throughout).
{ id = 8220008, need = 200, have = 37, order = 1 }
-- "kill monster 8220008 two hundred times; you're at 37."How conditions work
Separately from kills, quests can require record conditions — scripted flags the game writes during cutscenes, events, or story progression. key names the record, need is the target, have is the current value, and cond is one of "==" / ">=" / "<=":
{ key = "bh3", need = 1, have = 0, cond = "==" }
-- "the record bh3 must equal 1; it's 0" — advance by playing the story, not by grinding.A handful of quests express kills as record conditions keyed by a monster id (a 6–8 digit numeric key) — the objective is still classified "kill" when that happens. A few event quests compare date stamps; those conditions carry a 14-digit timestamp in need.
Examples
Dump everything about a quest:
local q = core.quester.get_quest_progress(3301)
if q then
core.log(q.name .. " [" .. q.objective .. "]")
for _, it in ipairs(q.items) do core.log((" item %d: %d/%d"):format(it.id, it.have, it.need)) end
for _, m in ipairs(q.mobs) do core.log((" mob %d: %d/%d"):format(m.id, m.have, m.need)) end
for _, c in ipairs(q.conditions) do core.log((" %s %s %d (have %d)"):format(c.key, c.cond, c.need, c.have)) end
endCheck whether a quest is ready to hand in (all items, kills, and conditions satisfied):
local function is_ready(quest_id)
local q = core.quester.get_quest_progress(quest_id)
if not q then return false end
for _, it in ipairs(q.items) do
if it.have < it.need then return false end
end
for _, m in ipairs(q.mobs) do
if m.have < m.need then return false end
end
for _, c in ipairs(q.conditions) do
local ok = (c.cond == "==" and c.have == c.need)
or (c.cond == ">=" and c.have >= c.need)
or (c.cond == "<=" and c.have <= c.need)
if not ok then return false end
end
return true
endAuto-complete a kill quest when it's done:
if is_ready(quest_id) then
local q = core.quester.get_quest_progress(quest_id)
if q and q.target_npc then
core.quester.complete_quest(q.target_npc, quest_id)
end
endcore.quester.get_quest_info(quest_id: number) -> table | nil
Available on GMS and CW
Returns what a quest needs to be accepted and to be handed in, including which NPC offers it and which NPC takes it back, and where those NPCs and the monsters it asks for can be found. Returns nil for an unknown quest.
Returned table:
id(number),name(string)auto_start(boolean) — the quest starts without talking to its NPC.auto_complete(boolean) — the quest completes on its own once its requirements are met.start(table) — what accepting needs:npc(number | nil) — the NPC that offers the quest;npc_maps(table) — maps that NPC stands in.min_level/max_level(number | nil)quests(table) — prerequisite quests, each{ id, state }wherestateis the state that quest must be in:"complete","in_progress", or"not_started"(the quest can't be taken once that one is started).quests_any(boolean) istruewhen one of them is enough.items(table) — items to hold, each{ id, need }.maps(table) — when not empty, the quest can only be accepted in one of these maps.
complete(table) — what handing in needs:npc(number | nil) — the NPC that takes the quest back;npc_maps(table) — maps that NPC stands in.items(table) —{ id, need }.mobs(table) —{ id, need, maps }, wheremapslists maps the monster appears in.maps(table) — maps to visit.conditions(table) — record rules, each{ key, need, cond }(see How conditions work).
npc_maps and maps are empty when the game data doesn't place that NPC or monster anywhere, such as ones that only appear during a quest.
local info = core.quester.get_quest_info(2952)
if info then
core.log(("accept at NPC %s, hand in to NPC %s"):format(tostring(info.start.npc), tostring(info.complete.npc)))
endcore.quester.can_start_quest(quest_id: number) -> boolean, table
Available on GMS and CW
Returns whether the character can accept the quest right now, and what is missing. Returns nil while the character isn't in a map.
The second value lists every unmet requirement; it is empty when the first value is true. See What's missing for the entries.
local ok, missing = core.quester.can_start_quest(2951)
for _, m in ipairs(missing) do
if m.type == "quest" then core.log(("finish quest %d first"):format(m.id)) end
if m.type == "level" and m.need then core.log(("needs level %d"):format(m.need)) end
endcore.quester.start_quest(npc_id: number, quest_id: number) -> boolean
Available on GMS and CW
Attempts to start the quest with the specified NPC. Check quest state first with get_quest_state().
If the NPC is farther away than one screen, the character is teleported to it first.
Returns: true if the request was sent. false if the NPC isn't on the current map (quests that start without an NPC are exempt), or if the game could not act on the quest.
core.quester.complete_quest(npc_id: number, quest_id: number) -> boolean
Available on GMS and CW
Attempts to complete the quest with the specified NPC. Check if completable first with can_complete_quest().
If the NPC is farther away than one screen, the character is teleported to it first.
Returns: true if the request was sent. false if the NPC isn't on the current map (quests that complete without an NPC are exempt), or if the game could not act on the quest.
core.quester.can_complete_quest(npc_id: number, quest_id: number) -> boolean, table
Available on GMS and CW
Returns true if the quest's requirements are met and it can be handed in to npc_id, and a list of what is missing (empty when the first value is true). Returns nil while the character isn't in a map.
When every requirement is met but npc_id is not the NPC that takes the quest back, the list holds one npc entry naming the right one.
core.quester.get_quest_record(quest_id: number, key?: string) -> string | nil
Available on GMS and CW
Reads a quest's saved progress values: the named values quests keep, such as step counters, clear flags and world states. With key, returns that value as a string, or nil when it isn't set. Without key, returns a table of every named value plus the whole record as a second string value.
Some records don't belong to a visible quest and hold world or event state. For example, record 63090 holds the Afterlands' day/night (DnN, "1" at night), raining and lightning values.
local night = core.quester.get_quest_record(63090, "DnN") == "1"
-- hit the Solar Pulley, then check the value flippedcore.quester.get_quest_timers() -> table
Available on GMS and CW
Returns the quest time limits counting down on screen, each { quest_id, remaining, total } with times in seconds. Empty when no timed quest is running or the character isn't in a map.
for _, t in ipairs(core.quester.get_quest_timers()) do
core.log(("quest %d: %.0fs left"):format(t.quest_id, t.remaining))
endWhat's missing
can_start_quest and can_complete_quest return one entry per unmet requirement. Every entry has a type:
type | Fields | Meaning |
|---|---|---|
"level" | need, have | Character level is below need |
"level" | max, have | Character level is above max |
"job" | – | The quest isn't offered to this job |
"quest" | id, need, have | Quest id must be in state need; have is its state now ("not_started", "in_progress" or "complete") |
"item" | id, need, have | Hold need of item id; have is the count held |
"mob" | id, need, have | Kill need of monster id; have is the live count |
"map" | id | Accept in (start) or visit (complete) map id |
"condition" | key, need, have, cond | A record rule (see How conditions work) |
"npc" | id | Hand in to NPC id instead |
"state" | id, need, have | The quest itself is in the wrong state: it must be "not_started" to accept it and "in_progress" to hand it in; have is its state now |
"blocked" | id | The game has this quest turned off |
"other" | code | Another requirement the game reports |
A quest that is already in progress or complete, or turned off, gets a single state or blocked entry. For a quest with several steps, can_complete_quest lists only what the current step still needs.
Auto Quest
Auto Quest accepts, travels to, fights for and hands in every quest between two quest IDs, in the order their prerequisites allow. It is the same run as Autos → Auto Quest in the web menu, so starting it here fills in that panel.
While a run is active it:
- travels between maps on its own and approaches NPCs before talking to them;
- answers NPC dialogs and blue-box prompts the way Auto NPC and Auto Blue Boxes do, even if those are switched off;
- turns on Kami (with Kami Loot) and Auto Attack, using your settings for them, while a quest needs kills or drops, as long as Fight for Quests is on in the panel; outside of that it keeps them off;
- skips quests your character can never take (another job, a finished repeatable, an event that ended);
- pauses during evasion, runes, lie detectors, conversations, cutscenes and while the character is dead or not in a map.
A run stops on its own when every quest it can do is complete, or when a quest needs something it can't provide, such as story progress, an item with no known source, or a higher level. get_line_status() then tells you why.
core.quester.run_line(start_id: number, end_id: number) -> boolean, string?
Available on GMS and CW
Starts an Auto Quest run over every quest from start_id to end_id (inclusive; the order of the two doesn't matter; at most 5000 IDs). Replaces any run already going.
Returns: true when the run started. false and a reason when it didn't.
core.quester.stop_line()
Available on GMS and CW
Stops the current Auto Quest run. Does nothing when no run is active.
core.quester.get_line_status() -> table
Available on GMS and CW
Returns the current or last Auto Quest run.
| Field | Type | Description |
|---|---|---|
running | boolean | A run is active |
step | string | "planning", "accepting", "traveling", "hunting", "visiting", "turning_in", "waiting", "finished", "stopped" or "idle" |
quest_id | number or nil | Quest being worked on |
quest_name | string or nil | Its name |
detail | string or nil | What the run is doing for that quest right now |
done | number | Quests in the line already complete |
total | number | Quests found in the line |
stop_reason | string or nil | Why the last run ended, once it has |
local watching = core.quester.run_line(2950, 2960)
function on_tick(stage)
if not watching then return end
local s = core.quester.get_line_status()
if not s.running then
watching = false
core.log("Auto Quest ended: " .. tostring(s.stop_reason))
end
endNPC Dialogue Selection
The quester uses a queue system for handling NPC dialogue choices and text inputs.
For full dialog control (reading dialog text, inspecting choices, conditionally responding), use the on_dialog callback instead. It fires before the queue system and lets scripts intercept, respond to, or block dialogs dynamically.
core.quester.queue_npc_selection(selection: string)
Available on GMS and CW
Enqueues a dialogue option substring. When the NPC dialogue appears with auto NPC enabled, the option containing this substring will be selected. For text input prompts, the queued string will be used as the answer.
core.quester.clear_npc_selection()
Available on GMS and CW
Clears the entire NPC selection queue.