View Format: Multi-Page

Simple Quest Forge

Generate Complete Quest Databases, Quest Chains, and Procedural Templates in Minutes.

Procedural Quest Forge Wizard

The Procedural Quest Forge lets you design quest templates with variable slots that are resolved at runtime to generate unlimited unique quests. Instead of hand-authoring hundreds of quests, you author a handful of templates and let the SimpleProceduralQuestGenerator produce concrete quest instances on the fly.

Open the wizard via Window › Simple Quest Forge › Procedural Quest Forge Wizard.

The 5 Steps: Setup → Definitions → Templates → Settings → Generate. This is the same step count as the Quest Forge wizard, but the content at each step is different because you are designing templates (blueprints for generating quests) rather than concrete quests.

How It Works

The procedural system uses a token replacement approach. You write template strings like "Hunt {COUNT} {ENEMY}s", define variables that control how {COUNT} and {ENEMY} are resolved, and at runtime the generator picks random values from each variable's pool and substitutes them into the template to produce a concrete quest.

1 Define Variables
Create named variable slots with source types. Example: ENEMY as a TextList with options ["Wolf", "Bear", "Bandit"], COUNT as a NumericRange from 5 to 20. Each variable has a key (the token name used in template strings) and a source type that determines how values are picked at runtime.
2 Write Templates
Use {VARIABLE_KEY} tokens in the quest name template, description template, objective templates, and reward templates. For example, a name template of "Hunt {COUNT} {ENEMY}s" with an objective that targets {ENEMY} with a count of {COUNT}.
3 Generate at Runtime
Call SimpleProceduralQuestGenerator.Generate() which resolves all variables and produces a SimpleGeneratedQuest struct with real values filled in. The generated quest contains fully resolved SimpleQuestObjective[] and SimpleQuestReward[] arrays ready for use with your game systems.
Template: "Hunt {COUNT} {ENEMY}s" Variables: COUNT = Random(5, 20), ENEMY = Random("Wolf", "Bear", "Bandit") Generated Quest: resolvedName: "Hunt 12 Wolfs" objectives[0].targetCode: "Wolf" objectives[0].targetCount: 12 rewards[0].amount: 240 (if reward uses {COUNT} * 20 pattern)

Variable System

Each variable has a key (the token name) and a sourceType (a plain string, not an enum) that determines how values are resolved at runtime. The three supported source types are:

Source Type Parameters Resolution Example
NumericRange rangeMin, rangeMax, isInteger Random number between min and max. When isInteger is true, produces whole numbers; otherwise produces decimal values rounded to one decimal place. COUNT: min=5, max=20, isInteger=true → resolves to "12"
TextList options[] (string array) Random pick from the options list. Options can be manually entered or populated from linked databases (enemy codes, item codes, etc.). ENEMY: ["Wolf", "Bear", "Bandit"] → resolves to "Wolf"
Custom (none — resolved externally) The generator leaves the token unresolved unless you provide an external variable pool via the variablePools parameter. Use this for values that depend on game state at runtime (player level, current region, time of day, etc.). PLAYER_LEVEL: resolved from game state at runtime via external pool
DB-linked pools: When databases are linked in Step 1, TextList options can be populated from actual database codes. For example, a variable ENEMY can pull its options from your SEF enemy database codes, ensuring generated quests always reference valid enemies that actually exist in your game. At runtime, you can also pass external pools via the variablePools parameter on Generate() to override or supplement a variable's built-in options.

Variable Fields

Each SimpleProceduralVariable struct contains:

Field Type Description
key string Token name used in template strings (e.g., "ENEMY", "COUNT"). This is what you wrap in curly braces: {ENEMY}.
sourceType string One of "NumericRange", "TextList", or "Custom". Note: this is a plain string, not an enum.
displayName string Human-readable label for the variable (e.g., "Target Enemy"). Used in the wizard UI.
rangeMin float Minimum value for NumericRange type.
rangeMax float Maximum value for NumericRange type.
isInteger bool When true, NumericRange produces whole numbers. When false, produces decimals (1 decimal place).
options string[] Available text options for TextList type. The generator picks one at random.

Template Strings

Template strings use {VARIABLE_KEY} token syntax. During generation, every occurrence of {KEY} in a template string is replaced with that variable's resolved value. Tokens can appear in any text field within a procedural quest template:

  • Name template"Hunt {COUNT} {ENEMY}s"
  • Description template"The village needs you to eliminate {COUNT} {ENEMY}s from the {REGION}."
  • Objective code template"KILL_{ENEMY}"
  • Objective description template"Kill {COUNT} {ENEMY}"
  • Reward code template"GOLD_{AMOUNT}"
  • Reward description template"{GOLD_AMOUNT} Gold"

