Overview

Objectives is an optional JSON array on each Page. Each entry is either a requirement string (same format as the Requirements list) or a plain-text text: entry. When the list is non-empty, the Quest Goal HUD and Journal detail panel render each entry as its own line with a live pass/fail icon and, where applicable, a progress counter.

// Rendered HUD / journal output
✓ Kill zombie: 5/5
· Break oak_log: 2/10
· Have iron_sword x1

The pass indicator is (green checkmark) when the requirement passes for the player and · (grey dot) when it does not. Requirements that carry a tracked progress count (kills, breaks, places, talk, travel, fish, crafts, harvest, named trackers) show current/required after the label. Requirements with no natural counter (e.g. item:, questCompleted:) show only the label.

💡
Objectives vs Requirements

Objectives are display only — they do not block page navigation or response selection. If you also want to gate the page on the same conditions, copy the strings into the page's Requirements list or the relevant response's Requirements.

Which Page Is Shown

The HUD and journal always show objectives for the player's current page, which is determined by the standard page resolution rules: the engine walks the quest's Pages list in order and picks the first page whose Requirements all pass for that player. That page's Objectives are what appear on screen.

As the player completes work and their requirement state changes, page resolution advances automatically and the displayed objectives switch to the next page — no additional wiring is needed. Structuring a quest as a chain of pages each with their own Objectives is the idiomatic way to build a multi-step goal.

Backwards Compatibility

Objectives defaults to an empty list if the JSON field is missing. Pages without objectives simply render nothing in the HUD goal area for that entry.

Label Override (::)

Raw IDs are rarely player-friendly — item:*Ore_Copper*:10 would render as "Have *Ore_Copper* x10" by default. Append a ::label suffix to replace the target identifier with any text you want, while keeping the surrounding wording and progress counter intact:

"Objectives": [
  "item:*Ore_Copper*:10::Copper Ore",
  "track:goblin_kills:10::Goblin (any type)",
  "cooldown:daily_reward:86400::Daily stipend ready"
]

The :: separator is parsed only on objective strings — not on ordinary page or response requirements. The label text supports icon tags (e.g. {icon:Ore_Copper}) and TextFormatter variables, so you can mix coloured text, icons, and dynamic values into the displayed label.

Icon Override (::icon:)

Every objective row leads with a 16×16 bullet icon. When you don't supply one, the HUD and journal pick a sensible default based on the requirement type — a sword for kill, a pickaxe for break, a sickle for harvest, leather gloves for place, a hammer for craft, the HyFishing rod for catchfish*, the HyFishing fish bag for releasefish, and so on. For item:/questitem: the concrete item ID is used directly (wildcards fall back to a backpack). For track: the tracker's own type drives the default.

Append ::icon:incompleteId or ::icon:incompleteId|completeId to override the default:

  • One value sets the icon shown while the objective is not yet satisfied. The completed state falls back to the usual checkmark.
  • Two values (separated by |) swap both icons: the first is the incomplete-state icon, the second replaces the checkmark when the objective passes.
"Objectives": [
  "track:goblin_kills:10::icon:Weapon_Sword_Bone::Goblin hunt",
  "item:Ore_Copper:20::icon:Ore_Copper|Ingredient_Bar_Copper::Smelting prep"
]

::icon: and ::label segments may appear in any order and are independent — either, both, or neither can be present.

Plain Text Objectives (text:)

An objective string that begins with text: is rendered as a plain informational line — no pass/fail icon, no progress counter, no evaluation. Everything after the text: prefix is passed through the TextFormatter and drawn directly as the objective row. Use this to add flavour, hints, countdown timers, or context that doesn't correspond to a single requirement.

"Objectives": [
  "text:Meet the courier at the north gate before sundown.",
  "text:Time remaining: {timeleft:courier_timer}::icon:Tool_Map",
  "track:courier_deliveries:1::Deliver the package"
]

Supported features

  • TextFormatter variables — anything documented in text-variables.md works here, including player vars ({playerName}), tracker counts ({track:id}), timed values ({timeleft:id}, {cooldown:id}), and colour/style tags.
  • Icon tags{icon:ItemId} renders the matching in-game item icon inline with the text.
  • Multi-line text — in the HUD, a single text: entry can span multiple lines by embedding \n or a real newline. Each line is rendered as its own row.
  • Leading icon — append ::icon:ItemId to draw a 16×16 ItemIcon bullet in front of the text. text: entries never "complete", so any second ID after | is ignored and there is no default — without ::icon: the line renders flush-left with no bullet, unchanged from the pre-existing layout.
  • Timer tick — when a text: objective contains {timeleft:} or {cooldown:}, the HUD automatically re-renders on every second so the displayed time updates live.
💡
Mixing text: with real objectives

Freely mix text: entries with requirement-based objectives in the same list. A common pattern is to put a one-line description at the top followed by the tracked requirements beneath it — the text line provides narrative context and the requirement lines show progress.

No evaluation, no gating

Because text: entries are not requirements, they are never evaluated for pass/fail and they are ignored by the quest engine when deciding which page is active. They are a pure display device.

Contextual Requirements

A small number of requirements only make sense during an active NPC interaction — for example npcname:, which tests which NPC opened the dialogue. The HUD and journal do not have an active NPC context, so contextual requirements are skipped when used as objectives and always render as unchecked.

This keeps them from misbehaving in the goal display, but it also means contextual requirements are effectively useless as objectives. Use them only on Requirements and Responses.

Full JSON Example

A single page that combines a narrative text: hint, a labelled tracker objective, and an item check — plus the matching page Requirements that actually gate the page:

{
  "Id":           "lumber_run_active",
  "Requirements": [
    "questStarted:lumber_run",
    "notTag:lumber_run_complete"
  ],
  "Objectives":   [
    "text:The foreman wants oak logs and a sturdy axe.",
    "track:oak_broken:10::Oak logs chopped",
    "item:iron_axe:1::Iron Axe in inventory"
  ],
  "Dialog":      "Come back when you have the wood and the axe."
}

See Quest Patterns for more complete examples combining Objectives with trackers, timed quests, daily cooldowns, and multi-step ritual flows.