Furniture Modding
Furniture packages should be located in subfolders in the "Furniture" folder with the game. A furniture package will inherit its name from the folder name.
Helpful console commands
- RELOAD_FURNITURE - Reloads all furniture mods immediately
- FURNITURE_DEBUG True - Adds an in-game overlay to the selected furniture showing its boundary, navigation mesh, interaction points and snap points
- FURNITURE_THUMBNAIL X - Generates a thumbnail for the the furniture named X and places it in the Furniture folder. This command only works in the main menu
- EXPORT_FURNITURE_BOUNDS X - Exports the auto generated furniture boundaries (created by adding the AutoBounds True record) and puts it in the TyD file for the furniture named X. This should be done when you're finished with the furniture, to avoid having the game generate them on every startup. This also allows you to change them in the TyD file.
- UNLOCK_FURNITURE - Allows you to place furniture that hasn't been unlocked in-game
Furniture modding
TyD file layout
Each furniture should be defined in separate TyD files.
The Furniture table should have a Thumbnail record with a relative path to a 128x128 thumbnail to use in-game.
The Furniture table can also have a Base record to inherit from an existing furniture from the game. If you define the record AutoBounds True, the game will auto generate the navigation mesh, building boundary, vertical span, x and y grid offset, width and vertical offset for wall furniture, and collision box for selection, based on the furniture meshes, otherwise these can be controlled in the Furniture component by setting the variables BuildBoundary, NavBoundary, Height1, Height2, OnXEdge, OnYEdge, WallWidth, YOffset and the BoxCollider component.
You can check your build and navigation boundary, and interaction and snap points by enabling furniture debugging using the command FURNITURE_DEBUG True in the console and selecting the furniture you want to review in-game. The navigation boundary is located on the floor and goes from green to pink. The build boundary also illustrates Height1 and Height2 and goes from cyan to red. Interaction points are blue and snap points are yellow:
It is highly recommended that you run the command EXPORT_FURNITURE_BOUNDS with your furniture name as parameter, if you've used the AutoBounds True record. This command bakes the bounds information in the TyD file so it doesn't have to be recalculated each time the game launches.
Hierarchy
Every object you add to your furniture can be given a name using a ComponentName record and can be added as a child transform to another object by using a TransformParent with the ComponentName of the other object. If TransformParent is not defined, the object will be added to the root furniture object.
You can define a list of empty Transforms using a Transform list with object that define Position, Rotation (in degrees as Euler angles) and Scale.
You can add Unity components to any tranforms and objects by using the TransformParent record in a table that has the same name as the UnityComponent you want to add, e.g. Light.
Meshes and materials
Meshes will be loaded from the Models list. The Models list should contain a list of tables that define meshes. Each mesh table should have a File record, with the relative path to an .obj file, a Position, Rotation (in degrees as Euler angles) and Scale (Should always be positive). Optionally, the mesh tag can have a TransformParent record, to make it a child transform of an object with the specified name, a ComponentName record to name the mesh for later reference(e.g. to make it a parent for another mesh or transform) and a Material record to give it a material.
If you omit the Material record, it will automatically select the in-game default furniture material, which has great performance and allows the player to colorize the furniture. The default material works by mapping the Red, Green and Blue channels to the colors the players has picked for the furniture, while greyscale pixels remain greyscale. Furthermore, the alpha channel is used to control material smoothness and emission/glow. 100% alpha will make the material rough and then smoother as the alpha decreases, 0% alpha will make the material glow in the dark (Note that some image editing software will destroy your RGB channels if the alpha channels is 0%, e.g. Photoshop). If you use the default material, you should UV map your furniture to the texture pictured below. You can also set the Material record to Glass to use the in-game glass shader.
If you want to use your own materials and textures, you need to add a Materials.tyd file. It should contain a list of tables that define your materials. The first record in a material table should be the Type, which can be one of the following values:
- Unity, the default Unity standard shader as of Unity 2017.4 (http://unity3d.com/get-unity/download/archive)
- Standard, the default furniture shader used in-game
- Atlas, a shader that allows the player to change the furniture texture, which works well for paintings
For each material you can add a Texture record to refer directly to an image file to use as the materials main texture. Each material table can have 3 tables
- Textures
- Floats
- Colors
Each can contain a set of records that define textures, numbers and colors, where the record names should reference actual shader variables as defined in Unity, e.g:
- _MainTex
- _BumpMap
- _OcclusionMap
- _Metallic
- _Glossiness
- _BumpScale
- _Color
The standard furniture shader
As described above the default standard furniture shader works by mapping the Red, Green and Blue channels to the colors the players has picked for the furniture. This also means that dark red will darken the color the player has chosen, so the color #800000, will darken the player's primary color by 50%. This can be used to add shadows and occlusion to your textures, while still allowing for player coloring.
Outside of the Texture record, this shader can also have a NormalMap record, to add normal maps to your furniture. Note that this incurs a small performance penalty. The alpha value of the normal map is used to make the material more or less metal looking, a value of 0% alpha will make the material look completely metallic.
There is also an ExtraMap record, which allows you to control smoothness with the Red channel, Emission strength with the Green channel and finally the Blue channel controls whether to use the color in the main texture as is or map it to the player color. The Blue channel of this texture will allow you to have static colors on parts of your furniture, while still allowing for player coloring. Having an ExtraMap makes the shader ignore the alpha channel of the main texture.
The atlas shader
The atlas shader allows you to use a texture as grid, and only map a section of it to the furniture at a time. For this you need to define these variables in the Furniture table:
- AtlasObject - The mesh that should use atlas texturing, this should be the ComponentName defined in the Mesh section of your furniture.
- AtlasDimensions - The width and height of your texture atlas, e.g. [ 2; 2 ] if you have a 2 by 2 texture atlas
- AtlasCount - The amount of images in your atlas, e.g. 3 if you're only using the (first row) and (second row, first column) of your atlas
Interaction points
Interaction points control how employees can interact with a piece of furniture.
If you have used the Base record, the furniture will inherit its interaction points from the base furniture, unless you add the "InteractionPoints" list.
Interaction points should define a Name, which at this point in time includes "Use", "Repair", "Serve", "Social" and "Visit" for the base game. They should also have Position and Rotation defined. You can define a ComponentName record to name the interaction points for later reference.
Interaction points can also define Animation(main animation used when interacting, ignored for some interactions), SubAnimation(used for repairs, 0, 1 or 2 for repair height), MinimumNeeded(how many need to be reachable before the player gets a warning) and ReachCheck(whether we should warn the player at all).
Finally, it can define a Child, which is the ith interaction point defined (Zero index). If you want a number of interaction points to only be used by one employee at a time you should define children such that they form a dependency loop(E.g. 0->1->2->3->0). When one interaction point gets reserved, the children will recursively get reserved as well.
Snap points
Snap points are points at which certain furniture can be snapped to other furniture, like chairs to tables.
If you have used the Base record, the furniture will inherit its snap points from the base furniture, unless you add the "SnapPoints" list.
Snap points should define a Name, which will control which furniture can snap to it, currently in use in the base game is "PCAddon", "AtTable" and "OnTable". "Food" is also used in the game for tables which should allow for food but not OnTable furniture, like benches. They should also have Position and Rotation defined.
You can define a ComponentName record to name the snap point for later reference.
Snap points can also have "CheckValid" defined, which controls whether the game should allow for something to be placed no matter if there are collisions. This collision calculation is heavy, but not needed for stuff like computers and computer add-ons, so you can leave this off in most cases.
Finally, snap points can define Links, which is a comma separated list of linked snap points, referencing the ith snap point defined (zero index). Links are currently used to link chairs with computers and chairs with food. E.g. for the square table, the OnTable point is linked to all AtTable points around it. The bench does not have chairs and instead the "Use" interaction points are defined in the same order as its "Food" snap points so the game knows where to put the employee’s food and how to save it to a file.
Replacements/upgrading
The root tag can use the UpgradeFrom and UpgradeTo attributes to control which furniture can be replaced with your furniture and which furniture your furniture can be replaced with. A furniture can be replaced by any furniture which UpgradeFrom or UpgradeTo matches the furniture's UpgradeTo.
<Root UpgradeFrom="Server" UpgradeTo="Big Server">
This furniture can be replaced by any furniture that has UpgradeFrom="Big Server" or UpgradeTo="Big Server", and any furniture that has UpgradeTo="Server" or UpgradeTo="Big Server" can be replaced by this furniture, but this furniture cannot be replaced by furniture that has UpgradeTo="Server". This is used to avoid upgrading a server that sits on the floor to a server that sits on a table, as the table would have to be created out of thin air.
In-game upgrade groups
- Big ProductPrinter (Large Product Printer)
- Big Server (Tower Server, Server Rack, Big Server Rack)
- Chair (Cheap Chair, Office Chair, Executive Chair)
- Coffee (Instant Coffee, Coffee, Espresso Machine)
- Computer (Old Computer, 90s Computer, Laptop, Modern Computer, HoloComputer)
- Couch (Waiting Chairs, Couch)
- Cubicle (Cubicle wall)
- Floor Decoration (Floor Lamp, Outdoor Lamp, Floor Plant, Big Plant)
- Misc Ceiling (Ceiling Fan, Lamp, Fluorescent lamp)
- Misc Food (Vending Machine, Fridge, Stove, WaterCooler)
- PCAddon (Phone, Drawing Tablet, Calculator, Inbox)
- ProductPrinter (Large Product Printer, Small Product Printer, Medium Product Printer)
- Server (Tower Server, Server Rack, Big Server Rack, Small Server, Medium Server)
- Standard Table (Table, Glass Table)
- Table Decoration (Desk Lamp, Table Plant, Table Cactus)
- Toilet (Toilet)
- Tree (Small tree, Small pine tree)
- WallDeco (Wall Lamp, Painting, TV, Clock)
- WallVent (Ventilation, AC Unit, Industrial ventilation)
Limitations
The game will be able to destroy tables if you're replacing something on a table with something that is not on a table, e.g. small server and server rack, but not the other way around. The game won't build new tables automatically, so you should be careful in differentiating between "UpgradeFrom" and "UpgradeTo", as these will not always be the same.
Components
Other than the tags described so far, all tags are interpreted as Unity MonoBehaviour components. If you added a tag called “<BoxCollider>”, the children tags would be interpreted as variables of the BoxCollider component and if the component does not exist, it will be instantiated for the Furniture. All MonoBehaviour variables in a Component will use the ComponentName reference set in the other tags, or alternatively you can add a Transforms tag to define empty objects with a Name, Position and Rotation. Using “self” as a value for a MonoBehaviour variable will find the corresponding component in the furniture object itself. This allows for anything to be added to a furniture, even MonoBehaviors from custom mods.
If you set the Parent attribute to a ComponentName reference or Transforms reference, the component will be fetched/added to the object instead. This could be used to add a PointLight component to an empty object to make your own lights. Make sure to add a LampScript component to your furniture object if you’re making a custom lamp, so the game can control shadows and lighting.
Setting a Destroy attribute to “True” for a tag will remove that MonoBehavior from the furniture.
Note that using MonoBehaviors from other mods requires setting the Namespace attribute and the Assembly attribute so the game knows where to find the classes, e.g. <MyCustomComponent Namespace=”MyMod.Behaviours.” Assembly=”MyMod”>.
Furniture
This component will always be present in a furniture. These variables have been extracted directly from the class description, note that some of these variables should remain unchanged:
- BuildBoundary
- NavBoundary
- FinalBoundary
- OnXEdge
- OnYEdge
- GenerateBoundaryOnStart
- CanRotate
- Height1
- Height2
- IgnoreBoundary
- BasementValid
- OnlyExteriorWalls
- WallFurn
- SnapPoints
- SnappedTo
- IsSnapping
- SnapsTo
- CanAssign
- ReverseLowPass
- ValidIndoors
- ValidOutdoors
- ValidOnFence
- NeedsChair
- ManualUsageCalculation
- Thumbnail
- InteractAnimation
- MaxQueue
- InteractStartClip
- InteractEndClip
- UpgradeFrom
- UpgradeTo
- InDemo
- ForceAccessible
- ITFix
- UnlockYear
- TemperatureController
- AlwaysOn
- OnWithParent
- ComputerTransform
- OriginalOffset
- PCAddonOffset
- OriginalRotation
- PCAddonRotation
- PokesThroughWall
- CanLean
- UseStandardMat
- TwoFloors
- OffsetPoints
- InterPoints
- InterPointsReversed
- UpperFloorFrame
- HoldablePoints
- DespawnHoldables
- DespawnHour
- RandomSFX
- SFXFiles
- RandomSFXMin
- RandomSFXMax
- DisableTableGrouping
- InRentMode
- Type
- Category
- FunctionCategory
- Cost
- ButtonDescription
- ComputerPower
- HeatCoolPotential
- HeatCoolArea
- EqualizeTemperature
- RoleBuffs
- Lighting
- Coffee
- Wait
- AuraValues
- Wattage
- Water
- Noisiness
- Comfort
- Environment
- HasConnectionBeam
- ConnectionBeam
- PlacedInEditMode
- MeshBoundary
- RotationOffset
- OriginalPosition
- AudioSrc
- isTemporary
- upg
- PathFailCount
- InteractionPoints
- Parent
- EnvironmentNoise
- ActorNoise
- FinalNoise
- ComputerChair
- Undo
- LastWattUse
- LastWaterUse
- Table
- _defaultColorGroup
- PrimaryColorName
- SecondaryColorName
- TertiaryColorName
- ColorPrimaryEnabled
- ColorSecondaryEnabled
- ColorTertiaryEnabled
- ForceColorSecondary
- ForceColorTertiary
- ColorPrimaryDefault
- ColorSecondaryDefault
- ColorTertiaryDefault
- Colorable
- ColorableLights
- FullColorMaterial
- LightPrimary
- TreeLeaves
Upgradable
This component makes this furniture repairable. These variables have been extracted directly from the class description, note that some of these variables should remain unchanged:
- TheScreen
- SmokePosition
- SmokeEmitter
- Quality
- RepairTime
- AtrophyModifier
- LastRepair
- Descriptions
- DisableObjs
- TimeToAtrophy
- OnMat
- OffMat
- DegradeAlways
- ModifiableAtrophy
- AffectedByTemp
- Broken
- CanBreak
- ChangeColorOffSecondary
- ChangeColorOffTertiary
- furn
- Anim
TableScript
This component handles tables and table grouping for meeting, canteens, etc.
LampScript
This component handles lighting and shadows for lamps. For best results, the "Lights" variable should contain references to the light components in your furniture, by using the name of the transforms you added the Light components to.
Server
This component makes this furniture a server. It is recommended to inherit from a server furniture, as the server component uses some in-game objects, like a text mesh and a wire mesh. Use the Power variable to change its power in mb.
Example
XML layout
<Root Base="Server Rack" Thumbnail="BigServerRackThumb.png" UpgradeFrom="Server Rack"> <Models> <Model> <File>BigServerRack.obj</File> <Position>0,0,0</Position> <Rotation>0,270,0</Rotation> <Scale>1,1,1</Scale> </Model> </Models> <InteractionPoints> <InteractionPoint> <Name>Repair</Name> <Position>0,0,1</Position> <Rotation>0,180,0</Rotation> <Animation>Repair</Animation> <Child>1</Child> </InteractionPoint> <InteractionPoint> <Name>Repair</Name> <Position>0,0,-1</Position> <Rotation>0,0,0</Rotation> <Animation>Repair</Animation> <Child>0</Child> </InteractionPoint> </InteractionPoints> <Furniture> <Cost>12000</Cost> <Wattage>50</Wattage> <Noisiness>2</Noisiness> <UnlockYear>2010</UnlockYear> <ButtonDescription>When you can't get enough server</ButtonDescription> <ColorPrimaryDefault>0.2,0.2,0.2,1</ColorPrimaryDefault> <PrimaryColorName>0.2,0.2,0.2,1</PrimaryColorName> <ColorSecondaryDefault>0.3,0.3,0.3,1</ColorSecondaryDefault> <ColorTertiaryDefault>0,0.8,1,1</ColorTertiaryDefault> <ColorSecondaryEnabled>True</ColorSecondaryEnabled> <ColorTertiaryEnabled>True</ColorTertiaryEnabled> <ForceColorSecondary>False</ForceColorSecondary> <ForceColorTertiary>False</ForceColorTertiary> <PrimaryColorName>Casing</PrimaryColorName> <SecondaryColorName>Casing</SecondaryColorName> <TertiaryColorName>LED</TertiaryColorName> <BuildBoundary> -0.99,-0.49 0.99,-0.49 0.99,0.49 -0.99,0.49</BuildBoundary> <NavBoundary> -1,-0.5 1,-0.5 1,0.5 -1,0.5</NavBoundary> <Height1>0</Height1> <Height2>1.8</Height2> <OnXEdge>True</OnXEdge> </Furniture> <Upgradable> <UpgradePrice>1000</UpgradePrice> </Upgradable> <Server> <Power>10000</Power> </Server> <BoxCollider> <center>0,0.9,0</center> <size>2,1.8,1</size> </BoxCollider> </Root>