Implementing Quests

This tutorial will guide you through creating and managing quests in your RPG. You’ll learn how to create quests, track objectives, integrate with events, handle rewards, and build quest chains.

Overview

The Barebones RPG Framework provides a comprehensive quest system with:

  • Quest Management: Automatic registration and tracking

  • Flexible Objectives: Kill enemies, collect items, talk to NPCs, reach locations, or custom conditions

  • Event Integration: Automatic progress tracking through game events

  • Rewards: Experience, gold, and item rewards

  • Quest Chains: Prerequisites and level requirements

  • Callbacks: Custom logic for quest lifecycle events

What You’ll Build

In this tutorial, you’ll create:

  1. A basic quest with a single objective

  2. A multi-objective quest with event integration

  3. A quest chain with prerequisites

  4. Custom objectives with callbacks

Basic Quest Creation

Let’s start with a simple quest:

from barebones_rpg import Quest, QuestObjective, ObjectiveType, QuestManager

# Create a quest
quest = Quest(
    name="Goblin Trouble",
    description="The goblins are threatening the village!",
    exp_reward=100,
    gold_reward=50
)

# Add an objective
quest.add_objective(QuestObjective(
    description="Defeat 5 goblins",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin",
    target_count=5
))

# Access the quest manager and add the quest
quest_manager = QuestManager()
quest_manager.add_quest(quest)

# Start the quest
quest_manager.start_quest(quest.id, events=game.events)

Key Points

  • Explicit Registration: Quests must be explicitly added to QuestManager with add_quest()

  • Quest IDs: Each quest gets a unique ID generated automatically

  • Rewards: Set exp_reward, gold_reward, and item_rewards when creating the quest

  • Optional Manager: You can manage quests yourself without using QuestManager if desired

Quest Objectives

Objectives define what the player needs to accomplish. The framework supports several objective types:

Objective Types

from barebones_rpg import QuestObjective, ObjectiveType

# Kill enemy objective
kill_objective = QuestObjective(
    description="Defeat the Goblin Chief",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin Chief",
    target_count=1
)

# Collect item objective
collect_objective = QuestObjective(
    description="Collect 3 healing herbs",
    objective_type=ObjectiveType.COLLECT_ITEM,
    target="Healing Herb",
    target_count=3
)

# Talk to NPC objective
talk_objective = QuestObjective(
    description="Speak with the village elder",
    objective_type=ObjectiveType.TALK_TO_NPC,
    target="Elder Marcus"
)

# Reach location objective
location_objective = QuestObjective(
    description="Travel to the Dark Forest",
    objective_type=ObjectiveType.REACH_LOCATION,
    target="Dark Forest"
)

# Custom objective (with custom logic)
custom_objective = QuestObjective(
    description="Reach level 10",
    objective_type=ObjectiveType.CUSTOM,
    condition=lambda obj: hero.level >= 10
)

Progress Tracking

Each objective tracks its progress with current_count and target_count:

objective = QuestObjective(
    description="Defeat 5 goblins",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin",
    target_count=5
)

# Check progress
print(objective.get_progress_text())  # "0/5"

# Manually increment progress
objective.increment(1)
print(objective.get_progress_text())  # "1/5"

# Check if completed
if objective.is_completed():
    print("Objective complete!")

Event Integration

The quest system automatically tracks progress through the event system, making quest implementation seamless.

Automatic Kill Tracking

When you start a quest with KILL_ENEMY objectives, the quest system automatically subscribes to death events:

from barebones_rpg import (
    Quest, QuestObjective, ObjectiveType,
    Game, GameConfig, Character, Enemy, Combat, Stats
)

# Initialize game
game = Game(GameConfig(title="Quest Example"))

# Create hero
hero = Character(
    name="Hero",
    stats=Stats(strength=15, constitution=12, dexterity=10,
                intelligence=8, charisma=10, base_max_hp=50,
                hp=100, mp=50)
)
hero.init_inventory()