Note that objective target codes and counts are resolved differently from template strings. Instead of embedding tokens in a string, you specify a variable key that the generator looks up directly. For example, an objective's targetVariable might be "ENEMY" and its countVariable might be "COUNT" — the generator resolves these variables and assigns the results to the generated objective's targetCode and targetCount fields.

// Example template with multiple variables Name Template: "Retrieve the {ITEM} from the {LOCATION}" Description Template: "A {ADJECTIVE} {ITEM} was last seen in the {LOCATION}." Objective 1: codeTemplate: "FIND_{ITEM}" descriptionTemplate: "Find the {ITEM}" targetVariable: "ITEM_CODE" → resolves to objective.targetCode countVariable: "" → uses fixedCount (1) Objective 2: codeTemplate: "DEFEAT_GUARDS" descriptionTemplate: "Defeat {COUNT} {ENEMY} guards" targetVariable: "ENEMY_CODE" → resolves to objective.targetCode countVariable: "COUNT" → resolves to objective.targetCount Reward: codeTemplate: "XP_REWARD" descriptionTemplate: "{XP_AMOUNT} XP" targetVariable: "" amountVariable: "XP_AMOUNT" → resolves to reward.amount

Step 1 — Setup

Configure your procedural quest database identity and optionally link companion databases for variable pool population.

Naming

Enter your Database Name, optional Namespace, and Class Prefix. The wizard previews the generated type names:

  • {Prefix}Type — Enum with one entry per template code
  • {Prefix}Database — ScriptableObject implementing ISimpleProceduralQuestDataSource
  • {Prefix}DatabaseEditor — Custom Inspector editor

Template Selection

Choose from 6 genre templates (Generic RPG, Open World, MMO, Survival, Narrative RPG, Roguelike) or start from scratch. Templates pre-populate property definitions and include example procedural templates with variables showing how to use the token replacement system effectively. See the Templates page for details on each genre.

Switching templates clears existing definitions. A confirmation dialog appears before replacement. Export your schema first if you want a backup of your current definitions.

Linked Databases (Optional)

Link up to 5 database types from companion packages for variable pool population. Each database type has its own ReorderableList with drag-and-drop support:

Database Type Bridge Provides Example Use
Enemy Databases SEFBridge Enemy codes Populate an ENEMY TextList variable with actual enemy codes from your game
Faction Databases SEFBridge Faction codes Populate a FACTION TextList with faction codes for reputation templates
Item Databases SIFBridge Item codes Populate a REWARD_ITEM TextList with item codes for reward templates
Loot Databases SIFBridge Loot table codes Populate a LOOT_TABLE TextList with loot table codes
Attribute Databases SAFBridge Attribute names Populate a STAT TextList with attribute names for stat-based templates

These linked databases are only visible when their respective companion package is installed. If SEF is not installed, the Enemy and Faction database lists won't appear (and a message explains why). This is purely optional — you can always type variable options manually.

Step 2 — Definitions

Define single-level dynamic properties for procedural quest templates. Unlike the Quest Forge which has two-level properties (quest + objective), the Procedural Quest Forge uses only template-level properties. These properties describe the template itself, not the generated quests.

The Four Property Types

Type Storage Example Properties Example Values
Categories int[] Template Type Kill Bounty, Fetch Quest, Escort, Delivery, Defense
Flags bool[] Is Repeatable, Has Timer, Scales With Level true / false
Numerics float[] Base XP, Base Gold, Min Level, Max Level 100, 50, 1, 60
Texts string[] Template Notes, Design Intent "Daily bounty board quest", "Scales enemy count with player level"

These properties are useful for filtering and categorizing templates at runtime. For example, you might query GetProceduralQuestsByCategory(0, 2) to get all "Escort" templates, or GetProceduralQuestsByFlag(0, true) to get all repeatable templates.

Schema Export / Import

Four buttons at the top of this step provide schema export/import:

  • Export Full JSON — Complete schema with definitions, existing templates, AI instructions, and variable documentation
  • Export Light JSON — Definitions and instructions only (no template data)
  • Export Markdown — Human-readable format for pasting into AI chat
  • Import Schema (JSON) — Import templates from a JSON file with 3-choice dialog

See the AI Workflow page for the full export/import workflow.

Step 3 — Templates

