Breeding
What it is
Some places in the game breed animals for you: a pen holds animals, and every game hour the game may add a young one. These places are called breeders. This API lists the breeders in the scene you are in, tells you when a breeder makes an offspring, and tells you when a rat trap goes off. It only reads and listens. It cannot start a breed or set off a trap.
The animals themselves are on the Animals page (Animals.All). The growing plants in a pen are on the Recipes page (Plants.All).
When to use it
- Count how many animals each breeder holds.
- Show a message or log a line when a breeder makes a new animal.
- Tell apart a breed that happens live from the catch-up breeds the game runs while a save loads.
- React when a rat trap catches a rat, a glowing rat, or nothing.
Quick example
This mod writes a log line each time a breeder makes an offspring and each time a rat trap goes off.
using ObenseuerCore;
[ModInfo("example.breedwatch", "Breed Watch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class BreedWatch : FrameworkMod
{
protected override void OnLoad()
{
Breeding.Bred += args =>
{
ItemDef species = Items.Get(args.SpeciesId);
string name = species != null ? species.DisplayName : "animal";
Log.Info("A breeder made a new " + name + (args.OfflineCatchUp ? " while the save was loading." : "."));
};
Breeding.TrapSprung += args => Log.Info("A rat trap went off and caught: " + args.Catch);
}
}
Bred and TrapSprung are events: the framework calls your code each time the thing happens. Items.Get looks up the offspring’s item by the id the event gives you.
Common tasks
List the breeders in the scene and how many animals each holds.
foreach (BreederInfo breeder in Breeding.All())
Log.Info(breeder.Id + " holds " + breeder.Count + " animals.");
Check that the breeding events work before you rely on them.
if (!Breeding.IsAvailable) Log.Warn("Breeding events are off in this game version.");
Ignore breeds that happen while a save is loading. These are the catch-up breeds, and each one has OfflineCatchUp set.
Breeding.Bred += args =>
{
if (args.OfflineCatchUp || SaveData.Busy) return;
Log.Info("A live breed happened at " + args.BreederId);
};
Reference
This is the full list of what the API gives you. Text in brackets at the end of a note is extra detail you can skip on a first read.
Breeders
Breeding.All() gives you one BreederInfo for each breeder in the scene you are standing in. A BreederInfo is a snapshot taken when you asked, so ask again for fresh numbers.
Breeding list the breeders and report whether breeding events work
Use All to list the breeders in the scene and count their animals. Check IsAvailable before you rely on the Bred event.
public static class Breeding
{
public static List<BreederInfo> All();
public static bool IsAvailable { get; }
}
| Method | Returns | What it does |
|---|---|---|
All() | List<BreederInfo> | Every breeder in the scene you are in. |
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | true when the Bred event can fire (see Details). |
BreederInfo a snapshot of one breeder
All gives you one of these for each breeder. Its Id matches the BreederId of a Bred event, and Count is the number of animals it holds.
public sealed class BreederInfo
{
public string Id { get; }
public int Count { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | A name for this breeder that stays the same between sessions in most cases. |
Count | int | How many animals the breeder holds right now. |
All()lists the breeders in the scene you are in, not the ones in the next level the game loads ahead of time. With no save loaded, or on the main menu, you get an empty list. (It lists every activeAnimalBreederin the active scene.)Countis the number of animals the breeder holds. An animal in a pen is a plant to the game, one for each growing spot, so a spot with nothing in it is not counted. (It counts theGrowingSpotslots of the breeder’sGrowingcomponent that have aPlant.)Idis the breeder’s save id, or a path through the scene when it has none. Use it to match aBredevent to a breeder. Do not save a path-style id for later, because it changes when the scene is edited. (The id is the object’sGUIDfield when it is set. Otherwise it is the scene name plus the child path.)
Offspring: the Bred event
Breeding the Bred event
Subscribe with += to hear each time a breeder makes an animal, including the catch-up breeds while a save loads. Each call gets a BredArgs.
public static class Breeding
{
public static event Action<BredArgs> Bred;
}
| Event | Gives you | When it fires |
|---|---|---|
Bred | BredArgs | A breeder has just made an offspring. |
BredArgs what happened
Bred hands you this for each animal a breeder makes. OfflineCatchUp says whether it was a catch-up breed while a save loaded.
public sealed class BredArgs
{
public string BreederId { get; }
public int SpeciesId { get; }
public bool OfflineCatchUp { get; }
}
| Property | Type | What it means |
|---|---|---|
BreederId | string | The same id as BreederInfo.Id. |
SpeciesId | int | The item id of the new animal. Use it with Items.Get. |
OfflineCatchUp | bool | true for a catch-up breed while a save loads, false for a live one. |
Bredfires once for each animal a breeder makes. It only fires when the animal was really placed in an empty spot, so a breed that finds no room does not fire it. (It fires afterGrowing.PlantExternalplaces the plant whileAnimalBreeder.Breedis running.)Bredalso fires while a save is loading. The game works out the hours you were away and breeds once for each of them. A load can raise severalBredevents at once, and each hasOfflineCatchUpset totrue. A breed in the running game has it set tofalse. (LoadDelaycallsBreed(hours * 3600)for each missed hour. The live hourly check callsBreed(1).OfflineCatchUpis true when the age is above 1 second.)SpeciesIdis the animal that was made, not something the breeder owns. A breeder has no list of species. It takes the animals inside it, finds a recipe for them, and plants the recipe’s result. That result is whatSpeciesIdnames. (AnimalBreeder.Breedreads each animal’sbreedItem, looks up a recipe withGetRecipesForItem, and plants the output item.)
Rat traps: the TrapSprung event
Breeding the TrapSprung event
Subscribe with += to hear when a rat trap goes off. Each call gets a TrapSprungArgs.
public static class Breeding
{
public static event Action<TrapSprungArgs> TrapSprung;
}
| Event | Gives you | When it fires |
|---|---|---|
TrapSprung | TrapSprungArgs | A rat trap has just gone off. |
TrapSprungArgs what happened
TrapSprung hands you this. Catch says what the trap holds now, and TrapId says which trap it was.
public sealed class TrapSprungArgs
{
public string TrapId { get; }
public TrapCatch Catch { get; }
}
| Property | Type | What it means |
|---|---|---|
TrapId | string | The trap’s save id, or its path through the scene. |
Catch | TrapCatch | What the trap now holds. |
TrapCatch what a trap holds after it went off
TrapSprungArgs.Catch holds this. Each value says whether the trap caught a rat, a glowing rat, or nothing.
public enum TrapCatch
{
Empty = 0,
Rat = 1,
GlowingRat = 2
}
| Value | Meaning |
|---|---|
Empty | The trap went off with nothing in it. The rat got away. |
Rat | The trap holds a rat. |
GlowingRat | The trap holds a glowing rat. |
TrapSprungfires once, when a rat steps in and the trap changes from set to sprung. A trap that has already gone off does not fire it again. (It fires afterRatTrap.OnTriggerEnterwhentriggeredwent from false to true.)Catchis what the trap shows after it went off. A rat can also get away. The trap still goes off, so you getEmpty. (The game rollsRandom.Range(0, 100) <= 10for the escape.Catchis read from the trap’sstateafter the call.)TrapSprungdoes not depend onBred. A missing breeding change never affectsTrapSprung, and a missing trap change never affectsBredorAll().
What you can’t do (yet)
- You can’t read fish catches. The game has no catch method and no catch event for fish. The fish spawner only refreshes its count on a spawn tick, so a change in it does not tell you that a fish was caught, and there is no safe signal to listen for. (
FishandFishspawnerhave no catch method.Fishspawner.CurrentFisheschanges only on a spawn tick.) - You can’t read a breeder’s food or growth. A breeder has no food of its own. Each animal is fed on its own, and its growth is on the plant, which you can read through
Plants.Allon the Recipes page. (Feeding goes throughAnimalFoodDetectorandAnimal.GetFood.) - You can’t start a breed or set off a trap. Both are driven by the game: the breed by the time of day, and the trap by a rat walking into it. Neither has a safe call for a mod to use. (
AnimalBreeder.Breedruns from the time-of-day tick, andRatTrap.OnTriggerEnteris a physics callback.)
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.breeding.log.
Everything not listed in this reference is internal and may change without notice.
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 breed or a trap happens. If a game update breaks one of them, the framework turns off the feature group that depends on it, and writes error lines and a warning to the group’s log file in BepInEx\logs. A feature group is the name for one such set of changes. This page has three: obenseuer.moddingframework.breeding (the breeding step), obenseuer.moddingframework.breeding.planting (the placing of the offspring) and obenseuer.moddingframework.breeding.trap (the rat trap). These are the names you will see in the log.
- A missing breeding or planting change stops
Bred, and a missing trap change stopsTrapSprung. Each feature group is turned off on its own and logs its own warning.All()keeps working in every case. (The patched methods areAnimalBreeder.Breed(prefix and finalizer),Growing.PlantExternal(postfix) andRatTrap.OnTriggerEnter(prefix and postfix). If installing them throws an error, such as a method name typo, all three groups are turned off.) Breeding.IsAvailabletells you whetherBredworks. It istruewhen the breeding and planting changes both worked. It does not look at the trap change, so it staystruewhen onlyTrapSprungis off.
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 only runs safely on the main thread (see the glossary).
- It is safe to call
All()at any time. With no save loaded, or on the main menu, you get an empty list. Nothing on this page throws an error, and nothing changes the game. - Your event handlers run inside the game’s own call, one at a time. If a handler throws an error, the framework logs it and still runs the other handlers.
Save loads
The framework does not hold back these events while a save loads. A handler that should ignore events raised while a save loads can check SaveData.Busy, which is true while a save is loading or being written.