# Create quest with kill objective
quest = Quest(
    name="Goblin Slayer",
    description="Defeat 3 goblins",
    exp_reward=150
)
quest.add_objective(QuestObjective(
    description="Defeat 3 goblins",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin",
    target_count=3
))

# Start the quest (registers kill listener automatically)
quest.start(events=game.events)

# Create and defeat goblins
for i in range(3):
    goblin = Enemy(
        name="Goblin",
        stats=Stats(strength=8, constitution=6, dexterity=12,
                   intelligence=5, charisma=5, base_max_hp=20, hp=30),
        exp_reward=50
    )

    # Combat automatically publishes death events
    combat = Combat(
        player_group=[hero],
        enemy_group=[goblin],
        events=game.events
    )
    combat.start()
    # ... execute combat ...

# Quest automatically tracks kills and completes when done
if quest.is_completed():
    print(f"Quest complete! Gained {quest.exp_reward} EXP")

Manual Objective Updates

For other objective types, update progress manually:

# Using QuestManager helper method
quest_manager = QuestManager()

# When player collects an item
quest_manager.update_objective(
    quest_id=quest.id,
    objective_type=ObjectiveType.COLLECT_ITEM,
    target="Healing Herb",
    amount=1,
    events=game.events
)

# When player talks to NPC
quest_manager.update_objective(
    quest_id=quest.id,
    objective_type=ObjectiveType.TALK_TO_NPC,
    target="Elder Marcus",
    events=game.events
)

# Or update objectives directly
for objective in quest.objectives:
    if objective.objective_type == ObjectiveType.REACH_LOCATION:
        if hero.current_location == objective.target:
            objective.complete()
            quest.check_completion(events=game.events)

Quest Rewards

Quests can provide experience, gold, and items as rewards:

quest = Quest(
    name="Village Defender",
    description="Protect the village from goblins",
    exp_reward=200,
    gold_reward=100,
    item_rewards=["potion_healing", "sword_iron"]
)

Handling Rewards

Listen for quest completion events to distribute rewards:

from barebones_rpg import EventType, Event

def on_quest_complete(event: Event):
    quest = event.data.get("quest")
    if quest:
        # Award experience
        hero.gain_exp(quest.exp_reward, game.events)

        # Award gold
        hero.inventory.add_gold(quest.gold_reward)

        # Award items
        for item_id in quest.item_rewards:
            item = create_item_from_id(item_id)
            hero.inventory.add_item(item)

        print(f"Quest '{quest.name}' completed!")
        print(f"Rewards: {quest.exp_reward} EXP, {quest.gold_reward} gold")

# Subscribe to quest completion events
game.events.subscribe(EventType.QUEST_COMPLETED, on_quest_complete)

Quest Chains and Prerequisites

Create quest chains by setting prerequisites:

Level Requirements

quest = Quest(
    name="Advanced Mission",
    description="A quest for experienced adventurers",
    required_level=10,
    exp_reward=500
)

# Check if player can start quest
if hero.stats.level >= quest.required_level:
    quest_manager.start_quest(quest.id, events=game.events)
else:
    print(f"You must be level {quest.required_level} to start this quest")

Quest Prerequisites

# First quest in chain
quest1 = Quest(
    name="The Beginning",
    description="Start your adventure",
    exp_reward=100
)
quest1.add_objective(QuestObjective(
    description="Defeat 5 slimes",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Slime",
    target_count=5
))

# Second quest requires first to be complete
quest2 = Quest(
    name="The Continuation",
    description="Your journey continues",
    required_quests=[quest1.id],
    exp_reward=200
)
quest2.add_objective(QuestObjective(
    description="Defeat the Goblin King",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin King"
))

# Third quest requires second to be complete
quest3 = Quest(
    name="The Finale",
    description="The final challenge",
    required_quests=[quest2.id],
    required_level=15,
    exp_reward=500
)