Create procedural templates in a split-panel layout. The left panel shows a list of all templates with search, sort, and pagination controls. The right panel shows the detail editor for the selected template.

Identity Section

Each template has basic identity fields:

  • Code — Unique template identifier (e.g., KILL_BOUNTY_TEMPLATE). Must be unique across all templates in this database.
  • Name — Display name for the template itself (e.g., "Monster Bounty"). This is the template's name, not the generated quest's name.
  • Name Template — Template string for the generated quest name (e.g., "{ENEMY} Bounty"). This is resolved at runtime.
  • Description Template — Template string for the generated quest description (e.g., "Adventurers needed to hunt {COUNT} {ENEMY}s in the {REGION}.").
  • Icon — Optional Sprite icon for the template.

Variables Section (Nested ReorderableList)

Define the variable slots for this template. Each variable has the fields described in the Variable System section above: key, sourceType, displayName, range parameters (for NumericRange), and options (for TextList).

You can add as many variables as needed. Common patterns include 2-5 variables per template. Variable keys must be unique within a template.

Objective Templates (Nested ReorderableList)

Each objective template defines how a generated objective will look. See Understanding Objective Templates below for the full field reference.

Reward Templates (Nested ReorderableList)

Each reward template defines how a generated reward will look. See Understanding Reward Templates below for the full field reference.

Template-Level Properties

The dynamic properties defined in Step 2 appear here for categorizing each template. Set category values, toggle flags, enter numeric values, and write text properties.

Understanding Objective Templates

Each SimpleProceduralObjective defines a blueprint for generating a concrete SimpleQuestObjective at runtime. The key difference from regular objectives is that instead of fixed values, you specify variable keys that are resolved during generation.

Field Type Description
codeTemplate string Template string for the generated objective's code field. Example: "KILL_{ENEMY}" → resolves to "KILL_WOLF"
descriptionTemplate string Template string for the generated objective's description field. Example: "Kill {COUNT} {ENEMY}" → resolves to "Kill 12 Wolf"
isOptional bool Whether this objective is optional (copied directly to the generated objective).
targetVariable string Variable key for target code resolution. The generator looks up this variable's resolved value and assigns it to objective.targetCode. Example: "ENEMY" → objective.targetCode = "Wolf"
countVariable string Variable key for target count resolution. Leave empty to use fixedCount. Example: "COUNT" → objective.targetCount = 12
fixedCount int Fixed target count used when countVariable is empty. Defaults to 1 if set to 0.
timeLimitVariable string Variable key for time limit resolution. Leave empty to use fixedTimeLimit.
fixedTimeLimit float Fixed time limit in seconds (0 = no limit). Used when timeLimitVariable is empty.
sortOrder int Sort order within the generated quest (copied directly to the generated objective).
Variable vs. template resolution: codeTemplate and descriptionTemplate use full template string resolution (all {KEY} tokens are replaced). targetVariable, countVariable, and timeLimitVariable are single variable key lookups — you specify just the variable name (e.g., "ENEMY"), not a template string (not "{ENEMY}").

Understanding Reward Templates

Each SimpleProceduralReward defines a blueprint for generating a concrete SimpleQuestReward at runtime.

Field Type Description
codeTemplate string Template string for the generated reward's code field. Example: "GOLD_REWARD"
descriptionTemplate string Template string for the generated reward's description field. Example: "{GOLD_AMOUNT} Gold""500 Gold"
targetVariable string Variable key for target code resolution. Resolves to reward.targetCode. Example: "REWARD_ITEM" → reward.targetCode = "HEALTH_POTION"
amountVariable string Variable key for amount resolution. Leave empty to use fixedAmount. Example: "GOLD_AMOUNT" → reward.amount = 500
fixedAmount float Fixed reward amount used when amountVariable is empty.
isChoice bool Whether this is a choice reward (copied to generated reward).
choiceGroup int Choice group ID. Rewards with the same choiceGroup and isChoice=true form a "pick one" group in the generated quest.

Step 4 — Settings

Configure output paths for the generated scripts and data asset. You can choose separate folders for scripts and the database asset, or use the default Assets/Generated/ProceduralQuests location.

  • Scripts Folder — Where to write the generated .cs files (enum, database, editor)
  • Data Folder — Where to create the .asset file

Step 5 — Generate

Click Generate to create your procedural quest database. This uses the same two-phase generation process as all SQF wizards:

