Objectives & progression

Intermediate Last updated Jul 9, 2026

On this page

Everything between “accept” and “turn in” is described here: how objectives are credited, how conditions gate content, how effects mutate state, and how quests link into chains.

Objectives

A trackable goal lives inside a step. The step uses a generic id (Goal, Deliver), while the concrete kind lives in objective.type, so you can change the kind without re-keying anything:

- id: Goal
  objective:
    type: Kill
    target: MyGame.Enemy.Wolf         # GameplayTag, MUST be registered
    count: 3
    location: MyGame.Area.Farm        # optional area gate
    desc: "Kill the wolves near the farm"
  next: Deliver
TypeMeaningHow it’s credited
Killkill N tagged enemiesyour death code reports a Kill event
Collectgather N tagged itemsyour pickup code reports a Collect event
Reachreach a tagged placea Reach event, or CompleteObjective from a trigger
TalkTotalk to someoneautomatic: starting a conversation credits it
Deliverreport back to the giversatisfied by the turn-in act itself
Customanything elseyour game reports Custom + your own tag

Two notable conveniences:

  • TalkTo targets a speaker key, not a tag (target: Mary, the same key used in speakers: and the Cast tab). Any conversation with that NPC credits the objective for the talking player only, and only while the objective is active, so talking to the NPC before accepting the quest correctly credits nothing.
  • Deliver never blocks the turn-in. It exists so the tracker shows a “report back” line, and it is auto-ticked when the quest is turned in.

Crediting from your game is one call (Blueprint or C++), server-authoritative and idempotent per EventSourceId:

Comp->ReportObjectiveEvent(Instigator, EQuestwrightObjectiveEventType::Kill,
                           MatchTags, LocationTags, EventSourceId);

Tag matching is hierarchical: an objective with target: Enemy.Wolf is credited by an event tagged Enemy.Wolf.Alpha, so there is no need to list every subtype.

Approaching a quest giver: the interact prompt. Talking credits TalkTo objectives automatically

GameplayTags: the one critical rule

Every target, location, choice require/grant, condition tag and effect grant_tag value is a GameplayTag that must already be registered in your project; otherwise it resolves to an invalid tag and is silently ignored. This is the #1 cause of “my objective never counts”. Register tags either way:

  1. Native C++ (preferred for shipped tags): UE_DEFINE_GAMEPLAY_TAG_COMMENT(...).
  2. Project Settings → Project → GameplayTags (writes Config/DefaultGameplayTags.ini).

Use a dotted hierarchy (MyGame.Enemy.Wolf, MyGame.Area.Farm) so hierarchical matching works for free. The only exception: a TalkTo target is a speaker key, not a tag.

Conditions

Conditions gate quest availability (available_when) and step entry (require). Atomic kinds:

FormMeaning
{ quest: <QuestId>, state: <state> }another quest is in a state (available, active, completed, failed, declined)
{ objective: <ObjId>, state: <state> }an objective is active / complete / failed / inactive
{ flag: <Name>, equals: <Value> }a per-playthrough quest variable equals a value
{ tag: <GameplayTag> }the player owns a gameplay tag
{ item: <ItemId>, min: <N> }inventory has ≥ N of an item (via your inventory provider)
{ attribute: <AttrId>, min: <N> }a stat is ≥ N (via your stat provider)

Composites: { all: [...] } (AND), { any: [...] } (OR), { not: <cond> }.

available_when:
  all:
    - { quest: Region.Intro, state: completed }
    - { not: { flag: Betrayed, equals: "true" } }

Effects (on_end:)

Applied when a scene exits:

on_end:
  quest: accept                     # accept | complete | decline | fail | turnin
  set: { RewardTier: Negotiated }   # write flags, read back by flag conditions
  grant_tag: Region.MetBran         # grant gameplay tag(s) to the player
  grant_quest: Region.FollowUp      # auto-accept follow-up quest(s)

A turn-in scene typically ends with on_end: { quest: turnin } plus end: completed.

Branching on player choices

To make the giver react to an earlier choice, set a flag when the choice happens, then author two turn-in steps: the flag-gated one first, the catch-all last. The giver routing picks the first turn-in whose require passes:

- id: Haggle
  scene: { ... }
  on_end: { quest: accept, set: { RewardTier: Negotiated } }

- id: DeliverHaggled                       # specific branch; keep it before the catch-all
  require:
    all:
      - { objective: Goal, state: complete }
      - { flag: RewardTier, equals: Negotiated }
  scene: { ... }
  on_end: { quest: turnin }
  end: completed

- id: Deliver                              # default branch, carries the tracked objective
  require: { objective: Goal, state: complete }
  objective: { type: Deliver, count: 1, desc: "Report back" }
  scene: { ... }
  on_end: { quest: turnin }
  end: completed

The shipped demo quest uses exactly this pattern (a haggle leads to a different pay-out scene).

Quest chains

A chain is just data; there is no separate asset:

  • Quest B’s available_when: { quest: A, state: completed } (prerequisite), and/or
  • Quest A’s on_end: { grant_quest: B } (auto-accept the follow-up).

priority orders multiple quests offered by one giver (higher first). The Studio’s New Chain UI writes these values for you by drag & drop; hand-authoring is equivalent. Validation catches chain cycles and dangling quest/objective references.

Barks

Ambient barks (barks:) play over the giver’s head on a randomized timer; idle barks (idle_barks:) play on interact when the giver has nothing left to offer. Both go through the same voice-over and face pipeline in the giver’s voice:

barks:
  interval: [8, 16]
  lines:
    - "Wolves howling again..."
    - { text: "After dark, nobody sets foot in the yard.", weight: 2 }

idle_barks:
  lines:
    - "Rest easy. The farm's safe, thanks to you."

Rewards

rewards:
  base:
    currency: 20
    xp: 50
    items:
      - { def: SmokedMeat, count: 1 }

currency, xp and items are recorded on turn-in; your game grants the actual goods through the inventory provider seam (see Runtime integration).