# Check prerequisites before starting
def can_start_quest(quest, hero, quest_manager):
    # Check level
    if hero.stats.level < quest.required_level:
        return False

    # Check required quests
    completed_ids = [q.id for q in quest_manager.get_completed_quests()]
    for req_id in quest.required_quests:
        if req_id not in completed_ids:
            return False

    return True

Custom Objectives and Callbacks

Add custom logic with callbacks for advanced quest behavior.

Custom Conditions

Use custom conditions for unique objective requirements:

# Objective with custom completion condition
objective = QuestObjective(
    description="Have at least 1000 gold",
    objective_type=ObjectiveType.CUSTOM,
    condition=lambda obj: hero.inventory.gold >= 1000
)

# Check condition
if objective.is_completed():
    print("Objective complete!")

Quest Callbacks

Add callbacks for quest lifecycle events:

def on_quest_start(quest):
    print(f"Quest started: {quest.name}")
    print(quest.description)
    # Spawn enemies, unlock areas, etc.

def on_quest_complete(quest):
    print(f"Quest completed: {quest.name}")
    # Unlock new quests, open gates, etc.

def on_quest_fail(quest):
    print(f"Quest failed: {quest.name}")
    # Handle failure consequences

quest = Quest(
    name="Special Mission",
    description="A quest with callbacks",
    on_start=on_quest_start,
    on_complete=on_quest_complete,
    on_fail=on_quest_fail,
    exp_reward=300
)

Objective Callbacks

Track progress with objective-level callbacks:

def on_objective_progress(objective):
    print(f"Progress: {objective.get_progress_text()}")

def on_objective_complete(objective):
    print(f"Objective completed: {objective.description}")

objective = QuestObjective(
    description="Defeat 10 goblins",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin",
    target_count=10,
    on_progress=on_objective_progress,
    on_complete=on_objective_complete
)

Complete Example

Here’s a full example integrating all concepts:

from barebones_rpg import (
    Game, GameConfig, Character, Enemy, Stats, Combat,
    Quest, QuestObjective, ObjectiveType, QuestManager,
    EventType, Event, AttackAction
)

# Initialize game
config = GameConfig(title="Quest System Demo")
game = Game(config)

# Create hero
hero = Character(
    name="Brave Knight",
    character_class="warrior",
    stats=Stats(
        strength=15, constitution=12, dexterity=10,
        intelligence=8, charisma=10, base_max_hp=50,
        hp=100, mp=50
    )
)
hero.init_inventory()

# Create a multi-objective quest
def on_quest_start(quest):
    print(f"\n{'='*50}")
    print(f"QUEST STARTED: {quest.name}")
    print(f"{'='*50}")
    print(quest.description)
    print("\nObjectives:")
    for i, obj in enumerate(quest.objectives, 1):
        print(f"  {i}. {obj.description}")

def on_quest_complete(quest):
    print(f"\n{'='*50}")
    print(f"QUEST COMPLETED: {quest.name}")
    print(f"{'='*50}")
    print(f"Rewards:")
    print(f"  - {quest.exp_reward} EXP")
    print(f"  - {quest.gold_reward} Gold")

quest = Quest(
    name="Goblin Threat",
    description="The village is under attack by goblins!",
    exp_reward=300,
    gold_reward=150,
    on_start=on_quest_start,
    on_complete=on_quest_complete
)

# Add multiple objectives
quest.add_objective(QuestObjective(
    description="Defeat 3 Goblin Warriors",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin Warrior",
    target_count=3,
    on_progress=lambda obj: print(f"  → {obj.description}: {obj.get_progress_text()}")
))

quest.add_objective(QuestObjective(
    description="Defeat the Goblin Chief",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Goblin Chief",
    target_count=1,
    on_complete=lambda obj: print(f"  ✓ {obj.description} - COMPLETE!")
))

# Start quest
quest.start(events=game.events)

# Create and fight enemies
print("\n--- Combat Phase ---\n")

