Modding:Quests

Revision as of 07:20, 26 June 2024 by Kernelmethod (talk | contribs) (Add more information on quest progression)
This page is about modding. See the modding overview for an abstract on modding.

This page will step you through writing your own questline. First, we will break down some of the common components of quests.

Quests.xml

The game defines quests in the Quests.xml file. Each quest is primarily broken down into the following:

  • A top-level <quest> tag that defines high-level properties of the quest, including the faction that assigned it (if any), journal accomplishments that should be added upon completing the quest, and so on, and
  • <step> nodes that define individual steps of the quest.

For example, here is the quest definition for O Glorious Shekhinah!:

<quest
    Name="O Glorious Shekhinah!"
    Level="3"
    System="TravelToStiltSystem" 
    Accomplishment="On the recommendation of a proselyte, you visited the merchant bazaar and grand cathedral at the Six Day Stilt."
    Hagiograph="=name= trekked through the salt pans, north and west, to the merchant bazaar and grand cathedral of the Six Day Stilt. There, the stiltfolk sang hymns in the sultan's honor."
    HagiographCategory="VisitsLocation">

    <step Name="Make a Pilgrimage to the Six Day Stilt" XP="1500">
      <text>Journey through the Great Salt Desert to visit the merchant bazaar and Mechanimist cathedral, where a proselyte asked you to make on offering of a trinket.</text>
    </step>
</quest>

IQuestSystem

A common component for many (but not all) quests is custom, per-quest systems that advance a player's progression through the quest as they achieve various objectives. Continuing our example from earlier, here's the system used by O Glorious Shekhinah!, TravelToStiltSystem:

using System;

namespace XRL.World.Quests;

[Serializable]
public class TravelToStiltSystem : IQuestSystem
{
        public override void Register(XRLGame Game, IEventRegistrar Registrar)
        {
                Registrar.Register(ZoneActivatedEvent.ID);
        }

        public override bool HandleEvent(ZoneActivatedEvent E)
        {
                if (E.Zone.ZoneID == "JoppaWorld.5.2.1.1.10" || E.Zone.ZoneID == "JoppaWorld.5.2.1.2.10")
                {
                        The.Game.FinishQuestStep("O Glorious Shekhinah!", "Make a Pilgrimage to the Six Day Stilt", -1, CanFinishQuest: true, E.Zone.ZoneID);
                }
                return base.HandleEvent(E);
        }

        // GetInfluencer is primarily used to determine who should be referenced when
        // naming an item. If the player successfully rolls an item naming opportunity
        // upon completing the quest, they will be able to name their item after the
        // culture of either Wardens Esther or Tszappur.
        public override GameObject GetInfluencer()
        {
                if (50.in100())
                {
                        return GameObject.FindByBlueprint("Wardens Esther");
                }
                return GameObject.FindByBlueprint("Tszappur");
        }
}

The main thing that this quest system does is register an event handler to check for ZoneActivatedEvent and determine whether the player has arrived at the  Six Day Stilt. If they have, then the quest is completed.

IQuestSystems are common but not universal for quest tracking, and many quests are tracked simply by

IQuestSystem versus QuestManager

QuestManager is the old interface that used to manage the progression of quests. While code using QuestManager is still present in the codebase, new code should in general prefer IQuestSystem instead.

Quest progression

In this section we will discuss various XML-only and scripting-enabled ways of progressing players through different stages of a quest.

Giving quests to players

XML-only

The StartQuest part generator for conversations can grant a player a quest. Here is an example of StartQuest usage from  Argyve's dialogue, starting the quest A Canticle for Barathrum.

<node ID="CanticleAccept3">
  <text>                                    
    Here you are. Now, go! Off with you! May you live long enough to do my bidding. Away, away!
  </text>
  <choice GotoID="End" StartQuest="A Canticle for Barathrum">
    <text>Farewell, Argyve.</text>
    <part Name="ReceiveItem" Blueprints="Droid Scrambler,Argyve's Data Disk" Identify="All" />
  </choice>
</node>

Via scripting

With scripting it is possible to start the quest using The.Game.StartQuest, e.g.

The.Game.StartQuest("A Canticle for Barathrum");

Completing steps of a quest

XML-only

There are several XML-only ways to trigger the completion of a quest step:

  • In a conversation, you can use the CompleteQuestStep part generator.
  • You can attach the QuestStepFinisher part to a widget that is placed in a zone to ensure that a quest step is finished upon entering the zone.
  • You can use the FinishQuestStepWhenSlain part to complete a quest step when a creature is killed.

Via scripting

To complete a quest step from C#, you can call The.Game.FinishQuestStep. This can be called from anywhere during the game; for example, here's a snippet from the FinishQuestStepWhenSlain part (used to -- as the name suggests -- complete a quest step after slaying a creature):

using System;
namespace XRL.World.Parts;

[Serializable]
public class FinishQuestStepWhenSlain : IPart
{
    public string Quest;
    public string Step;
    public string GameState;
    public virtual bool Clean => true;

    // ...

    public virtual void Trigger()
    {
        if (GameState != null)
        {
            The.Game.SetIntGameState(GameState, 1);
        }
        if (!The.Game.TryGetQuest(this.Quest, out var Quest))
        {
            if (!RequireQuest)
            {
                return;
            }
            Quest = The.Game.StartQuest(this.Quest);
        }
        The.Game.FinishQuestStep(Quest, Step);
        if (Clean)
        {
            ParentObject.RemovePart(this);
        }
    }
}

If you wish to signal the failure of a game step, you can use The.Game.FailQuestStep(QuestName, QuestStep) instead.

Completing a quest

A quest will automatically be marked as completed when you have completed all of its steps. However, in some cases you may want to manually trigger the completion of a quest, e.g. if the player (for whatever reason) completed the quest without finishing all of its objectives. This methodology is more robust when finishing the last step of a quest should amount to completing the quest, since there are many ways for players to skip quest steps in Caves of Qud.

XML-only

To manually complete a quest from a conversation, you can use FinishQuest. Here's the conversation node from  Otho's script that completes Decoding the Signal:

<node ID="PresentTheDisk">
  <text>
    Well done, =factionaddress:Barathrumites=. Present the disk.
  </text>
  <choice GotoID="InterpretSignal" CompleteQuestStep="Decoding the Signal~Return to Grit Gate|6000" FinishQuest="Decoding the Signal">
    <text>[Give Otho the disk]</text>
    <part Name="GritGateHandler" Rank="Journeyfriend" />
  </choice>
</node>

Via scripting

From C#, you can call The.Game.FinishQuest(QuestName). There also exists a method for failing quests, as there does for failing steps; this method is The.Game.FailQuest(QuestName).

Example: Nima Ruda's fetch quest

This article is a stub. You can help Caves of Qud Wiki by expanding it.