Skip to content

Delayed Quest Steps

Schedules can also unlock quest progress later. This is different from a personal delay timer: a schedule runs at a fixed server time for everyone. That makes it useful for quests where the world changes at a known time, such as "the bridge repair continues after the nightly maintenance".

In this tutorial, an engineer asks players to donate stone for a bridge. Players can donate during the day. At 18:00, a schedule unlocks the next step and the engineer starts accepting final inspection reports.

Requirements

It is helpful to understand tags, points, and schedules before starting this tutorial.

1. Creating the folder structure for the example quest🔗

Create a new quest package called "bridgeRepair".

The file structure should look like this:

  • bridgeRepair
    • package.yml
    • actions.yml
    • conditions.yml
    • items.yml
    • conversations
      • engineer.yml
Download the files for this tutorial

Instead of manually creating / filling the files, just download them using the command below:

/bq download BetonQuest/Quest-Tutorials refs/tags/v3.1.0 QuestPackages /Features/Schedules/Delayed-Quest-Steps/1-Setup /bridgeRepair overwrite

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
conversations:
  Engineer:
    quester: "Engineer"
    first: "intro"
    NPC_options:
      intro:
        text: "The bridge is almost ready, but I need stone donations before tonight's inspection."
        pointers: "askHow"
      explain:
        text: "Bring me cobblestone. At 18:00 I will close donations and inspect the bridge."
    player_options:
      askHow:
        text: "How can I help?"
        pointers: "explain"
1
2
3
4
5
npcs:
  EngineerNpc: "citizens 32" #(1)!

npc_conversations:
  EngineerNpc: "Engineer" #(2)!
  1. Replace 32 with the Citizens ID of your Engineer NPC.
  2. This links the NPC to the Engineer conversation.

2. Track donations before the scheduled unlock🔗

We store two kinds of progress:

  • The player's own donation tag, so they cannot donate repeatedly in this simple example.
  • A global point counter, so the server can track how much stone was donated in total.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
conversations:
  Engineer:
    quester: "Engineer"
    first: "inspectionReady,alreadyDonated,donateStone,missingStone,intro" #(1)!
    NPC_options:
      intro:
        text: "The bridge is almost ready, but I need stone donations before tonight's inspection."
        pointers: "askHow"
        conditions: "!bridgeInspectionOpen" #(2)!
      explain:
        text: "Bring me cobblestone. At 18:00 I will close donations and inspect the bridge."
      donateStone:
        text: "If you have 16 cobblestone, I can use it for the bridge supports."
        pointers: "donate"
        conditions: "!donatedBridgeStone,!bridgeInspectionOpen,hasBridgeStoneItems" #(3)!
      missingStone:
        text: "Bring me 16 cobblestone and I can use it for the bridge supports."
        conditions: "!donatedBridgeStone,!bridgeInspectionOpen,!hasBridgeStoneItems"
      alreadyDonated:
        text: "Your stone is already counted. Current bridge stock: %globalpoint.bridgeStone.amount%." #(4)!
        conditions: "donatedBridgeStone,!bridgeInspectionOpen"
      inspectionReady:
        text: "Donations are closed. The inspection is ready now."
        conditions: "bridgeInspectionOpen" #(5)!
    player_options:
      askHow:
        text: "How can I help?"
        pointers: "explain"
      donate:
        text: "Take this cobblestone."
        conditions: "hasBridgeStoneItems"
        actions: "donateBridgeStone" #(6)!
  1. The scheduled unlock option is checked first. Donation and missing-item responses are checked before the intro.
  2. The normal introduction is only available before inspection opens.
  3. This keeps the same player from donating twice and requires the 16 cobblestone before the donate option appears.
  4. The global point placeholder shows the shared server donation total.
  5. This option appears after the schedule adds the global tag.
  6. This player action is still protected by the item condition, then takes items and updates progress.
