Notes
Status: experimental.
What it is
In the game, the player collects notes and emails: single sheets with a title and some text, which they can read again in their notes window. This API lets a mod list all the single notes the game has, list the ones the player holds, check whether the player holds one, give one to the player, and get told when the player gets one.
Book pages are not covered here. Use Books (see Books) to read books, and Hud.ShowNote (see HUD) to open a note or a page on screen. Notebooks that the player writes in are not covered either.
When to use it
- Give the player a note when a quest step is done.
- Check that the player has read or collected a note before something happens.
- Log or react when the player collects a note, however they got it.
Quick example
This mod logs the title of every note the player collects. Once a save is loaded, it also logs how many single notes the game has and how many the player holds.
using ObenseuerCore;
[ModInfo("example.notewatch", "Note Watch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class NoteWatch : FrameworkMod
{
protected override void OnLoad()
{
Notes.Collected += args => Log.Info("Collected note: " + args.Title);
Events.WhenReady(() => Log.Info("The game has " + Notes.All().Count + " notes, the player holds " + Notes.Held().Count));
}
}
Collected is an event: the framework calls your code each time it happens. WhenReady runs your code once a save is loaded (see Using events).
Common tasks
Give the player a note by its id. Give returns true only when the note was added just now.
NoteEntryInfo first = Notes.All()[0];
if (Notes.Give(first.Id)) Log.Info("Gave the note " + first.Title);
Check whether the player already has a note.
bool has = Notes.Has("some-note-id");
List the notes the player holds.
foreach (NoteEntryInfo note in Notes.Held())
Log.Info(note.Id + ": " + note.Title);
Reference
This is the full list of what the API gives you. Text in brackets at the end of a note is extra detail for experienced modders, and you can skip it on a first read.
Notes
Notes reads and gives the game’s single notes. Each note is described by a NoteEntryInfo.
Notes list, check and give single notes
Call Give(id) to add a note the game already has, and Has(id) or Held() to check what the player holds. Collected fires each time a new note is collected.
public static class Notes
{
public static bool IsAvailable { get; }
public static List<NoteEntryInfo> All();
public static List<NoteEntryInfo> Held();
public static bool Has(string id);
public static bool Give(string id);
public static event Action<NoteCollectedArgs> Collected;
}
| Method | Returns | What it does |
|---|---|---|
All() | List<NoteEntryInfo> | Every single note the game has. |
Held() | List<NoteEntryInfo> | The single notes the player holds, in the order they were collected. |
Has(id) | bool | true when the player holds this note. |
Give(id) | bool | Gives the player the note. Returns true only when it was added just now. |
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | false when the collected event could not be set up. |
| Event | Gives you | When it fires |
|---|---|---|
Collected | NoteCollectedArgs | The player has just collected a note. |
NoteEntryInfo one note
All() and Held() give you one of these. Use its Id with Has and Give.
public sealed class NoteEntryInfo
{
public string Id { get; }
public string Title { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The note’s id. Use it with Has and Give. |
Title | string | The note’s title. |
NoteCollectedArgs what Collected gives you
Collected gives you one of these when the player gets a note they did not have. Id and Title say which note it was.
public sealed class NoteCollectedArgs
{
public string Id { get; }
public string Title { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The note’s id. |
Title | string | The note’s title. |
Things to know
All()lists every single note the game has, and leaves out book pages. It is the same list the game’s developer console uses. (It lists theNoteInfoassets in the game’sResources/Notesfolder and skips any withNoteInfo.bookset.)Held()lists the single notes the player has collected, in the order they were collected. Book pages are left out. (It readsNoteController.notesand skips entries that have a book.)- A note’s id is the note’s own id in the game files. If it has none, the id is where the note is stored, and if it has neither, the id is its title. (
IdisNoteInfo.Id: theidfield, elseaddressableAssetPath, elsetitle.) Has(id)andGive(id)find the note by its id, and do nothing if two notes share an id. If two notes in the game have the same id,HasandGivecan’t tell them apart, so both returnfalsefor that id.Give(id)adds the note the way the game’s own mail, tasks and bundles do. The notes window and the save both update as they would in the game. It does not show the “New note” pop-up, the same as the game’s mail and tasks. (It calls the game’sNoteController.AddNote.)Collectedfires once for each single note that goes from not held to held, however it was added. It fires forGive, for mail, for tasks and for the console. It does not fire again for a note the player already holds, and it does not fire when a save loads.IsAvailableisfalsewhen the collected event could not be set up. The reads andGivekeep working when it isfalse. See If something fails to set up.
Limits
Givereturnsfalsewhen it did not add the note. That happens when the id is not a note the game has, when the player already holds the note, when a save is loading or being written, and when the game’s note controller is not running (on the main menu, or before a save is loaded). An id ofnullalso returnsfalse, and writes one line to the log.
What you can’t do (yet)
- You can’t add a new note of your own. Every note is a data file that ships with the game, and the save stores a note by the path of that file. A mod can’t add a note without shipping such a file, so
Giveonly works for notes the game already has. (A note is aNoteInfoasset, stored in the save by its addressable path.)
Details
Target: Obenseuer 0.5.08 (buildid 25969064), Unity 6000.0.77f1 Mono, BepInEx 6 Unity.Mono (be.788, the tested build) or BepInEx 5 (5.4.23 or later). Namespace: ObenseuerCore. Logs go to BepInEx\logs\obenseuer.moddingframework.notes.log.
Everything not listed in this reference is internal and may change without notice. Items marked experimental may change too.
When you can call it
- Call everything from your mod’s normal code, not from a background thread you started. The game and Unity only work safely on one thread, the main thread (see the glossary).
- It is safe to call any of it at any time, even on the main menu or while a save is loading. If no save is loaded, or the game’s note controller is missing, the reads give an empty list or
false. - Your
Collectedhandler runs inside the game’s own call, one at a time. If one of your handlers throws an error, the framework logs it and still runs the other handlers. Collectedalso fires while a save is loading, if a note is added then. If you want to ignore those, checkSaveData.Busy, which istruewhile a save is loading or being written.
What to check for and what comes back
- Nothing here throws. A
nullid givesfalseand one line in the log. Everything else gives its plain default (falseor an empty list). IsAvailableonly tells you about theCollectedevent. In code it is “set up, and the feature group is not turned off”.
If something fails to set up
The framework changes some of the game’s methods when it starts, so it can tell your mod when a note is collected. If a game update breaks one of them, only that feature turns off, and the framework writes error lines and a warning to the log. The rest of the API keeps working.
- The
Collectedevent depends on the feature groupobenseuer.moddingframework.notes. This is the name you will see in the log. - If the framework can’t change the game’s method that adds a note, it turns the group off,
Collectedstops, and the framework writes error lines and a warning toBepInEx\logs\obenseuer.moddingframework.notes.log.All,Held,HasandGivekeep working. (The game method isNoteController.AddNote(NoteInfo, bool).)