Showers
Status: experimental.
What it is
A shower is a fixture the player can switch on to wash. This API lets your mod list the showers in the scene you are in, see whether each one is switched on and whether water is really running, switch one on or off, and get told when any shower is switched on or off, by the player or by another mod.
It does not give you the water tank behind a shower or the washing effect on the player. Those stay under the game’s control.
When to use it
- Switch a shower on or off from your mod, for example as part of a scene you build.
- React when the player switches a shower on or off.
- Check which showers have water running.
Quick example
This mod writes a log line every time a shower is switched on or off. Once a save is loaded, it also logs every shower in the scene and whether it is on.
using ObenseuerCore;
[ModInfo("example.showerwatch", "Shower Watch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class ShowerWatch : FrameworkMod
{
protected override void OnLoad()
{
Showers.Changed += args => Log.Info("Shower " + args.Id + " is now " + (args.On ? "on" : "off") + ".");
Events.WhenReady(() =>
{
foreach (ShowerInfo shower in Showers.All())
Log.Info("Shower " + shower.Id + ": on " + shower.On + ", water running " + shower.Water);
});
}
}
Changed is an event: the framework calls your code each time it happens. Events.WhenReady runs your code once a save is loaded, or at once if one already is. See Using events.
Common tasks
Switch every shower in the scene on. The call returns true when the shower is on afterwards.
foreach (ShowerInfo shower in Showers.All())
Log.Info(shower.Id + " switched on: " + Showers.SetOn(shower.Id, true));
Look up one shower by its id. Get returns null when no shower has that id.
ShowerInfo shower = Showers.Get("some-shower-id");
if (shower != null) Log.Info("Water is running: " + shower.Water);
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.
Showers list, read and switch showers
This is the class to start from. All() and Get(id) read showers, SetOn switches one on or off, and Changed tells you when any shower is switched on or off.
public static class Showers
{
public static List<ShowerInfo> All();
public static ShowerInfo Get(string id);
public static bool SetOn(string id, bool on);
public static bool IsAvailable { get; }
public static event Action<ShowerChangedArgs> Changed;
}
| Method | Returns | What it does |
|---|---|---|
All() | List<ShowerInfo> | Every shower in the scene you are in. |
Get(id) | ShowerInfo | The shower with this id. null when nobody has it. |
SetOn(id, on) | bool | Switches a shower on or off. true when it ends up in the state you asked for. |
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | false when the Changed event could not be set up. |
| Event | Gives you | When it fires |
|---|---|---|
Changed | ShowerChangedArgs | A shower was switched on or off. |
ShowerInfo a snapshot of one shower
All() and Get(id) give you one of these. On is the switch, and Water tells you whether water is really running, which can differ from On when the tank is empty. (It is filled from the game’s InteractableShower.)
public sealed class ShowerInfo
{
public string Id { get; }
public bool On { get; }
public bool Water { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The shower’s id. Pass it to Get and SetOn. |
On | bool | true when the shower is switched on. |
Water | bool | true when water is really running. |
ShowerChangedArgs what Changed tells you
The Changed event gives you one of these. On is true when the shower was switched on and false when it was switched off.
public sealed class ShowerChangedArgs
{
public string Id { get; }
public bool On { get; }
}
| Property | Type | What it means |
|---|---|---|
Id | string | The id of the shower, the same as ShowerInfo.Id. |
On | bool | true when it was switched on, false when it was switched off. |
Things to know
All()lists the showers that are switched on in the scene you are in. A shower that is in another scene, or that the game has hidden, is not listed. (It searches the active objects of the active scene forInteractableShower.)- A shower id is a piece of text that names one shower.
All()andGetgive it to you. It is stable as long as the scene does not change. A null or empty id finds nothing. (The id is the shower’s save GUID when it has one, otherwise its scene path, such asScene:Root[0]/Child[2]/Shower[1].) Get(id)gives you a snapshot, not a live view. The values do not change by themselves, so callGetagain for fresh values.OnandWatercan differ.Onmeans the shower is switched on.Watermeans water is really running. A shower with an empty water tank can be on whileWaterstaysfalse, as in the game. (OnreadsInteractableShower.showerOnandWaterreadswaterOn. Both are protected fields, read through reflection.)SetOn(id, on)does what the player does when they use the shower. The shower plays its sound and animation and starts or stops its water. It returnstruewhen the shower exists and ends up in the state you asked for. Asking for the state it is already in does nothing and also returnstrue. (It calls the game’s ownTurnOn()orTurnOff(), then checksshowerOnagain.)Changedfires when a shower goes from off to on or from on to off. It fires no matter who did it: the player using the shower, your mod, or another mod. It does not fire when the water starts or stops on its own, and it does not fire when a save loads, because a loaded shower is already in its saved state. (It is raised afterInteractableShower.TurnOn(bool, bool)andTurnOff(bool)whenshowerOndiffers from its value before the call. The game’s load code calls those methods too, but the state already matches by then.)Changedcan also fire while a save is loading or being written. If your handler should ignore those, checkSaveData.Busy, which istrueduring a load or a save.IsAvailabletells you whetherChangedworks. It isfalsewhen the framework could not set up the event. The reads andSetOncan still work.SetOnignores who owns the shower. The game’s own use does not check the owner either, so a shower marked as owned by a character still switches on. (The field isInteractableShower.owner. The “Owned” text comes fromUpdateUseMessageonly.)
Limits
SetOnreturnsfalsewhen it can’t do the job. That happens when no shower has that id, and while a save is loading or being written.- You can’t start or stop only the water, and you can’t touch the water tank or the washing effect. The water follows the shower: it starts when the shower is switched on and stops when it is switched off or the tank runs dry. Use
SetOnto control it. (StartWaterandStopWaterrun fromTurnOnandTurnOffand from the output tank (LiquidStorage). The cooling effect isSaunaCooling, which the shower turns on and off.)
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. Files: src/Api/Showers.cs, src/Api/ShowersPatches.cs.
Everything not listed in this reference is internal and may change without notice. Items marked experimental may change too.
The framework changes some of the game’s methods when it starts, so it can tell your mod when a shower is switched on or off. All of those changes belong to one feature group, obenseuer.moddingframework.showers, the name you will see in the log.
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). This goes for every member on this page, and for the code that handlesChanged. - It is safe to call any of it at any time, even on the main menu or while a save is loading. If there is nothing to read yet, you get an empty list,
nullorfalse. - Your
Changedhandler runs inside the game’s own call, right after the state changed. If your handler throws an error, the framework logs it and still runs the other handlers.
What to check for and what comes back
- Nothing here throws an error for a bad argument. A null or empty id finds no shower, so
GetgivesnullandSetOngivesfalse. - If the game itself fails while the framework works, you get an empty answer instead of an error. The answer is the plain default (an empty list,
nullorfalse), and the first failure of each member goes to the log as an error. Later failures of that member are not logged again.
If something fails to set up
The framework changes some of the game’s methods when it starts. If a game update breaks one of them, the framework removes all the changes of the feature group that depends on it, turns the group off, and writes error lines and a warning to the group’s log file in BepInEx\logs. The rest of the framework keeps working.
- If the shower event can’t be set up, only
Changedstops. The framework checks the two shower fields and then changes the game’sInteractableShower.TurnOn(bool, bool)andTurnOff(bool). If any of that fails, the feature groupobenseuer.moddingframework.showersturns off and error lines and a warning go to the log.IsAvailableis thenfalse. (If a field is missing,OnandWaterread asfalse, andSetOn(id, true)still calls the game but returnsfalse, because it checks the same field afterwards.)