| Line 7: |
Line 7: |
| | * '''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 | | * '''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. Note that this action removes any special formatting and comments. | | * '''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. Note that this action removes any special formatting and comments. |
| | + | * '''EXPORT_FURNITURE_POINTS X''' - Exports snap and interaction points for the the furniture named X and writes it to its TyD file. You can use this to inspect snap and interaction points for any furniture in the game, by using the Base furniture override. If these are already overridden in the file, it will just write out the same points twice. |
| | * '''UNLOCK_FURNITURE''' - Allows you to place furniture that hasn't been unlocked in-game | | * '''UNLOCK_FURNITURE''' - Allows you to place furniture that hasn't been unlocked in-game |
| | * '''GENERATE_LOCALIZATION''' X - Generates localization files based on the mod named X | | * '''GENERATE_LOCALIZATION''' X - Generates localization files based on the mod named X |
| | | | |
| | == Furniture modding == | | == Furniture modding == |
| | + | === Tutorial === |
| | + | Check out a video tutorial on how to create furniture mods here: |
| | + | |
| | + | <youtube>Lz_0CHwb74Q</youtube> |
| | + | |
| | === TyD file layout === | | === TyD file layout === |
| | [[File:FurnitureModLayout.png]] | | [[File:FurnitureModLayout.png]] |
| Line 89: |
Line 95: |
| | | | |
| | 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. | | 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. |
| | + | |
| | + | Watch a tutorial on how to create textures for the standard shader here: |
| | + | |
| | + | <Youtube>Xq_ysZb-bAA</Youtube> |
| | | | |
| | ==== The atlas shader ==== | | ==== The atlas shader ==== |
| Line 105: |
Line 115: |
| | If you have used the '''Base''' record, the furniture will inherit its interaction points from the base furniture, unless you add the "InteractionPoints" list. | | 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. | + | 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. | | 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). | + | 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), '''ReachCheck'''(whether we should warn the player at all), '''MainAction'''(whether the feet graphic should appear dark or not to tell the player whether it's important), '''ShowOnBuild'''(can be used to hide the feet graphic) and '''Outside'''(tells the game that this interaction point is used from the outside, which currently only used for garage ports). |
| | | | |
| | 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. | | 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. |
| Line 121: |
Line 131: |
| | You can define a '''ComponentName''' record to name the snap point for later reference. | | 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. | + | 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. |
| | + | |
| | + | You can also define its '''Group''', which should be a number above 0, all snap points with the same group number will be grouped together such that only one of them can be used at any time. |
| | | | |
| | 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. | | 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. |
| Line 162: |
Line 174: |
| | ==== Limitations ==== | | ==== 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. | | 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. |
| | + | |
| | + | === Mesh replacements === |
| | + | You can add mesh replacements, which work kind of like the texture atlas, but allow the player to replace entire meshes + materials for a piece of furniture. This feature is only available as of Beta 1.7.35. |
| | + | |
| | + | Replacements are grouped together, replacement groups can be mapped to one or more furniture. As an example, all stairs in the game use 2 replacement groups: "Stairs" and "Railings". Each group contains a list of possible replacements for that group, e.g. "metal", "glass", "fence". The replacement has a thumbnail for the UI and a material that all meshes that get replaced will use, when that replacement is active. Each replacement also has a list of key/mesh pairs, which tell the game which objects should use which mesh when that replacement is active, e.g "Stairs", "Platform" and "Support" for the "Stairs" replacement, and "Pole" and "Railing" for the "Railing" replacement. |
| | + | |
| | + | So the overall structure is: |
| | + | |
| | + | Replacement group -> replacements -> meshes |
| | + | |
| | + | To add mesh replacements, you need to add a "replacements.tyd" to the root of your mod folder. Here's an example of a replacement file: |
| | + | '''TelephoneReplacementGroup''' |
| | + | { |
| | + | '''OldTelephone''' |
| | + | { |
| | + | '''Material''' "Old Telephone Material" |
| | + | '''Thumbnail''' "Old telephone thumbnail.png" |
| | + | '''Meshes''' |
| | + | [ |
| | + | { |
| | + | '''Name''' "KeyPad" |
| | + | '''Mesh''' "OldKeyPad.obj" |
| | + | '''LOD1''' "OldKeyPad-LOD1.obj" |
| | + | '''LOD2''' "OldKeyPad-LOD2.obj" |
| | + | } |
| | + | { |
| | + | '''Name''' "Handle" |
| | + | '''Mesh''' "OldHandle.obj" |
| | + | '''LOD1''' "OldHandle-LOD1.obj" |
| | + | '''LOD2''' "OldHandle-LOD2.obj" |
| | + | } |
| | + | ] |
| | + | } |
| | + | '''NewTelephone''' |
| | + | { |
| | + | '''Material''' "New Telephone Material" |
| | + | '''ThumbnailRGB''' "New telephone thumbnail.png" |
| | + | '''Meshes''' |
| | + | [ |
| | + | { |
| | + | '''Name''' "KeyPad" |
| | + | '''Mesh''' "NewKeyPad.obj" |
| | + | '''LOD1''' "NewKeyPad-LOD1.obj" |
| | + | '''LOD2''' "NewKeyPad-LOD2.obj" |
| | + | } |
| | + | { |
| | + | '''Name''' "Handle" |
| | + | '''Mesh''' "NewHandle.obj" |
| | + | '''LOD1''' "NewHandle-LOD1.obj" |
| | + | '''LOD2''' "NewHandle-LOD2.obj" |
| | + | } |
| | + | ] |
| | + | } |
| | + | } |
| | + | |
| | + | Note that each mesh can optionally have LOD1 and LOD2 defined and that all meshes in a replacement uses the same material, so if you have multiple meshes for one replacement, they need to share materials and textures. The Thumbnail should either be defined as "Thumbnail" or "ThumbnailRGB", depending on whether it should be RGB color mapped in the selection menu. |
| | + | |
| | + | To make replacements work for your furniture, edit your furniture in the furniture editor and under the Replacement Options tab for your furniture add however many Replacement groups the furniture will use and add a default selection for each, in the same order. Then go to your mesh and select which mesh replacement it will be mapped to. As an example the telephone furniture could use the "TelephoneReplacementGroup" group, with "OldTelephone" as its default and then its keypad and handle meshes would be mapped to "KeyPad" and "Handle" mesh replacements. |
| | | | |
| | === Components === | | === Components === |
| | Other than the elements described so far, all tables added to the root table are interpreted as Unity MonoBehaviour components. If you added a table called '''BoxCollider''', the children records 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''' record set in the other tags, or alternatively you can add a Transforms list to define empty transforms with a Name, Position and Rotation. Using '''self''' as a value for a MonoBehaviour variable will find the corresponding component in the root furniture object itself. This allows for anything to be added to a furniture, even MonoBehaviors from custom mods. | | Other than the elements described so far, all tables added to the root table are interpreted as Unity MonoBehaviour components. If you added a table called '''BoxCollider''', the children records 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''' record set in the other tags, or alternatively you can add a Transforms list to define empty transforms with a Name, Position and Rotation. Using '''self''' as a value for a MonoBehaviour variable will find the corresponding component in the root furniture object itself. This allows for anything to be added to a furniture, even MonoBehaviors from custom mods. |
| | | | |
| − | If you set the '''ParentTransform''' record to a '''ComponentName''' reference, the component will be fetched/added to that object instead of the root furniture object. This could be used to add a Light 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. | + | If you set the '''TransformParent''' record to a '''ComponentName''' reference, the component will be fetched/added to that object instead of the root furniture object. This could be used to add a Light 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. |
| | | | |
| | Adding a '''RemoveComponent True''' record for a table will remove that MonoBehavior from the furniture or '''TransformParent'''. | | Adding a '''RemoveComponent True''' record for a table will remove that MonoBehavior from the furniture or '''TransformParent'''. |
| Line 192: |
Line 262: |
| | * '''BlocksFloor''' (True/False) - Whether this furniture blocks furniture that is placed into the floor, e.g. couches | | * '''BlocksFloor''' (True/False) - Whether this furniture blocks furniture that is placed into the floor, e.g. couches |
| | * '''IsSnapping''' (True/False) - Should snap to another furniture | | * '''IsSnapping''' (True/False) - Should snap to another furniture |
| − | * '''SnapsTo''' (String) - The name of snap point it should snap to | + | * '''SnapsTo''' (List of String) - The name(s) of snap point(s) it should snap to |
| | * '''CanAssign''' (True/False) - Can be assigned to an employee | | * '''CanAssign''' (True/False) - Can be assigned to an employee |
| | * '''ReverseLowPass''' (True/False) - Whether the sound should be muffled when inside or outside the parent room | | * '''ReverseLowPass''' (True/False) - Whether the sound should be muffled when inside or outside the parent room |