1
2
3
4
5
6
actions:
  donateBridgeStone: "folder takeBridgeStone,addBridgeStonePoint,addDonatedBridgeStone,notifyDonation" #(1)!
  takeBridgeStone: "take bridgeStone:16 abort" #(2)!
  addBridgeStonePoint: "globalpoint bridgeStone 16 action:add" #(3)!
  addDonatedBridgeStone: "tag add donatedBridgeStone" #(4)!
  notifyDonation: "notify &aYou donated 16 cobblestone. Server total: %globalpoint.bridgeStone.amount%. io:chat"
  1. The donation is split into small actions so each part is easy to reuse or change.
  2. Takes the required cobblestone from the player. abort prevents partial item removal if something changed.
  3. Adds to the server-wide donation counter.
  4. Stores that this player already donated.
1
2
3
4
conditions:
  donatedBridgeStone: "tag donatedBridgeStone" #(1)!
  hasBridgeStoneItems: "item bridgeStone:16" #(2)!
  bridgeInspectionOpen: "globaltag bridgeInspectionOpen" #(3)!
  1. A normal tag stores one player's personal donation state.
  2. Checks whether the player currently has 16 cobblestone to donate.
  3. A global tag stores the server-wide inspection phase.
1
2
items:
  bridgeStone: "simple cobblestone"

The global point category bridgeStone is shared by the entire server. The player tag donatedBridgeStone belongs to one player. This separation is important: schedules can change server-wide progress, while conversations still use player progress to decide what one player has already done.

Is the example not working?

