-
Notifications
You must be signed in to change notification settings - Fork 0
Authoring Recipes
This page answers the common “how do I do this?” questions. The JSON schema reference remains the field-by-field contract.
The easiest route is Tile Editor → New Mod. For hand-authored native data,
create one package folder containing Info.json and one or more
*.fuse.json files. Point FuseDataFiles at every fragment:
{
"Id": "Author.Route",
"DisplayName": "Author Route",
"Author": "Author",
"Version": "1.0.0",
"ManagerVersion": "0.27.10",
"Requirements": ["FUSE"],
"LoadAfter": ["FUSE"],
"FuseDataFiles": ["track.fuse.json", "industry.fuse.json"]
}Use FuseRequires for a hard data-package dependency. Use FuseLoadAfter or
FuseLoadBefore only for ordering. A missing hard dependency faults/skips the
affected package and appears in /fuse.report; it must not break unrelated
packages or game menus.
- Open the package in Tile Editor and connect to the game.
- Press
F9, open Geo, then select the node or segment in the world. - Use the node/segment transform controls. The relationship list shows every segment using a selected node and both endpoints of a selected segment.
- Save and force a track reload if the live preview ever differs from the selected JSON. The editor now rebuilds affected track objects after topology changes so stale copies do not accumulate.
- Reopen the package and verify the same IDs and geometry before release.
Do not create a second segment with the same ID to replace the first one. FUSE uses atomic replacement for existing span/segment IDs so industries keep their references, but duplicate definitions inside one package are still authoring errors.
Use the exact runtime industry ID, not its company-window display name:
{
"schemaVersion": "1.0",
"id": "Author.Route.industry-removals",
"name": "Industry removals",
"modVersion": "1.0.0",
"operations": {
"removals": {
"industries": ["exact-runtime-industry-id"]
}
}
}Run /fuse.dumpgraph and /fuse.dumpruntimegraph to find and compare IDs. The
Tile Editor Operations page can discover/select industries, but manual JSON is
still the safest way to remove a stock industry when the editor cannot identify
an unambiguous owning layer.
A rail industry component changes freight on cars spotted over a TrackSpan. It does not place a visible coal chute, water column, or fuel stand.
- Create/select a town and industry in Tile Editor →
F9→ Operations. - Create a whole, partial, or multi-segment TrackSpan from selected track.
- Add a
loader,unloader,formulaic,teamTrack,interchange, or other component to the industry. - Select the span and load ID, then configure capacity/rates.
- Verify
/fuse.operationsand the company window in a new sandbox save.
Choose the implementation that matches the asset:
- An existing vanilla-style loader prefab that already contains Railroader's
loader components can use native
operations.loaders. - A custom coal/water/diesel/bunker-C/wood facility should be placed through
world.scenery, then bound withToolshedServiceFacilities.json. Toolshed owns its outlet, animation, transfer, storage, and interaction behavior.
Do not split one custom facility between a scenery clone and an unrelated operations loader. That creates the “tank in one place, loading point in another” failure. The visible object and its service points must share the same placed root/transform contract.
Open F9 → Objects, disable Track Geometry if it blocks selection, then click
the object. Save its move/rotate/scale/visibility override as a scene clone or
mandela entry. Thin signs have a screen-space picking halo, and shared town/map
roots are blocked so one click cannot move an entire scene hierarchy.
Use Spliney for roads, rivers, bridges, and trestles. Base-game spline control
points are editable; the first change writes a same-name override into the active
layer. A legacy scene path such as World/Roads Sylva/Chipper Curve remains a
scene path—it is not an asset identifier.
Use Objects/Mandela for individual scene objects and Scenery for asset-pack models. They have different ownership and runtime rebuild paths.
Use a native world.splineys entry with type: "objectLine". This samples a
path at uniform spacing and places rigid modules; it does not stretch a fence
panel or retaining-wall block into a distorted mesh.
{
"world": {
"splineys": {
"yard:fence:north": {
"type": "objectLine",
"assetIdentifier": "your-pack:fence-panel",
"spacing": 3.0,
"instanceScale": { "x": 1, "y": 1, "z": 1 },
"rotationOffset": { "x": 0, "y": 90, "z": 0 },
"lateralOffset": 0,
"verticalOffset": 0,
"snapToTerrain": true,
"alignToSlope": false,
"placeAtEnd": true,
"maximumInstances": 1024,
"points": [
{ "position": { "x": 10, "y": 2, "z": 15 } },
{ "position": { "x": 55, "y": 3, "z": 22 } }
]
}
}
}
}Choose exactly one source: assetIdentifier is portable and recommended;
prefab accepts a safe FUSE prefab/scene-path URI for advanced stock-object
cloning. The Tile Editor exposes this as Geo → Spliney → Fence / Wall and
rebuilds the repeated modules while its point handles remain editable. This is
native FUSE only; the legacy RailLoader spline schema has no equivalent.
Converted conditional fragments use mixinto.requires:
{
"mixinto": {
"target": "game-graph",
"sourceFile": "OptionalYard.json",
"requires": [
{ "id": "Author.BaseYard", "notBefore": "1.2.0" }
]
}
}When the dependency is missing or outside the allowed range, FUSE skips this
fragment and reports the package, folder, source file, dependency ID, and action.
loadAfter is ordering and must not be flattened into a hard requirement.
Top-level settings creates controls in FUSE's per-mod settings page. Native
code can read them through FuseModSettingsAPI. The current v1 data schema does
not yet make arbitrary JSON sections conditional on a setting, so do not imply
that a slider automatically enables track/spans. Until a declarative condition
format is released, use separate optional data packages/fragments with explicit
dependencies, or a small independently authored code component that reads the
setting and applies supported API operations.
Run /fuse.report for a readable report or /fuse.report json for structured
output. A definition failure includes:
- package ID
- absolute package folder and source file
- JSON path
- line and column when Newtonsoft can provide them
- validation code/message
- suggested action
FUSE isolates the bad definition and continues loading unrelated packages. Fix the first syntax error first; later errors can be consequences of that one.
Mirrored from docs/AUTHORING_RECIPES.md — edit there, not here.