Quests (experimental)
The game gives the player tasks, and each task has steps, called objectives. Quests lets your mod list them, check how far the player is, move a task or a step on, and add progress to a step that counts things (for example “collect 5 cans”). It tells you when a task or step changes.
Quests list, check and move tasks and steps
Use All and Get to read the tasks, Reached to check a step, and SetTask, SetObjective or AddProgress to move them on. TaskChanged and ObjectiveChanged tell you when they change.
public static class Quests
{
public static event Action<TaskChangedArgs> TaskChanged;
public static event Action<ObjectiveChangedArgs> ObjectiveChanged;
public static bool IsAvailable { get; }
public static List<QuestInfo> All();
public static QuestInfo Get(string taskId);
public static bool Reached(string taskId, string objectiveId);
public static bool SetTask(string taskId, TaskState state);
public static bool SetObjective(string taskId, string objectiveId, ObjectiveState state);
public static bool AddProgress(string taskId, string objectiveId, int amount);
}
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | true when the Quests API works. |
| Method | Returns | What it does |
|---|---|---|
All() | List<QuestInfo> | Every task the player has started. |
Get(taskId) | QuestInfo | One task. null when none matches. |
Reached(taskId, objectiveId) | bool | true when the player has got to this step. |
SetTask(taskId, state) | bool | Closes a task. true when it has that state afterwards. |
SetObjective(taskId, objectiveId, state) | bool | Closes a step. true when it has that state afterwards. |
AddProgress(taskId, objectiveId, amount) | bool | Adds to a step’s counter. |
| Event | Gives you | When it fires |
|---|---|---|
TaskChanged | TaskChangedArgs | A task started or changed state. args.TaskId and args.State tell you which, and to what. |
ObjectiveChanged | ObjectiveChangedArgs | A step started or changed state. args.TaskId, args.ObjectiveId and args.State tell you which, and to what. |
QuestInfo a task
All and Get give you one of these for each task. Its Objectives lists the steps, as ObjectiveInfo values.
public sealed class QuestInfo
{
public string Id { get; }
public string Title { get; }
public string Description { get; }
public TaskState State { get; }
public ObjectiveInfo[] Objectives { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The task’s id (see Ids below). |
Title | string | The task’s name. |
Description | string | The text the player reads. |
State | TaskState | Where the task is. |
Objectives | ObjectiveInfo[] | The steps of the task. |
ObjectiveInfo one step of a task
Each step of a QuestInfo is one of these. Current and Target show its counter, and AddProgress adds to it. Target is 0 when the step has no counter.
public sealed class ObjectiveInfo
{
public string Id { get; }
public string Title { get; }
public string Description { get; }
public ObjectiveState State { get; }
public bool Optional { get; }
public int Current { get; }
public int Target { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The step’s id (see Ids below). |
Title | string | The step’s name. |
Description | string | The text the player reads. |
State | ObjectiveState | Where the step is. |
Optional | bool | true when the step is not needed to finish the task. |
Current | int | The counter now. |
Target | int | The counter the step needs. 0 when the step has no counter. |
TaskState where a task is
QuestInfo.State holds this. SetTask accepts Failed, Finished and Abandoned.
public enum TaskState
{
Active = 0,
Failed = 1,
Finished = 2,
Abandoned = 3
}
| Value | Meaning |
|---|---|
Active | The player is working on it. |
Failed | The player failed it. |
Finished | The player completed it. |
Abandoned | The task was dropped. |
ObjectiveState where one step of a task is
ObjectiveInfo.State holds this. SetObjective accepts Failed, Finished and Abandoned, and the game sets Hidden.
public enum ObjectiveState
{
Active = 0,
Failed = 1,
Finished = 2,
Abandoned = 3,
Hidden = 4
}
| Value | Meaning |
|---|---|
Active | The player is working on it. |
Failed | The player failed it. |
Finished | The player completed it. |
Abandoned | The step was dropped. |
Hidden | The game has set the step to hidden. |
Putting it together
Logs a line each time a task changes, then closes a step. The ids here are placeholders, so on a real save the call returns false and nothing changes. Use ids from the game.
using ObenseuerCore;
[ModInfo("example.questwatch", "Quest Watch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class QuestWatch : FrameworkMod
{
protected override void OnLoad()
{
Quests.TaskChanged += args => Log.Info(args.TaskId + " is " + args.State);
Events.WhenReady(() =>
{
bool done = Quests.SetObjective("<taskId>", "<objectiveId>", ObjectiveState.Finished);
Log.Info("finished the step: " + done);
});
}
}
| Step | What it does |
|---|---|
Quests.TaskChanged += ... | Logs the task id and its new state each time a task changes. |
Events.WhenReady(...) | Waits until a save is loaded before it runs the rest. |
Quests.SetObjective("<taskId>", "<objectiveId>", ObjectiveState.Finished) | Closes one step as finished, if the ids are real. |
Log.Info("finished the step: " + done) | Logs whether the step was closed. |
Things to know
Ids
- A task id is the game’s own id for the task. It is the task’s
idfield. If that is empty it is the task’s addressable asset path, and if that is empty too it is the task’s title. A step id works the same way.QuestInfo.IdandObjectiveInfo.Idgive you these values. (They areTaskInfo.IdandTaskObjective.Id.) - An id must match exactly one task (or one step). If it matches none or several, the call is refused. A task whose title is the same as another task’s title is refused as well, because the game treats such tasks as the same task. (The game compares tasks with
TaskInfo.Compare.)
Reading
All()lists every task the player has started, including finished and failed ones. It never calls the game’s own lookup that finishes a step as a side effect, so reading cannot change the player’s progress. (It never callsGetTaskAndObjective, which finishes the objective it looks up.)Get(taskId)returnsnullfor an empty, unknown or ambiguous id, and when the API is not available.Reached(taskId, objectiveId)istruewhen the player has got to that step. That means the game recorded the step as active, failed or abandoned, or as finished with its completion requirements met. The record stays after the step closes. It isfalsefor an empty id, an unknown id, and when the API is not available.Reacheddoes not check that the ids point to one task and one step. It reads the game’s own record, which stores the pair astaskId + "_" + objectiveId, so ids that contain_can collide. Avoid_in ids you choose yourself.
Changing
SetTask(taskId, state)closes a task. It acceptsFailed,FinishedandAbandoned, and the task must be active. It returnstruewhen the task has the new state after the call.SetTask(taskId, Finished)does not run the task’s rewards or start the tasks that follow it. It only marks the task finished and moves it to the finished list. Each step that is still active is marked abandoned, and the game then runs those steps’ own “abandoned” follow-ups. (The game’sTaskController.EditTaskStatusonly changes the task’s status and moves it to the finished list. Its follow-ups for an abandoned step are the objective’s abandon actions.)SetObjective(taskId, objectiveId, state)closes a step. It acceptsFailed,FinishedandAbandoned, and the step must be active.Finishedalso needs the game’s completion requirements for that step to pass. It returnstruewhen the step has the new state after the call.AddProgress(taskId, objectiveId, amount)adds to a step’s counter.amountmust be above 0, and the step must be active and have a counter. The amount is cut to the room left on the counter, so the counter never goes past its target. When the add reaches the target, the step completes through the game, unless it is set not to complete by its counter. In that case its completion requirements must pass first, or the call returnsfalseand changes nothing. It returnstruewhen the counter or the status changed.- Your event handlers run after the game’s change has finished, so they may call writers.
Events
TaskChangedfires when a new task starts (Active), or a task moves toFailed,FinishedorAbandoned. The same goes forObjectiveChangedand steps: it fires when a step starts (Active) or its state changes.- Events come in the order the game made the changes, the nested ones first. For a new task with a step,
ObjectiveChanged(Active)comes beforeTaskChanged(Active). A task closed bySetTaskraises oneTaskChangedwith the closed state. A task that is created and closed in one game call raises only the closing event. - An event carries the state at the moment of the change. Call
Getto see the state now.
Limits
- Writers are refused while the game is itself changing a task. If you call a writer from inside one of the game’s own task changes, including one made from another mod’s patch, you get
false. It is logged once as “called while a quest hook is running” (the log’s name for a task change the game is making). (The game calls areEditTask,EditTaskStatusandEditTaskObjective.) - Writers also need the task panel and the notifications to exist. The game uses them when a task starts. (They need
TaskPanel.instanceandNotifications.instance.)