C# API

Extend the Alegacy Quest Framework with custom actions, objectives, and integrations using the C# API.

💡 For chat commands, see Commands.

Getting a Reference

Access the quest system from any mod system class:

// In your ModSystem class
QuestSystem questSystem = api.ModLoader.GetModSystem();

Registering Custom Actions

Actions are executable commands triggered by quest events. Register your own during startup:

public class MyCustomAction : QuestActionBase
{
    public override string ActionId => "mycustomaction";

    public override void Execute(IServerPlayer player, string[] args, ActionContext ctx)
    {
        // args[0], args[1], etc. from the JSON "args" array
        string message = args.Length > 0 ? args[0] : "Hello!";
        player.SendMessage(GlobalConstants.GeneralChatGroup, message, EnumChatType.Notification);
    }
}

// Register in your ModSystem.StartServerSide():
questSystem.ActionRegistry.Register(new MyCustomAction());

Use in quest JSON:

{
  "id": "mycustomaction",
  "args": ["Welcome to the quest!"]
}

Or as an action string in dialogues:

mycustomaction 'Welcome to the quest!'

Registering Custom Objectives

Objectives define conditions players must complete:

public class MyCustomObjective : QuestObjectiveBase
{
    public override string ObjectiveId => "mycustomobjective";

    public override bool CheckProgress(IServerPlayer player, QuestObjectiveInstance instance)
    {
        // Return true when the objective is complete
        int current = GetPlayerProgress(player, instance);
        return current >= instance.Demand;
    }
}

// Register in your ModSystem.StartServerSide():
questSystem.ObjectiveRegistry.Register(new MyCustomObjective());

Then use in quest JSON:

{
  "id": "mycustomobjective",
  "args": ["param1", "5"]
}

Action String Executor

The ActionStringExecutor.Execute() method parses and runs action strings from dialogues and other triggers.

Parsing Rules

  • The input string is split by ; (semicolon) into individual action commands
  • Each command is parsed using regex: (?:'([^']*)')|([^\s]+)
  • The first token is the action ID
  • Subsequent tokens are arguments
  • Arguments wrapped in single quotes '...' preserve spaces inside them
  • The action ID is looked up in QuestSystem.ActionRegistry and executed
actionId arg1 arg2 'argument with spaces'; anotherActionId arg1

Reputation API

// Get current reputation value
int rep = questSystem.ReputationManager.GetReputation(player, "npcId");

// Add reputation (with optional max and once-key)
questSystem.ReputationManager.AddReputation(player, "npc", "npcId", 10, max: 100, onceKey: "quest1");

// Get current rank info
ReputationRank rank = questSystem.ReputationManager.GetCurrentRank(player, "npcId");

// Check if player meets reputation requirements
bool meets = questSystem.ReputationManager.MeetsRequirements(player, new[] {
    new ReputationRequirement { scope = "npc", id = "yourmod:innkeeper", min = 500 }
});

Quest Events

questSystem.OnQuestAccepted += (player, questId) => {
    // Fired when any quest is accepted
};

questSystem.OnQuestCompleted += (player, questId) => {
    // Fired when any quest is completed
};

questSystem.OnQuestFailed += (player, questId) => {
    // Fired when any quest fails
};

questSystem.OnObjectiveProgress += (player, questId, objectiveIndex, current, demand) => {
    // Fired when objective progress changes
};

questSystem.OnStageCompleted += (player, questId, stageIndex) => {
    // Fired when a stage is completed
};

Boss Hunt API

// Get current active boss key
string activeBoss = questSystem.BossHuntManager.GetActiveBossKey();

// Get time until next rotation
double hoursLeft = questSystem.BossHuntManager.GetHoursUntilRotation();

// Force rotate to next boss
questSystem.BossHuntManager.ForceRotate();

// Check if a specific boss is currently active
bool isActive = questSystem.BossHuntManager.IsBossActive("yourmod:dragon");

Journal API

// Add a journal entry
questSystem.JournalManager.AddEntry(player, "yourmod:lore-1", "group1");

// Get all entries for a player
List<JournalEntry> entries = questSystem.JournalManager.GetEntries(player, "group1");

// Check if player has an entry
bool has = questSystem.JournalManager.HasEntry(player, "yourmod:lore-1");

// Remove all entries in a group
questSystem.JournalManager.ClearGroup(player, "group1");

Database Sync API

// Manually sync a player (if you need to force-update)
questSystem.DbSyncService.SyncPlayer(player);

// Check if sync is enabled
bool enabled = questSystem.DbSyncService.IsEnabled;

Item Quality API

// Generate a quality item from action item config
ItemStack stack = questSystem.QualityManager.GenerateItem(
    player, 
    "yourmod:epic-sword", 
    qualityOverride: 3
);

// Get quality bonus for a specific attribute
float bonus = questSystem.QualityManager.GetBonus(stack, "damage");

// Reroll an item (consumes reroll token)
bool success = questSystem.QualityManager.Reroll(player, stack, "yourmod:weapon-reroll");

Building from Source

Requirements:

  • .NET 10 SDK
  • Vintage Story 1.22.1+
dotnet build

Complete ModSystem Example

using Vintagestory.API.Common;
using Vintagestory.API.Server;
using AlegacyVSQuest;

public class MyQuestMod : ModSystem
{
    private QuestSystem questSystem;

    public override void StartServerSide(ICoreServerAPI api)
    {
        questSystem = api.ModLoader.GetModSystem<QuestSystem>();
        
        // Register custom action
        questSystem.ActionRegistry.Register(new MyCustomAction());
        
        // Register custom objective
        questSystem.ObjectiveRegistry.Register(new MyCustomObjective());
        
        // Subscribe to events
        questSystem.OnQuestCompleted += (player, questId) => {
            api.Logger.Notification($"Player {player.PlayerName} completed {questId}");
        };
    }
}

Press ESC to close