Skip to main content

Quest System Guide

The quest system manages quest lifecycle, listens to event bus events, and tracks objective progress.

Architecture

UERPQuestBase (Abstract)
├── UERPSimpleQuest ← UERPSimpleQuestDefinition (DataAsset)
├── UERPBlueprintQuest (Blueprintable)
└── (Future: UERPGraphQuest)

The UERPQuestSubsystem manages all active quests and routes events to them. Each quest type implements its own ProcessEvent logic.

Quest Lifecycle

Inactive ──Activate()──► Active ──Complete()──► Completed

└──Fail()──► Failed

Simple Quests (DataAsset)

Creating a Definition

  1. Content Browser > Right-click > Miscellaneous > Data Asset
  2. Select ERPSimpleQuestDefinition
  3. Fill in the fields:
FieldDescription
Display NameShown in UI
DescriptionDetailed text for the journal
Quest TagsGameplayTags for categorization
Tracked By DefaultShow on HUD when activated
SequentialObjectives unlock one by one (vs. all at once)
ObjectivesList of FERPObjectiveDefinition
Completion Event TagBroadcast on the event bus when quest completes
Failure Event TagBroadcast on the event bus when quest fails

Objective Definition

FieldDescription
ObjectiveIdUnique name within the quest
DisplayTextShown in UI (e.g., "Kill 5 goblins")
EventTagWhich event bus tag progresses this objective
RequiredCountHow many events needed to complete

Starting a Simple Quest

UERPQuestSubsystem* QS = GetWorld()->GetSubsystem<UERPQuestSubsystem>();
UERPQuestBase* Quest = QS->StartQuestFromDefinition(MyDefinition);

How Events Progress Objectives

  1. Game broadcasts Enemy.Killed on the event bus
  2. Quest subsystem routes the event to all active quests
  3. UERPSimpleQuest::ProcessEvent checks each active objective
  4. If the objective's EventTag matches, increment its counter
  5. If counter reaches RequiredCount, mark objective complete
  6. If sequential, unlock the next locked objective
  7. If all objectives complete, auto-complete the quest
  8. Completion broadcasts the CompletionEventTag on the bus

Blueprint Quests

For quests that need custom logic beyond simple event counting:

  1. Create a Blueprint child of ERPBlueprintQuest
  2. Override ProcessEvent — handle events your way
  3. Override GetProgress — return 0.0-1.0
  4. Override GetCurrentObjectives — return objective info for UI
  5. Override Activate / Complete / Fail for custom lifecycle

Example: Timed Quest

// In BP override of ProcessEvent:
// - Start a timer on Activate
// - Check timer in ProcessEvent
// - Auto-fail if time runs out

Starting a Blueprint Quest

UERPBlueprintQuest* Quest = NewObject<UERPBlueprintQuest>(this, MyBPQuestClass);
Quest->DisplayName = FText::FromString("Timed Challenge");
QS->StartQuest(Quest);

Quest Subsystem API

Queries

UERPQuestSubsystem* QS = GetWorld()->GetSubsystem<UERPQuestSubsystem>();

// Get all active quests
TArray<UERPQuestBase*> Active = QS->GetActiveQuests();

// Get tracked quests (for HUD)
TArray<UERPQuestBase*> Tracked = QS->GetTrackedQuests();

// Find a specific quest
UERPQuestBase* Quest = QS->FindQuest(QuestId);

// Get quests by state
TArray<UERPQuestBase*> Completed = QS->GetQuestsByState(EERPQuestState::Completed);

Management

// Fail a quest
QS->FailQuest(QuestId);

// Abandon (remove from tracking)
QS->AbandonQuest(QuestId);

// Toggle tracking
QS->SetQuestTracked(QuestId, true);

Events

// Any quest state change
QS->OnAnyQuestStateChanged.AddDynamic(this, &AMyClass::OnQuestChanged);
// void OnQuestChanged(UERPQuestBase* Quest, EERPQuestState NewState)

// Any objective progress
QS->OnAnyObjectiveProgressed.AddDynamic(this, &AMyClass::OnObjective);
// void OnObjective(UERPQuestBase* Quest, const FERPObjectiveInfo& Info)

Extending: Custom Quest Types

Create your own quest type by extending UERPQuestBase:

UCLASS(BlueprintType)
class UMyGraphQuest : public UERPQuestBase
{
GENERATED_BODY()

// Your node graph data...
UPROPERTY() TArray<UMyQuestNode*> Nodes;

virtual bool ProcessEvent_Implementation(FGameplayTag Tag, const FERPEventPayload& Payload) override
{
// Route event to your graph nodes
// Return true if the quest progressed
}

virtual float GetProgress_Implementation() const override
{
// Calculate progress from graph state
}

virtual TArray<FERPObjectiveInfo> GetCurrentObjectives_Implementation() const override
{
// Build objective list from current graph node
}
};

The quest subsystem doesn't know or care about the internal structure — it only calls the abstract interface.

Next Steps