Guides

Questing & Storylines with open-source tools

This guide outlines the technical implementation of a persistent, branching quest system within a MUD environment. It focuses on the transition from static fetch-quests to state-driven narrative arcs that utilize player variables and scriptable triggers to manage complexity without bloating area files.

4-6 hours of development time plus testing6 steps
Questing & Storylines with open-source tools illustration
Placeholder illustration shown while custom artwork is being produced.
1

Define the Quest State Schema

Instead of hardcoding quest progress into room descriptions, establish a dedicated variable naming convention (e.g., 'q_dragon_slayer_state'). Use integer values to represent progress nodes: 0 for unstarted, 10 for accepted, 20 for mid-branch A, 21 for mid-branch B, and 100 for completed. This allows for rapid querying in scripts.

quest_handler.py
def get_quest_state(player, quest_id):
    return player.db.quest_data.get(quest_id, 0)

def set_quest_state(player, quest_id, state_value):
    player.db.quest_data[quest_id] = state_value

⚠ Common Pitfalls

  • Using strings for states increases memory overhead and risk of typos.
  • Failing to initialize the variable can cause script crashes on first-time players.
2

Implement Dialogue Tree Triggers

Map NPC responses to specific quest states. Use a 'switch' or 'if-else' block within the NPC's 'greet' or 'speech' trigger. Ensure that the NPC checks the player's quest variable before displaying text to prevent players from seeing spoilers or repeating completed content.

npc_script.lua
if quest_state == 0 then
  say("I have a task for you, traveler.")
elseif quest_state == 10 then
  say("Have you found the relic yet?")
elseif quest_state == 100 then
  say("Thank you for your help.")
end

⚠ Common Pitfalls

  • Overlapping triggers where multiple NPCs respond to the same keyword.
  • Circular dialogue loops that don't allow the player to exit the conversation.
3

Construct Branching Logic Gates

For non-linear stories, use conditional checks against player attributes or previous choices. If a player kills an NPC instead of helping them, set a 'reputation' flag that closes one branch and opens another. This requires checking both the primary quest state and secondary flags.

act_comm.c
if (get_qvar(ch, "q_traitor_path") == 1) {
    send_to_char("The guard glares at you and refuses to speak.\n", ch);
    return;
}

⚠ Common Pitfalls

  • Creating 'dead ends' where a player cannot progress because they missed a mutually exclusive flag.
  • Failing to log branch choices, making it impossible for staff to troubleshoot player progression issues.
4

Integrate Item and Mob Death Hooks

Link quest progression to world events. Use a 'death_trigger' on a mob to increment the quest variable or a 'get_trigger' on an item. Ensure these triggers only fire if the player is currently on the correct quest step to prevent 'pre-looting' quest items.

mob_progs.js
onDeath(victim, killer) {
  if (getQuestState(killer, 'orc_hunt') === 10) {
    setQuestState(killer, 'orc_hunt', 20);
    killer.send('The Orc Chieftain is dead. Return to the village.');
  }
}

⚠ Common Pitfalls

  • Item duplication bugs if the quest item is not set as 'unique' or 'no-drop'.
  • Group credit issues where only the person who lands the killing blow gets the quest update.
5

Validate Rewards and Economy Impact

Before finalizing the quest, implement a reward function that checks for inventory space and applies scaling rewards. Use a centralized reward table to ensure that XP and gold payouts remain consistent with the MUD's overall economy and level curve.

rewards.py
def complete_quest(player, quest_id):
    reward_xp = quest_table[quest_id]['xp']
    reward_gold = quest_table[quest_id]['gold']
    player.gain_exp(reward_xp)
    player.add_gold(reward_gold)
    set_quest_state(player, quest_id, 100)

⚠ Common Pitfalls

  • Repeatable quest exploits where players can trigger the reward logic multiple times.
  • Inflation caused by quest rewards that exceed the hourly gold-per-hour grinding average.
6

Deploy State Cleanup and Debugging Tools

Create administrative commands to view and modify quest states for players. This is essential for fixing 'stuck' players. Additionally, implement a 'quest log' command for players that parses their current variables into a readable format.

immortal_commands.txt
Syntax: qset <player> <quest_id> <state>
Example: qset Torgar dragon_slayer 20

⚠ Common Pitfalls

  • Inaccurate quest logs that don't update when a player loses a quest item.
  • Giving players too much information in the quest log, ruining the discovery aspect.

What you built

Successful quest implementation relies on a robust state machine and rigorous testing of edge cases, such as player disconnection during a transition. By decoupling narrative logic from area files and using a centralized variable system, builders can create complex, reactive worlds that feel alive to the player.