1 Phase 1: Write Scripts
The wizard writes 3 C# files ({Prefix}Type.cs, {Prefix}Database.cs, {Prefix}DatabaseEditor.cs) and calls AssetDatabase.Refresh() to trigger Unity's script compilation.
2 Phase 2: Create Asset
After the domain reload completes, an [InitializeOnLoad] handler reads wizard data from a temporary JSON file and creates the {Prefix}Database.asset ScriptableObject with all your template data populated.

The SessionState flag SimpleQuestForge_ProceduralQuest_WaitingForCompilation tracks the generation state across the domain reload.

Wait for compilation. After clicking Generate, wait for Unity's "Compiling Scripts" progress bar to finish completely before interacting with the wizard or Inspector. The asset is created automatically after compilation completes.

Runtime Generation

Use SimpleProceduralQuestGenerator to generate concrete quests at runtime. The generator is a static class with two main methods:

Generate a Single Quest

using SimpleQuestForge; using System.Collections.Generic; // 1. Get the procedural quest database [SerializeField] MyProceduralDatabase templateDB; // 2. Get a specific template from the database SimpleProceduralQuest? template = templateDB.GetProceduralQuestByCode("KILL_BOUNTY_TEMPLATE"); if (template.HasValue) { // 3. Generate a concrete quest from the template SimpleGeneratedQuest quest = SimpleProceduralQuestGenerator.Generate(template.Value); Debug.Log(quest.resolvedName); // "Hunt 12 Wolves" Debug.Log(quest.templateCode); // "KILL_BOUNTY_TEMPLATE" Debug.Log(quest.objectives[0].targetCode); // "WOLF" Debug.Log(quest.objectives[0].targetCount); // 12 Debug.Log(quest.rewards[0].amount); // 240 Debug.Log(quest.resolvedVariables["ENEMY"]); // "Wolf" Debug.Log(quest.resolvedVariables["COUNT"]); // "12" }
Important: Generate() takes a SimpleProceduralQuest struct, NOT a database reference. You must first retrieve the template from your database using GetProceduralQuestByCode() or GetProceduralQuests(), then pass the struct to Generate(). The return type is SimpleGeneratedQuest, not SimpleQuestDefinition.

Generate with External Variable Pools

// Override or supplement variable options with external pools var pools = new Dictionary<string, string[]> { // Override the ENEMY variable with enemies valid for the current zone { "ENEMY", new[] { "Sand Scorpion", "Desert Bandit", "Dust Wraith" } }, // Provide a value for a Custom-type variable { "PLAYER_LEVEL", new[] { playerLevel.ToString() } } }; SimpleGeneratedQuest quest = SimpleProceduralQuestGenerator.Generate( template.Value, variablePools: pools);

Generate with Deterministic Seeding

// Use a seeded random for reproducible results var seededRandom = new System.Random(42); SimpleGeneratedQuest quest = SimpleProceduralQuestGenerator.Generate( template.Value, random: seededRandom); // Same seed + same template = same generated quest every time

Generate a Batch of Quests

// Get all templates from the database SimpleProceduralQuest[] allTemplates = templateDB.GetProceduralQuests(); // Generate 5 quests from random templates (no duplicate templates) SimpleGeneratedQuest[] quests = SimpleProceduralQuestGenerator.GenerateBatch( allTemplates, count: 5, allowDuplicateTemplates: false); // Generate 10 quests allowing the same template to be used multiple times // (each generation still produces unique variable values) SimpleGeneratedQuest[] moreQuests = SimpleProceduralQuestGenerator.GenerateBatch( allTemplates, count: 10, allowDuplicateTemplates: true);

Resolve Template Strings Manually

// You can also resolve template strings manually without generating a full quest var variables = new Dictionary<string, string> { { "COUNT", "12" }, { "ENEMY", "Wolf" } }; string resolved = SimpleProceduralQuestGenerator.ResolveTemplate( "Hunt {COUNT} {ENEMY}s", variables); // Result: "Hunt 12 Wolfs"

Runtime Data Structures

SimpleProceduralQuest (the template)

This is the template struct stored in your generated database. It contains all the information needed to generate a concrete quest at runtime.

