Skip to content

Quest + NPC Workflow

This guide walks you through creating a complete side quest with an NPC quest giver, from config files to ingame testing. By the end, you will have a working quest that a player can accept from an NPC, complete objectives, and turn in for rewards.

You will create:

  • A quest with objectives and rewards
  • A citizen (NPC) that appears in the world
  • An NPC quest profile that links the citizen to the quest with dialog
  • Localization entries for all player-visible text

Create or edit the file configs/kyuubisoft_questbook/custom/custom_quests.json and add your quest:

{
"quests": [
{
"id": "my_first_quest",
"type": "side",
"category": "exploration",
"icon_item": "Torch",
"name_key": "quest.name.my_first_quest",
"description_key": "quest.desc.my_first_quest",
"objectives": [
{
"id": "gather_wood",
"type": "collect_item",
"description_key": "quest.my_first_quest.obj.gather_wood.desc",
"target": "Log_Oak",
"amount": 10
}
],
"rewards": [
{
"type": "item",
"item": "Coin_Gold",
"amount": 5
},
{
"type": "xp",
"amount": 100
}
],
"prerequisites": []
}
]
}
FieldDescription
idUnique quest identifier. Use snake_case.
typeQuest type: main, side, daily, or weekly.
categoryDisplay category in the quest book.
icon_itemItem ID shown as the quest icon.
name_keyLocalization key for the quest name.
description_keyLocalization key for the quest description.
objectivesArray of objectives the player must complete.
rewardsArray of rewards given on completion.
prerequisitesArray of quest IDs that must be completed first. Leave empty for no requirements.
TypeDescriptionFields
collect_itemCollect a specific itemtarget, amount
kill_entityKill a specific entity typetarget, amount
talk_to_npcTalk to a specific citizentarget (citizenId)
visit_locationVisit coordinatestarget (x,y,z), radius
craft_itemCraft a specific itemtarget, amount

Create or edit configs/kyuubisoft_questbook/localization/custom_en-US.json:

{
"quest.name.my_first_quest": "The Lumberjack's Request",
"quest.desc.my_first_quest": "The local lumberjack needs help gathering oak logs. Collect 10 oak logs and return to him.",
"quest.my_first_quest.obj.gather_wood.desc": "Gather Oak Logs"
}

For German translations, create configs/kyuubisoft_questbook/localization/custom_de-DE.json:

{
"quest.name.my_first_quest": "Die Bitte des Holzfaellers",
"quest.desc.my_first_quest": "Der Holzfaeller braucht Hilfe beim Sammeln von Eichenstammen. Sammle 10 Eichenstamme und bringe sie ihm.",
"quest.my_first_quest.obj.gather_wood.desc": "Eichenstamme sammeln"
}
PatternUsage
quest.name.<questId>Quest display name
quest.desc.<questId>Quest description
quest.<questId>.obj.<objectiveId>.descObjective description

Create or edit configs/kyuubisoft_core/custom/custom_citizens.json and add your NPC:

{
"citizens": [
{
"id": "lumberjack_oak",
"name": "Oakley",
"entityType": "Human_Male",
"skin": "NPC_Lumberjack",
"position": {
"x": 150.5,
"y": 72.0,
"z": -230.5
},
"worldName": "world",
"lookAtPlayer": true,
"invulnerable": true
}
]
}
FieldDescription
idUnique citizen identifier. Used to link quest profiles.
nameDisplay name shown above the NPC’s head.
entityTypeThe entity model to use (e.g. Human_Male, Human_Female).
skinSkin identifier for the NPC’s appearance.
positionWorld coordinates where the NPC spawns.
worldNameThe world the NPC spawns in. Must match exactly.
lookAtPlayerIf true, the NPC faces nearby players.
invulnerableIf true, the NPC cannot be damaged.

:::tip Getting Coordinates Stand at the desired NPC location ingame and run /ksdev pos. This prints your current coordinates, which you can copy into the config. :::

Create a new file at configs/kyuubisoft_questbook/quest_npc_profiles/lumberjack_oak.json:

