QuestLines Overview
QuestLines is a Hytale server plugin that ties NPC dialogue to a quest system. Players interact with NPCs to trigger branching dialogue — each response can have requirements that control visibility and actions that drive quest state forward.
How It Works
When a player interacts with an NPC, QuestLines looks up all quests linked to that NPC and walks through their pages in order. The first page whose requirements all pass is displayed as a dialogue screen. The player sees the NPC's text and response buttons. Choosing a response executes its actions — updating quest state, giving items, running commands, or navigating to another page.
Control when a page or response is visible. Quest state, tags, kill counts, items, cooldowns, and more.
Effects triggered by a response: start quests, give items, run commands, spawn NPCs, navigate pages.
Link quests to NPCs, use wand mode, and configure talk tracking.
Full worked examples: kill quests, item turn-ins, branching dialogue, cooldown rewards, timed quests.
Core Concepts
Quest
A Quest is the top-level container. It has a title, description, and an ordered list of page IDs. When a player interacts with an NPC, the quest's pages are evaluated in the order they appear in this list.
Page
A Page represents one screen of dialogue. It has its own requirements (which must pass
for the page to be selected), NPC display name, dialogue text, optional load actions,
and any number of responses. Load actions run automatically whenever the
page is shown — on initial NPC interaction and on any page: navigation —
regardless of which response the player clicks.
Pages also support an optional JournalText field that overrides the
dialogue text shown in the Quest Journal — useful when the in-world dialogue is spoken
by an NPC but you want the journal entry written from the player's perspective. When
blank, the Dialog field is used in the journal.
Response
A Response is one button on a dialogue screen. It has display text, its own requirements (controlling whether the button appears), and a list of actions executed when clicked.
PlayerData
Each player has a persistent data record storing: started quests, completed quests,
custom tags, named tracker progress counters, and timestamps for cooldown checks.
This data is stored in players.json and managed automatically.
Data Model
All configuration is stored as JSON files on the server. Below is the structure of each config type.
Quest File — quests/<id>.json
{
"Title": "The Lost Sword",
"Description": "Help the blacksmith retrieve his stolen blade.",
"Requirements": [],
"Actions": [],
"Pages": [
"lost_sword_intro",
"lost_sword_progress",
"lost_sword_turnin",
"lost_sword_complete"
]
}
Page (in quest JSON under PageData)
{
"Id": "lost_sword_intro",
"Title": "A Favour to Ask",
"Name": "Aldric the Smith", // NPC display name on this page
"Requirements": ["questNotStarted:lost_sword"],
"LoadActions": [], // actions run automatically when this page is shown
"Objectives": [], // requirement strings shown as pass/fail objectives in HUD + journal
"Dialog": "Ah, {username}! My sword was stolen by goblins. Will you help?",
"JournalText": "", // optional: overrides Dialog in the Quest Journal (e.g. a player-perspective entry)
"Portrait": "", // optional: per-page portrait (filename or resource-pack path)
"SecondaryPortrait": "", // optional: second portrait on opposite side
"Responses": [
{ "Text": "I'll find it for you.", "Requirements": [], "Actions": ["startquest:lost_sword", "track:goblin_kills:kill:Goblin*"] },
{ "Text": "Not right now.", "Requirements": [], "Actions": [] }
]
}
NPC Config — npcs.json
{
"Npcs": {
"citizen_42": "lost_sword,daily_reward",
"citizen_07": "goblin_king"
}
}
players.json — auto-managed
{
"Players": {
"player-uuid-here": {
"CompletedQuests": ["tutorial"],
"StartedQuests": ["lost_sword"],
"Tags": ["vip"],
"Progress": { "goblin_kills": 3 },
"Timestamps": { "daily_key": 1735000000000 },
"Variables": { "score": 7 },
"TrackedQuests": ["lost_sword"],
"PreferredLanguage": "en-US",
"AutoTrackQuests": true
}
}
}
Page Resolution Algorithm
Understanding how QuestLines selects a page is essential for authoring correct quest flows. The algorithm runs every time a player interacts with an NPC:
-
Look up quests for this NPC.
The NPC's citizen ID is looked up in
npcs.json. The comma-separated list of quest IDs is the evaluation order for that NPC. -
For each quest, iterate its Pages list.
Pages are evaluated in the order they appear in the Quest's
Pagesarray. -
Check this page's Requirements.
All requirements must pass. If any fails, skip to the next page.
-
Show the first matching page.
As soon as a page passes all requirements, that dialogue is opened. No further pages are evaluated.
-
If no page matches, no dialogue opens.
The player's interaction with the NPC silently does nothing.
Put your most specific pages first. A completed-state page must
come before an in-progress page, which must come before the intro page.
If the intro page (with questNotStarted) were listed last, it would
never match once the quest was started because the in-progress page would match first.
See Quest Patterns for full worked examples.
Admin Commands
| Command | Description |
|---|---|
| /ql create | Open the quest creation wizard to scaffold a new quest and its pages. Item-ID fields include a searchable dropdown of every registered item. |
| /ql quest (aliases: /ql ui, /ql q) | Open the full QuestLines editor UI to browse and edit quests, pages, NPCs, and players. Includes a Translate Quest button that fills missing locale strings via the configured translation backend. The Users tab lists every known player sorted by online-then-last-seen, with an [O] tag marking currently-online players. |
| /ql npc | Open the NPC management panel. |
| /ql wand <questId> | Enter wand mode. The next NPC you punch will be linked to the specified quest. |
| /ql wandcancel | Cancel an active wand mode assignment. |
| /ql reload | Reload all config files from disk without restarting the server. Quests are validated as part of the reload; problems are reported to console but never block the reload. |
| /ql validate | Run the quest validator on demand without reloading. Mirrors the checks performed at startup and on /ql reload; output is logged to console. |
| /ql achievement <list|grant|revoke|reset|reload> | Manage player achievement state. list [player] shows every achievement and its unlock state; grant <player> <id> [stage] force-unlocks a stage and runs its UnlockActions; revoke/reset <player> <id> removes every stage of the achievement; reload re-reads achievements/ from disk. See Achievements below. |
| /ql info | Show plugin version and dependency information. |
| /ql help | List all available /ql commands. |
Use /ql wand <questId> as the fastest way to link quests to NPCs
in-world. See NPC Setup for full details on wand mode
and alternative linking methods.
Player Commands
| Command | Description |
|---|---|
| /journal (aliases: /j, /log) | Open the Quest Journal. Shows active and completed quests with the last dialogue seen for each, plus objectives for the current page. Players can toggle quest tracking to show objectives on the HUD. The journal remembers the last tab and quest the player was viewing and reopens to it next time; if the remembered entry no longer exists (quest removed, completed), it opens to the first available quest instead. |
| /qlconfig | Open the personal quest settings UI to configure preferred language, auto-track HUD behaviour, HUD anchor corner, and exact HUD pixel offsets. Language changes take effect immediately. Requires the questlines.config.use permission. |
| /achievements (alias: /ach) | Open the Achievements UI to browse server-defined milestones, view unlock progress per stage, and see total points earned. Requires the questlines.achievements.use permission. |
Permissions
QuestLines uses the following permission nodes. Admin commands require
questlines.admin; it defaults to false (op-only).
Player commands have their own nodes that default to true.
| Permission | Default | Description |
|---|---|---|
| questlines.admin | false | Required for all /ql admin commands (quest editor, NPC setup, wand mode, reload, etc.). |
| questlines.journal.use | true | Required to open the Quest Journal via /journal. |
| questlines.config.use | true | Required to open the player settings UI via /qlconfig. |
| questlines.achievements.use | true | Required to open the Achievements UI via /achievements. |
QuestLines ships with built-in translations for English (en-US), Spanish (es-ES),
German (de-DE), French (fr-FR), Portuguese (pt-BR), and Russian (ru-RU). Players
can choose their preferred language via /qlconfig. Admins can add or
override translations by placing .lang files in
{pluginDataDir}/lang/{locale}.lang. The server default locale is set
in the plugin config; individual players override it per-player.
Quest text itself (name, description, page dialogue, responses, objectives) is stored as a locale->string map. Authors can translate a quest in one click with the Translate Quest button in the editor, which fills missing locales via a pluggable HTTP backend — see Translation Backend. Bare-string legacy configs remain valid and are preserved byte-identical on disk until a translation is added.
Configuration
Global plugin settings live in mods/QuestLines/QuestLines.conf — a simple
key=value file that is created with sensible defaults on first startup and
re-saved with any new keys after a plugin upgrade. Reload with /ql reload.
HUD & Dialogue Defaults
| Key | Default | Description |
|---|---|---|
default_language | en-US | Language assigned to newly-created player records. Existing players keep their /qlconfig selection. Supported: en-US, de-DE, es-ES, fr-FR, pt-BR, ru-RU. |
default_hud_position | TopRight | Starting HUD corner for new players. Options: TopRight, TopLeft, BottomRight, BottomLeft. |
default_hud_offset_x | 10 | Default horizontal offset (pixels) from the HUD anchor corner for new players. Players can fine-tune their own values via /qlconfig. |
default_hud_offset_y | 10 | Default vertical offset (pixels) from the HUD anchor corner for new players. Players can fine-tune their own values via /qlconfig. |
hud_refresh_rate | 500 ms | How often the Quest Goal HUD re-evaluates and redraws per player. |
damage_tracking_time | 5000 ms | Window during which damage contributors receive kill credit. |
dialog_width_full / _mid / _compact | 20 / 350 / 570 | Horizontal anchor offsets for each dialogue width option. |
hit_to_talk_default / f_to_talk_default | true | Global fallbacks for whether hitting or pressing F on an NPC opens dialogue. Overridable per-NPC in npcs.json. |
debug_toasts | false | Show toast notifications on the right of the screen when tracking events fire (useful when authoring quests). |
Storage Backend
QuestLines stores per-player state in a file by default, but can be switched to a database
for production deployments. The choice is transparent to quests and actions — the same
PlayerData model is used either way.
| Key | Default | Description |
|---|---|---|
storage_backend | file | One of file (JSON at players.json), sqlite, mysql, or mariadb. |
db_url | (empty) | Full JDBC URL override. When set, db_host/_port/_name are ignored. For SQLite, a relative path here becomes the database filename. |
db_host / db_port | localhost / 3306 | MySQL/MariaDB host and port. |
db_name / db_user / db_password | questlines / (empty) / (empty) | MySQL/MariaDB schema and credentials. |
db_pool_size | 5 | HikariCP maximum pool size. SQLite is capped at 1 regardless. |
db_table_prefix | ql_ | Prefix applied to every table name (e.g. ql_player_data). |
There is no automatic migration between file and database backends.
Stop the server, copy players.json aside if you need to seed, switch
storage_backend, and restart. Existing player data in the previous
backend is not read by the new one.
Translation Backend
The in-editor Translate Quest button uses a pluggable HTTP backend to
auto-fill missing locale strings on a quest. Source language defaults to
default_language; the button fills the remaining supported locales in one
pass. Existing translations are never overwritten.
| Key | Default | Description |
|---|---|---|
translation_provider | mymemory | One of mymemory (free, zero-setup), libretranslate (self-host or hosted), or none (disables the Translate button). |
translation_endpoint | (empty) | Optional URL override for the provider. Blank uses the provider's default public endpoint. |
translation_email | (empty) | Optional contact email for MyMemory — raises the daily character quota. |
translation_api_key | (empty) | Optional API key for LibreTranslate instances that require authentication. |
Achievements
Achievements are server-defined milestones unlocked when a player’s state
satisfies a list of requirement criteria. Each achievement is stored in
QuestLines/achievements/<id>.json and is made up of one or
more ordered stages. A single-stage achievement is just an
achievement with one stage; the editor and runtime treat all achievements
uniformly. Players open the Achievements UI with /achievements
(alias /ach); admins manage state with
/ql achievement.
When achievements are evaluated
The AchievementManager walks every loaded achievement and unlocks
any stage whose Criteria currently pass. Cascading stages can
unlock together on a single pass. Re-evaluation happens after:
- Tracking events (kill, break, place, craft, harvest, pickup, fishing) processed by
QuestManager. - Quest completion (the
questCompleted:action and the implicit completion that fires when a quest’s last page is reached). - Player join (after
PlayerReadyEvent) so quests-completed-while-offline show up at next login.
When a stage unlocks the manager fires that stage’s UnlockActions
through the normal ActionManager, so any action available to a
response (give items, grant titles, run commands, send chat, etc.) can be used
as an achievement reward.
Tracking-style criteria (kill:, break:, craft:,
fishing, etc.) used in achievement Criteria are stored in
private named trackers under the reserved __ach__ prefix.
They are completely independent of the tag-based progress counters quests
use, so a quest’s removeTag:tracking:kill:zombie never
rolls back the achievement’s lifetime kill count.
Achievement File — achievements/<id>.json
{
"Id": "goblin_slayer",
"Title": "Goblin Slayer",
"Description": "Slay goblins across the realm.",
"Category": "combat",
"Icon": "Sword_Iron",
"Hidden": false,
"Stages": [
{
"Title": "", // blank → falls back to "Goblin Slayer I"
"Points": 5,
"Rewards": "100 gold",
"Criteria": ["kill:Goblin*:10"],
"UnlockActions": ["economy:give:100", "chat:You earned a stage of Goblin Slayer!"]
},
{
"Points": 10,
"Rewards": "250 gold + Goblin Slayer title",
"Criteria": ["kill:Goblin*:50"],
"UnlockActions": ["economy:give:250", "giveTitle:combat:goblinslayer"]
}
]
}
| Field | Description |
|---|---|
Id | Unique achievement ID. Must NOT contain : — the unlock-time map keys use id:tier internally. |
Title / Description | Achievement-level localized text. Falls back to the per-stage equivalents when those are blank. |
Category | Free-form grouping string used by the Achievements UI to organize entries. |
Icon | Item ID shown next to the achievement in the UI. |
Hidden | When true the achievement is concealed in the UI until the first stage unlocks. |
Stages | Ordered array of one or more stages. Each stage carries its own Title, Description, Points, Rewards text, Criteria, and UnlockActions. Criteria accept any requirement; UnlockActions accept any action. |
Editing
Achievements have a dedicated ACHIEVEMENTS tab in
/ql quest. The tab supports full CRUD on both single-stage and
staged achievements; the editor distinguishes the achievement-level
Title/Description/Icon/Category/Hidden fields from the per-stage
Points/Rewards/Criteria/UnlockActions fields by switching between a
Base context and a Stage N context, shown in the
#AchievementContextLabel at the top of the editor.
Achievement Requirements
Quests can react to achievement progress via three requirement types (full details on the Requirements page):
achievement:<id>[:<minStage>]— player has unlocked the achievement (any stage) or at least the given tier.achievementcount:<n>— player has unlocked at least n distinct achievements.achievementpoints:<n>— player’s total achievement points are at least n.achievementstages:<id>:<n>— player has unlocked at least n stages of a specific achievement.
Integrations
QuestLines ships with opt-in integrations that activate automatically when a compatible plugin is detected. These are in addition to the optional requirement/action dependencies (VaultUnlocked, MMOSkillTree, RPGLeveling, OrbisGuard, HyFishing) documented on the Requirements and Actions pages.
Insight HUD
When the Insight HUD plugin is installed alongside QuestLines Core, the plugin registers contributors that surface live quest data in the Insight HUD target panel while the player is looking at an NPC:
- Quest availability — whether the NPC has a quest available, in progress, completed, or on cooldown for the viewing player.
- Active tracking — for any quest on the NPC that is currently tracked, the player's goal text / objective progress is displayed.
There is no configuration required. If Insight HUD is not installed, the integration is silently skipped at startup.
QL Claims
When QuestLines Claims is loaded, Core registers actions, requirements, and variables that bridge quest state to claim flags and region membership. See the Claims wiki page for the full list; all of them are only active when the Claims plugin is present.
Anglers’ Almanac
When Anglers’ Almanac (AA) is detected on startup, QuestLines
registers a bridge listener for AA’s catch and minigame events. Successful
catches from the AA minigame feed the existing
catchfish: and catchfishrarity: counters using the
caught item’s ID and rarity, so existing fishing quests work transparently
whether the player is using base HyFishing or AA. AA also exposes
legendary/new-discovery flags and a per-cast performance rating, which drive
a new family of requirements, trackers, and text variables — and a
separate counter for fish lost in the minigame:
- Requirements:
fishmiss:,catchfishlegendary:,catchfishnew:,catchfishperf:<rating>:. - Tracking tags / tracker types:
tracking:fishmiss:,tracking:catchfishlegendary,tracking:catchfishnew,tracking:catchfishperf:<rating>(plus the matchingtrack:id:<type>:[targets]forms). - Text variables:
{fishmissprogress:<fishItemId>},{catchlegendaryprogress},{catchnewprogress},{catchperfprogress:<rating>}.
Performance ratings are perfect, great, good,
fail, and nil. Biome and zone are not provided by AA, so
catchfishbiome: / catchfishzone: remain HyFishing-only.
The bridge is fully optional — if AA is not installed, Core never references
its classes.
Books and Papers
When the Books and Papers plugin is detected on startup, Core registers two action types and one requirement that route through Books-and-Papers state without forcing the dependency.
- Actions:
mail:itemId:qtypushes an item stack to the player’s mailbox in their current world;givebook:title:author:body(with the optionalgivebook:itemId:title:author:bodyform for letter items) gives the player a signed book whose pages come from body. Use\fto break pages and\nfor line breaks within a page. - Requirement:
hasmail— true while the player has at least one queued item in their mailbox.
All three are no-ops when Books and Papers is absent, and all direct references to the plugin’s types are isolated in
BooksAndPapersBridge so the QuestLines JAR loads cleanly without the dependency.