Mail schedules
Status: experimental.
What it is
Every day the game posts a little random mail to the player: items, notes, recipes and parcels. The part of the game that does this is a random mail sender. This API lets your mod list the random mail senders in the scene you are in and make one send its mail right now, instead of waiting for its daily time.
When to use it
- Send the player a batch of the game’s own random mail when something happens in your mod, for example after a quest step.
- Find out what kinds of mail a sender can send.
- Test how your mod reacts to new mail without waiting for the next day.
Quick example
This mod waits until a save is loaded, then makes every random mail sender in the scene send one mail.
using ObenseuerCore;
[ModInfo("example.mailnow", "Mail Now", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class MailNow : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (MailSenderInfo sender in MailSchedules.Senders())
{
if (MailSchedules.SendRandom(Mod.Id, sender.Id, 1, 2))
Log.Info("Sender " + sender.Id + " has " + sender.Entries + " entries, and sent one mail.");
else
Log.Warn("No mail was sent for " + sender.Id + ". See the limits below.");
}
});
}
}
Mod.Id is your mod’s id, the first value in ModInfo. See the glossary. Events.WhenReady runs your code once a save is loaded, or at once if one already is. See Using events. The limits are in the Limits section below.
Common tasks
Each example below is a complete mod, and each one runs once a save is loaded, because the senders are in the scene.
Send each sender’s own daily mail now
The sender uses its own smallest and largest amount, which in the game’s files is 1 to 3 mails (so one or two). SendNow returns true when the mail was sent. The limits are in the Limits section below.
using ObenseuerCore;
[ModInfo("example.mailsendnow", "Mail Send Now", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class MailSendNow : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (MailSenderInfo sender in MailSchedules.Senders())
{
if (MailSchedules.SendNow(Mod.Id, sender.Id))
Log.Info("Asked " + sender.Id + " to send its mail.");
else
Log.Warn("No mail was sent for " + sender.Id + ". See the limits below.");
}
});
}
}
Send a number of mails you choose
The largest amount is not included, so 3, 6 sends 3, 4 or 5 mails. SendRandom returns false for amounts that make no sense, as the Limits section below says.
using ObenseuerCore;
[ModInfo("example.mailsendrandom", "Mail Send Random", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class MailSendRandom : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (MailSenderInfo sender in MailSchedules.Senders())
{
if (MailSchedules.SendRandom(Mod.Id, sender.Id, 3, 6))
Log.Info("Asked " + sender.Id + " to send 3 to 5 mails.");
else
Log.Warn("No mail was sent for " + sender.Id + ". See the limits below.");
}
});
}
}
See what kinds of mail a sender can send
This names every kind of mail the sender can choose from.
using ObenseuerCore;
[ModInfo("example.mailnames", "Mail Names", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class MailNames : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (MailSenderInfo sender in MailSchedules.Senders())
Log.Info(sender.Id + ": " + string.Join(", ", sender.MailNames.ToArray()));
});
}
}
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.
MailSchedules list senders and send their mail
Call Senders() to find the random mail senders in the scene, then SendNow or SendRandom to make one send its mail.
public static class MailSchedules
{
public static List<MailSenderInfo> Senders();
public static bool SendNow(string modId, string senderId);
public static bool SendRandom(string modId, string senderId, int min, int max);
}
| Method | Returns | What it does |
|---|---|---|
Senders() | List<MailSenderInfo> | The random mail senders in the scene you are in. |
SendNow(modId, senderId) | bool | Sends mail with the sender’s own amounts. true when it was sent. |
SendRandom(modId, senderId, min, max) | bool | Sends a number of mails from min up to, but not including, max. true when it was sent. |
MailSenderInfo one random mail sender
Senders() gives you one of these for each sender. Pass its Id to SendNow or SendRandom. (It is built from the game’s RandommailSender objects.)
public sealed class MailSenderInfo
{
public string Id { get; }
public int Entries { get; }
public List<string> MailNames { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The sender’s id. Pass it to SendNow and SendRandom. |
Entries | int | How many kinds of mail the sender can choose from. |
MailNames | List<string> | The names the mail can have, each listed once. |
Things to know
Senders()lists the random mail senders in the scene you are in. For each one you get its id, how many kinds of mail it can choose from, and the names that mail can have. (It lists the activeRandommailSenderobjects of the active scene.Entriesis the size of the sender’srandomMaillist.MailNamescollects themailNamesof every entry, without repeats.)- Every sender has the same id, so you can only reach one sender in a scene. If a scene has more than one sender,
SendNowandSendRandomuse the first one they find. (The id is the game’s constantRandommailSender.GUID, which isRandomMail, so every sender reports it.) SendNowsends as many mails as the sender would on its own. In the game files that is one or two mails, chosen at random each time. (The sender’s privateminAmountandmaxAmountare 1 and 3, and the game’sUnityEngine.Random.Range(int, int)leaves out the top value.)SendRandomsends a random number of mails fromminup to, but not including,max. SoSendRandom(id, sender, 1, 2)always sends exactly one mail. Ifminandmaxare equal it sendsminmails. Each mail picks one of the sender’s kinds of mail at random, so two mails can be the same kind.- Sending mail now does not change the sender’s own daily schedule. The game still sends its usual mail on its usual day, so the player gets both.
- You can’t read when the next scheduled mail comes, and you can’t reach the game’s fake bills. The game keeps the time of the last send in a field that mods can’t see, and it makes its fake bills inside the sender where nothing outside can call them. (
lastSendis a protected field andSendFakeInvoiceis private.) - The framework keeps no data of its own for this API. The game saves its own sender state.
Limits
SendRandomrefuses amounts that make no sense. It returnsfalsewhenminis below 0 ormaxis belowmin. It throws anArgumentExceptionwhenmaxis above 100. Each mail is a separate send and the game waits for all of them, so a huge number would freeze the game. (Each mail is oneMailController.SendMailcall, made one after another on the main thread.)- Both send calls return
falsewhen they can’t send. That happens when your mod id is empty, when no sender has that id, and while a save is loading or saving. - A
trueresult means the framework asked the game to send, not that the mail arrived. If the game fails while sending, for example because a kind of mail has nothing in it, the game writes the error to its own log and the call still returnstrue. (RandommailSender.SendRandomMailcatches every exception itself and logs it withDebug.LogException.)
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. - 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 (
OnLoad, event handlers,Update), not from a background thread you started. The game and Unity only work safely on one thread, the main thread (see the glossary).
What to check for and what comes back
- This API changes no game method, so it has no
IsAvailableand no feature group. It only calls the game’s own mail sender. Senders()gives an empty list when there is no sender. That is normal on the main menu, or in a scene without one.- If the game itself fails while the framework works, you get
falseor an empty list instead of an error. The first failure of each call goes to the log, filed under your mod id. Later failures of the same call are not logged again.