public struct SimpleProceduralQuest { // Identity public string code; // Unique template code public string name; // Template display name public string nameTemplate; // Template string for generated quest name public string descriptionTemplate; // Template string for generated description public Sprite icon; // Template icon // Content public SimpleProceduralVariable[] variables; // Variable definitions public SimpleProceduralObjective[] objectives; // Objective templates public SimpleProceduralReward[] rewards; // Reward templates // Template-level dynamic properties public int[] categoryValues; public bool[] flagValues; public float[] numericValues; public string[] textValues; // Convenience accessors public int GetCategoryValue(int categoryIndex); public bool GetFlagValue(int flagIndex); public float GetNumericValue(int numericIndex); public string GetTextValue(int textIndex); }

SimpleGeneratedQuest (the output)

This is the struct returned by Generate(). It contains the fully resolved quest with all variable tokens replaced by concrete values.

public struct SimpleGeneratedQuest { public string templateCode; // Which template generated this public string resolvedName; // Fully resolved quest name public string resolvedDescription; // Fully resolved description public Dictionary<string, string> resolvedVariables; // All variable resolutions public SimpleQuestObjective[] objectives; // Fully resolved objectives public SimpleQuestReward[] rewards; // Fully resolved rewards }
Generated objectives and rewards use the same SimpleQuestObjective and SimpleQuestReward structs as hand-authored quests. This means generated quests are fully compatible with SimpleQuestTracker, SimpleQuestHelper, and all other runtime APIs. You can treat a generated quest exactly like a regular quest.

SimpleProceduralVariable

public struct SimpleProceduralVariable { public string key; // Token name (ENEMY, COUNT, etc.) public string sourceType; // "NumericRange", "TextList", or "Custom" (plain string) public string displayName; // Human-readable label public float rangeMin; // For NumericRange public float rangeMax; // For NumericRange public bool isInteger; // For NumericRange: whole numbers vs. decimals public string[] options; // For TextList: available options // Convenience methods public string Resolve(System.Random random = null); // Self-resolve public string Resolve(string[] pool, System.Random random = null); // Resolve from external pool }

ISimpleProceduralQuestDataSource

The interface implemented by generated procedural quest databases. Use this for type-safe database access without depending on the generated class name.

Method Returns Description
ProceduralQuestCount int Total number of procedural quest templates
GetProceduralQuests() SimpleProceduralQuest[] Get all procedural quest templates
GetProceduralQuestByCode(string) SimpleProceduralQuest? Get a template by code (nullable — returns null if not found)
GetProceduralQuestCodes() string[] Get all template codes
GetProceduralQuestNames() string[] Get all template display names
HasProceduralQuest(string) bool Check if a template exists by code
GetProceduralQuestEnumType() Type Get the generated enum type for type-safe access
GetCategoryLabels() string[] Category labels from definitions
GetCategoryEntries(int) string[] Entries for a specific category
GetFlagNames() string[] Flag names from definitions
GetNumericNames() string[] Numeric property names from definitions
GetTextNames() string[] Text property names from definitions
GetProceduralQuestsByCategory(int, int) SimpleProceduralQuest[] Filter templates by category value
GetProceduralQuestsByFlag(int, bool) SimpleProceduralQuest[] Filter templates by flag value
GetProceduralQuestsByNumericRange(int, float, float) SimpleProceduralQuest[] Filter templates by numeric range

Designing Good Templates

Tips for creating procedural quest templates that generate diverse, interesting quests:

Use Enough Variables

Templates with only 1-2 variables produce repetitive quests. Aim for 3-5 variables per template: a target (ENEMY/ITEM), a quantity (COUNT), a location (REGION), and optionally a modifier (ADJECTIVE) and a reward scaler (REWARD_MULTIPLIER).

Wide Numeric Ranges

A COUNT range of 10-12 produces nearly identical quests. Use wider ranges like 5-25 for noticeable variety. Consider using isInteger=false for decimal values like gold amounts (100.0-500.0) when precision doesn't matter.

Link Real Databases

When possible, link your SEF/SIF databases so TextList options are populated from actual game data. This ensures generated quests always reference valid enemy codes, item codes, or faction codes that exist in your game.

Use External Pools for Context

Pass variablePools to Generate() to inject context-aware values. Filter enemies by the player's current zone, scale reward amounts by player level, or limit items to what the current vendor stocks. This is especially powerful for Custom-type variables.

Template Families

Create multiple templates of the same type with different flavors. Instead of one "Kill Bounty" template, create "Monster Bounty", "Pest Control", "Mercenary Contract", and "Arena Challenge" — all kill-type but with different descriptions, reward scales, and variable pools.

Use Properties for Filtering

Set template-level properties thoughtfully. Tag templates as "Daily" or "Weekly" with flags, set min/max level ranges with numerics, and categorize by type. At runtime, use GetProceduralQuestsByCategory/Flag/NumericRange to select appropriate templates for the current game context.