Defining furniture
This page uses Sprite from Unity. The glossary says what each one is.
FurnitureRegistry is where you add furniture. A furniture is described by a FurnitureDefinition and has an id, a text name that you use for it afterwards. The id looks like obx:<mod id>/<key>.
FurnitureRegistry adding and finding furniture
This is the class you use to add and find furniture. Define adds a furniture, and Get returns the game’s object for one.
public static class FurnitureRegistry
{
public static bool IsAvailable { get; }
public static string IdFor(string modGuid, string key);
public static string Define(string modGuid, string key, FurnitureDefinition definition);
public static Furniture Get(string id);
public static bool Give(string furnitureId, int amount);
public static List<string> ShopGuids();
public static event Action Loaded;
}
| Method | Returns | What it does |
|---|---|---|
IdFor(modGuid, key) | string | The id a furniture would have. It does not define anything. |
Define(modGuid, key, definition) | string | Adds a furniture. Returns its id, or null when refused. |
Get(id) | Furniture | The game’s furniture object for an id. null when there is none. |
Give(furnitureId, amount) | bool | Puts pieces into the player’s stock. true when done. |
ShopGuids() | List<string> | The id of every furniture shop in the loaded levels. |
| Property | Type | What it means |
|---|---|---|
IsAvailable | bool | true when furniture can be added. |
| Event | Gives you | When it fires |
|---|---|---|
Loaded | Nothing | The furniture has been restored after a save loaded. |
FurnitureDefinition the description of one new furniture
You fill one in and pass it to FurnitureRegistry.Define. Set TemplatePath or TemplateName to pick the game piece to copy, and leave the other fields null or -1 to keep the copy’s own values.
public sealed class FurnitureDefinition
{
public string TemplatePath;
public string TemplateName;
public string Title;
public string Details;
public int PriceOC = -1;
public int PriceRM = -1;
public Sprite Icon;
public string[] ShopIds;
public int ShopStock = 3;
public ModelHandle Model;
public FurniturePlacement Placement = FurniturePlacement.Template;
public Bounds[] Colliders;
public string CategoryName;
}
| Property | Type | What it means |
|---|---|---|
TemplatePath | string | The game piece to copy, by its asset path. Set this or TemplateName. |
TemplateName | string | The game piece to copy, by its name in the console’s furniture list. Set this or TemplatePath. |
Title | string | The name the player sees, 1 to 200 characters. null keeps the template’s. |
Details | string | The description, up to 2000 characters. null keeps the template’s. |
PriceOC | int | The price in OC, 0 or more. -1 keeps the template’s. |
PriceRM | int | The price in RM, 0 or more. -1 keeps the template’s. |
Icon | Sprite | The picture shown in menus. null keeps the template’s. |
ShopIds | string[] | The shops that sell it, by shop id. See ShopGuids. |
ShopStock | int | How many each listed shop gets at every restock, 1 or more. |
Model | ModelHandle | Your own 3D model. null keeps the template’s look. |
Placement | FurniturePlacement | Where the piece may be placed. |
Colliders | Bounds[] | Your own collision boxes. null keeps a box fitted to the model. |
CategoryName | string | A build menu group for the piece. null keeps the template’s group. |
FurniturePlacement where a piece may be placed
Placement in a FurnitureDefinition takes one of these. The framework sets the preview’s orientation from it, using the game’s PlaceType of the same name for Floor, Wall, Ceiling and FloorAndWall. (It calls ObjectPreview.SetOrientationType.)
public enum FurniturePlacement
{
Template,
Floor,
Wall,
Ceiling,
FloorAndWall,
Any
}
| Value | Meaning |
|---|---|
Template | Keeps the template’s own rule. |
Floor | On the floor only. |
Wall | On a wall only. |
Ceiling | On a ceiling only. |
FloorAndWall | On a floor or on a wall. |
Any | On a floor, a wall or a ceiling. |
Things to know
- A new furniture is a copy of one the game already has. It keeps the template’s behaviour and, unless you set a model, its look. (The copy keeps the template’s prefabs.)
- Set exactly one of
TemplatePathandTemplateName.TemplatePathis the game’s asset path of a piece, for exampleAssets/Content/Prefabs/Building System/Furniture/Tables/Wooden Table.asset.TemplateNameis the asset’s name as listed by the developer console’s furniture list. (TemplatePathis the Addressables key of a vanilla furniture.) - Call
DefinefromOnLoad. Calling it again with the same mod id and key gives back the same id, and the first definition is kept. The key is compared exactly, soDeskanddeskare two different furniture. Definereturnsnullonce a furniture has been built or a save with mod furniture has loaded. (GetclosesDefinefor the session too, because it builds the piece on the first call.)- A bad template is only noticed later. The game may not be ready when
Defineruns, so an unknown or unusable template is not refused byDefine. It is written to the log when the first piece is built. IdFor(modGuid, key)gives the id without defining anything. The mod id and the key may contain letters, digits, dots, underscores and dashes, 1 to 64 characters each. The key may not contain a/.IdFor,GiveandShopGuidswork even whenIsAvailableisfalse, butDefinedoes not.- Furniture is saved under its own id, and the game can still load the save without your mod. Without the mod, the saved piece loads as its template, so no placed piece is lost. Putting the mod back brings back the real furniture. (The saved id is
obx:<modGuid>/<key>and the saved path stays the template’s path.) - A piece whose mod is missing loads as a stand-in. With the framework installed and the mod missing, a saved piece loads as its template, keeps its id, title and prices, and one warning per id goes to the log. If the player places it again, they get the plain template.
- Furniture appears in your listed shops at every restock, and once in an existing save when it loads. A piece the player has bought out does not come back on load. The shop also buys the piece back from the player. (A load that never finishes keeps the offers in memory, and the next save writes them, so stock sold before that load stays sold.)
- Stock for a shop that is queued during a load is applied once the furniture has been restored, or at the next in-game hour.
Get(id)returns the game’s object for a furniture, ornull. It builds the piece on the first call. If the build fails, one line goes to the log. It never throws.Loadedfires each time the furniture is restored. That is when a save loads and when the main menu resets. A handler that wants to useGetshould wait for it. (It runs inside the framework’s save data load, which is one of theEvents.Loadedhandlers.)Building.FurnitureLoadedfires after each save load, for each of your pieces in the loaded levels. That includes the load after a level change. Write the handler so that running it twice on the same piece does no harm, for example checkGetComponentbeforeAddComponent. It fires afterLoaded, soGetworks in it. See Housing.CategoryNameputs the piece in its own group in the build menu and the furniture shops. The same name from two mods, ignoring capitals, is one shared group. A name that matches a game group, such asChairsorKitchen, uses the game’s group.- The group is set when the piece is first built and is not saved, so a piece that loads without its mod shows its template’s group. (The game adds the
ItemsandSurfacestabs itself, so a piece named for one of them would show the tab twice. If the category patch is not installed, the template’s group is kept, with one warning at startup.) Placementsets where the piece may be placed.Templatekeeps the template’s rule, andAnymeans a floor, a wall or a ceiling. If the template has no placement preview at all, the piece keeps its own placement and one line goes to the log. (The framework finds the preview prefab’sObjectPreviewwithGetComponentInChildren, the same lookup the game makes on the live preview (BuildingSystem), and callsObjectPreview.SetOrientationType.Floor,Wall,CeilingandFloorAndWallmap to the game’sPlaceTypeof the same name, andAnymaps toPlaceType.all.)- A
Ceilingpiece is shown on the ceiling display in the shop. Other placements keep the template’s display. (It sets the piece’sdisplayStyleto the game’s ceiling style.) - How the game turns each piece when it is placed matters if you use your own model. A floor piece has the model’s up (+Y) up and its front on +Z. A wall piece is turned so that +Y points out of the wall, away from the surface, and +Z points up. A ceiling piece has +Y pointing down.
- For
FloorAndWallon a wall, the piece hangs upright only when the template can do that, which a mod piece keeps from its template. A template that can’t hangs like aWallpiece. (FloorAndWallhangs upright only when the template’sObjectPreviewhas analternativeOrigin.) - When you model a piece, build it the way the game will turn it. A wall piece has its height on +Z and its room-facing side on +Y. A ceiling piece has its room-facing side, the underside, on +Y, so its top is on -Y, and its front on +Z.
- Use
EulerOffset(in the model options) to bring an export to this. A wall piece exported standing up with its front on -Z needs a 90 degree turn about X so that the height ends up on +Z. IsAvailableisfalsewhen furniture can’t be added. It isfalsewhen the framework could not set up its furniture features, or when the feature group was turned off.
Limits
Definealso returnsnulland logs one line whenCollidershas more than 64 boxes.- A bad value throws an
ArgumentException. That is an invalid mod id or key, aTitleorDetailsoutside its length, a price below -1,ShopStockbelow 1, or anamountforGivebelow 1 or above 1000. An undefinedPlacementthrows too. Getreturnsnullfor anull, unknown or vanilla id, and until the furniture has been restored once after a save load (afterLoadedfires).Give(furnitureId, amount)returnsfalsewhile a save is loading or being written, when the id is unknown, and when no building is loaded.CategoryNamevaluesAll,None,SurfacesandItemsare refused with anArgumentException, and so is a name over 40 characters.Surfacesand trains cannot be a template, and they are refused.- A
TemplateNamethat has no asset path is refused when the first piece is built.