# Fight goblin warriors
for i in range(3):
    print(f"Encounter {i+1}: Goblin Warrior")
    warrior = Enemy(
        name="Goblin Warrior",
        stats=Stats(
            strength=10, constitution=8, dexterity=12,
            intelligence=5, charisma=5, base_max_hp=30, hp=40
        ),
        exp_reward=50
    )

    combat = Combat(
        player_group=[hero],
        enemy_group=[warrior],
        events=game.events
    )
    combat.start()

    # Simple combat loop
    while combat.is_active():
        current = combat.get_current_combatant()
        if current == hero:
            action = AttackAction()
            combat.execute_action(action, hero, [warrior])
            combat.end_turn()

    print(f"Defeated Goblin Warrior {i+1}\n")

# Fight the chief
print("Final Encounter: Goblin Chief")
chief = Enemy(
    name="Goblin Chief",
    stats=Stats(
        strength=15, constitution=12, dexterity=10,
        intelligence=8, charisma=8, base_max_hp=50, hp=80
    ),
    exp_reward=100
)

combat = Combat(
    player_group=[hero],
    enemy_group=[chief],
    events=game.events
)
combat.start()

while combat.is_active():
    current = combat.get_current_combatant()
    if current == hero:
        action = AttackAction()
        combat.execute_action(action, hero, [chief])
        combat.end_turn()

print("Defeated Goblin Chief!\n")

# Check quest completion
quest.check_completion(events=game.events)

# Access quest manager
quest_manager = QuestManager()
print(f"\nActive quests: {len(quest_manager.get_active_quests())}")
print(f"Completed quests: {len(quest_manager.get_completed_quests())}")

Best Practices

Quest Organization

  1. Use Descriptive Names: Make quest and objective descriptions clear and specific

  2. Balance Objectives: Mix different objective types for variety

  3. Appropriate Rewards: Scale rewards to quest difficulty

Progress Tracking

  1. Leverage Events: Use automatic event tracking for kill objectives

  2. Manual Updates: Call update_objective() for custom tracking

  3. Check Completion: Always call check_completion() after manual updates

Quest Chains

  1. Logical Progression: Ensure prerequisites make narrative sense

  2. Level Gating: Use required_level to prevent players from accessing difficult content too early

  3. Clear Requirements: Communicate prerequisites to players

Performance

  1. Event Cleanup: Quest listeners are automatically managed

  2. Quest Manager: Use the singleton to avoid creating multiple managers

  3. Save/Load: Quest state is automatically serialized with the game

Common Patterns

Story Progression Quest:

quest = Quest(
    name="The Hero's Journey",
    required_quests=[previous_quest.id],
    required_level=5
)
quest.add_objective(QuestObjective(
    description="Talk to the village elder",
    objective_type=ObjectiveType.TALK_TO_NPC,
    target="Elder"
))

Fetch Quest:

quest = Quest(name="Herb Gathering")
quest.add_objective(QuestObjective(
    description="Collect 5 healing herbs",
    objective_type=ObjectiveType.COLLECT_ITEM,
    target="Healing Herb",
    target_count=5
))

Kill Quest:

quest = Quest(name="Monster Slayer")
quest.add_objective(QuestObjective(
    description="Defeat 10 slimes",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Slime",
    target_count=10
))

Boss Quest:

quest = Quest(
    name="Dragon's Lair",
    required_level=20,
    exp_reward=1000
)
quest.add_objective(QuestObjective(
    description="Defeat the ancient dragon",
    objective_type=ObjectiveType.KILL_ENEMY,
    target="Ancient Dragon",
    target_count=1
))

Next Steps

Now that you understand the quest system:

  1. Try creating a quest chain for your game’s storyline

  2. Experiment with custom objectives using conditions

  3. Integrate quests with the Creating Dialog Trees system

  4. Review the Quest System for complete API documentation

  5. Check out the Mini RPG Example for a complete implementation