{
"citizenId": "lumberjack_oak",
"quests": ["my_first_quest"],
"dialogs": {
"my_first_quest": {
"offer": {
"text": "quest.npc.lumberjack_oak.offer",
"acceptButton": "Accept Quest",
"declineButton": "Maybe Later",
"action": "start_quest"
},
"active": {
"text": "quest.npc.lumberjack_oak.active"
},
"complete": {
"text": "quest.npc.lumberjack_oak.complete",
"action": "complete_quest"
}
}
}
}

Then add the dialog localization keys to your custom_en-US.json:

{
"quest.npc.lumberjack_oak.offer": "Hello there, traveler! I could use some help. Would you gather 10 oak logs for me? I'll pay you well!",
"quest.npc.lumberjack_oak.active": "Still gathering those logs? I need 10 oak logs total. Keep at it!",
"quest.npc.lumberjack_oak.complete": "Wonderful! You brought all the logs. Here's your reward, friend. Thank you!"
}
FieldDescription
citizenIdMust match the citizen’s id from citizens.json.
questsArray of quest IDs this NPC can give.
dialogsDialog configuration per quest, with offer, active, and complete phases.
actionstart_quest to accept, complete_quest to turn in.

Each quest-NPC dialog has three phases:

Shown when the player talks to the NPC and the quest is available (prerequisites met, not yet started).

  • text — The dialog message (localization key or direct text).
  • acceptButton — Label for the accept button.
  • declineButton — Label for the decline button.
  • action — Set to start_quest to start the quest on accept.

Shown when the player has the quest in progress.

  • text — A reminder or progress message.
  • No buttons needed (the dialog just closes on click).

Shown when the player has completed all objectives and talks to the NPC.

  • text — The completion/reward message.
  • action — Set to complete_quest to finalize and grant rewards.

For more complex conversations, you can use node-based dialogs with multiple choices:

{
"offer": {
"nodes": [
{
"id": "start",
"text": "quest.npc.lumberjack_oak.offer.start",
"options": [
{
"text": "Tell me more.",
"next": "details"
},
{
"text": "I'll do it!",
"action": "start_quest"
},
{
"text": "Not right now.",
"action": "close"
}
]
},
{
"id": "details",
"text": "quest.npc.lumberjack_oak.offer.details",
"options": [
{
"text": "Alright, I'll help.",
"action": "start_quest"
},
{
"text": "Maybe later.",
"action": "close"
}
]
}
]
}
}

You can use command macros in dialog text for advanced behavior:

MacroDescription
{cmd:ksquest accept <player> <questId>}Accept a quest via command
{cmd:ksquest complete <player> <questId>}Complete a quest via command
{player}Replaced with the player’s name

After creating all config files, test your quest ingame:

/ksadmin reload

This reloads all config files from disk without restarting the server.

/kscitizen respawn

Forces all citizens to despawn and respawn, picking up any new or changed citizen configs.

/ksquestadmin debugmode

Toggles a debug overlay showing quest states, objective progress, and trigger events in real time.

  1. Walk to your NPC and interact.
  2. Accept the quest from the dialog.
  3. Gather 10 oak logs.
  4. Return to the NPC and complete the quest.
/ksquestadmin reset <yourname> my_first_quest

Resets the quest so you can test it again from the beginning.

ProblemSolution
NPC does not appearCheck worldName, position, and run /kscitizen respawn
NPC has no dialogVerify citizenId in the quest profile matches the citizen config
Quest does not startCheck prerequisites, verify quest ID matches in profile
Objectives do not trackCheck objective type and target spelling
Rewards not givenVerify reward type and item IDs

All of the above steps can also be done visually using the Mod Editor at modeditor.kyuubisoft.com. The editor provides:

  • A quest builder with drag-and-drop objectives
  • An NPC configurator with position picker
  • Automatic localization key generation
  • Built-in validation
  • Export as ZIP or quest pack

See the Mod Editor Workflow guide for details.