Violet

Quester

On this page

Provides quest interaction and management functionality.

Functions

core.quester.get_quest_state(quest_id: number) -> number

Returns the quest state for the specified quest.

Quest States:

  • -1 - Not available
  • 0 - Available
  • 1 - In progress
  • 2 - Completed

core.quester.get_quest_progress(quest_id: number) -> table | nil

Returns a read-only view of a quest's completion requirements and your live progress, built from the client's loaded quest data. Returns nil if the quest has no completion demand (an unknown quest, or one with no 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".
  • 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 by the server 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).

lua
{ 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 "==" / ">=" / "<=":

lua
{ key = "bh3", need = 1, have = 0, cond = "==" }
-- "the record bh3 must equal 1; it's 0" — advance by playing the story, not by grinding.
Edge cases

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:

lua
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
end

Check whether a quest is ready to hand in (all items, kills, and conditions satisfied):

lua
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
end

Auto-complete a kill quest when it's done:

lua
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
end

core.quester.start_quest(npc_id: number, quest_id: number) -> boolean

Attempts to start the quest with the specified NPC. Check quest state first with get_quest_state().

Returns: true if the request was sent, false if the NPC is present on the current map but the player is out of interaction range.

Info

If the NPC isn't on the current map, the call still proceeds — this supports auto-complete flows that don't require a live NPC. Proximity is only enforced when the NPC is actually spawned.

No Auto-Teleport

Proximity is checked, not corrected. Position the character within range before calling — this function will not teleport you to the NPC.


core.quester.complete_quest(npc_id: number, quest_id: number) -> boolean

Attempts to complete the quest with the specified NPC. Check if completable first with can_complete_quest().

Returns: true if the request was sent, false if the NPC is present on the current map but the player is out of interaction range.

Info

If the NPC isn't on the current map, the call still proceeds — this supports auto-complete flows that don't require a live NPC. Proximity is only enforced when the NPC is actually spawned.

No Auto-Teleport

Proximity is checked, not corrected. Position the character within range before calling — this function will not teleport you to the NPC.


core.quester.can_complete_quest(npc_id: number, quest_id: number) -> boolean

Returns true if quest requirements are met and can be completed.

NPC Dialogue Selection

The quester uses a queue system for handling NPC dialogue choices and text inputs.

Advanced: on_dialog callback

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)

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()

Clears the entire NPC selection queue.