Shop hours
Status: experimental.
What it is
Traders in the game open and close at set hours, and the player sees “(Trade)” or “(Closed)” next to a trader’s name. This API reads those opening hours for each day of the week and tells you whether a trader is open right now. It only reads. It can’t change opening hours or open or close a shop.
When to use it
- Show the opening hours of a trader in your own screen.
- Check whether a trader is open before your mod sends the player there.
- Find out why a trader is closed: the clock says closed, or the game is holding it closed.
Quick example
This mod, once a save is loaded, logs for each trader whether it is open now and whether its opening hours say it should be.
using ObenseuerCore;
[ModInfo("example.hourswatch", "Hours Watch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class HoursWatch : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (ShopInfo shop in Shops.All())
if (shop.Kind == ShopKind.Trader)
Log.Info(shop.Name + ": open=" + ShopHours.IsOpen(shop.Id) + ", by the clock=" + ShopHours.IsScheduledOpen(shop.Id));
});
}
}
Shops.All() is on the Economy page. It gives each shop’s Id, which is what you pass to ShopHours. WhenReady runs your code once the game is ready. See Using events for more on it. 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 shops are in the scene. Each one takes the first trader from Shops.All(), so there is no key to write down by hand.
Log the opening hours of one shop
This writes one line per day. The limits are in the Limits section below.
using ObenseuerCore;
[ModInfo("example.shophourslog", "Shop Hours Log", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class ShopHoursLog : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (ShopInfo shop in Shops.All())
{
if (shop.Kind != ShopKind.Trader)
continue;
foreach (ShopDayInfo day in ShopHours.Hours(shop.Id))
Log.Info(shop.Name + " " + day.Day + ": " + day.Opens + " to " + day.Closes);
break;
}
});
}
}
Check whether a shop is open to the player now
using ObenseuerCore;
[ModInfo("example.shopopencheck", "Shop Open Check", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class ShopOpenCheck : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (ShopInfo shop in Shops.All())
{
if (shop.Kind != ShopKind.Trader)
continue;
if (ShopHours.IsOpen(shop.Id))
Log.Info(shop.Name + " is open.");
else
Log.Info(shop.Name + " is closed.");
break;
}
});
}
}
Find out whether the game holds a shop open or closed
IsOpen and IsScheduledOpen disagree when the game holds a trader open or closed apart from the clock, for example while the player stands close by. The limits are in the Limits section below.
using ObenseuerCore;
[ModInfo("example.shophoursmismatch", "Shop Hours Mismatch", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public class ShopHoursMismatch : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (ShopInfo shop in Shops.All())
{
if (shop.Kind != ShopKind.Trader)
continue;
if (ShopHours.IsOpen(shop.Id) != ShopHours.IsScheduledOpen(shop.Id))
Log.Info(shop.Name + " is not following its opening hours right now.");
break;
}
});
}
}
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.
Opening hours
ShopHours reads the opening hours of a trader and whether it is open. A shop is named by its Id, which you get from Shops.All() on the Economy page.
ShopHours reads opening hours and whether a shop is open
This is the class to start from. Hours(shopId) gives the opening hours for each day, and IsOpen and IsScheduledOpen tell you whether a trader is open to the player now and whether its clock says it should be.
public static class ShopHours
{
public static List<ShopDayInfo> Hours(string shopId);
public static bool IsOpen(string shopId);
public static bool IsScheduledOpen(string shopId);
}
| Method | Returns | What it does |
|---|---|---|
Hours(shopId) | List<ShopDayInfo> | The opening hours for seven days, Monday first. Empty when the shop is not found. |
IsOpen(shopId) | bool | true when the trader is open to the player now. false when not found. |
IsScheduledOpen(shopId) | bool | true when the opening hours say open at the current game time. false when not found. |
ShopDayInfo the opening hours for one day
ShopHours.Hours(shopId) gives you seven of these, Monday first. Opens and Closes are the hours, and the shop is closed all day when they are the same.
public sealed class ShopDayInfo
{
public DayOfWeek Day { get; }
public int Opens { get; }
public int Closes { get; }
}
| Property | Type | What it means |
|---|---|---|
Day | DayOfWeek | The weekday (System.DayOfWeek). |
Opens | int | The hour it opens, 0 to 24. |
Closes | int | The hour it closes, 0 to 24. |
Things to know
- Vending machines have no opening hours. For a vending machine,
Hoursis empty andIsOpenandIsScheduledOpenare bothtrue. (the shop id isShopInfo.Id, theGUIDof aTrade(a trader) or aVendingMachine) Hoursalways lists seven days, Monday first. (Monday is the game’s weekday 0)- If
OpensandClosesare the same hour, the shop is closed all day. - If
Opensis beforeCloses, the shop is open fromOpensuntilCloses. - If
Opensis afterCloses, the shop is open late into the night, and the night part counts for the same weekday row. A row does not carry on into the next day. For example, Monday withOpens20 andCloses4 means open on Monday from midnight until 04:00 and again from 20:00. (the game applies both windows,Opensuntil midnight and midnight untilCloses, to the same weekday row) IsOpenis what the player sees, “(Trade)” or “(Closed)”.IsScheduledOpenis only what the opening hours say at the current game time, so the two can disagree. The game can hold a trader open or closed apart from the clock, for example while the player is standing close by, and for a short moment right after a scene starts. (IsOpenreadsTrade.open;IsScheduledOpenreadsOpeningTimes.IsOpen(); they differ when the trader hasautoOpenCloseoff or the player is near, and in the first half second after a scene starts)
Limits
- You can only read opening hours. You can’t change a shop’s opening hours or open and close a shop. The framework only reads what the game has.
- A shop that is not found gives the plain default. An unknown id, an empty id, or a shop that is not in the scene you are in gives an empty list or
false. Anullid throws anArgumentException. (the lookup covers only the active scene)
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.shophours.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 (
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). - 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 or
false. (if the game itself fails, you get the same plain default and the failure goes to the log at debug level)
If something fails to set up
- There is nothing to set up here. The framework does not change any game method for shop hours, so there is no feature group for this page, no events and no
IsAvailable.