Get the correct configs by running the following command.
⚠ This will overwrite any changes (including NPC ID's and locations) you have made to the example.
Linking NPCs to conversations is explained in the basics tutorial.

/bq download BetonQuest/Quest-Tutorials refs/tags/v3.1.0 QuestPackages /Features/Schedules/Delayed-Quest-Steps/2-DonationProgress /bridgeRepair overwrite

3. Unlock the next step at a fixed time🔗

Now add the schedule. At 18:00, the server sets a global tag called bridgeInspectionOpen. After that, the engineer uses a different conversation option.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
npcs:
  EngineerNpc: "citizens 32"

npc_conversations:
  EngineerNpc: "Engineer"

schedules:
  openBridgeInspection: #(1)!
    type: realtime-daily #(2)!
    time: '18:00' #(3)!
    actions: openBridgeInspection #(4)!
  1. This schedule controls the global unlock.
  2. The schedule runs once per day.
  3. At 18:00 server time, donations close and inspection opens.
  4. The action is executed player independent.
1
2
3
4
5
6
7
8
9
actions:
  donateBridgeStone: "folder takeBridgeStone,addBridgeStonePoint,addDonatedBridgeStone,notifyDonation"
  takeBridgeStone: "take bridgeStone:16 abort"
  addBridgeStonePoint: "globalpoint bridgeStone 16 action:add"
  addDonatedBridgeStone: "tag add donatedBridgeStone"
  notifyDonation: "notify &aYou donated 16 cobblestone. Server total: %globalpoint.bridgeStone.amount%. io:chat"
  openBridgeInspection: "folder addBridgeInspectionOpen,announceBridgeInspection" #(1)!
  addBridgeInspectionOpen: "globaltag add bridgeInspectionOpen" #(2)!
  announceBridgeInspection: "notifyall &eThe bridge inspection is now open. Talk to the Engineer. io:chat" #(3)!
  1. The schedule calls this folder so the unlock and announcement happen together.
  2. This global tag changes the NPC dialogue for everyone.
  3. notifyall tells online players that the new phase is available.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
conversations:
  Engineer:
    quester: "Engineer"
    first: "inspectionReady,alreadyDonated,donateStone,missingStone,intro" #(1)!
    NPC_options:
      intro:
        text: "The bridge is almost ready, but I need stone donations before tonight's inspection."
        pointers: "askHow"
        conditions: "!bridgeInspectionOpen"
      explain:
        text: "Bring me cobblestone. At 18:00 I will close donations and inspect the bridge."
      donateStone:
        text: "If you have 16 cobblestone, I can use it for the bridge supports."
        pointers: "donate"
        conditions: "!donatedBridgeStone,!bridgeInspectionOpen,hasBridgeStoneItems"
      missingStone:
        text: "Bring me 16 cobblestone and I can use it for the bridge supports."
        conditions: "!donatedBridgeStone,!bridgeInspectionOpen,!hasBridgeStoneItems"
      alreadyDonated:
        text: "Your stone is already counted. Current bridge stock: %globalpoint.bridgeStone.amount%."
        conditions: "donatedBridgeStone,!bridgeInspectionOpen"
      inspectionReady:
        text: "Donations are closed. We collected %globalpoint.bridgeStone.amount% cobblestone. Can you inspect the bridge?" #(2)!
        conditions: "bridgeInspectionOpen,!bridgeInspectionDone" #(3)!
        pointers: "inspect"
      inspected:
        text: "Thanks for checking the bridge. We can open it safely."
        conditions: "bridgeInspectionDone" #(4)!
    player_options:
      askHow:
        text: "How can I help?"
        pointers: "explain"
      donate:
        text: "Take this cobblestone."
        conditions: "hasBridgeStoneItems"
        actions: "donateBridgeStone"
      inspect:
        text: "The bridge looks stable."
        actions: "finishBridgeInspection" #(5)!
  1. inspectionReady stays first because it should win after the scheduled unlock.
  2. The NPC can read the final global donation total.
  3. The inspection can be done only after the schedule opened it and before this player completed it.
  4. This personal tag stores that the player already finished the inspection step.
  5. This action rewards the player and saves their inspection progress.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
actions:
  donateBridgeStone: "folder takeBridgeStone,addBridgeStonePoint,addDonatedBridgeStone,notifyDonation"
  takeBridgeStone: "take bridgeStone:16 abort"
  addBridgeStonePoint: "globalpoint bridgeStone 16 action:add"
  addDonatedBridgeStone: "tag add donatedBridgeStone"
  notifyDonation: "notify &aYou donated 16 cobblestone. Server total: %globalpoint.bridgeStone.amount%. io:chat"
  openBridgeInspection: "folder addBridgeInspectionOpen,announceBridgeInspection"
  addBridgeInspectionOpen: "globaltag add bridgeInspectionOpen"
  announceBridgeInspection: "notifyall &eThe bridge inspection is now open. Talk to the Engineer. io:chat"
  finishBridgeInspection: "folder addBridgeInspectionDone,rewardInspector" #(1)!
  addBridgeInspectionDone: "tag add bridgeInspectionDone" #(2)!
  rewardInspector: "give inspectionReward:2" #(3)!
  1. The finish folder groups progress and reward.
  2. This is a player tag because each player can finish the inspection separately.
  3. Gives the reward item defined in items.yml.
1
2
3
4
5
conditions:
  donatedBridgeStone: "tag donatedBridgeStone"
  hasBridgeStoneItems: "item bridgeStone:16"
  bridgeInspectionOpen: "globaltag bridgeInspectionOpen"
  bridgeInspectionDone: "tag bridgeInspectionDone" #(1)!
  1. This condition checks the player's personal inspection completion.
1
2
3
items:
  bridgeStone: "simple cobblestone"
  inspectionReward: "simple emerald" #(1)!
  1. The reward action gives two of this item with give inspectionReward:2.

This pattern is useful when a quest step should unlock at a known time, not after an individual player's timer. If you need a personal timer like "24 hours after this player finished", use a delay objective instead.

Download this part of the tutorial

Enter this command in the chat to download this part of the tutorial:

/bq download BetonQuest/Quest-Tutorials refs/tags/v3.1.0 QuestPackages /Features/Schedules/Delayed-Quest-Steps/3-FullExample /bridgeRepair overwrite