docs(research): TpF3 mod tooling and structure, offline wiki/script-doc copies

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
haylanandClaude Bot committed 2026-10-04 22:01:16 +02:00
commit 0213e22d66
39 files changed
+66135

No files matched your search

+89
View File
@@ -0,0 +1,89 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/api.html (fetched 2026-10-04) -->
[index](index.html) / api
---
# Module api[🔗](#api)
```
global record api res: Res
gui: Gui
engine: Engine
type: Type
util: ApiUtil
cmd: Cmd
modhub: ModhubApi
end
global app: App
global loaderHelper: LoaderHelper
```
## Category: globals[🔗](#category-globals)
### api[🔗](#$api~api)
```
global record api res: Res
gui: Gui
engine: Engine
type: Type
util: ApiUtil
cmd: Cmd
modhub: ModhubApi
end
```
### api.res[🔗](#$api~api.res)
```
api.res: Res
```
### api.gui[🔗](#$api~api.gui)
```
api.gui: Gui
```
### api.engine[🔗](#$api~api.engine)
```
api.engine: Engine
```
### api.type[🔗](#$api~api.type)
```
api.type: Type
```
### api.util[🔗](#$api~api.util)
```
api.util: ApiUtil
```
### api.cmd[🔗](#$api~api.cmd)
```
api.cmd: Cmd
```
### api.modhub[🔗](#$api~api.modhub)
```
api.modhub: ModhubApi
```
### app[🔗](#$api~app)
```
global app: App
```
### loaderHelper[🔗](#$api~loaderHelper)
```
global loaderHelper: LoaderHelper
```
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+723
View File
@@ -0,0 +1,723 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/api/res.html (fetched 2026-10-04) -->
[index](../index.html) / [api](../api.html) / res
---
# Module api.res[🔗](#api.res)
Repositories containing game resources (models, tracks, streets, ...)
```
global record Res buildingTypeRep: ResTypeRep<integer, BuildingType>
bridgeTypeRep: ResTypeRep<integer, BridgeType>
constructionRep: ResTypeRep<integer, ConstructionDesc>
metaConstructionRep: ResTypeRep<integer, MetaConstructionDesc>
genericRep: GenericRep
economyRep: ResTypeRep<integer, EconomyDesc>
climateRep: ResTypeRep<integer, ClimateDesc>
terrainGeneratorRep: ResTypeRep<integer, TerrainGeneratorDesc>
environmentRep: ResTypeRep<integer, Environment>
playlistRep: ResTypeRep<integer, PlaylistDesc>
gameScriptRep: ResTypeRep<integer, GameScriptType>
cargoClassRep: CargoClassRep
cargoTypeRep: CargoTypeRep
nameRep: ResTypeRep<integer, NameDesc>
campaignRep: ResTypeRep<integer, CampaignDesc>
missionRep: ResTypeRep<integer, MissionDesc>
modelRep: ModelRep
multipleUnitRep: ResTypeRep<integer, MultipleUnit>
streetTemplateRep: ResTypeRep<integer, StreetTemplate>
edgeDecorationRep: ResTypeRep<integer, EdgeDecoration>
terrainMaterialTypeRep: ResTypeRep<integer, TerrainMaterialType>
trafficLightTypeRep: ResTypeRep<integer, TrafficLightType>
tunnelTypeRep: ResTypeRep<integer, BridgeType>
moduleRep: ResTypeRep<integer, ModuleDesc>
railroadCrossingTypeRep: ResTypeRep<integer, RailroadCrossingType>
streetStyleRep: ResTypeRep<integer, StreetStyle>
builderAudioSetRep: ResTypeRep<integer, BuilderAudioSet>
soundSetRep: ResTypeRep<integer, SoundSet>
groundTextureRep: ResTypeRep<string, GroundTexture>
autoGroundTexRep: ResTypeRep<string, AutoGroundTex>
getBaseConfig: function(): BaseConfig
end
global record CargoClassRep getCargoClassId: function(cargoClass : string): CargoClassId
hasCargoClass: function(cargoClass : string): boolean
end
global record CargoTypeRep forEachCargoType: function(cargoTypeSet : CargoTypeSet, callback : function(CargoTypeId))
hasCargoType: function(cargoTypeSet : CargoTypeSet, test : CargoTypeId, inverse : boolean): boolean
getPassengerCargoTypeId: function(): CargoTypeId
end
global record GameScriptType getState: function(GameScriptType): {<any type> : <any type>}
end
global record GenericRep getAllOfType: function(typeName : string, includeInvisible : boolean): {integer}
end
global record ModelRep forEachModelWithMetadata: function(metadata : string, callback : function(string))
end
global interface ResTypeRep<K, T> get: function(id : K): T
getName: function(id : K): string
find: function(resName : string): K
findAndGet: function(resName : string): T
getAll: function(includeInvisible : boolean): {K : string}
add: function(resName : ResName, resource : T, visible : boolean): K
setVisible: function(id : K, visible : boolean)
isVisible: function(id : K): boolean
getAsTable: function(id : K): T
setAsTable: function(id : K, t : T): boolean
addAsTable: function(the : ResName, t : T): K
end
```
## Category: main[🔗](#category-main)
### Res[🔗](#$api.res~Res)
```
global record Res buildingTypeRep: ResTypeRep<integer, BuildingType>
bridgeTypeRep: ResTypeRep<integer, BridgeType>
constructionRep: ResTypeRep<integer, ConstructionDesc>
metaConstructionRep: ResTypeRep<integer, MetaConstructionDesc>
genericRep: GenericRep
economyRep: ResTypeRep<integer, EconomyDesc>
climateRep: ResTypeRep<integer, ClimateDesc>
terrainGeneratorRep: ResTypeRep<integer, TerrainGeneratorDesc>
environmentRep: ResTypeRep<integer, Environment>
playlistRep: ResTypeRep<integer, PlaylistDesc>
gameScriptRep: ResTypeRep<integer, GameScriptType>
cargoClassRep: CargoClassRep
cargoTypeRep: CargoTypeRep
nameRep: ResTypeRep<integer, NameDesc>
campaignRep: ResTypeRep<integer, CampaignDesc>
missionRep: ResTypeRep<integer, MissionDesc>
modelRep: ModelRep
multipleUnitRep: ResTypeRep<integer, MultipleUnit>
streetTemplateRep: ResTypeRep<integer, StreetTemplate>
edgeDecorationRep: ResTypeRep<integer, EdgeDecoration>
terrainMaterialTypeRep: ResTypeRep<integer, TerrainMaterialType>
trafficLightTypeRep: ResTypeRep<integer, TrafficLightType>
tunnelTypeRep: ResTypeRep<integer, BridgeType>
moduleRep: ResTypeRep<integer, ModuleDesc>
railroadCrossingTypeRep: ResTypeRep<integer, RailroadCrossingType>
streetStyleRep: ResTypeRep<integer, StreetStyle>
builderAudioSetRep: ResTypeRep<integer, BuilderAudioSet>
soundSetRep: ResTypeRep<integer, SoundSet>
groundTextureRep: ResTypeRep<string, GroundTexture>
autoGroundTexRep: ResTypeRep<string, AutoGroundTex>
getBaseConfig: function(): BaseConfig
end
```
List of all repositories
### Res.buildingTypeRep[🔗](#$api.res~Res.buildingTypeRep)
```
Res.buildingTypeRep: ResTypeRep<integer, BuildingType>
```
A resource repository containing data for town buildings.
### Res.bridgeTypeRep[🔗](#$api.res~Res.bridgeTypeRep)
```
Res.bridgeTypeRep: ResTypeRep<integer, BridgeType>
```
A resource repository containing data from the bridge configuration files and from dynamically generated bridges.
### Res.constructionRep[🔗](#$api.res~Res.constructionRep)
```
Res.constructionRep: ResTypeRep<integer, ConstructionDesc>
```
A resource repository containing data from the ".con" files and from dynamically generated constructions.
### Res.metaConstructionRep[🔗](#$api.res~Res.metaConstructionRep)
```
Res.metaConstructionRep: ResTypeRep<integer, MetaConstructionDesc>
```
A resource repository containing data for all custom resources.
### Res.genericRep[🔗](#$api.res~Res.genericRep)
```
Res.genericRep: GenericRep
```
### Res.economyRep[🔗](#$api.res~Res.economyRep)
```
Res.economyRep: ResTypeRep<integer, EconomyDesc>
```
A resource repository containing data from the ".eco" economy resources.
### Res.climateRep[🔗](#$api.res~Res.climateRep)
```
Res.climateRep: ResTypeRep<integer, ClimateDesc>
```
A resource repository containing data for the climates.
### Res.terrainGeneratorRep[🔗](#$api.res~Res.terrainGeneratorRep)
```
Res.terrainGeneratorRep: ResTypeRep<integer, TerrainGeneratorDesc>
```
A resource repository containing data for the terrain generation.
### Res.environmentRep[🔗](#$api.res~Res.environmentRep)
```
Res.environmentRep: ResTypeRep<integer, Environment>
```
A resource repository containing data for the environment.
### Res.playlistRep[🔗](#$api.res~Res.playlistRep)
```
Res.playlistRep: ResTypeRep<integer, PlaylistDesc>
```
A repository for playlists.
### Res.gameScriptRep[🔗](#$api.res~Res.gameScriptRep)
```
Res.gameScriptRep: ResTypeRep<integer, GameScriptType>
```
A resource repository containing game scripts.
### Res.cargoClassRep[🔗](#$api.res~Res.cargoClassRep)
```
Res.cargoClassRep: CargoClassRep
```
A resource repository containing data with all the cargo classes.
### Res.cargoTypeRep[🔗](#$api.res~Res.cargoTypeRep)
```
Res.cargoTypeRep: CargoTypeRep
```
A resource repository containing data with all the cargo types.
### Res.nameRep[🔗](#$api.res~Res.nameRep)
```
Res.nameRep: ResTypeRep<integer, NameDesc>
```
A resource repository for names.
### Res.campaignRep[🔗](#$api.res~Res.campaignRep)
```
Res.campaignRep: ResTypeRep<integer, CampaignDesc>
```
A resource repository for campaign chapters.
### Res.missionRep[🔗](#$api.res~Res.missionRep)
```
Res.missionRep: ResTypeRep<integer, MissionDesc>
```
A resource repository for campaign missions.
### Res.modelRep[🔗](#$api.res~Res.modelRep)
```
Res.modelRep: ModelRep
```
A resource repository containing all the game models and metadata.
### Res.multipleUnitRep[🔗](#$api.res~Res.multipleUnitRep)
```
Res.multipleUnitRep: ResTypeRep<integer, MultipleUnit>
```
A resource repository containing data from the multiple unit files and from dynamically generated multiple units.
### Res.streetTemplateRep[🔗](#$api.res~Res.streetTemplateRep)
```
Res.streetTemplateRep: ResTypeRep<integer, StreetTemplate>
```
A resource repository containing data from the street template files and from dynamically generated street templates.
### Res.edgeDecorationRep[🔗](#$api.res~Res.edgeDecorationRep)
```
Res.edgeDecorationRep: ResTypeRep<integer, EdgeDecoration>
```
### Res.terrainMaterialTypeRep[🔗](#$api.res~Res.terrainMaterialTypeRep)
```
Res.terrainMaterialTypeRep: ResTypeRep<integer, TerrainMaterialType>
```
A resource repository containing materials.
### Res.trafficLightTypeRep[🔗](#$api.res~Res.trafficLightTypeRep)
```
Res.trafficLightTypeRep: ResTypeRep<integer, TrafficLightType>
```
A resource repository containing data from the traffic lights.
### Res.tunnelTypeRep[🔗](#$api.res~Res.tunnelTypeRep)
```
Res.tunnelTypeRep: ResTypeRep<integer, BridgeType>
```
A resource repository containing data from the tunnel configuration files and from dynamically generated tunnels.
### Res.moduleRep[🔗](#$api.res~Res.moduleRep)
```
Res.moduleRep: ResTypeRep<integer, ModuleDesc>
```
A resource repository containing data from the ".modules" files and from dynamically generated modules.
### Res.railroadCrossingTypeRep[🔗](#$api.res~Res.railroadCrossingTypeRep)
```
Res.railroadCrossingTypeRep: ResTypeRep<integer, RailroadCrossingType>
```
A resource repository containing data from the railroad crossing files and from dynamically generated railroad crossing types.
### Res.streetStyleRep[🔗](#$api.res~Res.streetStyleRep)
```
Res.streetStyleRep: ResTypeRep<integer, StreetStyle>
```
A resource repository containing data from the street type files and from dynamically generated street types.
### Res.builderAudioSetRep[🔗](#$api.res~Res.builderAudioSetRep)
```
Res.builderAudioSetRep: ResTypeRep<integer, BuilderAudioSet>
```
A resource repository containing data for audio sets for building
### Res.soundSetRep[🔗](#$api.res~Res.soundSetRep)
```
Res.soundSetRep: ResTypeRep<integer, SoundSet>
```
A resource repository containing data for sound sets
### Res.groundTextureRep[🔗](#$api.res~Res.groundTextureRep)
```
Res.groundTextureRep: ResTypeRep<string, GroundTexture>
```
A resource repository containing data for ground textures.
### Res.autoGroundTexRep[🔗](#$api.res~Res.autoGroundTexRep)
```
Res.autoGroundTexRep: ResTypeRep<string, AutoGroundTex>
```
A resource repository containing data for automatic ground texture definitions.
### Res.getBaseConfig[🔗](#$api.res~Res.getBaseConfig)
```
function Res.getBaseConfig(): BaseConfig
```
Gets the base config, it contains most of the data which was read from "base/base\_config.lua".
**Returns**
1. `BaseConfig` — Table the configuration table. See https://wiki.transportfever3.com/doku.php?id=modding:baseconfig for a detailed explanation.
## Category: globals[🔗](#category-globals)
### CargoClassRep[🔗](#$api.res~CargoClassRep)
```
global record CargoClassRep getCargoClassId: function(cargoClass : string): CargoClassId
hasCargoClass: function(cargoClass : string): boolean
end
```
### CargoClassRep.getCargoClassId[🔗](#$api.res~CargoClassRep.getCargoClassId)
```
function CargoClassRep.getCargoClassId(cargoClass : string): CargoClassId
```
Gets the CargoClassId for a given CargoClass name
**Parameters**
* `cargoClass` :`string` — cargo class name
**Returns**
1. `CargoClassId` — cargo class id
### CargoClassRep.hasCargoClass[🔗](#$api.res~CargoClassRep.hasCargoClass)
```
function CargoClassRep.hasCargoClass(cargoClass : string): boolean
```
Checks if the given cargo class is present, this can be used if some cargo classes exist but are invisible
**Parameters**
* `cargoClass` :`string` — cargo class name
**Returns**
1. `boolean` — true if present
### CargoTypeRep[🔗](#$api.res~CargoTypeRep)
```
global record CargoTypeRep forEachCargoType: function(cargoTypeSet : CargoTypeSet, callback : function(CargoTypeId))
hasCargoType: function(cargoTypeSet : CargoTypeSet, test : CargoTypeId, inverse : boolean): boolean
getPassengerCargoTypeId: function(): CargoTypeId
end
```
### CargoTypeRep.forEachCargoType[🔗](#$api.res~CargoTypeRep.forEachCargoType)
```
function CargoTypeRep.forEachCargoType(cargoTypeSet : CargoTypeSet, callback : function(CargoTypeId))
```
Iterates over all cargo types in a given cargo type set
**Parameters**
* `cargoTypeSet` :`CargoTypeSet` — the cargo type set to iterate over
* `callback` :`function(CargoTypeId)` — a callback for each cargo type
### CargoTypeRep.hasCargoType[🔗](#$api.res~CargoTypeRep.hasCargoType)
```
function CargoTypeRep.hasCargoType(cargoTypeSet : CargoTypeSet, test : CargoTypeId, inverse : boolean): boolean
```
Checks if a cargo type set contains a specific cargo type
**Parameters**
* `cargoTypeSet` :`CargoTypeSet` — the cargo type set to check
* `test` :`CargoTypeId` — the cargo type to test for
* `inverse` :`boolean` — if true, the function returns true when any other cargo exists
**Returns**
1. `boolean`
### CargoTypeRep.getPassengerCargoTypeId[🔗](#$api.res~CargoTypeRep.getPassengerCargoTypeId)
```
function CargoTypeRep.getPassengerCargoTypeId(): CargoTypeId
```
Returns the cargo type id for passenger
**Returns**
1. `CargoTypeId`
### GameScriptType[🔗](#$api.res~GameScriptType)
```
global record GameScriptType getState: function(GameScriptType): {<any type> : <any type>}
end
```
### GameScriptType.getState[🔗](#$api.res~GameScriptType.getState)
```
function GameScriptType.getState(GameScriptType): {<any type> : <any type>}
```
**Parameters**
* `GameScriptType`
**Returns**
1. `{<any type> : <any type>}`
### GenericRep[🔗](#$api.res~GenericRep)
```
global record GenericRep getAllOfType: function(typeName : string, includeInvisible : boolean): {integer}
end
```
### GenericRep.getAllOfType[🔗](#$api.res~GenericRep.getAllOfType)
```
function GenericRep.getAllOfType(typeName : string, includeInvisible : boolean): {integer}
```
Gets all Generic resource types of a given type name
does not allow typeName in { "gui*res*overwrite", "gui\_res" }, for those, use `api.gui.genericRep`
**Parameters**
* `typeName` :`string` — type name
* `includeInvisible` :`boolean` — whether to include invisible
**Returns**
1. `{integer}` — resource ids
### ModelRep[🔗](#$api.res~ModelRep)
```
global record ModelRep forEachModelWithMetadata: function(metadata : string, callback : function(string))
end
```
### ModelRep.forEachModelWithMetadata[🔗](#$api.res~ModelRep.forEachModelWithMetadata)
```
function ModelRep.forEachModelWithMetadata(metadata : string, callback : function(string))
```
Iterates over all the models with a specific metadata
**Parameters**
* `metadata` :`string` — the name of the metadata
* `callback` :`function(string)` — a callback for every model that contains the specified metadata
### ResTypeRep[🔗](#$api.res~ResTypeRep)
```
global interface ResTypeRep<K, T> get: function(id : K): T
getName: function(id : K): string
find: function(resName : string): K
findAndGet: function(resName : string): T
getAll: function(includeInvisible : boolean): {K : string}
add: function(resName : ResName, resource : T, visible : boolean): K
setVisible: function(id : K, visible : boolean)
isVisible: function(id : K): boolean
getAsTable: function(id : K): T
setAsTable: function(id : K, t : T): boolean
addAsTable: function(the : ResName, t : T): K
end
```
Generic interface for repositories
Resources are generally static (initialized when the game starts and don't change)
Make sure that resource exists before trying to access them
Resource ids are only valid within the same repository
##### Type Parameters
* **`K`**
* **`T`**
### ResTypeRep.get[🔗](#$api.res~ResTypeRep.get)
```
function ResTypeRep.get(id : K): T
```
Gets the resource for a given id - (can be used to directly modify the resource,
but this is discouraged, except for performance reasons. Use getAsTable and setAsTable instead.
**Parameters**
* `id` :`K` — a resource id
**Returns**
1. `T` — the resource
### ResTypeRep.getName[🔗](#$api.res~ResTypeRep.getName)
```
function ResTypeRep.getName(id : K): string
```
Gets the resource name for a given id
**Parameters**
* `id` :`K` — a resource id
**Returns**
1. `string` — the resource name
### ResTypeRep.find[🔗](#$api.res~ResTypeRep.find)
```
function ResTypeRep.find(resName : string): K
```
Finds the id of the given resource name and returns its id
**Parameters**
* `resName` :`string` — a resource name
**Returns**
1. `K` — the id of the resource, -1 if the resource doesn't exist
### ResTypeRep.findAndGet[🔗](#$api.res~ResTypeRep.findAndGet)
```
function ResTypeRep.findAndGet(resName : string): T
```
Finds the resource with the given name and returns it
**Parameters**
* `resName` :`string` — a resource name
**Returns**
1. `T` — the resource, and an error if the resource is not found
### ResTypeRep.getAll[🔗](#$api.res~ResTypeRep.getAll)
```
function ResTypeRep.getAll(includeInvisible : boolean): {K : string}
```
Gets all resources
**Parameters**
* `includeInvisible` :`boolean` — if invisible resources should be included, false by default
**Returns**
1. `{K : string}` — a dictionary containing all id and resource names
### ResTypeRep.add[🔗](#$api.res~ResTypeRep.add)
```
function ResTypeRep.add(resName : ResName, resource : T, visible : boolean): K
```
Adds a new resource to the repository
**Parameters**
* `resName` :`ResName` — the resource name of the new resource
* `resource` :`T` — the resource itself
* `visible` :`boolean` — if the resource is visible
**Returns**
1. `K` — the id of the newly added resource
### ResTypeRep.setVisible[🔗](#$api.res~ResTypeRep.setVisible)
```
function ResTypeRep.setVisible(id : K, visible : boolean)
```
Sets a resource visible
**Parameters**
* `id` :`K` — the id of the resource
* `visible` :`boolean` — if the resource should be visible
### ResTypeRep.isVisible[🔗](#$api.res~ResTypeRep.isVisible)
```
function ResTypeRep.isVisible(id : K): boolean
```
Checks if a resource is visible
**Parameters**
* `id` :`K` — the id of the resource
**Returns**
1. `boolean` — if the resource is visible
### ResTypeRep.getAsTable[🔗](#$api.res~ResTypeRep.getAsTable)
```
function ResTypeRep.getAsTable(id : K): T
```
Gets the resource for a given id as a pure lua table
**Parameters**
* `id` :`K` — a resource id
**Returns**
1. `T` — the resource
### ResTypeRep.setAsTable[🔗](#$api.res~ResTypeRep.setAsTable)
```
function ResTypeRep.setAsTable(id : K, t : T): boolean
```
Updates the resource for a given id from a pure lua table (as if it was the resource file)
**Parameters**
* `id` :`K` — a resource id
* `t` :`T` — the resource
**Returns**
1. `boolean` — success
### ResTypeRep.addAsTable[🔗](#$api.res~ResTypeRep.addAsTable)
```
function ResTypeRep.addAsTable(the : ResName, t : T): K
```
Add a new resource from a pure lua table (as if it was the resource file)
**Parameters**
* `the` :`ResName` — name of the resource to add (should be unique)
* `t` :`T` — the resource to add
**Returns**
1. `K` — the newly added id
File diff suppressed because it is too large. Load diff
+479
View File
@@ -0,0 +1,479 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/api/type/mod.html (fetched 2026-10-04) -->
[index](../../index.html) / [api](../../api.html) / [type](../type.html) / mod
---
# Module api.type.mod[🔗](#api.type.mod)
Contains all type definitions for the mods.
```
[Expand All] [Collapse All]
global record Mod record ModId new: function(): ModId
equals: function(ModId, ModId): boolean
name: string
end
record ModRef modId: ModId
revisionMin: integer
revisionMax: integer
end
record ModRefInfo displayName: string
url: string
end
record ModDependency mod: ModRef
optional: boolean
loadBefore: boolean
modInfo: ModRefInfo
end
record ModIncompatibility mod: ModRef
end
record GameModDesc modId: ModId
modSource: string
modhubModId: string
name: string
severityRemove: integer
end
record ModDesc severityAdd: integer
severityRemove: integer
params: {ScriptParam}
visible: boolean
cosmetic: boolean
autoActivate: boolean
url: string
dependencies: {ModDependency}
incompatibilities: {ModIncompatibility}
revision: integer
end
record ModSize numModels: integer
numTextures: integer
numMeshes: integer
totalEstimatedSizeMB: integer
end
end
global type ModId = integer
```
## Category: main[🔗](#category-main)
### Mod[🔗](#$api.type.mod~Mod)
```
[Expand All] [Collapse All]
global record Mod record ModId new: function(): ModId
equals: function(ModId, ModId): boolean
name: string
end
record ModRef modId: ModId
revisionMin: integer
revisionMax: integer
end
record ModRefInfo displayName: string
url: string
end
record ModDependency mod: ModRef
optional: boolean
loadBefore: boolean
modInfo: ModRefInfo
end
record ModIncompatibility mod: ModRef
end
record GameModDesc modId: ModId
modSource: string
modhubModId: string
name: string
severityRemove: integer
end
record ModDesc severityAdd: integer
severityRemove: integer
params: {ScriptParam}
visible: boolean
cosmetic: boolean
autoActivate: boolean
url: string
dependencies: {ModDependency}
incompatibilities: {ModIncompatibility}
revision: integer
end
record ModSize numModels: integer
numTextures: integer
numMeshes: integer
totalEstimatedSizeMB: integer
end
end
```
Refer to https://wiki.transportfever3.com/doku.php?id=modding:moddefinition for more details about mod and mod interface in general
### Mod.ModId[🔗](#$api.type.mod~Mod.ModId)
```
record Mod.ModId new: function(): ModId
equals: function(ModId, ModId): boolean
name: string
end
```
Mod identifier
### Mod.ModId.new[🔗](#$api.type.mod~Mod.ModId.new)
```
function Mod.ModId.new(): ModId
```
**Returns**
1. `ModId`
### Mod.ModId.equals[🔗](#$api.type.mod~Mod.ModId.equals)
```
function Mod.ModId.equals(ModId, ModId): boolean
```
**Parameters**
* `ModId`
* `ModId`
**Returns**
1. `boolean`
### Mod.ModId.name[🔗](#$api.type.mod~Mod.ModId.name)
```
Mod.ModId.name: string
```
A string that identify the mod
### Mod.ModRef[🔗](#$api.type.mod~Mod.ModRef)
```
record Mod.ModRef modId: ModId
revisionMin: integer
revisionMax: integer
end
```
A reference to a mod
### Mod.ModRef.modId[🔗](#$api.type.mod~Mod.ModRef.modId)
```
Mod.ModRef.modId: ModId
```
Mod id
### Mod.ModRef.revisionMin[🔗](#$api.type.mod~Mod.ModRef.revisionMin)
```
Mod.ModRef.revisionMin: integer
```
The minimum compatible revision
### Mod.ModRef.revisionMax[🔗](#$api.type.mod~Mod.ModRef.revisionMax)
```
Mod.ModRef.revisionMax: integer
```
The maximum compatible revision
### Mod.ModRefInfo[🔗](#$api.type.mod~Mod.ModRefInfo)
```
record Mod.ModRefInfo displayName: string
url: string
end
```
User facing mod informations
### Mod.ModRefInfo.displayName[🔗](#$api.type.mod~Mod.ModRefInfo.displayName)
```
Mod.ModRefInfo.displayName: string
```
Display name of the mod
### Mod.ModRefInfo.url[🔗](#$api.type.mod~Mod.ModRefInfo.url)
```
Mod.ModRefInfo.url: string
```
URL to the mod page
### Mod.ModDependency[🔗](#$api.type.mod~Mod.ModDependency)
```
record Mod.ModDependency mod: ModRef
optional: boolean
loadBefore: boolean
modInfo: ModRefInfo
end
```
Describes a dependency to another mod
### Mod.ModDependency.mod[🔗](#$api.type.mod~Mod.ModDependency.mod)
```
Mod.ModDependency.mod: ModRef
```
Mod Ref of the depedency
### Mod.ModDependency.optional[🔗](#$api.type.mod~Mod.ModDependency.optional)
```
Mod.ModDependency.optional: boolean
```
If the depedency is optional
### Mod.ModDependency.loadBefore[🔗](#$api.type.mod~Mod.ModDependency.loadBefore)
```
Mod.ModDependency.loadBefore: boolean
```
If this depedency needs to be loaded before the dependant
### Mod.ModDependency.modInfo[🔗](#$api.type.mod~Mod.ModDependency.modInfo)
```
Mod.ModDependency.modInfo: ModRefInfo
```
Mod info for the depedency
### Mod.ModIncompatibility[🔗](#$api.type.mod~Mod.ModIncompatibility)
```
record Mod.ModIncompatibility mod: ModRef
end
```
Describes an incompatibility with another mod
### Mod.ModIncompatibility.mod[🔗](#$api.type.mod~Mod.ModIncompatibility.mod)
```
Mod.ModIncompatibility.mod: ModRef
```
### Mod.GameModDesc[🔗](#$api.type.mod~Mod.GameModDesc)
```
record Mod.GameModDesc modId: ModId
modSource: string
modhubModId: string
name: string
severityRemove: integer
end
```
Contains general information about the mod
### Mod.GameModDesc.modId[🔗](#$api.type.mod~Mod.GameModDesc.modId)
```
Mod.GameModDesc.modId: ModId
```
Mod id
### Mod.GameModDesc.modSource[🔗](#$api.type.mod~Mod.GameModDesc.modSource)
```
Mod.GameModDesc.modSource: string
```
The origin of the mod (eg. User Mod, Staging Area, DLC, mod.io, ...)
### Mod.GameModDesc.modhubModId[🔗](#$api.type.mod~Mod.GameModDesc.modhubModId)
```
Mod.GameModDesc.modhubModId: string
```
Mod id in the modhub backend
### Mod.GameModDesc.name[🔗](#$api.type.mod~Mod.GameModDesc.name)
```
Mod.GameModDesc.name: string
```
Display name of the mod (User facing)
### Mod.GameModDesc.severityRemove[🔗](#$api.type.mod~Mod.GameModDesc.severityRemove)
```
Mod.GameModDesc.severityRemove: integer
```
If removing this mod could cause instability to a savegame.
Any value above 0 will trigger a message when trying to remove the mod from a savegame
0: None, 1: Warning, 2:Critical
### Mod.ModDesc[🔗](#$api.type.mod~Mod.ModDesc)
```
record Mod.ModDesc severityAdd: integer
severityRemove: integer
params: {ScriptParam}
visible: boolean
cosmetic: boolean
autoActivate: boolean
url: string
dependencies: {ModDependency}
incompatibilities: {ModIncompatibility}
revision: integer
end
```
### Mod.ModDesc.severityAdd[🔗](#$api.type.mod~Mod.ModDesc.severityAdd)
```
Mod.ModDesc.severityAdd: integer
```
If adding this mod could cause instability to a savegame.
Any value above 0 will trigger a message when trying to add the mod to a savegame
0: None, 1: Warning, 2:Critical
### Mod.ModDesc.severityRemove[🔗](#$api.type.mod~Mod.ModDesc.severityRemove)
```
Mod.ModDesc.severityRemove: integer
```
If removing this mod could cause instability to a savegame.
Any value above 0 will trigger a message when trying to remove the mod from a savegame
0: None, 1: Warning, 2:Critical
### Mod.ModDesc.params[🔗](#$api.type.mod~Mod.ModDesc.params)
```
Mod.ModDesc.params: {ScriptParam}
```
Script parameters to dynamically configure the mod
### Mod.ModDesc.visible[🔗](#$api.type.mod~Mod.ModDesc.visible)
```
Mod.ModDesc.visible: boolean
```
If if the mod is visible, hidden mod cannot auto-activate
### Mod.ModDesc.cosmetic[🔗](#$api.type.mod~Mod.ModDesc.cosmetic)
```
Mod.ModDesc.cosmetic: boolean
```
If the mod is purely cosmetic, cosmetic mods still allow achievement from being earned while playing
### Mod.ModDesc.autoActivate[🔗](#$api.type.mod~Mod.ModDesc.autoActivate)
```
Mod.ModDesc.autoActivate: boolean
```
If the mod is automatically activate by the game
### Mod.ModDesc.url[🔗](#$api.type.mod~Mod.ModDesc.url)
```
Mod.ModDesc.url: string
```
URL to the mod page
### Mod.ModDesc.dependencies[🔗](#$api.type.mod~Mod.ModDesc.dependencies)
```
Mod.ModDesc.dependencies: {ModDependency}
```
Mods this mod depends on
### Mod.ModDesc.incompatibilities[🔗](#$api.type.mod~Mod.ModDesc.incompatibilities)
```
Mod.ModDesc.incompatibilities: {ModIncompatibility}
```
Mods this mod is incompatible with
### Mod.ModDesc.revision[🔗](#$api.type.mod~Mod.ModDesc.revision)
```
Mod.ModDesc.revision: integer
```
version of the mod
### Mod.ModSize[🔗](#$api.type.mod~Mod.ModSize)
```
record Mod.ModSize numModels: integer
numTextures: integer
numMeshes: integer
totalEstimatedSizeMB: integer
end
```
Information about the size of the mod
### Mod.ModSize.numModels[🔗](#$api.type.mod~Mod.ModSize.numModels)
```
Mod.ModSize.numModels: integer
```
The number of models contained in the mod
### Mod.ModSize.numTextures[🔗](#$api.type.mod~Mod.ModSize.numTextures)
```
Mod.ModSize.numTextures: integer
```
The number of texture contained in the mod
### Mod.ModSize.numMeshes[🔗](#$api.type.mod~Mod.ModSize.numMeshes)
```
Mod.ModSize.numMeshes: integer
```
The number of meshes contained in the mod
### Mod.ModSize.totalEstimatedSizeMB[🔗](#$api.type.mod~Mod.ModSize.totalEstimatedSizeMB)
```
Mod.ModSize.totalEstimatedSizeMB: integer
```
The approximate size of the mod
## Category: globals[🔗](#category-globals)
### ModId[🔗](#$api.type.mod~ModId)
```
global type ModId = integer
```
File diff suppressed because it is too large. Load diff
+788
View File
@@ -0,0 +1,788 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/app.html (fetched 2026-10-04) -->
[index](index.html) / app
---
# Module app[🔗](#app)
Module controlling high level functions for the application, can be used to manipulate save games.
```
[Expand All] [Collapse All]
global record App record ProgressMonitor getProgress: function(ProgressMonitor): number
getTask: function(ProgressMonitor): string
end
quit: function(instant : boolean)
restart: function()
printMemoryStats: function()
startGame: function(startGameParams : StartGameParams)
startGame2: function(map : GameMap, seed : string, startDate : Date, startTimeOfDaySeconds : integer, startCloudCoverage : number, isMapEditor : boolean, mods : {Mod.ModId}, configDict : {{string, string}}, modParams : {string : {string : integer}})
stopGame: function()
findAllSavegames: function(namespace : string): {SaveGameInfo}
loadGame: function(saveId : SaveGameId, isMapEditor : boolean, info : SaveGameData.SaveGameDetails)
loadMission: function(saveId : SaveGameId, campaign : string, mission : string)
saveGame: function(name : string, callBack : function(), isMapEditor : boolean, skipSetName : boolean)
parseSavegameName: function(savegameName : string): ParsedSavegameName
getSavegameComplexity: function(saveGameDetails : SaveGameData.SaveGameDetails): integer
getSavegameComplexityLimit: function(): integer
getSavegameInfo: function(saveId : SaveGameId): Async<SaveGameData>
getSavegameImage: function(saveId : SaveGameId): GameScreenshot | nil
deleteSavegame: function(saveId : SaveGameId): boolean
getUserProfile: function(): UserProfile
findBuiltinSavegames: function(isMapSave : boolean): {SaveGameInfo}
getSaveGameStorageInfo: function(): SaveGameStorageInfo
getSupportedMSAASampleCounts: function(): {integer}
loadUserdata: function(directory : string, fileName : string): {<any type> : <any type>}
saveUserdata: function(directory : string, fileName : string, table : {<any type> : <any type>})
removeUserdata: function(directory : string, fileName : string)
getAllUserdata: function(directory : string): {string}
playVoiceOverOrStop: function(resName : string)
makeEmptyMap: function(climate : string, numTiles : Vec2i, waterLevel : number): GameMap
getInputActionRep: function(): InputActionRep
refreshMods: function()
setWaitForStartReadyGame: function()
isWaitForStartReadyGame: function(): boolean
startReadyGame: function()
getRandomTips: function(): {string}
getModTagTranslation: function(tag : string): string
getProgressMonitor: function(): ProgressMonitor
openUserDataFolder: function()
openUserDataSubFolder: function(directory : string)
getUserDataFolder: function(): string
getAllHeightmaps: function(): {string}
getGPUCapabilities: function(): GPUCapabilities
writeHeightmap: function(map : GameMap, path : string, writeStl : boolean, sixteenBits : boolean): boolean
showNodeEditor: function()
record SaveGameNamespace getUserdata: function(): string
getSavegame: function(): string
getSavemap: function(): string
end
record Debug setForcedGamepadDynamicScrollRepeatSpeed: function(speed : integer)
pushGamepadButtonKeyInputEvent: function(gamepadButtonName : string, press : boolean)
end
debug: Debug
end
```
## Category: globals[🔗](#category-globals)
### App[🔗](#$app~App)
```
[Expand All] [Collapse All]
global record App record ProgressMonitor getProgress: function(ProgressMonitor): number
getTask: function(ProgressMonitor): string
end
quit: function(instant : boolean)
restart: function()
printMemoryStats: function()
startGame: function(startGameParams : StartGameParams)
startGame2: function(map : GameMap, seed : string, startDate : Date, startTimeOfDaySeconds : integer, startCloudCoverage : number, isMapEditor : boolean, mods : {Mod.ModId}, configDict : {{string, string}}, modParams : {string : {string : integer}})
stopGame: function()
findAllSavegames: function(namespace : string): {SaveGameInfo}
loadGame: function(saveId : SaveGameId, isMapEditor : boolean, info : SaveGameData.SaveGameDetails)
loadMission: function(saveId : SaveGameId, campaign : string, mission : string)
saveGame: function(name : string, callBack : function(), isMapEditor : boolean, skipSetName : boolean)
parseSavegameName: function(savegameName : string): ParsedSavegameName
getSavegameComplexity: function(saveGameDetails : SaveGameData.SaveGameDetails): integer
getSavegameComplexityLimit: function(): integer
getSavegameInfo: function(saveId : SaveGameId): Async<SaveGameData>
getSavegameImage: function(saveId : SaveGameId): GameScreenshot | nil
deleteSavegame: function(saveId : SaveGameId): boolean
getUserProfile: function(): UserProfile
findBuiltinSavegames: function(isMapSave : boolean): {SaveGameInfo}
getSaveGameStorageInfo: function(): SaveGameStorageInfo
getSupportedMSAASampleCounts: function(): {integer}
loadUserdata: function(directory : string, fileName : string): {<any type> : <any type>}
saveUserdata: function(directory : string, fileName : string, table : {<any type> : <any type>})
removeUserdata: function(directory : string, fileName : string)
getAllUserdata: function(directory : string): {string}
playVoiceOverOrStop: function(resName : string)
makeEmptyMap: function(climate : string, numTiles : Vec2i, waterLevel : number): GameMap
getInputActionRep: function(): InputActionRep
refreshMods: function()
setWaitForStartReadyGame: function()
isWaitForStartReadyGame: function(): boolean
startReadyGame: function()
getRandomTips: function(): {string}
getModTagTranslation: function(tag : string): string
getProgressMonitor: function(): ProgressMonitor
openUserDataFolder: function()
openUserDataSubFolder: function(directory : string)
getUserDataFolder: function(): string
getAllHeightmaps: function(): {string}
getGPUCapabilities: function(): GPUCapabilities
writeHeightmap: function(map : GameMap, path : string, writeStl : boolean, sixteenBits : boolean): boolean
showNodeEditor: function()
record SaveGameNamespace getUserdata: function(): string
getSavegame: function(): string
getSavemap: function(): string
end
record Debug setForcedGamepadDynamicScrollRepeatSpeed: function(speed : integer)
pushGamepadButtonKeyInputEvent: function(gamepadButtonName : string, press : boolean)
end
debug: Debug
end
```
### App.ProgressMonitor[🔗](#$app~App.ProgressMonitor)
```
record App.ProgressMonitor getProgress: function(ProgressMonitor): number
getTask: function(ProgressMonitor): string
end
```
### App.ProgressMonitor.getProgress[🔗](#$app~App.ProgressMonitor.getProgress)
```
function App.ProgressMonitor.getProgress(ProgressMonitor): number
```
Returns the current progress as a value from 0 to 1
**Parameters**
* `ProgressMonitor`
**Returns**
1. `number`
### App.ProgressMonitor.getTask[🔗](#$app~App.ProgressMonitor.getTask)
```
function App.ProgressMonitor.getTask(ProgressMonitor): string
```
Returns the current task being executed
**Parameters**
* `ProgressMonitor`
**Returns**
1. `string`
### App.quit[🔗](#$app~App.quit)
```
function App.quit(instant : boolean)
```
Quit the application.
**Parameters**
* `instant` :`boolean` — immediately quit one exit
### App.restart[🔗](#$app~App.restart)
```
function App.restart()
```
Restart the application
### App.printMemoryStats[🔗](#$app~App.printMemoryStats)
```
function App.printMemoryStats()
```
Prints memory statistics to the log output.
### App.startGame[🔗](#$app~App.startGame)
```
function App.startGame(startGameParams : StartGameParams)
```
Starts a new test game by specifying some basic parameters
**Parameters**
* `startGameParams` :`StartGameParams` — start game parameters (optional) - if not set, the game will start with default values
### App.startGame2[🔗](#$app~App.startGame2)
```
function App.startGame2(map : GameMap, seed : string, startDate : Date, startTimeOfDaySeconds : integer, startCloudCoverage : number, isMapEditor : boolean, mods : {Mod.ModId}, configDict : {{string, string}}, modParams : {string : {string : integer}})
```
Starts a new game
**Parameters**
* `map` :`GameMap` — the map to use
* `seed` :`string` — the seed use for the map generation
* `startDate` :`Date` — the start date
* `startTimeOfDaySeconds` :`integer` — is the start time of the day in seconds
* `startCloudCoverage` :`number` — is the start cloud coverage
* `isMapEditor` :`boolean` — starting from map editor
* `mods` :`{Mod.ModId}` — list of mods to start the game with
* `configDict` :`{{string, string}}` — list of game settings
* `modParams` :`{string : {string : integer}}` — list of mod params
### App.stopGame[🔗](#$app~App.stopGame)
```
function App.stopGame()
```
Stops the current game.
### App.findAllSavegames[🔗](#$app~App.findAllSavegames)
```
function App.findAllSavegames(namespace : string): {SaveGameInfo}
```
Obtain list of all savegames.
**Parameters**
* `namespace` :`string` — from which to obtain information (see app.SaveGameNamespace.getSavemap() or app.SaveGameNamespace.getSavegame() for the namespaces)
**Returns**
1. `{SaveGameInfo}` — a list of all savegame
### App.loadGame[🔗](#$app~App.loadGame)
```
function App.loadGame(saveId : SaveGameId, isMapEditor : boolean, info : SaveGameData.SaveGameDetails)
```
Loads a savegame
**Parameters**
* `saveId` :`SaveGameId` — the savegame id
* `isMapEditor` :`boolean` — if the savegame is loaded in the map editor
* `info` :`SaveGameData.SaveGameDetails` — savegame info (optional)
### App.loadMission[🔗](#$app~App.loadMission)
```
function App.loadMission(saveId : SaveGameId, campaign : string, mission : string)
```
Loads a mission
**Parameters**
* `saveId` :`SaveGameId` — the savegame id
* `campaign` :`string` — campaign name
* `mission` :`string` — mission name
### App.saveGame[🔗](#$app~App.saveGame)
```
function App.saveGame(name : string, callBack : function(), isMapEditor : boolean, skipSetName : boolean)
```
Saves the current game.
**Parameters**
* `name` :`string` — the name of the savegame
* `callBack` :`function()` — callback when the savegame is finished
* `isMapEditor` :`boolean` — is saving from the map editor
* `skipSetName` :`boolean` — skips setting the savegame name as the current default savegame name (optional)
### App.parseSavegameName[🔗](#$app~App.parseSavegameName)
```
function App.parseSavegameName(savegameName : string): ParsedSavegameName
```
Parse savegame info from its file name
**Parameters**
* `savegameName` :`string` — the name of the savegame
**Returns**
1. `ParsedSavegameName` — The info (most notably, savegame type and group name)
### App.getSavegameComplexity[🔗](#$app~App.getSavegameComplexity)
```
function App.getSavegameComplexity(saveGameDetails : SaveGameData.SaveGameDetails): integer
```
Get an estimated value of the savegame complexity
**Parameters**
* `saveGameDetails` :`SaveGameData.SaveGameDetails` — the savegame data of the savegame
**Returns**
1. `integer` — a value that represents the estimate size of the savegame
### App.getSavegameComplexityLimit[🔗](#$app~App.getSavegameComplexityLimit)
```
function App.getSavegameComplexityLimit(): integer
```
Get an limit for the savegame complexity for the current platform
**Returns**
1. `integer` — the complexity limit for the current platform
### App.getSavegameInfo[🔗](#$app~App.getSavegameInfo)
```
function App.getSavegameInfo(saveId : SaveGameId): Async<SaveGameData>
```
Loads savegame metadata asynchronously.
**Parameters**
* `saveId` :`SaveGameId` — the savegame id
**Returns**
1. `Async<SaveGameData>` — an async object with the savegame data
### App.getSavegameImage[🔗](#$app~App.getSavegameImage)
```
function App.getSavegameImage(saveId : SaveGameId): GameScreenshot | nil
```
Obtains the savegame preview image
Note: This is - usually, if not explicitly changed in userdata - the same image as GameMetadata.screenshot but with the full available resolution
**Parameters**
* `saveId` :`SaveGameId` — the savegame id
**Returns**
1. `GameScreenshot | nil` — the image, or nil if no preview image is available
### App.deleteSavegame[🔗](#$app~App.deleteSavegame)
```
function App.deleteSavegame(saveId : SaveGameId): boolean
```
Deletes a savegame
**Parameters**
* `saveId` :`SaveGameId` — the savegame to delete
**Returns**
1. `boolean` — if the savegame was successfully deleted
### App.getUserProfile[🔗](#$app~App.getUserProfile)
```
function App.getUserProfile(): UserProfile
```
Obtain the user profile.
**Returns**
1. `UserProfile` — the user profile
### App.findBuiltinSavegames[🔗](#$app~App.findBuiltinSavegames)
```
function App.findBuiltinSavegames(isMapSave : boolean): {SaveGameInfo}
```
Obtains a list of built-in savegames.
**Parameters**
* `isMapSave` :`boolean` — true to list built-in maps, false to list built-in savegames
**Returns**
1. `{SaveGameInfo}` — list of savegames
### App.getSaveGameStorageInfo[🔗](#$app~App.getSaveGameStorageInfo)
```
function App.getSaveGameStorageInfo(): SaveGameStorageInfo
```
Returns the savegame storage infos
**Returns**
1. `SaveGameStorageInfo` — savegame storage info
### App.getSupportedMSAASampleCounts[🔗](#$app~App.getSupportedMSAASampleCounts)
```
function App.getSupportedMSAASampleCounts(): {integer}
```
Returns the supported MSAA sample counts
**Returns**
1. `{integer}` — list of supported sample counts
### App.loadUserdata[🔗](#$app~App.loadUserdata)
```
function App.loadUserdata(directory : string, fileName : string): {<any type> : <any type>}
```
Load a custom userdata file
**Parameters**
* `directory` :`string` — a folder in the userdata directory
* `fileName` :`string` — a lua filename (without .lua extension)
**Returns**
1. `{<any type> : <any type>}` — the deserialized lua data
### App.saveUserdata[🔗](#$app~App.saveUserdata)
```
function App.saveUserdata(directory : string, fileName : string, table : {<any type> : <any type>})
```
Saves a custom table as lua file.
**Parameters**
* `directory` :`string` — a folder in the userdata directory
* `fileName` :`string` — the filename to save to (without .lua extension)
* `table` :`{<any type> : <any type>}` — the table to serialize
### App.removeUserdata[🔗](#$app~App.removeUserdata)
```
function App.removeUserdata(directory : string, fileName : string)
```
Removes a userdata.
**Parameters**
* `directory` :`string` — a folder in the userdata directory
* `fileName` :`string` — the filename to remove (without .lua extension)
### App.getAllUserdata[🔗](#$app~App.getAllUserdata)
```
function App.getAllUserdata(directory : string): {string}
```
Returns a list of all saved mod preset names.
**Parameters**
* `directory` :`string` — a folder in the userdata directory
**Returns**
1. `{string}` — a list of mod preset names
### App.playVoiceOverOrStop[🔗](#$app~App.playVoiceOverOrStop)
```
function App.playVoiceOverOrStop(resName : string)
```
Plays a voice over if not currently playing, otherwise stops it.
**Parameters**
* `resName` :`string` — the file to play
### App.makeEmptyMap[🔗](#$app~App.makeEmptyMap)
```
function App.makeEmptyMap(climate : string, numTiles : Vec2i, waterLevel : number): GameMap
```
Creates an empty map (for tooling / map generation workflows).
**Parameters**
* `climate` :`string` — climate resource name
* `numTiles` :`Vec2i` — map dimension in tiles
* `waterLevel` :`number` — water level height
**Returns**
1. `GameMap` — the map
### App.getInputActionRep[🔗](#$app~App.getInputActionRep)
```
function App.getInputActionRep(): InputActionRep
```
Returns the global input action repository.
**Returns**
1. `InputActionRep` — input action repository
### App.refreshMods[🔗](#$app~App.refreshMods)
```
function App.refreshMods()
```
Refreshes the installed mods
### App.setWaitForStartReadyGame[🔗](#$app~App.setWaitForStartReadyGame)
```
function App.setWaitForStartReadyGame()
```
Wait for the game to be ready to start. This is called when loading a game to prevent the game from starting automatically
### App.isWaitForStartReadyGame[🔗](#$app~App.isWaitForStartReadyGame)
```
function App.isWaitForStartReadyGame(): boolean
```
Return if the `setWaitForStartReadyGame` had been called for this game.
**Returns**
1. `boolean`
### App.startReadyGame[🔗](#$app~App.startReadyGame)
```
function App.startReadyGame()
```
Start Game that is fully loaded and ready to play. This is called after the progress page has finished loading the game.
### App.getRandomTips[🔗](#$app~App.getRandomTips)
```
function App.getRandomTips(): {string}
```
Gets the list of random tips
**Returns**
1. `{string}` — a list of tips as string
### App.getModTagTranslation[🔗](#$app~App.getModTagTranslation)
```
function App.getModTagTranslation(tag : string): string
```
Returns a translated name for a mod tag.
**Parameters**
* `tag` :`string` — tag to translate
**Returns**
1. `string` — the translated tag
### App.getProgressMonitor[🔗](#$app~App.getProgressMonitor)
```
function App.getProgressMonitor(): ProgressMonitor
```
Gets the load progress monitor
**Returns**
1. `ProgressMonitor`
### App.openUserDataFolder[🔗](#$app~App.openUserDataFolder)
```
function App.openUserDataFolder()
```
Opens the user data folder in the OS file manager
### App.openUserDataSubFolder[🔗](#$app~App.openUserDataSubFolder)
```
function App.openUserDataSubFolder(directory : string)
```
Opens the heightmaps folder in the OS file manager
**Parameters**
* `directory` :`string` — subfolder
### App.getUserDataFolder[🔗](#$app~App.getUserDataFolder)
```
function App.getUserDataFolder(): string
```
Gets the directory containing userdata
**Returns**
1. `string`
### App.getAllHeightmaps[🔗](#$app~App.getAllHeightmaps)
```
function App.getAllHeightmaps(): {string}
```
Gets a list of all heightmaps in the userdata directory
**Returns**
1. `{string}` — list of heightmaps files
### App.getGPUCapabilities[🔗](#$app~App.getGPUCapabilities)
```
function App.getGPUCapabilities(): GPUCapabilities
```
Get information about the GPU
**Returns**
1. `GPUCapabilities`
### App.writeHeightmap[🔗](#$app~App.writeHeightmap)
```
function App.writeHeightmap(map : GameMap, path : string, writeStl : boolean, sixteenBits : boolean): boolean
```
Writes a heightmap image (and optionally an STL) for a map.
**Parameters**
* `map` :`GameMap` — the map to write
* `path` :`string` — the relative path (extension will be added if missing)
* `writeStl` :`boolean` — write as an STL file as well
* `sixteenBits` :`boolean` — use 16 bits depth for the heightmap (default 8 bits depth)
**Returns**
1. `boolean` — if the heightmap was successfully written
### App.showNodeEditor[🔗](#$app~App.showNodeEditor)
```
function App.showNodeEditor()
```
Shows the node editor.
### App.SaveGameNamespace[🔗](#$app~App.SaveGameNamespace)
```
record App.SaveGameNamespace getUserdata: function(): string
getSavegame: function(): string
getSavemap: function(): string
end
```
Savegame namespace constants.
### App.SaveGameNamespace.getUserdata[🔗](#$app~App.SaveGameNamespace.getUserdata)
```
function App.SaveGameNamespace.getUserdata(): string
```
Returns the userdata savegame namespace.
**Returns**
1. `string`
### App.SaveGameNamespace.getSavegame[🔗](#$app~App.SaveGameNamespace.getSavegame)
```
function App.SaveGameNamespace.getSavegame(): string
```
Returns the savegame namespace.
**Returns**
1. `string`
### App.SaveGameNamespace.getSavemap[🔗](#$app~App.SaveGameNamespace.getSavemap)
```
function App.SaveGameNamespace.getSavemap(): string
```
Returns the savemap namespace.
**Returns**
1. `string`
### App.Debug[🔗](#$app~App.Debug)
```
record App.Debug setForcedGamepadDynamicScrollRepeatSpeed: function(speed : integer)
pushGamepadButtonKeyInputEvent: function(gamepadButtonName : string, press : boolean)
end
```
### App.Debug.setForcedGamepadDynamicScrollRepeatSpeed[🔗](#$app~App.Debug.setForcedGamepadDynamicScrollRepeatSpeed)
```
function App.Debug.setForcedGamepadDynamicScrollRepeatSpeed(speed : integer)
```
Overrides gamepad dynamic scroll repeat speed.
**Parameters**
* `speed` :`integer` — the scroll speed (0: 1/sec, 3: max speed), or nil to clear the override
### App.Debug.pushGamepadButtonKeyInputEvent[🔗](#$app~App.Debug.pushGamepadButtonKeyInputEvent)
```
function App.Debug.pushGamepadButtonKeyInputEvent(gamepadButtonName : string, press : boolean)
```
Pushes a gamepad button event into the input queue.
**Parameters**
* `gamepadButtonName` :`string` — the button to push
* `press` :`boolean` — if pressed
### App.debug[🔗](#$app~App.debug)
```
App.debug: Debug
```
@@ -0,0 +1,123 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/content/base/difficulty_util.html (fetched 2026-10-04) -->
[index](../../index.html) / content / base / difficulty\_util
---
# Module content.base.difficulty\_util[🔗](#content.base.difficulty_util)
```
global record DifficultyUtil calculateDifficulty: function({string : integer}): GameDifficulty
getModParamPreset: function(GameDifficulty): {string : integer}
getLabel: function(GameDifficulty): string
getScale: function(string, integer): number
getAllValues: function(string): {number}
end
global enum GameDifficulty "Easy"
"Hard"
"Medium"
"VeryHard"
end
```
## Category: globals[🔗](#category-globals)
### DifficultyUtil[🔗](#$content.base.difficulty_util~DifficultyUtil)
```
global record DifficultyUtil calculateDifficulty: function({string : integer}): GameDifficulty
getModParamPreset: function(GameDifficulty): {string : integer}
getLabel: function(GameDifficulty): string
getScale: function(string, integer): number
getAllValues: function(string): {number}
end
```
### DifficultyUtil.calculateDifficulty[🔗](#$content.base.difficulty_util~DifficultyUtil.calculateDifficulty)
```
function DifficultyUtil.calculateDifficulty({string : integer}): GameDifficulty
```
**Parameters**
* `{string : integer}`
**Returns**
1. `GameDifficulty`
### DifficultyUtil.getModParamPreset[🔗](#$content.base.difficulty_util~DifficultyUtil.getModParamPreset)
```
function DifficultyUtil.getModParamPreset(GameDifficulty): {string : integer}
```
**Parameters**
* `GameDifficulty`
**Returns**
1. `{string : integer}`
### DifficultyUtil.getLabel[🔗](#$content.base.difficulty_util~DifficultyUtil.getLabel)
```
function DifficultyUtil.getLabel(GameDifficulty): string
```
**Parameters**
* `GameDifficulty`
**Returns**
1. `string`
### DifficultyUtil.getScale[🔗](#$content.base.difficulty_util~DifficultyUtil.getScale)
```
function DifficultyUtil.getScale(string, integer): number
```
**Parameters**
* `string`
* `integer`
**Returns**
1. `number`
### DifficultyUtil.getAllValues[🔗](#$content.base.difficulty_util~DifficultyUtil.getAllValues)
```
function DifficultyUtil.getAllValues(string): {number}
```
**Parameters**
* `string`
**Returns**
1. `{number}`
### GameDifficulty[🔗](#$content.base.difficulty_util~GameDifficulty)
```
global enum GameDifficulty "Easy"
"Hard"
"Medium"
"VeryHard"
end
```
### GameDifficulty."Easy"[🔗](#$content.base.difficulty_util~GameDifficulty.)
### GameDifficulty."Hard"[🔗](#$content.base.difficulty_util~GameDifficulty.)
### GameDifficulty."Medium"[🔗](#$content.base.difficulty_util~GameDifficulty.)
### GameDifficulty."VeryHard"[🔗](#$content.base.difficulty_util~GameDifficulty.)
@@ -0,0 +1,427 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/content/base/model_metadata_util.html (fetched 2026-10-04) -->
[index](../../index.html) / content / base / model\_metadata\_util
---
# Module content.base.model\_metadata\_util[🔗](#content.base.model_metadata_util)
```
global record ModelMetadataUtil convertEmissionData: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addEmissionMetadata: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addCostMetadata: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTransportVehicleTransportModes: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addFilterTags: function(string, {<any type> : <any type>}): {<any type> : <any type>}
newLoadSpeedsAndMaintenanceFactor: function(string, {<any type> : <any type>}): {<any type> : <any type>}
turnRoadAndRailToLandVehicle: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addVehicleExtent: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTransformatorConfig: function(string, {<any type> : <any type>}): {<any type> : <any type>}
toCompartmentList: function(string, {<any type> : <any type>}): {<any type> : <any type>}
replaceMeshIdWithNodeName: function(string, {<any type> : <any type>}): {<any type> : <any type>}
squeezeConfigsToOneConfigForVehicles: function(string, {<any type> : <any type>}): {<any type> : <any type>}
sortLods: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTextureLods: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTreeReputationFactor: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeLoadIndicators: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeLoadIndicatorFormats: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeCargoEntry: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeCargoTypeSet: function(string, {<any type> : <any type>}): {<any type> : <any type>}
convertCargoType: function(string, string): string
convertCargoTypeSet: function({<any type> : <any type>}, string): {<any type> : <any type>}
convertCargoTypes: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addIconMetadata: function(string, {<any type> : <any type>}): {<any type> : <any type>}
convertParticleSystem: function(string, {<any type> : <any type>}): {<any type> : <any type>}
end
```
## Category: globals[🔗](#category-globals)
### ModelMetadataUtil[🔗](#$content.base.model_metadata_util~ModelMetadataUtil)
```
global record ModelMetadataUtil convertEmissionData: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addEmissionMetadata: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addCostMetadata: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTransportVehicleTransportModes: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addFilterTags: function(string, {<any type> : <any type>}): {<any type> : <any type>}
newLoadSpeedsAndMaintenanceFactor: function(string, {<any type> : <any type>}): {<any type> : <any type>}
turnRoadAndRailToLandVehicle: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addVehicleExtent: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTransformatorConfig: function(string, {<any type> : <any type>}): {<any type> : <any type>}
toCompartmentList: function(string, {<any type> : <any type>}): {<any type> : <any type>}
replaceMeshIdWithNodeName: function(string, {<any type> : <any type>}): {<any type> : <any type>}
squeezeConfigsToOneConfigForVehicles: function(string, {<any type> : <any type>}): {<any type> : <any type>}
sortLods: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTextureLods: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addTreeReputationFactor: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeLoadIndicators: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeLoadIndicatorFormats: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeCargoEntry: function(string, {<any type> : <any type>}): {<any type> : <any type>}
makeCargoTypeSet: function(string, {<any type> : <any type>}): {<any type> : <any type>}
convertCargoType: function(string, string): string
convertCargoTypeSet: function({<any type> : <any type>}, string): {<any type> : <any type>}
convertCargoTypes: function(string, {<any type> : <any type>}): {<any type> : <any type>}
addIconMetadata: function(string, {<any type> : <any type>}): {<any type> : <any type>}
convertParticleSystem: function(string, {<any type> : <any type>}): {<any type> : <any type>}
end
```
### ModelMetadataUtil.convertEmissionData[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.convertEmissionData)
```
function ModelMetadataUtil.convertEmissionData(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addEmissionMetadata[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addEmissionMetadata)
```
function ModelMetadataUtil.addEmissionMetadata(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addCostMetadata[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addCostMetadata)
```
function ModelMetadataUtil.addCostMetadata(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addTransportVehicleTransportModes[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addTransportVehicleTransportModes)
```
function ModelMetadataUtil.addTransportVehicleTransportModes(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addFilterTags[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addFilterTags)
```
function ModelMetadataUtil.addFilterTags(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.newLoadSpeedsAndMaintenanceFactor[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.newLoadSpeedsAndMaintenanceFactor)
```
function ModelMetadataUtil.newLoadSpeedsAndMaintenanceFactor(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.turnRoadAndRailToLandVehicle[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.turnRoadAndRailToLandVehicle)
```
function ModelMetadataUtil.turnRoadAndRailToLandVehicle(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addVehicleExtent[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addVehicleExtent)
```
function ModelMetadataUtil.addVehicleExtent(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addTransformatorConfig[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addTransformatorConfig)
```
function ModelMetadataUtil.addTransformatorConfig(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.toCompartmentList[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.toCompartmentList)
```
function ModelMetadataUtil.toCompartmentList(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.replaceMeshIdWithNodeName[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.replaceMeshIdWithNodeName)
```
function ModelMetadataUtil.replaceMeshIdWithNodeName(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.squeezeConfigsToOneConfigForVehicles[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.squeezeConfigsToOneConfigForVehicles)
```
function ModelMetadataUtil.squeezeConfigsToOneConfigForVehicles(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.sortLods[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.sortLods)
```
function ModelMetadataUtil.sortLods(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addTextureLods[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addTextureLods)
```
function ModelMetadataUtil.addTextureLods(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addTreeReputationFactor[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addTreeReputationFactor)
```
function ModelMetadataUtil.addTreeReputationFactor(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.makeLoadIndicators[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.makeLoadIndicators)
```
function ModelMetadataUtil.makeLoadIndicators(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.makeLoadIndicatorFormats[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.makeLoadIndicatorFormats)
```
function ModelMetadataUtil.makeLoadIndicatorFormats(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.makeCargoEntry[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.makeCargoEntry)
```
function ModelMetadataUtil.makeCargoEntry(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.makeCargoTypeSet[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.makeCargoTypeSet)
```
function ModelMetadataUtil.makeCargoTypeSet(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.convertCargoType[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.convertCargoType)
```
function ModelMetadataUtil.convertCargoType(string, string): string
```
**Parameters**
* `string`
* `string`
**Returns**
1. `string`
### ModelMetadataUtil.convertCargoTypeSet[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.convertCargoTypeSet)
```
function ModelMetadataUtil.convertCargoTypeSet({<any type> : <any type>}, string): {<any type> : <any type>}
```
**Parameters**
* `{<any type> : <any type>}`
* `string`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.convertCargoTypes[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.convertCargoTypes)
```
function ModelMetadataUtil.convertCargoTypes(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.addIconMetadata[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.addIconMetadata)
```
function ModelMetadataUtil.addIconMetadata(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
### ModelMetadataUtil.convertParticleSystem[🔗](#$content.base.model_metadata_util~ModelMetadataUtil.convertParticleSystem)
```
function ModelMetadataUtil.convertParticleSystem(string, {<any type> : <any type>}): {<any type> : <any type>}
```
**Parameters**
* `string`
* `{<any type> : <any type>}`
**Returns**
1. `{<any type> : <any type>}`
@@ -0,0 +1,41 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/content/base/post_run_util.html (fetched 2026-10-04) -->
[index](../../index.html) / content / base / post\_run\_util
---
# Module content.base.post\_run\_util[🔗](#content.base.post_run_util)
```
global record PostRunUtil applyCostScale: function()
updateDesc: function(ModelMetadata.Description, number, number)
end
```
## Category: globals[🔗](#category-globals)
### PostRunUtil[🔗](#$content.base.post_run_util~PostRunUtil)
```
global record PostRunUtil applyCostScale: function()
updateDesc: function(ModelMetadata.Description, number, number)
end
```
### PostRunUtil.applyCostScale[🔗](#$content.base.post_run_util~PostRunUtil.applyCostScale)
```
function PostRunUtil.applyCostScale()
```
### PostRunUtil.updateDesc[🔗](#$content.base.post_run_util~PostRunUtil.updateDesc)
```
function PostRunUtil.updateDesc(ModelMetadata.Description, number, number)
```
**Parameters**
* `ModelMetadata.Description`
* `number`
* `number`
@@ -0,0 +1,27 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/content/gui/main/mod_entry_point.html (fetched 2026-10-04) -->
[index](../../../index.html) / content / gui / main / mod\_entry\_point
---
# Module content.gui.main.mod\_entry\_point[🔗](#content.gui.main.mod_entry_point)
```
global record ModEntryPoint ModEntryPointExtension: ExtensionPoint0
end
```
## Category: globals[🔗](#category-globals)
### ModEntryPoint[🔗](#$content.gui.main.mod_entry_point~ModEntryPoint)
```
global record ModEntryPoint ModEntryPointExtension: ExtensionPoint0
end
```
### ModEntryPoint.ModEntryPointExtension[🔗](#$content.gui.main.mod_entry_point~ModEntryPoint.ModEntryPointExtension)
```
ModEntryPoint.ModEntryPointExtension: ExtensionPoint0
```
@@ -0,0 +1,505 @@
<!-- Source: https://wiki.transportfever3.com/script-doc/content/gui/main/script_param_util.html (fetched 2026-10-04) -->
[index](../../../index.html) / content / gui / main / script\_param\_util
---
# Module content.gui.main.script\_param\_util[🔗](#content.gui.main.script_param_util)
```
[Expand All] [Collapse All]
global record ScriptParamUtil record ParamForUi enum IconStyle "Colored"
"Default"
end
uiType: ScriptParamType
iconStyle: IconStyle
name: string
values: {string}
numbers: {number}
tooltips: {string}
defaultIndex: integer
hideLabel: boolean
allowCoalesce: boolean
makeCompactButtonFn: function(number): TreeNodeId
formatValueFn: function(number): string
stepValueFn: function(number, integer, boolean): number
end
record CompSimpleParam scriptParam: ParamForUi
currentValue: number
onValueChange: function(number)
toggleButtonsFlowLayout: boolean
vertical: boolean
compact: boolean
addSpacer: boolean
disableGamepadNavigation: boolean
onHover: function(boolean)
gameCtx: GameContext
updateCurrentValue: boolean
end
buildScriptParamCompSimple: function(CompSimpleParam): TreeNodeId
buildScriptParamComp: function(ScriptParam, integer, function(integer), boolean, boolean, function(boolean), boolean): TreeNodeId
searchAndBuildScriptParamComp: function(string, function(string) : number, function(string, number), boolean, function(boolean), {string}, boolean): MenuIconReactUtil.HintWidgetParam.Data, TreeNodeId
wrap: function(string, boolean, TreeNodeId, boolean, function(boolean), string): TreeNodeId
getScriptParam: function(string, {string}): ScriptParam
clampScriptParamToDiscreteValue: function(number, ScriptParam): number
clampScriptParamValueIndex: function(number, ScriptParam): integer
getScriptParamDefaultValue: function({number}, integer): number
getScriptParamDefaultValueIndex: function({number}, integer): integer
getScriptParamLabel: function(string, number, {string}): string
getScriptParamLabelUnfiltered: function(string, number): string
end
```
## Category: globals[🔗](#category-globals)
### ScriptParamUtil[🔗](#$content.gui.main.script_param_util~ScriptParamUtil)
```
[Expand All] [Collapse All]
global record ScriptParamUtil record ParamForUi enum IconStyle "Colored"
"Default"
end
uiType: ScriptParamType
iconStyle: IconStyle
name: string
values: {string}
numbers: {number}
tooltips: {string}
defaultIndex: integer
hideLabel: boolean
allowCoalesce: boolean
makeCompactButtonFn: function(number): TreeNodeId
formatValueFn: function(number): string
stepValueFn: function(number, integer, boolean): number
end
record CompSimpleParam scriptParam: ParamForUi
currentValue: number
onValueChange: function(number)
toggleButtonsFlowLayout: boolean
vertical: boolean
compact: boolean
addSpacer: boolean
disableGamepadNavigation: boolean
onHover: function(boolean)
gameCtx: GameContext
updateCurrentValue: boolean
end
buildScriptParamCompSimple: function(CompSimpleParam): TreeNodeId
buildScriptParamComp: function(ScriptParam, integer, function(integer), boolean, boolean, function(boolean), boolean): TreeNodeId
searchAndBuildScriptParamComp: function(string, function(string) : number, function(string, number), boolean, function(boolean), {string}, boolean): MenuIconReactUtil.HintWidgetParam.Data, TreeNodeId
wrap: function(string, boolean, TreeNodeId, boolean, function(boolean), string): TreeNodeId
getScriptParam: function(string, {string}): ScriptParam
clampScriptParamToDiscreteValue: function(number, ScriptParam): number
clampScriptParamValueIndex: function(number, ScriptParam): integer
getScriptParamDefaultValue: function({number}, integer): number
getScriptParamDefaultValueIndex: function({number}, integer): integer
getScriptParamLabel: function(string, number, {string}): string
getScriptParamLabelUnfiltered: function(string, number): string
end
```
### ScriptParamUtil.ParamForUi[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi)
```
[Expand All] [Collapse All]
record ScriptParamUtil.ParamForUi enum IconStyle "Colored"
"Default"
end
uiType: ScriptParamType
iconStyle: IconStyle
name: string
values: {string}
numbers: {number}
tooltips: {string}
defaultIndex: integer
hideLabel: boolean
allowCoalesce: boolean
makeCompactButtonFn: function(number): TreeNodeId
formatValueFn: function(number): string
stepValueFn: function(number, integer, boolean): number
end
```
### ScriptParamUtil.ParamForUi.IconStyle[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.IconStyle)
```
enum ScriptParamUtil.ParamForUi.IconStyle "Colored"
"Default"
end
```
### ScriptParamUtil.ParamForUi.IconStyle."Colored"[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.IconStyle.)
### ScriptParamUtil.ParamForUi.IconStyle."Default"[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.IconStyle.)
### ScriptParamUtil.ParamForUi.uiType[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.uiType)
```
ScriptParamUtil.ParamForUi.uiType: ScriptParamType
```
### ScriptParamUtil.ParamForUi.iconStyle[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.iconStyle)
```
ScriptParamUtil.ParamForUi.iconStyle: IconStyle
```
### ScriptParamUtil.ParamForUi.name[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.name)
```
ScriptParamUtil.ParamForUi.name: string
```
### ScriptParamUtil.ParamForUi.values[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.values)
```
ScriptParamUtil.ParamForUi.values: {string}
```
### ScriptParamUtil.ParamForUi.numbers[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.numbers)
```
ScriptParamUtil.ParamForUi.numbers: {number}
```
### ScriptParamUtil.ParamForUi.tooltips[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.tooltips)
```
ScriptParamUtil.ParamForUi.tooltips: {string}
```
### ScriptParamUtil.ParamForUi.defaultIndex[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.defaultIndex)
```
ScriptParamUtil.ParamForUi.defaultIndex: integer
```
### ScriptParamUtil.ParamForUi.hideLabel[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.hideLabel)
```
ScriptParamUtil.ParamForUi.hideLabel: boolean
```
### ScriptParamUtil.ParamForUi.allowCoalesce[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.allowCoalesce)
```
ScriptParamUtil.ParamForUi.allowCoalesce: boolean
```
### ScriptParamUtil.ParamForUi.makeCompactButtonFn[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.makeCompactButtonFn)
```
function ScriptParamUtil.ParamForUi.makeCompactButtonFn(number): TreeNodeId
```
**Parameters**
* `number`
**Returns**
1. `TreeNodeId`
### ScriptParamUtil.ParamForUi.formatValueFn[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.formatValueFn)
```
function ScriptParamUtil.ParamForUi.formatValueFn(number): string
```
**Parameters**
* `number`
**Returns**
1. `string`
### ScriptParamUtil.ParamForUi.stepValueFn[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.ParamForUi.stepValueFn)
```
function ScriptParamUtil.ParamForUi.stepValueFn(number, integer, boolean): number
```
**Parameters**
* `number`
* `integer`
* `boolean`
**Returns**
1. `number`
### ScriptParamUtil.CompSimpleParam[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam)
```
record ScriptParamUtil.CompSimpleParam scriptParam: ParamForUi
currentValue: number
onValueChange: function(number)
toggleButtonsFlowLayout: boolean
vertical: boolean
compact: boolean
addSpacer: boolean
disableGamepadNavigation: boolean
onHover: function(boolean)
gameCtx: GameContext
updateCurrentValue: boolean
end
```
### ScriptParamUtil.CompSimpleParam.scriptParam[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.scriptParam)
```
ScriptParamUtil.CompSimpleParam.scriptParam: ParamForUi
```
### ScriptParamUtil.CompSimpleParam.currentValue[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.currentValue)
```
ScriptParamUtil.CompSimpleParam.currentValue: number
```
### ScriptParamUtil.CompSimpleParam.onValueChange[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.onValueChange)
```
function ScriptParamUtil.CompSimpleParam.onValueChange(number)
```
**Parameters**
* `number`
### ScriptParamUtil.CompSimpleParam.toggleButtonsFlowLayout[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.toggleButtonsFlowLayout)
```
ScriptParamUtil.CompSimpleParam.toggleButtonsFlowLayout: boolean
```
### ScriptParamUtil.CompSimpleParam.vertical[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.vertical)
```
ScriptParamUtil.CompSimpleParam.vertical: boolean
```
### ScriptParamUtil.CompSimpleParam.compact[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.compact)
```
ScriptParamUtil.CompSimpleParam.compact: boolean
```
### ScriptParamUtil.CompSimpleParam.addSpacer[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.addSpacer)
```
ScriptParamUtil.CompSimpleParam.addSpacer: boolean
```
### ScriptParamUtil.CompSimpleParam.disableGamepadNavigation[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.disableGamepadNavigation)
```
ScriptParamUtil.CompSimpleParam.disableGamepadNavigation: boolean
```
### ScriptParamUtil.CompSimpleParam.onHover[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.onHover)
```
function ScriptParamUtil.CompSimpleParam.onHover(boolean)
```
**Parameters**
* `boolean`
### ScriptParamUtil.CompSimpleParam.gameCtx[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.gameCtx)
```
ScriptParamUtil.CompSimpleParam.gameCtx: GameContext
```
### ScriptParamUtil.CompSimpleParam.updateCurrentValue[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.CompSimpleParam.updateCurrentValue)
```
ScriptParamUtil.CompSimpleParam.updateCurrentValue: boolean
```
### ScriptParamUtil.buildScriptParamCompSimple[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.buildScriptParamCompSimple)
```
function ScriptParamUtil.buildScriptParamCompSimple(CompSimpleParam): TreeNodeId
```
**Parameters**
* `CompSimpleParam`
**Returns**
1. `TreeNodeId`
### ScriptParamUtil.buildScriptParamComp[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.buildScriptParamComp)
```
function ScriptParamUtil.buildScriptParamComp(ScriptParam, integer, function(integer), boolean, boolean, function(boolean), boolean): TreeNodeId
```
**Parameters**
* `ScriptParam`
* `integer`
* `function(integer)`
* `boolean`
* `boolean`
* `function(boolean)`
* `boolean`
**Returns**
1. `TreeNodeId`
### ScriptParamUtil.searchAndBuildScriptParamComp[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.searchAndBuildScriptParamComp)
```
function ScriptParamUtil.searchAndBuildScriptParamComp(string, function(string) : number, function(string, number), boolean, function(boolean), {string}, boolean): MenuIconReactUtil.HintWidgetParam.Data, TreeNodeId
```
**Parameters**
* `string`
* `function(string): number`
* `function(string, number)`
* `boolean`
* `function(boolean)`
* `{string}`
* `boolean`
**Returns**
1. `MenuIconReactUtil.HintWidgetParam.Data`
2. `TreeNodeId`
### ScriptParamUtil.wrap[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.wrap)
```
function ScriptParamUtil.wrap(string, boolean, TreeNodeId, boolean, function(boolean), string): TreeNodeId
```
**Parameters**
* `string`
* `boolean`
* `TreeNodeId`
* `boolean`
* `function(boolean)`
* `string`
**Returns**
1. `TreeNodeId`
### ScriptParamUtil.getScriptParam[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.getScriptParam)
```
function ScriptParamUtil.getScriptParam(string, {string}): ScriptParam
```
**Parameters**
* `string`
* `{string}`
**Returns**
1. `ScriptParam`
### ScriptParamUtil.clampScriptParamToDiscreteValue[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.clampScriptParamToDiscreteValue)
```
function ScriptParamUtil.clampScriptParamToDiscreteValue(number, ScriptParam): number
```
**Parameters**
* `number`
* `ScriptParam`
**Returns**
1. `number`
### ScriptParamUtil.clampScriptParamValueIndex[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.clampScriptParamValueIndex)
```
function ScriptParamUtil.clampScriptParamValueIndex(number, ScriptParam): integer
```
**Parameters**
* `number`
* `ScriptParam`
**Returns**
1. `integer`
### ScriptParamUtil.getScriptParamDefaultValue[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.getScriptParamDefaultValue)
```
function ScriptParamUtil.getScriptParamDefaultValue({number}, integer): number
```
**Parameters**
* `{number}`
* `integer`
**Returns**
1. `number`
### ScriptParamUtil.getScriptParamDefaultValueIndex[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.getScriptParamDefaultValueIndex)
```
function ScriptParamUtil.getScriptParamDefaultValueIndex({number}, integer): integer
```
**Parameters**
* `{number}`
* `integer`
**Returns**
1. `integer`
### ScriptParamUtil.getScriptParamLabel[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.getScriptParamLabel)
```
function ScriptParamUtil.getScriptParamLabel(string, number, {string}): string
```
**Parameters**
* `string`
* `number`
* `{string}`
**Returns**
1. `string`
### ScriptParamUtil.getScriptParamLabelUnfiltered[🔗](#$content.gui.main.script_param_util~ScriptParamUtil.getScriptParamLabelUnfiltered)
```
function ScriptParamUtil.getScriptParamLabelUnfiltered(string, number): string
```
**Parameters**
* `string`
* `number`
**Returns**
1. `string`
@@ -0,0 +1,172 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:bestpractice (fetched 2026-10-04) -->
### Table of Contents
* [Common pitfalls](#common_pitfalls)
+ [Draw Calls and Lods](#draw_calls_and_lods)
+ [Polycount and Lods](#polycount_and_lods)
+ [Icons](#icons)
* [Best practices](#best_practices)
+ [Content Creation](#content_creation)
- [Materials, Textures & UVs](#materials_textures_uvs)
- [Modeling](#modeling)
+ [Model Editor](#model_editor)
- [Lights](#lights)
* [Some Final Tips](#some_final_tips)
# Common Pitfalls and Best Practices
An overview of good workflows to prevent bad performance, cleanup work and errors later on. Its intended to increase understanding of some technical topics related to content creation.
## Common pitfalls
Some concepts of game engines can be difficult to grasp if you're not working directly in the games industry. To give some insight on what's happening behind the scenes and why its important, this section will list some of the bottlenecks of modern rendering, as well as specifics to Transport Fever 3.
### Draw Calls and Lods
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=51c33d&media=modding:general:drawcalls.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:drawcalls.png "modding:general:drawcalls.png")**Draw Calls are a major source of performance impact in TF3.** When working on a model, you can see your materials and meshes as unique instructions getting sent by the CPU to the GPU. **Unique combinations of materials and meshes** adds to the total number of instructions that are needed to render the full model. The amount of draw calls displayed for a model is roughly(!) the sum of these instructions. **The more individual meshes and materials, the higher the draw call number**. Some rules of thumb:
* New meshes start new draw calls, new materials as well
* Using **Mesh Instances that have the same material doesn't create new draw calls** for the instances
* Skinned meshes always generate new draw calls, even on instances ( = same mesh/material) .
* Sorted Materials also always generate new draw calls per instance
* Vehicle materials should be split into an exterior and interior material. This makes the later lods as optimized as possible, since interior can just be deleted and the exterior can still use the full texture. Same for small detail meshes with additional textures that can be disabled in later lods.
* A higher amount of draw calls for lod0 is generally reasonable, but should serve a purpose. For example glass windows can be split into separate local groups to improve sorting. Generally splitting every object is not recommended, since it increases draw calls quite fast.
---
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=7ad7ad&media=modding:general:draw_calls_lod0.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:draw_calls_lod0.png "modding:general:draw_calls_lod0.png") In terms of re-usability and Texel Size, sometimes there are not really good alternatives to using separate materials; at least for lod0. This in general is fine and common standard in Transport Fever 3 as well as other current-gen games. The focus is more on **reducing the number of draw calls for later lods** by merging objects, **removing small detail materials like glasses** and also **combining textures onto a single texture set** (explained down below), saving the need for separate materials in the process.
* For most gameplay scenarios, total number of Draw Calls is probably bottlenecking earlier than the total polycount.
* Thus its required to reduce not only the polycount per lod but also the amount of Draw Calls.
* This can be achieved by removing small detail materials and merging meshes, especially individual glasses from lod1 onwards
* For lod0 in general its fine to have a higher number of draw calls as long as it can reasonably be explained and it switches to an optimized lod1 at distance 50-100.
* Its smart to plan your materials a bit beforehand in terms of Draw Calls, to have an easier time optimizing them later down the road.
---
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=06bfe5&media=modding:general:draw_calls_last_lod.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:draw_calls_last_lod.png "modding:general:draw_calls_last_lod.png") As mentioned (generally speaking) a higher number of Draw Calls in lod0 is not the biggest issue, it becomes more of a problem if later lods still retain a high number of Draw Calls and cannot fallback to a low single digit number. The more we zoom out, the more they add up. Especially for the last lod, just a single draw call is the optimal target.
For vehicles its often easy to just keep the chassis/shell of the vehicle as the last remaining mesh and material, but it can be a bit of a challenge for buildings. They often use separate trim sheets or tileable textures for ceiling, walls and ground and still have a noticable color contrast from a far distance that cannot be discarded easily without some really visible plopping between lod levels.
That's where the process of [Material Baking](https://wiki.transportfever3.com/doku.php?id=modding:tools:external:blender#material_baking "modding:tools:external:blender") can come in handy.
### Polycount and Lods
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=cdadd0&media=modding:general:polycount_lods.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:polycount_lods.png "modding:general:polycount_lods.png")Polycount is measured as the total number of triangles that make up a mesh. A reasonable polycount in lod0 is strongly recommended to keep the game running smoothly even with dozens of the same vehicle spawned. Even more important than the lod0 polycount is proper fallback in later lods. When the camera zooms out, **the camera will need to render more objects, thus each individual object needs to have fewer triangles; proportionally to its size on the screen**.
* lod0 polycount should be in the range defined in the guidelines
* each lod should be a meaningful reduction to its predecessor. Internally, we aim for 1/4th reduction
* **The last lod needs to be as simple as possible** and can be aggressively reduced. Getting down to 1000 triangles is realistic for most models
* Automatic polygon reduction can help, but some options provide better results than others. For most models, un-subdividing models should be preferred over random polyreduce.
* Generally, **two lods in addition to the base lod should be sufficient** for most models. **More lods dont necessarily improve performance.**
* For a model, every lod always resides in the RAM. More lods mean more RAM usage. Its good practice to prevent redundant lods.
* If you feel like you cannot reduce the draw calls anymore for the last lod due to plopping, you could try increasing the distance if this enables you to go lower. This is generally the better tradeoff since the draw call count on the last lod quickly adds up.
* Also, getting rid of the transparent materials from lod1 onwards has performance advantages since they are more expensive to render compared to opaque
### Icons
Icons can be generated using the model editor. Its important to have all relevant icons shipped with the mod to support all of the menu items, including cblend masks.
We strongly encourage using cblend for some models in your mod since most players like tinting their vehicles.
## Best practices
### Content Creation
#### Materials, Textures & UVs
##### Trim sheets for multiple different materials on a single texture
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=ebc429&media=modding:general:trim_sheets.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:trim_sheets.png "modding:general:trim_sheets.png")Trim sheets are basically strips of different materials packed onto a single texture. In most cases they are tileable horizontally and stacked vertically.
This way you can get a lot out of a single material/texture by overlapping the uvs in the areas where you need them. And the final model will only have a single draw call.
**Different materials still make sense if you need different shaders like Physical or Transparent.**
##### Tileable textures for layouting of complex patterns (and optimizing texel density)
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=d6ec4a&media=modding:general:tileable_textures.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:tileable_textures.png "modding:general:tileable_textures.png")Similarly, single tileable textures can be salvaged in the same way to create complex patterns from differently layouted uvs onto the same texture. This way you won't run into texel density issues, no matter how many variations you need. As you can see, different patterns can even be aligned along each other.
**Keep in mind you will get baking issues if you intend to bake additional maps like AO since the UVs overlap.**
#### Modeling
##### Spirals / Tubes / Pipes
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=384d8f&media=modding:general:polycount_spirals.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:polycount_spirals.png "modding:general:polycount_spirals.png")Pipes, Tubes and spiraling wires are common elements of vehicles. They tend to increase the polycount quite a bit and should be used with caution. Especially when being constructed procedurally by spiral objects or spline curves in your content creation tool, they can have more polygons than necessary. Often a way lower polycount is enough while still looking good from a reasonable distance. Keep in mind even LOD0 should be game-ready, which means small wires don't need to have so much resolution that they still look smooth when zoomed in full-screen. Most of the time they will be seen from a humanly reasonable distance and that's the distance where the resolution should suffice.
**Especially the number of segments for the curve profile (the "base circle" of such spirals) increase the polycount quite a bit since it scales wih the length of the whole curve. Often, shading these curve smoothly is a better tradeoff compared to more polygons for the curve profile.**
##### Bevels and Smooth Shading
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=f9f244&media=modding:general:polycount_bevels.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:polycount_bevels.png "modding:general:polycount_bevels.png")In reality, perfectly sharp edges are almost non-existent and smoothing edges a bit can be an important tool to convey a realistic feeling of surfaces. Especially for modern technical objects like vehicles, they are quite common. Unfortunately, if we just subdivide surfaces for perfect smoothness, this blows up the polycount quite fast. Luckily, there are some tricks to prevent this.
**Smooth shaded edges can convincingly make surfaces look more detailed than they actually are.** In many cases, a rounded edge with only two subdividion loops will provide the same visual quality as a comparably much more subdivided edge.
**It's best practice to start with lower subdivisions** and keep track of the polycount, since it can increase very fast with just a couple more subdivisions, depending on the complexity of the model. Even for this simple cube, you can see how fast the polycount increased, with just a tiny bit more accentuated smoothing gained from it.
**If you still want to sharpen the edge a bit more, you can use Blenders Bevel Node and bake a normal map without having to increase the polycount further.**
##### Glass Sorting
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=8e43be&media=modding:general:glass_sorting.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:glass_sorting.png "modding:general:glass_sorting.png")Correctly sorting overlapping transparencies can be a challenge even for modern game engines. In general, **transparencies are not sorted per pixel like conventional opaque geometry, but per object.** And triangles inside a single geometry are sorted by their index, if no other factor (for example a setting in the submaterial) sorts them differently. **This will become a problem if we can see one transparent objects from different sides and transparent surfaces overlap each other in different ways.** Then it will look like some parts are drawn above others, even if spacially the overlap differently.
Knowing this, we can help the game engine sort our transparencies correctly by **providing additional pivot points for sorting**. This can simply be done by splitting one object into separate ones and centering the pivot. The game engine will then calculate the distance from these objects to the camera based on the current view and sort our objects accordingly. **This way, it will look correct from all angles.**
**Keep in mind: Splitting objects creates additional Draw Calls!** It's often not necessary to split every single glass, but rather keep all those glass triangles in a single object that can't overlap each other anyway.
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=282c37&media=modding:general:glass_vehicles.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:glass_vehicles.png "modding:general:glass_vehicles.png")A good rule of thumb would be for vehicles: left windows, right windows, front windows, back windows.
There are some setups that can break this approach, like doors which are movable/animated as well as overlapping glass windows due to the design of a vehicle. For example some trains have a pretty curved front where multiple parts of the cabin can intersect perspectively. In this case, it should be split into multiple objects that are coplanar to each other, meaning they form a single flat plane.
**→ roughly 9 Draw Calls in total for all the glass on the vehicle.**
##### Map Baking and UV Transfer
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=23ea35&media=modding:general:uv_projection_baking.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:uv_projection_baking.png "modding:general:uv_projection_baking.png")Sometimes you'll want to repeat patterns along a surface and triplanar projection or regular uvs are not an option. In these cases you can use additional uv sets to layout your texture and project the result back to your regular UVs. It's very easy to do in Substance Painter:
1. Create your additional UV in your content creation software
2. Set the projection method in SP to "UV set to UV set projection".
3. Select the uv you want to project from. It can be anything except uv0.
4. This way you can also use multiple different UV sets for different parts of your model, or even different material layers on the same polygons.
### Model Editor
#### Lights
Rendering lights tends to be among the more costy processes of rendering in games. Both the light count and the light range are the two parameters that can drastically impact light performance. To keep performance impact of an asset low, there shouldn't be too many individual light sources per asset, and overall lights shouldn't be too large.
##### Vehicles
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=2563b2&media=modding:general:light_sources_vehicles.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:light_sources_vehicles.png "modding:general:light_sources_vehicles.png")Vehicles tend to have quite a lot of lights that increase their bounding box quite a bit. A larger bounding box means the vehicle will be rendered longer, even if its already out of screen.
Some good rules of thumb for vehicles (especially trains):
* the max light range should be no larger than 2x the length of a regular train, which is around 32 units
* Longer trains don't necessarily need longer-ranged lights
* Lights should be parented to their visual light-emitting objects so they are properly disabled via animations
* Often a reason for longer ranged lights is to increase brightness or get a smoother falloff. Try to experiment with the light strength, it should still give good results sticking to these guidelines.
* Tilting the cone a bit more to the floor or sky can influence brightness and falloff in positive way
* Lights should be disabled at some distance for lower lods.
##### Buildings
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=500&tok=b405eb&media=modding:general:light_sources_buildings.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Abestpractice&media=modding:general:light_sources_buildings.png "modding:general:light_sources_buildings.png")Buildings can be more difficult to light compared to vehicles since its a very individual process. The guideline constraints remain the same though: try to minimize light count and range. It can get quite cumbersome to represent every light-emitting object in your building with its own light source. Approximations might make the setup cleaner, better performing and provide the same visual fidelity.
* The game has many different light types, use the shapes appropriate for your case
* Try to find a good balance between enough lights for contrast/atmosphere and not too many that make the setup noisy and performance-heavy
* Mode:ABSORBER lights can be used to darken areas and enhance contrast / atmosphere
* If you just need surfaces to be brighter and don't need to illuminate other objects, you can also use emissive materials with a mask
## Some Final Tips
* Actively use the draw call / polycount stats panel in the Model Editor
* Test cBlend where applicable, generate all icons and check ingame
* Use the validator actively in the process
* Base Level of Weathering is highly encouraged to fit with other ingame assets
* Files and node names should use lower case letters
@@ -0,0 +1,212 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:guidelines (fetched 2026-10-04) -->
### Table of Contents
* [Content Guidelines](#content_guidelines)
* [Technical Requirements](#technical_requirements)
* [Mod Metadata](#mod_metadata)
+ [Template](#template)
# Guidelines & Requirements
This page outlines the guidelines and technical requirements for mods. The primary goals are a consistent user experience as well as stability, performance and compatibility.
## Content Guidelines
These guidelines aim to provide a consistent and familiar experience for players worldwide, ensuring that mods feel like an official part of the game.
**Basic Principles**
* Mod content should integrate in a seamless way, feeling like a natural extension of the original game.
* This includes adhering to technical requirements as well as respecting the game's visual style.
* Content should be available in English (with optional localizations) to appeal to a global audience.
* In general, follow the original game content as a reference for design and implementation.
* That said, if your goal is to introduce something entirely new or experimental, that is of course also possible!
**Visual Style**
* Ensure that the proportions are correct, with specific details potentially slightly exaggerated. As a rule of thumb, imagine a scale model world rather than the real world.
* The elements that define the silhouette are particularly important and should be modeled, while minor details can be left on normal maps.
* Make use of the full range of materials (metal, plastic, paint, wood, stone etc.) offered by the PBR metallic approach. Be generous with details and avoid repetitions to achieve authentic and believable surfaces.
* Always add some signs of weathering (light dirt, edge damage, diesel traces etc.) to avoid an unnaturally clean look. Cargo vehicles and industrial items should be more weathered than their passenger counterparts.
**Modeling/Texturing**
* Geometry
+ Find a good compromise for highly detailed elements so that you don't end up with too many triangles. Spend more triangles for important/characteristic elements than for minor details.
+ Avoid having many different meshes/objects, but keep in mind that functional (movable) parts need to be separate meshes. Each unique (sub)mesh/material combination requires a "draw call", which can negatively affect performance.
+ In general, maintain a vertex and triangle count consistent with original game assets. For example, typical vehicles range from 20.000 to 50.000 triangles depending on size and complexity. Some of the largest vehicles have up to 100.000 triangles.
* Texturing
+ Texel density: Aim for a density between 128 and 170 pixels/meter and keep it consistent for all UV islands. If necessary, you can allocate a smaller density for less important elements like interior or hidden surfaces.
+ Make sure that the UV space is well used (not too much empty space), but have some padding (32 px) between different regions. When creating variants of the same model (such as the cars in a multiple-unit train), try to minimize duplicate texture space.
+ Avoid having too many texture sets. For example, typical vehicles have between 1 and 3 sets (depending on size/complexity).
* LOD (Level of Detail)
+ Provide an adequate number of lower-detail versions of the model. A typical asset has 1 to 3 LODs (in addition to the base version) depending on complexity and how far the asset is visible.
+ LODs should reduce both triangle count and number of meshes/materials (to reduce the number of draw calls). Typically, the triangle count is reduced by a factor of 4 per level.
+ Pay particular attention to the lowest-detail version as it usually has the largest impact on performance. Reduce the geometry to the bare minimum and make sure only a single mesh/material is used.
**Folder Structure**
* While resources don't *have* to be organized in a specific way, it is considered good practice to follow the game's conventions.
* The general recommendation is to have one subfolder per asset and all its dependencies.
* If some resources are shared by multiple assets, place them in a "shared" folder (next to the asset folders).
* For models, there is typically a subfolder per resource type ("msh", "mat" and "ani") with textures placed in "mat/tex".
* Try to avoid extra long names, deeply nested folders, name repetitions, empty folders etc.
**Vehicle-Specific Topics**
* Bounding Box & Extent
+ Make sure that the bounding box contains the model and all light sources. In contrast, the "extent" should only contain the physical model.
+ Tip: Use the Model Editor to calculate these properties.
* Aging & Coloring
+ Include the necessary masks (cblend/dirt/rust) to make these features work.
+ There should be enough visual contrast between a brand-new vehicle and one in a bad state.
* Windows & Interior
+ Provide an interior mesh (with reduced detail level) so that it's possible to see through the windows.
+ Keep in mind that when the doors are open, it may be possible to see through the windows from the inside.
+ Use "sorted" meshes/materials to make transparency work from all angles.
* Animations
+ Create animations for doors, blinkers, wheels etc. (if applicable).
* Axles & Fake Bogies
+ These properties are needed for a vehicle to be correctly positioned on the rails (or on the road).
* Seats & Cargo Bays
+ Use a "Seat provider" to define seats for passengers and the driver.
+ Load indicators allow to define how cargo (loaded in a vehicle) is displayed.
* Particle Systems
+ Define emitters so that smoke and steam is generated in a realistic way.
+ Refer to the base game vehicles to configure strength and appearance.
+ Make sure that each particle system is attached to the correct node (via the "child" property).
* Lights
+ Configure light sources (such as headlights) so that vehicles work with night mode.
+ Avoid making light volumes too large (as a guideline, not more than twice the length of the vehicle).
* Display Panels
+ In order for a vehicle to have functioning display panels, "labels" should be defined.
## Technical Requirements
To ensure stability, performance, and compatibility across all platforms (PC and Console), all mods must adhere to the following requirements.
Note: All mods undergo an automatic validation process for both PC and console versions, which includes structural & file acceptance tests as well as syntax, metadata and resource integrity checks. Validation is mandatory to pass for a mod to be released on all platforms.
**Basics**
* Integrity: All resources must be syntactically correct and load without errors or warnings.
* Security: Batch files, shell scripts or other executable files are not allowed.
* Encoding: Text-based files (e.g. .lua, .json, .mdl, .msh) must be encoded in UTF-8 without BOM.
**Files & Folders**
* Size limit: The maximum unpacked size for a single mod must not exceed 500 MB. Avoid using more space than comparable assets in the base game.
* Naming conventions: Use lowercase only for all files and folders. Avoid whitespaces and special characters.
* No ballast: Include only essential files. Remove source assets (like .psd or .fbx) and temporary files before uploading.
**Models & Textures**
* The lowest-detail LOD meshes (blob files) must not exceed 256 KB.
* Texture format: Use the DDS format *with mipmaps*. Dimensions must be power-of-two (e.g. 512, 1024, 2048).
**Scripting**
* API usage: Only use the official Transport Fever 3 scripting API.
* Robustness: Scripts should be written to handle edge cases in a graceful way.
**Misc**
* Audio: All .wav files must be exported with a sample width of 16-bit (sampwidth: 2) and a frame rate of 48.000 Hz.
* Stability: The severityRemove parameter in mod.json must be correctly defined to indicate the impact of removing the mod from a save game.
For consoles, there are a few additional requirements:
**Console-Specific Constraints**
* No shaders: Custom shader files are not permitted on consoles due to platform-specific constraints.
* Stable gameplay: Mods may not alter fundamental gameplay features that could compromise performance.
## Mod Metadata
The metadata needs to be set up according to the description in [Mod Definition](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition "modding:general:moddefinition"). This covers the mod name, description and images which are the most relevant elements to gather attention by the users. Hence it is important to have a consistent and appealing quality.
The **name** of your mod is the first thing users will see, making it crucial for their decision to explore further. It should be clear, memorable, and in title case for consistency. Use Title Case (e.g., “Awesome Mod Name”) to ensure uniformity across all mods. This name should not be longer than 32 characters and may not contain line breaks.
The **cover** (sometimes called "mod listing image" or "thumbnail") and the other gallery images should show the main subject of the mod and contain only English texts, if necessary at all.
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=400&tok=bc6908&media=modding:general:bestpractice:mods_screenshot.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Aguidelines&media=modding:general:bestpractice:mods_screenshot.png "modding:general:bestpractice:mods_screenshot.png")
Cover based on ingame screenshot
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=400&tok=dd68e2&media=modding:general:bestpractice:mods_screenshot_with_overlay.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Aguidelines&media=modding:general:bestpractice:mods_screenshot_with_overlay.png "modding:general:bestpractice:mods_screenshot_with_overlay.png")
Cover based on ingame screenshot with icons
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=400&tok=71579e&media=modding:general:bestpractice:mods_ui.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Aguidelines&media=modding:general:bestpractice:mods_ui.png "modding:general:bestpractice:mods_ui.png")
Cover based on ui screenshot with tilt-shift effect
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=400&tok=04b2f2&media=modding:general:bestpractice:mods_icons_example.png)](https://wiki.transportfever3.com/lib/exe/detail.php?id=modding%3Ageneral%3Aguidelines&media=modding:general:bestpractice:mods_icons_example.png "modding:general:bestpractice:mods_icons_example.png")
Cover with stylized icon and colored accent
Refer to these **guidelines for creating a high-quality cover image**:
* Highlight the main subject: The image should showcase the primary element of your mod in an attractive and clear way.
* Use in-game screenshots: Using a screenshot from the game is the recommended approach for authenticity.
* Avoid extra elements: Do not add text, logos, or graphical overlays; let the image speak for itself. Keep in mind that the mod title is always displayed directly below the image. If desired, a small branding logo may be added (see below).
* Frame the subject appropriately: Ensure the main subject occupies a significant portion of the total image area. Use the "Advanced Camera" to find the best perspective (e.g. telephoto view for planes).
* Balance contrast and detail: Ensure the subject stands out from the background without making the environment look too plain or boring.
* Seek inspiration: Review other popular mods to see how they compose their images for effective presentation.
If the mod is difficult to represent visually (e.g. **script mods**), use one of the following alternatives:
* Add graphical overlays: Place simple icons or other visual elements over an in-game screenshot (using a 3D perspective if appropriate). Avoid using text.
* Show the GUI: Use a direct screenshot of an in-game menu, settings screen, or other relevant user interface. Frame the image so that the most important element is clearly highlighted (e.g. with a tilt-shift effect).
* Use a schematic representation: Create a clean, minimalist 2D icon that illustrates the mod's function. Use the provided background or, where available, the full category template as visual inspiration.
In technical terms, the image should be:
* 1920 x 1080 px
* 16:9 aspect ratio
* At most 8MB in size
If you (as an individual or group) plan to release multiple mods, you may want to add a small **logo** to the cover image. This so-called branding helps other players recognize your content immediately. Note that the logo should represent you as the creator, not the content of a specific mod. When designing the logo, keep in mind that the cover image is usually displayed in thumbnail size. Therefore, avoid including small details or too many graphical elements. Please also follow these technical guidelines for a consistent appearance:
* (Assuming the cover image is 1920 x 1080 px)
* Size: 270 x 270 px (or similar area if non-square)
* Position: Bottom left corner
* Margin: 54 px (from the left and bottom)
### Template
The templates for mod thumbnails are available here:
* [png](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:templates:background_png.zip "modding:templates:background_png.zip (1 MB)")
* [psd (for Photoshop)](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:templates:script_mod_template_psd.zip "modding:templates:script_mod_template_psd.zip (21.4 MB)")
* [xcf (for Gimp)](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:templates:script_mod_template_xcf.zip "modding:templates:script_mod_template_xcf.zip (32.3 MB)")
The **description** will be shown in the ingame browser and on the website. It is mandatory that the description is provided in English. Not all formatting types are supported ingame, hence we recommend to use the following ones:
* Paragraph, Header 1, Header 2 and Header 3
* Bold text
* Unordered and ordered lists
* Horizontal lines
It is not recommended to use tables and embedded images or videos in the description. Both images and videos can be added to the gallery at the top instead.
Make sure you have included some **tags** describing your mod. They are used to offer filter categories to the players. See the [list of supported tags](https://wiki.transportfever3.com/doku.php?id=modding:general:modtags "modding:general:modtags"). This example shows how to add tags in the `modinfo.json`:
```lua
{
"name": "MOD_NAME",
...
"tags": [
"Town Building",
"Depot"
],
...
}
```
@@ -0,0 +1,214 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition (fetched 2026-10-04) -->
### Table of Contents
* [mod.json](#modjson)
+ [Dependencies & Incompatibilities](#dependencies_incompatibilities)
+ [Mod Parameters & Scripts](#mod_parameters_scripts)
* [modinfo.json](#modinfojson)
* [strings.json](#stringsjson)
# Mod Definition
Mods are folders containing resource files similar to the base game and some additional metadata files to describe and define the mod:
```lua
mods/
urbangames_example_mod (1)
_metadata/ (2)
modinfo.json (3)
description.html (4) (optional)
0.png (5) (optional)
...
content (6)
mod.script.tl (7) (optional)
mod.json (8)
strings.json (9) (optional)
```
1. The directory name has no functional relevance. Best practice is to name the mod in the form `<author>_<modname>`. It may not contain characters other than `A-Z`, `a-z`, `0-9`, `_` and .
2. The `_metadata` folder contains the metadata used to describe the mod in the Mod Browser. It contains:
3. The `modinfo.json` file contains all descriptive metadata (like name, description, tags, …). See below for more information.
4. The `description.html` is an optional file for the description which is shown in the ingame mod browser and on mod.io.
5. PNG files with `<number>.png` are used for the Mod Browser and preview. The `0.png` will be used in the grid, the others in the detail view. The images should have 1920x1080 pixels.
6. All the game resources of the mod like vehicles, scripts, … are located in the `content` folder. It is best practice to use a structure similar to the base game.
7. The `mod.script.tl` contains the executable script functions referenced from mod.json. See [here](https://wiki.transportfever3.com/doku.php?id=modding:general:modscripts "modding:general:modscripts") for further information.
8. The `mod.json` contains the technical definition of the mod, most important its id. See below for further information.
9. The `strings.json` file contains translations of the mod’s texts. See below for further information.
## mod.json
The `mod.json` file contains the most important technical properties to define the mod. It is written in [JSON snytax](https://en.wikipedia.org/wiki/JSON "https://en.wikipedia.org/wiki/JSON") and its basic structure is as follows:
```lua
{
"modId": "urbangames_example_mod",
"revision": 1,
"severityAdd" : "Critical",
"severityRemove" : "Critical",
"visible" : true,
"cosmetic" : false
}
```
The `modId` property is a string that is used to identify the mod. It may not contain characters other than `a-z`, `0-9` and `_`. It is best practice to name the mod in the form `<author>_<modname>`. The mods are identified based on this id and independent of its location of installation, e.g. a mod in the staging area and in the directory for manually installed mods is considered as the same if the `modId` is the same. If more than one installed mod has the same ID, the game prefers them in following order:
Staging Area > Manual Installation Directory > Subscribed Mods.
While the folder name has no functional meaning in Transport Fever 3, it is recommended to keep it in sync with the `modId`. In comparison to previous games of the Transport Fever series, there is no major version like `_3` at the end of the `modId`.
The `revision` integer is used to indicate updated versions of the mod. It is also shown in the ingame mod detail pages. Whenever a new update of the mod is published, this number should be increased. In case of an update that is not compatible with the previous version of the mod, it is recommended to change the mod id and publish the new version as an independent mod to not break user savegames with the update of the mod.
To inform players of expected behavior when adding mods to or removing mods from existing savegames, the `severityAdd` and `severityRemove` properties are used. There are three possible values:
* `None` may be used when adding or removing may not harm the savegame in any way, e.g. when only doing cosmetic adjustments.
* `Warning` shall be used when some impacts, e.g. missing vehicle models, are expected, but the savegame will not get into an unstable state.
* `Critical` is used for anything that might result in broken savegames, e.g. missing cargo types.
If the properties are unset, they behave like `severityAdd` is set to `None` and `severityRemove` is set to `Warning`.
To hide mods from appearing in the list of mods, it is possible to set `visible` to `false`. The default value if emitted is `true`. This might be relevant for mods that provide content for a campaign savegame and should not be used in other free games.
Purely cosmetic mods can set the `cosmetic` property to `true` to ensure that achievements can still be earned if only cosmetic mods are used. It is up to the modder to judge if a mod can be considered as cosmetic. Anything that has an impact on the simulation, e.g. faster vehicles, higher capacities or different production output should not be considered as cosmetic.
### Dependencies & Incompatibilities
Beyond the two properties above, it is possible to define dependencies and incompatibilities to other mods. Dependencies are used to reference mods that are required to use this mod properly, e.g. a base mod containing a vehicle that you provide a repaint skin for. If a mod is not compatible, due to conflicting behavior, it is possible to warn the player by referencing it as an incompatibility.
The user will be informed in both cases while activating mods. Dependency mods will be activated before the dependent mod if needed and dependencies that are not yet installed will be offered to be downloaded. There will be a warning if a user tries to start a game with missing dependencies or active incompatible mods.
```lua
{
...
"dependencies": [
{
"mod": {
"modId": "testing_mod_params",
"revisionMin": 1,
"revisionMax": 1
},
"modInfo": {
"displayName": "Testing Mod Params",
"url": "http://www.urbangames.com"
},
"loadBefore": false,
"optional": false
}
],
...
}
```
Each dependency has four properties:
* `mod` is used to identify the other mod. It consists of three values:
+ `modId` is the modId as defined in the mod.json of the other mod.
+ `revisionMin` is an optional lower revision limit, if earlier version of the other mod are not yet providing what is needed. Omitting this parameter or setting it to `-1` results in no lower limit.
+ `revisionMax` is an optional upper revision limit, if only older versions of other mods are compatible. If omitted or set to `-1`, any (newer) version is considered as compatible. Usually this is not required as mod updates should be backward compatible anyway.
* `modInfo` contains info that is displayed when the dependency mod is not installed, so players can see what they need to install:
+ `displayName` is shown in the Mod Browser.
+ `url` is an optional url to reference a location where the mod can be downloaded manually, e.g. from 3rd-party sites.
* `loadBefore` is an optional bool that needs to be set to `true`, if it is necessary that the dependency mod is loaded before this mod. If unset, it is considered as `true`.
* `optional` is an optional bool that can be set to `true` to specify that the dependency is not a must have, but is recommended to be used to have the full functionality available. If unset, it is considered as `false`.
```lua
{
...
"incompatibilities": [
{
"mod": {
"modId": "urbangames_sandbox",
"revisionMax": 1,
"revisionMin": 1
}
}
],
...
}
```
For incompatibilities, the `mod` property is defined as above with the dependencies.
### Mod Parameters & Scripts
Mods may have parameters to customize the user experience depending on the user preferences. The selected parameters can be used in the scripts that each mod can provide. See the page about these [parameters and scrips](https://wiki.transportfever3.com/doku.php?id=modding:general:modscripts "modding:general:modscripts") for more details.
## modinfo.json
The display name and description for the mod browser are configured in the `modinfo.json` file in the `_metadata` folder:
```lua
{
"name": "MOD_NAME",
"summary": "MOD_SUMMARY",
"description": "MOD_DESCRIPTION",
"localization" : {
"en": {
"name": "Wiki Example Config Change",
"summary": "Vehicle brake intensity change",
"description": "Vehicles brake stronger or weaker based on the configured parameter."
},
"de": {
"name": "Wiki-Beispiel Konfigurationsänderung",
"summary": "Fahrzeug-Bremsintensitätsänderung.",
"description": "Fahrzeuge bremsen stärker oder schwächer, abhängig vom eingestellten Wert."
}
},
"authors": [
{
"name": "Urban Games",
"role": "CREATOR"
}
],
"tags": [
"Script Mod"
],
"dependencies": [
"5499706"
],
"url": ""
}
```
The three important attributes are:
* `name`: The display name of the mod. This name should not be longer than 32 characters and may not contain line breaks.
* `summary`: This brief summary of the mod content is shown as a tooltip when hovering over a mod in the mod browser. It may not be longer than 100 characters and may not contain line breaks.
* `description`: This may be a longer description text and supports line breaks. It is ignored if a seperate description.html is available.
To localize these texts, it is possible to provide a set for some language in the `localization` attribute. The default ones are used for all other languages. For a list of possible language codes, have a look at the information on the [localization](https://wiki.transportfever3.com/doku.php?id=modding:misc:localizations "modding:misc:localizations") page.
As mod.io currently does not support multiple languages, the different languages are concatenated to a longer description which is uploaded for the mod browser. Only the english title is uploaded. The support of multiple languages is planned to arrive until Q2/2027.
The `authors` list contains a list of authors with their roles.
The `tags` list may contain one or more of the officially supported tags. See the [list of supported tags](https://wiki.transportfever3.com/doku.php?id=modding:general:modtags "modding:general:modtags") for more details.
To automatically link to the correct dependencies on mod.io, the `dependencies` list contains the mod.io internal modIds. They can be found in the info boxes on the right side of a mod detail page on mod.io.
When a dependency shall be removed, removing from the list above alone is not sufficient. This has to be manually updated on mod.io.
The optional `url` attribute may contain a link to a place where more info about this mod can be found, e.g. a github repository or website. Please be aware that it is not possible to access this url from ingame when playing on console. Hence the description should already contain every relevant info for using the mod.
Currently, the URL is not shown ingame, but it is accessible for players on mod.io.
## strings.json
To provide support for multiple languages, a `strings.json` can be provided in the mod:
```lua
{
"en" : {
"brake_intensity_name" : "Brake Intensity",
"brake_intensity_tooltip" : "Adjust the global brake intensity."
},
"de" : {
"brake_intensity_name" : "Bremsintensität",
"brake_intensity_tooltip" : "Passen Sie die globale Bremsintensität an."
}
}
```
This file contains translations for strings, specified as a list of key/value pairs for each language. If a key does not exist for a given language, the English text is used. If this isn’t defined neither, the key itself is displayed. For a list of possible language codes, have a look at the information on the [localization](https://wiki.transportfever3.com/doku.php?id=modding:misc:localizations "modding:misc:localizations") page.
@@ -0,0 +1,276 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:modscripts (fetched 2026-10-04) -->
### Table of Contents
* [Mod Params](#mod_params)
* [Script Functions](#script_functions)
+ [preRunScript](#prerunscript)
+ [runScript](#runscript)
+ [postRunScript](#postrunscript)
- [Resource Filtering](#resource_filtering)
- [Resource Modification](#resource_modification)
- [Resource Addition](#resource_addition)
# Mod Parameters and Scripts
Mods may have parameters to customize the user experience depending on the user preferences. The selected parameters can be used in the scripts that each mod can provide.
## Mod Params
Mods can provide individual parameters in the `params` property. These use the parameter types that are available for [constructions](https://wiki.transportfever3.com/doku.php?id=modding:constructions:basics#parameters "modding:constructions:basics") too. The user can select his preferences in the mod selection when creating a new game or loading an existing savegame. Parameters of all mods are available in the script functions described below.
```lua
{
...
"params": [
{
"key": "param_key",
"name": "Parametername",
"tooltip": "Parameter-Tooltip",
"uiType": "ComboBox",
"values": [
"Era A",
"Era B",
"Era C"
],
"numbers": [1, 2, 3],
"defaultIndex": 2
},
...
],
...
}
```
Each parameter has several properties:
* `key` is a string that is used to reference this parameter in functions.
* `name` is the string that is shown in the ui.
* `tooltip` is an optional string that is displayed when the user moves to mouse over the name of the parameter.
* `uiType` is used to select the type of parameter. It is either `Button`, `Slider`, `ComboBox`, `IconButton` or `CheckBox`.
* `values` is either a list of filepaths in case of `IconButton` parameters or a list of strings for all other types.
* `numbers` is an optional list of doubles that are returned when referencing the parameters in script functions. If unset, the default values are 1, 2, 3, …
* `defaultIndex` is an optional integer to specify the default option of the parameter. If unset, the first one is used.
While indices in lua and Teal files are 1-based, the `defaultIndex` in the mod.json needs to be set 0-based.
## Script Functions
The static `mod.json` does not contain executable functions itself, but it may reference a `.script.lua` file for these functions:
```lua
{
...
"preRunScript": {
"fileName": "ug_wiki_example_mod::mod.script@preRunFn"
},
"runScript": {
"fileName": "ug_wiki_example_mod::mod.script@runFn"
},
"postRunScript": {
"fileName": "ug_wiki_example_mod::mod.script@postRunFn"
},
...
}
```
The functions can be referenced with `<modid>::<filename>@<functionname>`. There are three different functions that can be specified for each mod. The base game functions are executed first, then in the order of mod activation:
[![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:general:scriptfn.png)](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:general:scriptfn.png "modding:general:scriptfn.png")
### preRunScript
The `preRunScript` function is called before the game and mod resources are loaded. It receives three parameters:
* `configDict` containing the selected climate, economy and name list.
* `allModParams` containing a list of all mod parameters with the mod id as key of the list items. To retrieve the list of parameters for the current mod, `allModParams[getCurrentModId()]` can be used.
* `baseConfig` a list of all properties that are relevant for the simulation and gameplay aspects.
The preRunFn allows the adjustment of this `baseConfig` by overriding the individual values. See [game configs](https://wiki.transportfever3.com/doku.php?id=modding:scripting:baseconfig "modding:scripting:baseconfig") for a detailed list of all existing configuration properties in the base config.
### runScript
The main function that is called when a mod is loaded is the `runScript` function. It receives two parameters:
* `configDict` containing the selected climate, economy and name list.
* `allModParams` containing a list of all mod parameters with the mod id as key of the list items. To retrieve the list of parameters for the current mod, `allModParams[getCurrentModId()]` can be used.
It is possible to register file filters and resource modifiers that are considered when loading the game resources after all the `runFn` are executed. However it is recommended to filter and alter resources in the `postRunFn` whenever that is possible. See [resource modifiers](https://wiki.transportfever3.com/doku.php?id=modding:scripting:modifiersfilters#resource_modifiers "modding:scripting:modifiersfilters") and [file filters](https://wiki.transportfever3.com/doku.php?id=modding:scripting:modifiersfilters#file_filters "modding:scripting:modifiersfilters") for the filters and modifiers and below for the `postRunFn`.
### postRunScript
The `postRunScript` function is called once after all resources are loaded. It receives two parameters:
* `configDict` containing the selected climate, economy and name list.
* `allModParams` containing a list of all mod parameters with the mod id as key of the list items. To retrieve the list of parameters for the current mod, `allModParams[getCurrentModId()]` can be used.
It is possible to use the `api.res.*` script functions provided by the [Scripting API](https://wiki.transportfever3.com/doku.php?id=modding:scripting:api "modding:scripting:api"), e.g. to add additional multiple unit configurations or construction modules based on the loaded resources.
#### Resource Filtering
This example has a mod parameter that allows optional filtering of models. It is available for [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:templates:wiki_example_mod_param_filter.zip "modding:templates:wiki_example_mod_param_filter.zip (136.1 MB)").
Explanation
The parameter is defined in the `mod.json`:
```lua
...
"params": [
{
"key": "paramVehicleFilter",
"name": "Available Designs",
"tooltip": "Enables the liveries",
"uiType": "Combobox",
"values": [
"TGV PSE",
"TGV Atlantique",
"TGV PSE & Atlantique"
],
"defaultIndex": 2
}
],
...
```
The result of the defined mod parameter can be retrieved in the `postRunFn` in the `mod.script.tl` to be used for filtering the models and multiple units in the repository.
```lua
local modelsToHide: {{string}} = {
[1] = { -- Param Option 1
"wiki_example_mod_param_filter::/vehicle/train/tgv/tgv_atlantique_front.mdl",
"wiki_example_mod_param_filter::/vehicle/train/tgv/tgv_atlantique_middle1.mdl",
...
},
[2] = { -- Param Option 2
"wiki_example_mod_param_filter::/vehicle/train/tgv/tgv_front.mdl",
"wiki_example_mod_param_filter::/vehicle/train/tgv/tgv_middle1.mdl",
...
},
[3] = {} -- Param Option 3
}
 
local multipleUnitsToHide: {{string}} = {
[1] = {
"wiki_example_mod_param_filter::/vehicle/train/tgv/tgv_atlantique.mu",
},
[2] = {
"wiki_example_mod_param_filter::/vehicle/train/tgv/tgv.mu",
},
[3] = { }
}
```
These two arrays of arrays contain the list of models/multiple units that will be hidden depending on the value of the script param.
```lua
local selectedFilterSet: integer = allModParams[getCurrentModId()].paramVehicleFilter
```
The value of the parameter is fetched based on its key.
```lua
for _, v in ipairs(modelsToHide[selectedFilterSet]) do
local modelId : number = api.res.modelRep.find(v)
if modelId ~= -1 then
api.res.modelRep.setVisible(modelId, false)
end
end
for _, v in ipairs(multipleUnitsToHide[selectedFilterSet]) do
local muId : number = api.res.multipleUnitRep.find(v)
if muId ~= -1 then
api.res.multipleUnitRep.setVisible(muId, false)
end
end
```
Depending on the parameter value, one of the lists is looped to hide the models/multiple units. This is done by finding the id of the resource in the appropriate [repository](https://wiki.transportfever3.com/script-doc/api/res.html#$api.res~Res "https://wiki.transportfever3.com/script-doc/api/res.html#$api.res~Res") with the function `find` and then setting it's visibility to false with `setVisible`. This hinders it from appearing in vehicle stores at all.
#### Resource Modification
This example has a mod parameter that allows optional modification of vehicle capacities. It is available for [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:templates:wiki_example_mod_param_modifier.zip "modding:templates:wiki_example_mod_param_modifier.zip (2.9 KB)").
Explanation
The parameter is defined in the `mod.json`:
```lua
...
"params": [
{
"key": "paramVehicleModifier",
"name": "Capacity",
"tooltip": "Scales the vehicle capacity",
"uiType": "Slider",
"values": [
"1x",
"2x",
"3x",
"4x"
],
"defaultIndex": 1
}
],
...
```
The result of the defined mod parameter can be retrieved in the `postRunFn` in the `mod.script.tl` to be used for modifying the models in the repository.
```lua
local capacityMultiplier: integer = allModParams[getCurrentModId()].paramVehicleModifier
```
The value of the parameter is fetched based on its key.
```lua
api.res.modelRep.forEachModelWithMetadata("transportVehicle", function(name : string)
local model = api.res.modelRep.getAsTable(api.res.modelRep.find(name))
local tv = model.metadata["transportVehicle"] as ModelMetadata.TransportVehicle
if tv then
for i = 1, #tv.compartments do
for j = 1, #tv.compartments[i].loadConfigs do
if tv.compartments[i].loadConfigs[j].cargoEntry ~= nil then
local cargoEntry = tv.compartments[i].loadConfigs[j].cargoEntry
if cargoEntry and cargoEntry.capacity then
tv.compartments[i].loadConfigs[j].cargoEntry.capacity = cargoEntry.capacity * capacityMultiplier
end
end
end
end
api.res.modelRep.setAsTable(api.res.modelRep.find(name), model)
end
end)
```
A loop over all models with metadata `transportVehicle` is started. The model is fetched from the `api.res.modelRep` by using the `getAsTable(index)` function. This function provides a lua table representation of the model that can be edited freely. After finishing the editing, the result needs to be pushed back to the repository by using the `setAsTable(index, data)` function.
#### Resource Addition
This example adds a second version of all vehicle models that have no purchase cost but increased maintenance price, mimicking a "leasing" option. It is available for [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:templates:wiki_example_mod_param_adder.zip "modding:templates:wiki_example_mod_param_adder.zip (2.1 KB)").
Explanation
The `postRunFn` in the `mod.script.tl` is used to loop over all models with `transportVehicle` metadata:
```lua
api.res.modelRep.forEachModelWithMetadata("transportVehicle", function(name : string)
local model = api.res.modelRep.getAsTable(api.res.modelRep.find(name))
local mc = model.metadata["maintenance"] as ModelMetadata.Maintenance
local cost = model.metadata["cost"] as ModelMetadata.Cost
local tv = model.metadata["transportVehicle"] as ModelMetadata.TransportVehicle
if cost and mc then
local costPrice = cost.price
local lifespan = mc.lifespan
mc.runningCosts = mc.runningCosts + (3 * costPrice) / (2 * lifespan / 1460)
cost.price = 0
tv.groupFileName = name
api.res.modelRep.addAsTable(name .. "_leasing", model)
end
end)
```
A loop over all models with metadata `transportVehicle` is started. The model is fetched from the `api.res.modelRep` by using the `getAsTable(index)` function. This function provides a lua table representation of the model that can be edited freely. After finishing the editing, the result is pushed back to a new entry in the model repository by using the `addAsTable(name, data)` function.
Be aware that it's not possible to add new 3d resources with the scripts, it is only possible to reference existing `.mdl` data for the visual models by adjusting the `modelPath` reference in the `model` struct.
@@ -0,0 +1,90 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:modtags (fetched 2026-10-04) -->
### Table of Contents
* [Map/Savegame](#map_savegame)
* [Climate](#climate)
* [Vehicle](#vehicle)
* [Station](#station)
* [Track/Road](#track_road)
* [Building](#building)
* [Asset](#asset)
* [Misc](#misc)
# Mod Tags
The mod hub is divided in several categories. Each of the categories has some distinct tags that are used for subcategories.
Transport Fever 3 supports the following tags:
#### Map/Savegame
* `Map`
* `Savegame`
#### Climate
* `Temperate`
* `Subarctic`
* `Dry`
* `Tropical`
* `Custom`
#### Vehicle
* `Locomotive`
* `Wagon`
* `Multiple Unit`
* `Bus`
* `Truck`
* `Tram`
* `Ship`
* `Plane`
* `Helicopter`
#### Station
* `Rail Station`
* `Road Station`
* `Airport/Heliport`
* `Port`
#### Track/Road
* `Track`
* `Road`
* `Bridge`
* `Tunnel`
* `Signal`
* `Rail Crossing`
* `Road Construction`
* `Track Construction`
#### Building
* `Industry`
* `Town Building`
* `Warehouse`
* `Landmark`
* `Depot`
* `Maintenance Building`
#### Asset
* `Vegetation`
* `Rock`
* `Other Asset`
#### Misc
* `Script Mod`
* `Car`
* `Resident`
* `Animal`
* `Sound`
* `Shader`
* `Campaign`
* `Localization`
* `Misc`
To keep the categories clean, it is recommended to choose at most up to three major tags that support what users would use to find a mod. A combined industry with embedded station would primarily count as `Industry`. A combined airport with underground train station would likely equally fit in both `Rail Station` and `Airport/Heliport`. A script mod that adjusts metadata of locomotives is still a `Script Mod` and not a `Locomotive`.
@@ -0,0 +1,108 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:publishing (fetched 2026-10-04) -->
### Table of Contents
* [Compensation and Monetization of Mods](#compensation_and_monetization_of_mods)
* [Publish](#publish)
+ [Customizing](#customizing)
* [Update](#update)
* [Publish on other Platforms](#publish_on_other_platforms)
# Publish a Mod
Once a mod is finished, you may consider to upload the mod to a place where other users may download it to use. The official mod browser is hosted by our partner [mod.io](https://mod.io/ "https://mod.io/"). It allows players on all platforms (Win/Mac/Linux as well as PS5 and Xbox Series) to access mods directly within the game.
To ensure fair play for everyone, there are a few mandatory rules to follow:
* Do not upload content for which you do not have copyright or permission to distribute yourself!
* Mods must be non-commercial and accessible in full for free to all users. (see below)
* Mods must not contain offensive or inappropriate content.
* Mods (aka User Generated Content) must comply with the End User License Agreement and the Terms of Use by mod.io.
* Mods must follow the instructions from our [modding manual](https://wiki.transportfever3.com/doku.php?id=modding:start "modding:start").
Urban Games allows the use of game files from the base game of Transport Fever, Transport Fever 2 and Transport Fever 3 to create mods for Transport Fever 3. When using game files, it is mandatory to state the source of the files. You are not permitted to use the game files for games or projects other than the Transport Fever series without explicit permission from Urban Games.
## Compensation and Monetization of Mods
We recognize that creating and maintaining mods can require a significant investment of time and, in some cases, money. As with the previous titles of the Transport Fever series, modders should therefore have reasonable ways to receive compensation and support for their work. As a fair and consistent rule direct **support of modders is permitted**, the **sale of content is not**.
Allowed forms of modder support include:
* **Donations:** Users may voluntarily provide financial support to modders without any expectation to receive something in return.
* **Direct commissions:** Users may commission modders to create or modify content according to their individual requests, providing that the resulting content is provided to all players free of charge.
* **Patreon and similar support programs:** Modders may use membership or donation platforms to receive financial support from their community. Supporter programs may provide additional benefits, such as:
+ Early beta access to mods, for a period of **up to 31 days before public release**
+ Participation in polls or voting on future mod development
+ Exclusive development blogs, posts, videos, or behind-the-scenes content
+ Similar benefits that provide additional interaction or insight into the modding process
Not permitted selling of content:
* **Permanent paywalls:** A mod may not be permanently restricted to paying supporters or made available only through a Patreon membership or similar paid subscription for greater than the 31 day early beta method.
* **Selling individual mods:** Mods may not be sold through online shops, marketplaces, or other commercial storefronts as products.
* **Advertising with incomplete mods:** Feature reduced or other dummy mods may not be published to advertise exclusive feature-complete beta mods that are only accessible with the support programs.
Accordingly, a modder may use Patreon or similar platforms to generate income and offer supporters additional benefits, but the full mod itself must ultimately be made available to the general community without requiring payment. Additional means of support may be provided by Urban Games as part of the [Curated Mods Program](https://www.transportfever3.com/curated-mods-program/ "https://www.transportfever3.com/curated-mods-program/") (application and approval is required).
## Publish
Start the game and select the Mod Hub in the main menu. There is a My Mods tab, if there is at least one mod in your staging area. If not yet done, copy your mod to the `staging_area` subdirectory in the userdata directory. Usually it's located here:
`C:\Program Files (x86)\Steam\userdata\<your Steam ID>\3493540\local\staging_area\`
Select the mod that you would like to publish in order to see the mod details and add your final adjustments.
It is possible to adjust:
* 1 the name of the mod
* 2 the tags of the mod
* 3 the cover and additional images for the gallery
* 4 the visibility
* 5 the changelog message
Once you are ready publish your mod by clicking Upload at the bottomn left. Before upload, the game performs a validation pass to ensure the mod fulfills the requirements for Transport Fever 3. If it fits to the stricter console requirements, a version with adapted textures is prepared and automatically approved for PS5 and Xbox Series as well.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:publishing:mod_upload.png)
### Customizing
Once the upload of the mod is complete, the entry will be opened on mod.io. Several edit functions are available by pressing the edit button in the top right corner.
In the General settings tab, there are four sub pages:
* Mod Profile name, summary, description, URL, listing image (cover/thumbnail), visibility setting and the selection of tags.
* Media Additional screenshots for the gallery section on the detail page of the mod.
* Files The history of uploaded files and the approval status for the different platforms.
* Dependencies References to other mods that are required and a list of mods that require your mod.
The Preview tab offers the possibility to give preview access to other players so they can beta test your mod. It is also possible to give preview access via url instead of manual approval of seperate user accounts.
It is possible to add other users as Team members of a mod. There are three roles to choose from:
* Administrator - Full access (includes editing the mod details themselves, as well as the team)
* Manager - Ability to manage mod details. Cannot edit team or delete files
* Moderator - Ability to moderate comments
All reports by players are listed in the Reports tab. It is possible to respond to the reporter to discuss additional details.
Download numbers, regional distribution of players and other statistics are collected in the Stats tab.
## Update
To update an existing mod, simply open the Mod Hub in the main menu and go to My Mods again. Then select the mod to update. If you can read `The existing mod on mod.io will be updated` in the bottom left box next to an Update button, the mod.io item that should be updated is properly recognized. Otherwise, the reference might be missing.
Once a mod is uploaded to mod.io, a `mod.io_fileid.txt` file is generated in the \_metadata folder of the mod. This file is required to find the right existing mod.io entry to update. If you lost the file, you may add it manually. The content is just the entry id that you may find in the info box on the right side of a mod detail page on mod.io.
Please increase the revision number in the `mod.json` before upload and provide a useful changelog message to inform the other players what changed.
If you modified title or description texts directly on mod.io, these changes will be overridden by the content from the mods name and description that is used in the `modinfo.json`! Thus you're advised to manually backup them elsewhere to restore them after the update.
## Publish on other Platforms
In difference to mod.io, the upload to other mod hosters works manually and not through the game.
Thus, the workflow may vary depending on the hoster. In most cases, the hosters have some terms of service which have to be accepted before uploading content.
For example, the mostly german community website has a download section. The [filebase on transportfever.net](https://www.transportfever.net/filebase/ "https://www.transportfever.net/filebase/") can be accessed by everyone, so Win, MacOS and Linux users may install mods from there as well.
It is recommended to include the url in the [modinfo.json](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition#modinfojson "modding:general:moddefinition") to point to the download possibility. This helps other users to locate the source of missing mods.
@@ -0,0 +1,284 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes (fetched 2026-10-04) -->
### Table of Contents
* [File Endings](#file_endings)
* [File Names & References](#file_names_references)
* [Static Resources & Scripts](#static_resources_scripts)
* [3D Files](#d_files)
+ [.mdl for Model Definitions](#mdl_for_model_definitions)
+ [.msh / .msh.blob for Mesh Data](#msh_mshblob_for_mesh_data)
+ [.mtl for Material Definition](#mtl_for_material_definition)
+ [.ani for Extracted Animations](#ani_for_extracted_animations)
* [Texture Files](#texture_files)
+ [DDS (DirectDraw Surface)](#dds_directdraw_surface)
+ [TGA (TARGA)](#tga_targa)
* [Directory Structure](#directory_structure)
# Resource Types & Structure
In Transport Fever 3, the game resources are not distinguished by their folder, but by their individual file endings.
## File Endings
The game resources as well as the mod resources consist of code files (scripts, configurations, …), 3D files, texture files and soundfiles. The following table contains an overview over all used file endings in Transport Fever 3. Below are additional infos for the most common ones.
| Category | Resource | File Endings |
| --- | --- | --- |
| General | Models | `.mdl` |
| | Meshes | `.msh`, `.msh.blob` |
| | Materials | `.mtl` |
| | Animations | `.ani` |
| | Textures | `.dds`, `.tga`, `.hdr` |
| | Icons | `.tga` |
| | Transformators | `.trf.lua` |
| | Sound Sets | `.snd.lua` |
| | [Sound Effects](https://wiki.transportfever3.com/doku.php?id=modding:misc:soundeffects "modding:misc:soundeffects") | `.wav` |
| Configurations | Climates | `.clima.lua` |
| | Terrain Generators | `.gen.lua` |
| | Economies | `.eco.lua` |
| | Environments | `.env.lua` |
| | Terrain Materials | `.tmat.lua` |
| | Grass | `.grass.lua` |
| Scripts | Generic Scripts/Utils | `.lua`, `.tl` |
| Scripts | Script Resources | `.script.lua`, `.script.tl` |
| | Teal Definitions | `.d.tl` |
| | Game Scripts | `.gs.lua` |
| | Campaigns | `.campaign.lua` |
| | Missions | `.mission.lua` |
| Content | Constructions | `.con.lua` |
| | Modules | `.module.lua` |
| | Ground Textures | `.gtex.lua` |
| | Auto Ground Textures | `.agt.lua` |
| | Track/Street Templates | `.street_template.lua` |
| | Track/Street Styles | `.street.lua` |
| | Bridges | `.bridge.lua` |
| | Tunnels | `.tunnel.lua` |
| | Railroad Crossings | `.rcr.lua` |
| | Traffic Lights | `.trl.lua` |
| | Cargo Types | `.cargo.lua` |
| | Cargo Formats | `cmf.lua` |
| | Cargo Classes | `.cargoclass.lua` |
| | Multiple Units | `.mu.lua` |
| Rendering | [Shaders](https://wiki.transportfever3.com/doku.php?id=modding:misc:shaders "modding:misc:shaders") | `.fs`, `.tesc`, `.tese`, `.vs`, `.gs`, `.glsl` |
| | Render Descriptors | `desc` |
| | Render Programs | `.prog` |
| | Techniques | `.tec` |
| | Material Properties | `.prop` |
| | Material Types | `.mat` |
| Misc | Stylesheets | `.css.lua` |
| | Playlists | `.plist.lua` |
| | Songs | `.ogg` |
| | Layer Nodes | `.node.lua` |
| | Names | `.names.lua` |
| | [Locale](https://wiki.transportfever3.com/doku.php?id=modding:misc:localizations "modding:misc:localizations") | `.lang.lua` |
| | Generic Resources | `.res.lua` |
## File Names & References
All text files (models, meshes, scripts etc.) should be encoded in **UTF-8 without BOM**. For file and folder names the following conditions apply:
* Only use lower case letters `a-z`, underscore `_`, dash `-` and numbers `0-9`.
* Do **not** use upper case letters and blank spaces.
* Keep folder and file names short
To reference another file, it is possible to either reference relative to the current file or absolute from the base game `content` directory or the root of a mod:
```lua
"/assets/<...>/filename.mdl" -- absolute path
"tex/logo_mga.dds" -- path relative to current file
```
To reference files in another mod than the current one or from the base game, the whole path is prefixed:
```lua
"/assets/<...>/filename.mdl" -- absolute path in the current mod
"::/assets/<...>/filename.mdl" -- absolute path in the base game
"mod_id::/assets/<...>/filename.mdl" -- absolute path in mod ''mod_id''
 
"tex/logo_mga.dds" -- path relative to current file in the current mod
"::tex/logo_mga.dds" -- path relative to current file but in the folders of the base game
"mod_id::tex/logo_mga.dds" -- path relative to current file but in the folders of the mod ''mod_id''
```
It is not possible to use `../` to reference to the parent directory.
## Static Resources & Scripts
In general, the lua and Teal based static resources consist at least of the `data()` function that is executed once when the game loads all the resources at the start of a savegame:
```lua
function data()
return {
-- content of data struct depending on type of configuration
}
end
```
Beside the static metadata file, many of the resources like constructions have a dynamic script part. The static resource file has a reference to the script file like in the following example:
```lua
...
updateScript = {
fileName = "underground_station.script@updateFn",
params = {
key = value,
}
},
```
The `fileName` follows the pattern: `resourceRef@functionKey` where `resourceRef` may be a full path as described above. The `functionKey` refers to a key in the result of the `data()` function in that script file.
The `params` struct is injected as the first parameter into these referenced functions and is commonly called `captureParams`:
```lua
function data()
return {
updateFn = function(captureParams, params)
...
end,
...
}
end
```
## 3D Files
The model resources consist of the following file types:
* `.mdl` for model definitions
* `.msh` for mesh index files
* `.msh.blob` for mesh 3D data
* `.mtl` for material definitions
* `.ani` for extracted animations
### .mdl for Model Definitions
Model files are a special kind of configuration file as well, thus their structure is similar with a `data()`-function.
```lua
function data()
return {
boundingInfo = { ... }, -- optional bounding box used e.g. for render borders
collider = { ... }, -- optional collider for collision calculation
lods = { ... }, -- geometric information for 3D data and textures
metadata = { ... }, -- metadata depending on model type
version = 2, -- required to distinguish from the old Transport Fever 2 model format
}
end
```
Detailed information about bounding boxes, colliders and the lod tree can be found in the [model documentation](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl "modding:general:resourcetypes:mdl").
### .msh / .msh.blob for Mesh Data
The mesh files contain the threedimensional mesh information known as the actual 3D model. A mesh can consist of more than one submesh. This data is stored in a binary blob file that contains all the positions, orientations etc. of every vertex, edge and face. To tell the engine where the right information is stored in the binary blob file, there is a second file with indices. This text based file can be edited with any text editor.
```lua
function data()
return {
subMeshes = { ... },
vertexAttr = { ... },
}
end
```
Both files share the same name, but the blob file has `.blob` as a suffix. For example, if the index file is called `cube_lod_0.msh`, then the blob file is called `cube_lod_0.msh.blob`. Read more about meshes and the internal structure of these files in the [mesh documentation](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:msh "modding:general:resourcetypes:msh").
### .mtl for Material Definition
Materials contain the reference to texture files. The basic structure is a `data()` function as well.
```lua
function data()
return {
order = 0, -- used to determine rendering order between materials of same type
params = { ... }, -- parameters vary depending on type
type = "PHYSICAL_NRML_MAP",
}
end
```
There are many different material types available. For a full overview of available types, see the [material documentation](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl "modding:general:resourcetypes:mtl").
### .ani for Extracted Animations
If animations are extracted from `.mdl` files to external files, they are located in `.ani` files.
These files contain a `data()` function as most of the other resources:
```lua
function data()
return {
times = { 0, ... }, -- timestamps in milliseconds from 0 as begin of animation
transfs = {
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, -- transformation matrix for each timestamp
...
},
}
end
```
The number of timestamps must equal the number of matrices!
## Texture Files
The preferred texture format for Transport Fever 3 is `.dds` with compression and mip maps. In some rare cases and for ui elements the `.tga` format is files and compressed `.dds` files.
### DDS (DirectDraw Surface)
The `.dds` textures are compressed and have a smaller filesize, reducing the memory usage.
The common compression types supported by Transport Fever 3 are:
* BC1/DXT1 with binary alpha information
* BC2/DXT3 with 4bit alpha
* BC3/DXT5 with interpolated alpha
* BC7/DX11 with interpolated alpha
* BC5/3Dc for normal maps
* BC4/R8/L8 for m8 bit greyscale masks
While DXT1 has the smallest file size, it is only suitable where no transparency or full transparency (either 100% opaque or 100% transparent) is needed.
For normalmaps, the `3Dc ATI2A2XY` standard is required. See [external tools documentation](https://wiki.transportfever3.com/doku.php?id=modding:tools:external "modding:tools:external") for more information on tools supporting these compressions.
To ensure compatibility with ATI and Intel graphic cards, it is important that the texture resolutions use multiples of 2 for the sizes: 16, 32, 64, 128, 256, 512, 1024, 2048, 4096.
Keep in mind that larger textures occupy a lot more memory space. Therefore only use large textures when absolutely necessary.
### TGA (TARGA)
`.tga` files usually are used for UI elements and smaller graphics. To provide graphics with high resolution for 4k-monitors, the ui icons usually are provided in double scale, meaning a 24 px large icon is actually 48 px large in the file. The file name ends with `@2x.tga` in that case.
As `.tga` filesize is greater than the file size of a similar image with compressed `.dds`-format, it is not recommended to use `.tga` for model textures.
## Directory Structure
While in most cases there is no mandatory directory structure for mods, it is recommended to use a similar directory structure as used in the base game of Transport Fever 3. Below is a description of all relevant folders inside of the `content` directory of Transport Fever 3. Only those which are relevant for a specific mod need to be included in a mod.
The directories in Transport Fever 3 are grouped by game element, not by filetype. Thus the folders contain 3D models, sounds and additional script files all in one place:
| Folder | Description |
| --- | --- |
| assets | 3D models without special functionality. Used to decorate other game elements, e.g. town buildings and industries |
| base | Important files for the basic functionality of the game |
| buildings | 3D models and construction files for town buildings |
| cargos | 3D models, icons and definitions for cargo types |
| characters | 3D models for sim people and drivers |
| climates | metadata and generator files for climate zones |
| depots | 3D models and construction files for [depots](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types#depots "modding:constructions:types") and maintenance buildings |
| economy | economy definition files |
| gui | scripts and icons for the user interface |
| industries | 3D models and construction files for [industries](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types#industries "modding:constructions:types") |
| infrastructure | 3D models and definition files for [tracks](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:tracksstreets#tracks "modding:infrastructure:tracksstreets"), [streets](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:tracksstreets#streets "modding:infrastructure:tracksstreets"), [bridges](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:bridgestunnels#bridges "modding:infrastructure:bridgestunnels"), [tunnels](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:bridgestunnels#tunnels "modding:infrastructure:bridgestunnels"), [signals](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:signals#signals "modding:infrastructure:signals"), [railroad crossings](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:railroadcrossings "modding:infrastructure:railroadcrossings"), [traffic lights](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:trafficlights "modding:infrastructure:trafficlights") and [edge addons](https://wiki.transportfever3.com/doku.php?id=modding:edgeaddons "modding:edgeaddons") |
| landmarks | 3D models and construction files for [landmarks](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types#landmarks "modding:constructions:types") |
| mission | scripts for campaign mechanics |
| music | music tracks for the background music |
| names | lists of [people, street and town names](https://wiki.transportfever3.com/doku.php?id=modding:misc:names "modding:misc:names") |
| scripts | generic scripts used in various places |
| stations | 3D models and construction files for [stations](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types#stations "modding:constructions:types") of all types of transport |
| terrain | textures and configurations of [terrain materials](https://wiki.transportfever3.com/doku.php?id=modding:environment:terrainmaterials "modding:environment:terrainmaterials") used by terrain generation as well as brush tools |
| vehicle | 3D models for all types of [vehicles](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics "modding:vehicles:basics") and configurations of [multiple units](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:types#multiple_units "modding:vehicles:types") |
| warehouses | 3D models and construction files for [warehouses](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types#warehouses "modding:constructions:types") |
@@ -0,0 +1,254 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:general:syntax (fetched 2026-10-04) -->
### Table of Contents
* [JSON](#json)
* [LUA & Teal](#lua_teal)
+ [Functions](#functions)
+ [Types](#types)
+ [Variables](#variables)
+ [Data Structs / Arrays](#data_structs_arrays)
- [Structs with Keys](#structs_with_keys)
- [Structs without Keys](#structs_without_keys)
+ [Loops](#loops)
- [while](#while)
- [for](#for)
+ [Conditionals](#conditionals)
+ [Operators](#operators)
- [Mathematical Operators](#mathematical_operators)
- [Comparators](#comparators)
- [Logic](#logic)
- [String Concatenation](#string_concatenation)
+ [Imports](#imports)
# Syntax
This page gives a brief introduction into the used scripting language.
See the documentation of [resource types](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes "modding:general:resourcetypes") for the internal structures of files.
## JSON
The static [JavaScript Object Notation](https://en.wikipedia.org/wiki/JSON "https://en.wikipedia.org/wiki/JSON") is used for the header files of mods to ensure a broken mod definition does not prevent the game from starting.
More details on the syntax rules for JSON can be found at [W3 Schools](https://www.w3schools.com/js/js_json.asp "https://www.w3schools.com/js/js_json.asp").
## LUA & Teal
For all scripts and the other external resources which are not hard coded into the game binary, the [LUA](https://en.wikipedia.org/wiki/Lua "https://en.wikipedia.org/wiki/Lua") language is used. LUA is a script language which is used by many programs to offer extendability and customization. To assist coding and reduce potential for errors, scripts usually use the [Teal](https://teal-language.org/ "https://teal-language.org/") dialect for lua. It provides static typing annotations to better specify the data types of function parameters and arrays.
The most common syntax structures used for Transport Fever 3 are described briefly below. A full reference of the LUA syntax is available at the [website of LUA](https://www.lua.org/manual/5.2/ "https://www.lua.org/manual/5.2/").
Files with LUA content are plain textfiles and can be edited with any text editor. Common used editors are [Notepad++](https://notepad-plus-plus.org "https://notepad-plus-plus.org") and [Visual Studio Code](https://code.visualstudio.com/ "https://code.visualstudio.com/") which both support syntax highlighting of LUA code. The later also supports syntax highlighting for Teal.
### Functions
Functions can be called by their name from other functions or the game engine.
```lua
local function functionname(parametername1, parametername2)
-- code
return resultname
end
```
Functions are defined by the keyword `function` followed by a name for the function.
In brackets, some `parameters` may be required too. Then they can be called inside the function code below. If no parameters are needed, the brackets are left empty. If the function should return a result, the `return` keyword is used. The `end` marks the end of the function. All code between `function` and `end` belongs to the function.
An exemplaric function call looks like this:
```lua
multiply(5, 21)
```
When working with Teal, the parameter and return value types are assigned with `: <type>`.
```lua
local function functionname(parametername1: number, parametername2: number): number
-- code
return resultname
end
```
### Types
The basic types used in Teal are:
* `any` undefined type similar to lua default behavior
* `nil`
* `boolean`
* `integer` positive and negative full numbers
* `number` any decimal number.
* `string`
### Variables
Variables can be used to temporarily store some values or data structs in code.
```lua
--define variable
local variablename
 
--define and write variable
local variablename = -- content
 
--override variable
variablename = -- new content
```
To define a variable, the keyword `local` followed by an identifier is used. Transport Fever 3 does not support global variables, thus `local` is mandatory!
A variable can be filled with content by using the keyword `=` followed by the content.
To read content from a variable, simply call the variable by its name.
As with the functions, variables are typed in Teal:
```lua
--define variable
local variablename: number
```
### Data Structs / Arrays
In many places of Transport Fever 3, data structs are used to store more than single values or key-value paired contents. They are called **arrays** or **tables** too.
#### Structs with Keys
Most of the configurational data in Transport Fever uses keys for the different properties.
```lua
local structname = {
key1 = value1,
key2 = value2,
key3 = value3,
-- ...
}
```
These values later can be called by using `structname.keyname`, e.g. `structname.key2`.
To add a new key at a later point, use `structname[key] = value`.
In Teal, these structs are defined as a `record`:
```lua
local record ExampleParams
record ExampleChildRecord
exampleChildValue : number
end
 
exampleFunction : function(paramName: number) : number
exampleValue : string
end
```
These records may then be referenced as types for parameters, return types or other data structs.
#### Structs without Keys
In many cases, the storage of a list of similar contents is needed.
```lua
local array = { value1, value2, value3 }
```
These values later can be called by using an index number. These structs are one-indexed, meaning the first index is 1. The total number of values can be gathered by using `#variablename`. In the above example, `#array` would return 3 and the indices 1,2 or 3 could be used.
### Loops
There are several solutions for looping code. The two common ones are while- and for-loops.
#### while
A while loop checks a condition and loops as long as the condition returns true
```lua
while condition do --condition could be something like #result < 10
-- looped code
end
```
#### for
A for loop has an index that counts the number of loop runs. It's exit condition is determined by comparing the index to an end value
```lua
for i = startvalue, endvalue, stepwidth do
-- looped code
end
```
The index starts with the startvalue and is modified by stepwidth every time it finishes a loop run. if stepwidth is omitted, the step is 1. As soon as the index is greater or equal to the endvalue, no new loop runs are started.
To iterate over all key-value pairs in a struct, it is possible to use the `pairs` function:
```lua
for k,v in pairs(t) do
-- looped code
end
```
In the looped code, the key can be used with `k` and the value can be used with `v`.
### Conditionals
To skip some code under certain circumstances, conditionals are used.
```lua
if condition then
-- some code
elseif othercondition then
-- some other code
else
-- some default code
end
```
If the condition applies, some code is executed. Otherwise optional other conditions are checked. If no condition applies, optionally the default code applies. Otherwise the execution jumps below the `end`.
### Operators
In conditions as well as in some codes, operators are used for mathematical operations or compares. There are several other operators as well.
#### Mathematical Operators
* `+` for addition
* `-` for subtraction
* `*` for multiplication
* `/` for division
* `%` for modulo
* `^` for exponentiation
#### Comparators
* `==` equal
* `!=` not equal
* `~=` not equal
* `<` less
* `>` greater
* `⇐` less or equal
* `>=` greator or equal
#### Logic
* `and`
* `or`
#### String Concatenation
* `..` to combine strings: `"string a " .. "string b"` results in `"string a string b"`
### Imports
Some files use functions from other script files. These can be imported:
```lua
-- import
local scriptvariablename = ug_require "::/scripts/scriptname.lua"
 
-- use
scriptvariablename.functionname(parameter)
```
The file path for the imported script follows the pattern described on the page about [resource types](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes#file_names_references "modding:general:resourcetypes").
@@ -0,0 +1,76 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:introduction (fetched 2026-10-04) -->
### Table of Contents
* [Typical Use Cases](#typical_use_cases)
+ [Repaints of existing vehicle models](#repaints_of_existing_vehicle_models)
+ [New vehicle or asset models](#new_vehicle_or_asset_models)
+ [Add new constructions with parameters](#add_new_constructions_with_parameters)
+ [Change config values by mod](#change_config_values_by_mod)
+ [Rebalance existing models](#rebalance_existing_models)
* [Hello Modding-World](#hello_modding-world)
+ [Demo Asset](#demo_asset)
+ [Demo Config Change](#demo_config_change)
# Introduction
While the vanilla game of Transport Fever 3 provides the player with a great number of models and content from different regions of the world, some people prefer to extend the variety further by adding more content. Others like to tweak some game mechanics or to alter some configurations to better suite their style of gameplay. The modding support of Transport Fever 3 reaches from the addition of new model content over the altering of existing contents to the scripting possibilities which reach deep into the game mechanics.
It is not recommended to adjust the files in the installation directory of Transport Fever 3! Instead, a mod should be created. From a file system perspective, a mod is simply a directory containing all the necessary data and script files. If you like to setup your own mod, look at the two examples at the bottom of this page or the [mod definition](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition "modding:general:moddefinition") description. Read more for the [syntax](https://wiki.transportfever3.com/doku.php?id=modding:general:syntax "modding:general:syntax") and the [common resource types](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes "modding:general:resourcetypes").
Working mods can be [shared](https://wiki.transportfever3.com/doku.php?id=modding:general:publishing "modding:general:publishing") to other users.
## Typical Use Cases
Below are some typical use cases for mods with varying degrees of difficulty. Please note, that mods are not restricted to one of these possibilities, but can be a combination of more than one or go beyond them in many ways.
### Repaints of existing vehicle models
For many vehicles there are lots of color variations out there in the real world. As somebody who is not familiar with 3D modelling, this kind of mod might be a way to get into vehicle modding. Further information on this topic can be found in the [manual for vehicle repaints](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:repaints "modding:vehicles:repaints").
### New vehicle or asset models
Both vehicles as well as static models like decorative assets mainly focus on 3D modelling and texturing. The amount of coding needed to get those contents into the game are very limited, thus it's a good entry point for interested people with less coding affinity. People interested in vehicle modding can find some details in the [vehicle modding section](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics "modding:vehicles:basics"). For assets, a simple example can be found below.
### Add new constructions with parameters
As single static models might not cover every needs for decoration, the possibility to add parameters is one step further into scripting. Models might get rotated or scaled, different models get added depending on the users choice, … the possibilities are almost endless, but the needed information can be found in the [construction section](https://wiki.transportfever3.com/doku.php?id=modding:constructions:basics "modding:constructions:basics").
### Change config values by mod
A lot of basic configurations can be altered by simple mods. The amount of script code needed is very limited and the 3D models and textures do not need to be touched. An example for this type of mod can be found below.
### Rebalance existing models
If desired, certain or all vehicles can be adapted in the same way, e.g. to increase the capacity equally without manually modifying the source files. The details for resource modifiers are located in the [scripting section](https://wiki.transportfever3.com/doku.php?id=modding:scripting:modifiersfilters "modding:scripting:modifiersfilters").
## Hello Modding-World
Here are two exemplaric demo mods which show small examples of what can be modded for Transport Fever 3.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=300&tok=ba8b95&media=modding:introduction:demo_asset.png)
### Demo Asset
This demo adds the Transport Fever logo as an asset to the game. It can be placed ingame.
The minimal setup for a static 3D model in Transport Fever consist of three parts:
* [''mod.json''](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition#modjson "modding:general:moddefinition") and [''\_metadata/modinfo.json''](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition#modinfojson "modding:general:moddefinition") for the setup of the mod itself
* a 3D model with [''.msh''](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:msh "modding:general:resourcetypes:msh"), [''.mdl''](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl "modding:general:resourcetypes:mdl") and [textures](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes#texture_files "modding:general:resourcetypes") for the content
* [''.con.lua''](https://wiki.transportfever3.com/doku.php?id=modding:constructions:basics "modding:constructions:basics") and [''.script.lua''](https://wiki.transportfever3.com/doku.php?id=modding:constructions:basics#update_script "modding:constructions:basics") for the scripting as a placeable asset.
You can [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:introduction:ug_tf3_example_asset.zip "modding:introduction:ug_tf3_example_asset.zip (2 MB)") the mod for further investigation. This mod includes a simplified asset construction based on the `assetbuilderutil`, too.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=300&tok=a4e9c3&media=modding:introduction:demo_config_change.png)
### Demo Config Change
This demo overrides a config value from the [base config](https://wiki.transportfever3.com/doku.php?id=modding:scripting:baseconfig "modding:scripting:baseconfig") to force trains to start breaking earlier.
The minimal setup for a mod that changes a base config value consists just of the first part:
* [''mod.json''](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition#modjson "modding:general:moddefinition") and [''\_metadata/modinfo.json''](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition#modinfojson "modding:general:moddefinition") for the setup of the mod itself
* `mod.script.tl` for the script function that changes the config
You can [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:introduction:wiki_example_config_change.zip "modding:introduction:wiki_example_config_change.zip (1.8 KB)") the mod for further investigation.
@@ -0,0 +1,9 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:scripting:api (fetched 2026-10-04) -->
# API Reference
The `api` resources that can be accessed in game scripts and other areas of scripting are documented in the **API Reference**. This reference is not yet complete but will be expanded in the near future.
It is recommended to use the [console](https://wiki.transportfever3.com/doku.php?id=modding:tools:ingame#console "modding:tools:ingame") to test commands. To get debug output of table structures, you may use `debugPrint(<someTable>)`.
[Scripting Reference](https://wiki.transportfever3.com/script-doc/ "https://wiki.transportfever3.com/script-doc/")
@@ -0,0 +1,192 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:scripting:baseconfig (fetched 2026-10-04) -->
### Table of Contents
* [Config variables](#config_variables)
This page is not yet adapted for Transport Fever 3. The content may be outdated.
# Base Config
Global properties that are relevant for the simulation and gameplay aspects are defined in the `config` struct. It's initial values are loaded from the `base_config.lua` file in the `res/config/` folder.
To set new values for the properties, call:
* `game.config.<property> = <new value>` in the `runFn` function.
* `api.res.getBaseConfig().<property> = <new value>` in the `postRunFn` function.
To read values from the properties, call:
* `game.config.<property>` in the `runFn` function.
* `api.res.getBaseConfig().<property>` in `postRunFn` or game scripts.
Calling `game.config.<property>` outside of the `runFn` will respond with outdated data and should not be used.
## Config variables
| Property | Default Value | Description |
| --- | --- | --- |
| Costs (not available with getBaseConfig()) | | |
| cost.terrainRaise | 0.75 \* 6.0 | Cost per m³ to raise the land |
| cost.terrainLower | 0.75 \* 7.0 | Cost per m³ to lower the land |
| cost.railroadCatenary | 0.3 | Fraction of track building cost for catenary construction |
| cost.roadBusLane | 0.1 | Fraction of street building cost for bus lane construction |
| cost.roadTramLane | 0.2 | Fraction of street building cost for tram lane construction |
| cost.roadElectricTramLane | 0.4 | Fraction of street building cost for electric tram lane construction |
| cost.bulldozeRefundTimeOut | 60 | Time after construction, in which a certain fraction of the building costs is refunded |
| cost.bulldozeRefundFactor | 0.75 | Fraction of construction cost that is refunded while still in time |
| cost.removeField | 200000 | Bulldozing cost for removal of farm fields. |
| Construction Function (not available with getBaseConfig()) | | |
| ConstructWithModules | <function> | Function that is called for every construction during placement. See the [explanation of constructions](https://wiki.transportfever3.com/doku.php?id=modding:constructions:modular#method_cycle "modding:constructions:modular") for more information. |
| Scaffolding (not available with getBaseConfig()) | | |
| scaffold.<era>.scaffold | <list> | List with two model references relative to `res/models/`. The first references the normal scaffolding element, the second is a reference to an element with a ladder. |
| scaffold.<era>.crane | <list> | List with five models that are used to construct the crane that is displayed during town building construction. |
| Terrain Tools | | |
| terrainToolMinStrength | 5 | Minimum limit of the strength slider |
| terrainToolMaxStrength | 100 | Maximum limit of the strength slider |
| terrainToolMaxSize | 16 | Maximum terrain tool size in game (large values may result in performance issues!) |
| terrainPainterMaxSize | 256 | Maximum paint tool size in game |
| terrainToolMaxSizeEditor | 256 | Maximum terrain tool size in map editor |
| terrainPainterMaxSizeEditor | 256 | Maximum paint tool size in map editor |
| assetBrushMaxSize | 256 | Maximum asset brush tool size |
| GUI | | |
| gui.layers.defaultColors.baseTerrainColor | { 0.82, 0.82, 0.82, 1.0 } | The default terrain color used in most data layers. |
| gui.layers.defaultColors.baseWaterColorFactors | { 0.5, 0.5, 0.5, 0.25 } | A vector of four factors that alter the default water color relative to the baseTerrainColor property. |
| gui.layers.defaultColors.entityColorFactor | 0.33 | Factor that darkens streets, tracks & bridges. |
| gui.layers.contourLines.numLevels | 6 | Number of color levels of the contour lines data layer. |
| gui.layers.contourLines.baseTerrainColor | { 0.82, 0.82, 0.82, 1.0 } | Base color that is used for the contour lines layer. |
| gui.layers.contourLines.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.contourLines.terrainMinColor | {.82,.82,.82,1 } | Color that is used for the lowest terrain level. |
| gui.layers.contourLines.terrainMaxColor | {.235,.54,.25,1 } | Color that is used for the highest terrain level. |
| gui.layers.contourLines.waterColor | {.47, .64, .76, 1.0 } | Color that is used for the water. |
| gui.layers.contourLines.contours | <list> | A list of 4 contour line definitions with different names, colors, levels, widths and fade out distances. |
| gui.layers.navigableWaters.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.navigableWaters.navigableWaterColor | { 0.435, 0.827, 0.380, 1.0} | Color that is used for navigable waters. |
| gui.layers.navigableWaters.baseWaterColor | { 0.318, 0.494, 0.600, -0.85} | Color that is used for non-navigable waters. The negative alpha value indicates that the color is drawn underneath the navigable water color. |
| gui.layers.landUse.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.landUse.residentialColors | <list> | A list of 2 colors that are used for the residential buildings. The first is the minimum color, used for level 1 buildings, the second is the maximum color, used for level 4 buildings. |
| gui.layers.landUse.commercialColors | <list> | Like above but for commerical buildings. |
| gui.layers.landUse.industrialColors | <list> | Like above but for industrial buildings. |
| gui.layers.trackSpeedLimits.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.trackSpeedLimits.numLevels | 5 | Number of color levels of the speed limits data layer. |
| gui.layers.trackSpeedLimits.tunnelOutlineSize | 2.0 | Size of the tunnel outline. |
| gui.layers.trackSpeedLimits.tunnelOutlineColor | { 0.0, 0.0, 0.0, 0.3 } | Color that is used for the tunnel outline. |
| gui.layers.trackSpeedLimits.speedLimitColors | <list> | A list of 3 colors that are used for the track speed limits. The first color is used for very bad speeds, the second for neutral speeds and the third for very good speeds. |
| gui.layers.destinations.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.destinations.residentialColor | { 0.4, 0.4, 0.4, 1.0 } | Color that is used for residential buildings. |
| gui.layers.destinations.destinationColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for destinations. |
| gui.layers.destinations.publicTransportColors | <list> | A list of 2 colors that are used for public transport. The first is the minimum color, the second is the maximum color. |
| gui.layers.destinations.privateTransportColors | <list> | Like above but for private transport. |
| gui.layers.destinations.numTransportLevels | 5 | Number of color levels of the destinations data layer. |
| gui.layers.cargo.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.cargo.transportColors | <list> | A list of 2 colors that are used for transport routes. The first is the minimum color, the second is the maximum color. |
| gui.layers.cargo.residentialColors | <list> | A list of 2 colors that are used for residential buildings. The first is the minimum color, the second is the maximum color. This property does not have an effect unless there are residential buildings that accept cargo. |
| gui.layers.cargo.commercialColors | <list> | A list of 2 colors that are used for commercial buildings. The first is the minimum color, the second is the maximum color. |
| gui.layers.cargo.industrialColors | <list> | Like above but for industrial buildings. |
| gui.layers.cargo.numTransportLevels | 5 | Number of color levels for transport routes. |
| gui.layers.cargo.numBuildingLevels | 3 | Number of color levels for the buildings. |
| gui.layers.stations.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.stations.stationColors | <list> | A list of 3 colors that are used for the stations. The first color is used for very overloaded stations, the second for neutral stations and the third for very good stations. |
| gui.layers.stations.numLevels | 5 | Number of color levels of the stations data layer. |
| gui.layers.streetTraffic.baseEntityColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used for buildings, vehicles, rocks and trees. |
| gui.layers.streetTraffic.trafficColors | <list> | A list of 3 colors that are used for the streets. The first color is used for very congested streets, the second for neutral streets and the third for very good streets. |
| gui.layers.streetTraffic.numLevels | 5 | Number of color levels of the street traffic data layer. |
| gui.layers.emissions.baseTerrainColor | { 0.82, 0.82, 0.82, 1.0 } | The terrain color used in the emissions data layer. |
| gui.layers.emissions.baseEntityColor | { 1.0, 1.0, 1.0, -1.0 } | Color that is used for buildings, vehicles, rocks and trees. The negative alpha value indicates that the color is drawn underneath the emissions color. |
| gui.layers.emissions.residentialColor | { 0.4, 0.4, 0.4, 1.0 } | Color that is used for residential buildings. |
| gui.layers.emissions.emissionColors | <list> | A list of 2 colors that are used for emissions. The first is the minimum color, the second is the maximum color. |
| gui.layers.emissions.numLevels | 17 | Number of color levels of the emissions data layer. |
| gui.signConfig.signRenderDistance | 1500 | The distance from which number signs, used in speed limits, destinations and cargo data layers, are shown. |
| gui.signConfig.signBackgroundTintColor | { 1.0, 1.0, 1.0, 1.0 } | Color that is used to tint the background of these number signs. The color is multiplied with the texture color of res/textures/ui/traffic\_sign\_min.tga. Only has an effect, if the background texture has a non-black color. |
| gui.signConfig.signTextColor | { 0.9, 0.9, 0.9, 1.0 } | Color that is used for the text in these number signs. |
| gui.iconRenderDistances | <struct> | Far limit scale for rendering of specific HUD icon types. |
| gui.defaultChartColors | <list> | A list of base colors that are used for the statistical charts. |
| gui.incomeCostsChartColors | <list> | A list of 2 colors that are used for the income and cost chart. |
| gui.loadedUnloadedChartColors | <list> | A list of 2 colors that are used for the chart with transported items. |
| gui.landUseChartColors | <list> | A list of 3 colors that are used for the land use chart. |
| gui.lineColors | <list> | A list of colors that is used for the transport lines. |
| gui.modifierColors | <list> | A list of 8 colors that are used for the track and street modifiers. There are 4 values each for enabled and disabled state in certain situations. |
| Audio | | |
| audio.environment.lake | <struct> | Name of the sound file that should be used for lake areas and reference distance in which the sound can be heard at full volume. |
| audio.environment.birds | <struct> | Like above for area with birds. |
| audio.environment.forest | <struct> | Like above for forest areas. |
| audio.environment.meadow | <struct> | Like above for meadow areas. |
| audio.environment.mountain | <struct> | Like above for mountain areas. |
| audio.environment.commercial1850 | <struct> | Like above for commercial buildings in era a. |
| audio.environment.commercial1920 | <struct> | Like above for commercial buildings in era b. |
| audio.environment.commercial1990 | <struct> | Like above for commercial buildings in era c. |
| audio.environment.industrial1850 | <struct> | Like above for industrial buildings in era a. |
| audio.environment.industrial1920 | <struct> | Like above for industrial buildings in era b. |
| audio.environment.industrial1990 | <struct> | Like above for industrial buildings in era c. |
| audio.environment.residential1850 | <struct> | Like above for residential buildings in era a. |
| audio.environment.residential1920 | <struct> | Like above for residential buildings in era b. |
| audio.environment.residential1990 | <struct> | Like above for residential buildings in era c. |
| audio.environment.crowd | <struct> | Like above for areas with many people. |
| audio.environment.traffic1850 | <struct> | Name of the sound file that should be used for traffic in era a and reference distance and height in which the sound can be at full volume. |
| audio.environment.traffic1920 | <struct> | Like above for traffic in era b. |
| audio.environment.traffic1990 | <struct> | Like above for traffic in era c. |
| audio.environment.atmosphere | <struct> | Name of the sound file that should be used when the camera is far away from ground and reference distance in which the sound can be at full volume. |
| Locations | | |
| locations.town.maxNumberPerArea | 0.2 | Number of towns per km² (by default overridden based on map settings) |
| locations.town.allowInRoughTerrain | false | Allows towns in steeper regions of the map |
| locations.industry.absoluteMinimum | 5 | Minimum number of industries on the map |
| locations.industry.maxNumberPerArea | 0.8 | Number of industries per km² on map creation (by default overridden based on map settings) |
| locations.industry.targetMaxNumberPerArea | 0.8 | Target number of industries per km² over time (by default overridden based on map settings) |
| locations.makeInitialStreets | true | Builds the initial street network on map creation |
| Animals | | |
| animal.populationDensityMultiplier | 1 | All population densities (in km²) are multiplied by this value |
| animal.useLocalSpawning | true | If set to `true`, spawning is done in subgrids of size 'localTileSize'. This results in a distribution that is closer to the chosen density but less uniform |
| animal.localTileSize | 1000 | Size of local spawning grid |
| animal.notifyAnimalDespawn | true | Play a notification when animal despawns |
| Geometry Quality | | |
| settings.geometryQualityOptions | <list> | This list contains 5 elements which each represent one distance level from the camera (Low, Medium, High, Camera Tool, Cockpit View). Each of the levels has 3 properties. Keep in mind that extending the ranges may have a heavy impact on the rendering performance! |
| `viewNearFar` for the render limits in the area near the camera and far away. |
| `fogStartEndFarPerc` for the start and end factors of the fog that appears in distance. |
| `lodDistanceScaling` for the scaling of the distance influence on LOD choosing for models. |
| Particles | | |
| particles.textures | <struct> | Texture references for smoke particles |
| water.textures | <struct> | Texture references for water foam particles |
| Economy | | |
| economy.industryDevelopment.spawnTargetTimeSpan | 50 \* 365.25 \* 2000 | Time span on which the industry spawning is projected |
| economy.industryDevelopment.spawnProbabilityExponent | 3.0 | Probability exponent for industry spawning |
| economy.industryDevelopment.spawnIndustries | false | Enables/Disables industry spawning (by default overridden based on map settings) |
| economy.industryDevelopment.closureCountdownTimeSpan | 730.5 \* 2000 | Time span between notification and actual closure |
| economy.industryDevelopment.closureProbability | 1 | Probability exponent for industry closure (by default overridden based on map settings) |
| economy.townDevelopment.cargoNeedsPerTown | 2 | Probability exponent for industry closure (by default overridden based on map settings) |
| Advanced options | | |
| advancedOptions.maximumLoanScale | 1.0 | Scales the maximum amount of money that can be borrowed from the bank. (by default overridden based on map settings) |
| advancedOptions.loanInterestScale | 1.0 | Scales the height of the interest rate for a loan. (by default overridden based on map settings) |
| advancedOptions.publicTransportDestinationsSensitivityScale | 1.0 | Scales the influence of reachable destinations by public transport on town growth. (by default overridden based on map settings) |
| advancedOptions.privateTransportDestinationsSensitivityScale | 1.0 | Scales the influence of reachable destinations by private transport on town growth. (by default overridden based on map settings) |
| advancedOptions.cargoSupplySensitivityScale | 1.0 | Scales the influence of the cargo supply rating on town growth. (by default overridden based on map settings) |
| advancedOptions.emissionSensitivityScale | 1.0 | Scales the influence of emissions around town buildings on town growth. (by default overridden based on map settings) |
| advancedOptions.trafficSpeedSensitivityScale | 1.0 | Scales the influence of traffic slowdowns on town growth. (by default overridden based on map settings) |
| advancedOptions.stationOverflowSensitivityScale | 1.0 | Scales the influence of overcrowded passenger stations on town growth. (by default overridden based on map settings) |
| Misc | | |
| environment | "temperate.lua" | Default environment |
| climate | "temperate.clima.lua" | Default climate |
| terrain.lodErrorMultiplier | 1 | Factor influencing the terrain simplification threshold |
| nameList.folder | "" | Names list folder reference |
| loadspeedPenaltyForTrainOverlength | true | Enables load speed penalty for trains which are longer than the platform |
| earnAchievementsWithMods | false | Enables achievements even with mods |
| industryButton | false | Enables the industry button in free game |
| sandboxButton | false | Enables the sandbox button (to build towns) in free game |
| difficulty | "EASY" | Default difficulty level |
| noCosts | false | disables all costs |
| enforceMainConnections | true | checks if main connections are interrupted and throws warnings |
| townDevelopInterval | 60 | Number of half days after which the town will be adjusted (grow or shrink) |
| chargeMaintenanceInterval | 60 | Length of interval in half days after which the maintenance fees will be charged |
| simulateCargoWeight | true | Simulates the weight of loaded cargo |
| simPersonDestinationRecomputationProbability | 1.0 | The probability that a person will look for a new job or shopping facility. Smaller values reduce performance load. Larger values will adapt to changes in the offered public transport faster. |
| townMajorStreetAngleRange | 0 | Angle of default town crossings. 0 results in parallel streets without curves in town. |
| townInitialMajorStreetAngleRange | 0 | The same, but only during first creation of a town. |
| tramCatenaryYearFrom | 1875 | Start date of tram catenaries |
| trackCatenaryYearFrom | 1910 | Start date of rail catenaries |
| busLaneYearFrom | 1925 | Start date of bus lanes |
| maxVehicleCloneAmount | 50 | Maximum number of vehicles that can be cloned at once. Only used for console edition. |
| trainAccelerationFactor | 1.0 | Global acceleration factor |
| trainBrakeDeceleration | 2.5 | Global deceleration factor. Lower values will result in slower deceleration. |
[Scripting Basics](https://wiki.transportfever3.com/doku.php?id=modding:scripting:basics "modding:scripting:basics")
[Modifiers & Filters](https://wiki.transportfever3.com/doku.php?id=modding:scripting:modifiersfilters "modding:scripting:modifiersfilters")
@@ -0,0 +1,344 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:scripting:missions (fetched 2026-10-04) -->
### Table of Contents
* [Campaign File](#campaign_file)
* [Mission Folder](#mission_folder)
+ [info.mission.lua](#infomissionlua)
+ [savegame.sav](#savegamesav)
+ [mission.tl](#missiontl)
* [Mission Scripting](#mission_scripting)
* [Mission Tasks](#mission_tasks)
+ [Task Scripts](#task_scripts)
# Missions
Transport Fever 3 ships with one [campaign](https://wiki.transportfever3.com/doku.php?id=gamemanual:gamemodes:campaign "gamemanual:gamemodes:campaign"). For modders, it is possible to provide additional campaigns or single missions. These are then available over CAMPAIGN in the main menu.
Each campaign consists of several missions.
### Campaign File
The campaign file is must carry the suffix `.campaign.lua`. The file can be placed either in a mission mod file or within a separate mod.
It should have the following content specified:
```lua
function data()
return {
name = _("CAMPAIGN_NAME"),
icon = "campaign.tga",
description = _("CAMPAIGN_DESC"),
order = 1,
unlockingProgression = true,
}
end
```
The properties are:
* `name` for the title of the campaign that is displayed in the campaign selection menu.
* `icon` for the image button that is used to select the campaign. The double sized `<name>@2x.tga` image should have a resolution of 675 × 1080 pixels.
* `description` for the descriptive text in the campaign selection menu.
* `order` for the sort order in the campaign selection menu. The vanilla campaigns have place 1.
* `unlockingProgression` whether all missions are unlocked or if the next mission must be unlocked by achieving at least one star.
### Mission Folder
Inside each mission folder should consist of the following files, which we will explain one after the other:
* `info.mission.lua` specified the metadata of the mission
* `savegame.sav` the actual savegame
* `mission.res.lua` entry point for the overall mission simulation script in the base game
* `mission.script.lua` entry point for the specific mission script
* `mission.tl` actual implementation of the mission
* `mission_story.tl` task logic of the mission
#### info.mission.lua
```lua
function data()
return {
name = _("MISSION_01_NAME"),
description = _("MISSION_01_DESC"),
image = "/gui/mission/m01_preview.tga",
imagePreview = "/gui/mission/m01_preview2.tga",
imageSuccess = "/gui/mission/m01_suc.tga",
imageFailure = "/gui/mission/m01_fail.tga",
languageScript = "urbangames_campaign_mission_01::/mission/keyframes/languages.script@get",
loadscreen = "/gui/mission/m01_loadscreen.tga",
loadscreenDescription = _("MISSION_01_LOADSCREEN"),
loadscreenVoiceOver = "/audio/voice_over/MISSION_01_LOADSCREEN.wav",
savegame = "/savegames/savegame.sav",
starsDescription = _("MISSION_01_STARS_INFO"),
stars = {
{
id = "MISSION_01_STAR_1",
name = _("MISSION_01_STAR_1"),
iconLocked = "::/gui/menu/icons/star_1.tga",
iconCompleted = "::/gui/menu/icons/star_1.tga",
},
{
id = "MISSION_01_STAR_2",
name = _("MISSION_01_STAR_2"),
iconLocked = "::/gui/menu/icons/star_2.tga",
iconCompleted = "::/gui/menu/icons/star_2.tga",
},
{
id = "MISSION_01_STAR_3",
name = _("MISSION_01_STAR_3"),
iconLocked = "::/gui/menu/icons/star_3.tga",
iconCompleted = "::/gui/menu/icons/star_3.tga",
},
},
medals = {
{
id = "MISSION_01_MEDAL_1",
name = _("MISSION_01_MEDAL"),
nameLocked = _("MISSION_01_MEDAL_LOCKED"),
iconLocked = "::/gui/menu/icons/star_1.tga",
iconCompleted = "::/gui/menu/icons/star_1.tga",
},
},
characters = {
{
name = _("MISSION_01_CHARACTER_1_NAME"),
info = _("MISSION_01_CHARACTER_1_INFO"),
portrait = "urbangames_campaign_mission_01::/mission/dialogue/major_neutral.tga",
},
{
name = _("MISSION_01_CHARACTER_2_NAME"),
info = _("MISSION_01_CHARACTER_2_INFO"),
portrait = "urbangames_campaign_mission_01::/mission/dialogue/katie_baker_neutral.tga",
},
{
name = _("MISSION_01_CHARACTER_3_NAME"),
info = _("MISSION_01_CHARACTER_3_INFO"),
portrait = "urbangames_campaign_mission_01::/mission/dialogue/andrew_neutral.tga",
},
},
location = _("MISSION_01_LOCATION"),
year = 1906,
campaign = "urbangames_campaign::/info.campaign",
order = 1,
musicTracks = {
{ "urbangames_campaign_mission_01::/audio/music/m1_intro.ogg", false },
{ "urbangames_campaign_mission_01::/audio/music/m1_theme_chapter_1.ogg", true },
},
}
end
```
Notable properties are:
* `campaign` each mission must belong to a campaign
* `stars` required if you want to have a progress in your campaign
* `languageScript` required for dialogue tasks. At least one language should be supported
#### savegame.sav
You can either create a new game or use an existing savegame. Important is that the mission mod is active, when creating/loading the game so that the game scripts for the mission are loaded. Then you can save the game and copy the savegame to your mission folder.
#### mission.tl
This is the entry point for your mission. The mission interface is highly flexible, but as a starting point, we recommend this pattern here:
```lua
local mission_boot_util = ug_require "::/mission/mission_boot_util.tl" as MissionBootUtil
local taskDataFactory = ug_require "mission_story.tl" as function(taskKey : string) : MissionTaskUtil.StaticTaskInfo
 
return mission_boot_util.makeMission({
scriptKey = "urbangames_campaign_mission_01::/mission/mission.script",
taskDataFactory = taskDataFactory,
bootTasks = {
{ taskKey = "your_first_task" },
},
})
```
The `mission_story.tl` contains the entire logic of the mission and it contains a function that takes a key as an input and returns a mission task.
The bootTasks select all tasks that are triggered at the start of the mission.
These will be retrieved from the `mission_story.tl` file.
## Mission Scripting
Basically a mission is the combination of an orchestrating [game script](https://wiki.transportfever3.com/doku.php?id=modding:scripting:gamescripts "modding:scripting:gamescripts") and a savegame that is played on.
### Mission Tasks
Missions are just a sequence of tasks. These tasks can either be tasks that have to be completed by the player, or they can be executed in the background and can be completed by themselves when their internal logic is completed.
#### Task Scripts
Task scripts are the base of all tasks. These tasks have parameters that need to be specified in the `mission_story.tl` file and they are triggered sequentially.
Task scripts can define various methods in them. We show here only the most essential methods, and we refer to the source code for a complete definition.
```lua
interface TaskScript<TaskSpecificState, TaskParams>
taskScriptName : string
 
-- prepare the initial state when attempting to spawn (return nil to prevent spawning)
onSpawn : function(TaskParams, guiTimeSeconds : number) : TaskSpecificState | nil
 
isComplete : function(state: TaskContext<TaskSpecificState, TaskParams>) : boolean
 
onUpdate : function(ctx : TaskContext<TaskSpecificState, TaskParams>, readOnlyApi : MissionTaskReadOnlyApi)
handleEvent : function(src : string, id : string, name : string, param : any, ctx : TaskContext<TaskSpecificState, TaskParams>, missionTaskApi : MissionTaskApi) : any
guiHandleEvent : function(src : string, id : string, name : string, param : any, ctx : TaskContext<TaskSpecificState, TaskParams>, taskApi : DeferredTaskApi) : any
guiUpdate : function(ctx : TaskContext<TaskSpecificState, TaskParams>, taskApi : DeferredTaskApi)
end
```
Here, we will focus on the most important ones:
* Usually, each task has a completeness check: `isComplete`. If it returns true, the follow-up task is triggered and the task is completed.
* `onSpawn` initializes the task state, usually given the parameters specified in the story file.
* `onUpdate` is executed every simulation step. Here lies usually the main logic of the task.
* `guiUpdate` is executed in every frame
* `handleEvent` receives all game script events
* `guiHandleEvent` receives all gui events
The `mission_story.tl` file registers each task with a unique key within the file.
The base game already provides a variety of pre-defiend tasks in `::/mission/tasks/`. For example, there is the `buy_vehicle` task:
```lua
global record MissionTaskBuyVehicle is MissionInterface.TaskScript<MissionTaskBuyVehicle.State, MissionTaskBuyVehicle.Params>
record Params
carrier : Carrier
cargoTypeRes : string
minCount : integer
allowBuyVehicles : {string: integer}
end
record State
result : {Engine.Entity}
purchasedVehicles : {string: integer}
end
end
```
This task takes as parameters:
* `carrier` which carrier type the player is supposed to buy, e.g., road, rail, etc.
* `cargoTypeRes` the path to the cargo resource which the vehicle is supposed to be able to transport
* `minCount` the number of vehicles the player is supposed to buy
* `allowBuyVehicles` a map of resource paths to integer, which specified which vehicles are allowed to be bought in the vehicle store during this tasks lifetime
The state is used by the task internally.
For tasks that are to be completed by the player's action, usually some UI is wanted. In this case, the task info must be specified. Here, we display the essential fields. See the source code for the complete definition.
```lua
record TaskInfo
record Paragraph
text : string
end
record Option
text : string
key : any
end
 
name : string
paragraphs : { Paragraph }
options : { Option }
 
instructionData : MissionDialogueData.InstructionData
end
```
The fields are:
* `name` of the task
* `paragraphs` description of the task. First entry is the task description, follow up components are *hints*.
* `options` represent buttons the player can select in the mission UI
* `instructionData` a record that holds voice over/subtitle/portrait images
In the `mission_story.tl` file, the task might be defined as follows:
```lua
if taskKey == "buy_truck" then
return {
taskScript = buy_vehicle as MissionInterface.TaskScript<any, any>,
taskParams : MissionTaskBuyVehicle.Params = {
carrier = api.type["enum"].Carrier.ROAD,
cargoTypeRes = "::/cargos/logs/logs.cargo",
},
taskInfo = {
name = _("Buy a Truck"),
paragraphs = {
{ text = _("Buy a truck that can transport logs.") },
},
 
},
followup = {
{ taskKey = "<NEXT TASK>" },
},
}
end
```
We present one more example, that shows a branching point. The flow of the mission can be imagined as a directed graph. In this graph, branching points are allowed. The conditions can be given in the follow up definition. We present here the example where the player can choose between two different options `A` and `B`:
```lua
if taskKey == "branching_point" then
return {
taskScript = page as MissionInterface.TaskScript<any, any>,
showInHistory = true,
taskInfo = {
name = _("Branching point"),
paragraphs = {
{ text = _("Make your decision") },
},
options = {
{ text = _("Choose A"), key = "finish-a" },
{ text = _("Choose B"), key = "finish-b" },
},
},
followup = {
 
{
taskKey = "a",
condition = function(ctx : MissionInterface.TaskContext<any, any>) : boolean
if ctx.genericState.optionDecision == "finish-a" then
return true
end
end,
},
{
taskKey = "b",
condition = function(ctx : MissionInterface.TaskContext<any, any>) : boolean
if ctx.genericState.optionDecision == "finish-b" then
return true
end
end,
},
}
}
end
```
Here we used the `page` task. This is an empty task that completes once the player selects a button in the mission window.
To conclude this section, we explain how to end the mission. There are two possible endings: success and failure, defined by the `finish` task.
```lua
if taskKey == "failure" then
return {
taskScript = finish as MissionInterface.TaskScript<any, any>,
taskParams = {
success = false,
},
}
end
if taskKey == "success" then
return {
taskScript = finish as MissionInterface.TaskScript<any, any>,
}
end
```
**Takeaway**
1. Functions in handlers operate on engine thread (api.gui unavailable, engine functions available)
2. Functions in guiHandlers operate on ui thread (api.gui available, engine functions unavailable)
@@ -0,0 +1,156 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:scripting:modifiersfilters (fetched 2026-10-04) -->
### Table of Contents
* [Resource Modifiers](#resource_modifiers)
+ [Example](#example)
* [File Filters](#file_filters)
+ [File Filter Util](#file_filter_util)
+ [Example](#example1)
This page is not yet adapted for Transport Fever 3. The content may be outdated.
# Modifiers and Filters
It is possible to modify and filter game resources from vanilla and mods by using resource modifiers and filters in the runFn function in mod.lua file.
## Resource Modifiers
Modifier functions allow to modify resources on the fly at the time they’re loaded. This is useful to edit the same value in multiple files. in fact, it’s much more convenient than copying and editing all affected files by hand. Furthermore, multiple modifiers can be added by different mods without one mod overriding the files of the other.
A modifier is a function that receives two parameters:
* `fileName` is the name of the resource file that might get modified.
* `data` is the content of the `data()` function in this file.
The function should return the `data` unchanged or adjusted.
To register a resource modifier, call `addModifier(category, fn)` where:
* `category` is the category of resource file that this modifier should be applied to.
* `fn` is the modifier function explained above.
Resource modifiers can be specified for the following categories:
* `loadModel`
* `loadModule`
* `loadStreet`
* `loadTrack`
* `loadBridge`
* `loadTunnel`
* `loadMultipleUnit`
* `loadRailroadCrossing`
* `loadTrafficLight`
* `loadConstruction`
* `loadConstructionCategory`
* `loadConstructionMenu`
* `loadClimate`
* `loadEnvironment`
* `loadTerrainMaterial`
* `loadTerrainGenerator`
* `loadGrass`
* `loadGroundTex`
* `loadCargoType`
* `loadSoundSet`
* `loadScript`
* `loadGameScript`
### Example
As an exemplaric use case, the following modifier function sets the speed limit of all bridges to 200%:
```lua
local function bridgeModifier (fileName, data)
if data.speedLimit then
data.speedLimit = 2.0 * data.speedLimit
end
 
return data
end
 
addModifier( "loadBridge", bridgeModifier )
```
## File Filters
File filters filter the content of a directory, i.e. they define which files are visible to the game. Mods can install as many file filters as needed, and multiple file filters per category can be specified (to form a filter chain).
A filter is a function that receives two parameters:
* `fileName` is the name of the resource file that might get filtered.
* `data` is the content of the `data()` function in this file.
The function should return `false` if the resource should be deactivated and `true` if the resource should not be deactivated.
To register a file filter, call `addFileFilter(category, fn)` where:
* `category` is the category of resource file that this filter should be applied to.
* `fn` is the filter function explained above.
File filters can be specified for the following categories:
* `model/vehicle`
* `model/person`
* `model/car`
* `model/rock`
* `model/tree`
* `model/other`
* `multipleUnit`
* `street`
* `track`
* `bridge`
* `tunnel`
* `railroadCrossing`
* `trafficLight`
* `environment`
* `construction`
* `module`
* `autoGroundTex`
* `groundTex`
* `terrainGenerator`
* `terrainMaterial`
* `cargoType`
* `grass`
* `gameScript`
* `climate`
### File Filter Util
The `filefilterutil.lua` in `res/scripts/` provides various functions to help with file filtering:
* `util.always` is a pass through filter that does not deactivate anything.
* `util.combineOr` is a function to combine the results of several file filters with an `or` condition. If any of the file filters returns `true`, the function returns `true`.
* `util.combineAnd` is a function to combine the results of several file filters with an `and` condition. If any of the file filters returns `false`, the function returns `false`.
* `package.base` is a file filter that tests if the resource is not part of a DLC or mod.
* `package.mod` is a file filter that tests if the resource is part of a mod.
* `package.baseOrMod` is a file filter that tests if the resource is part of the base game or a mod.
* `package.dlcOrMod` is a file filter that tests if the resource is part of a DLC or mod.
* `model` contains several additional file filters that test if the resource is a model of a certain type.
### Example
The following code installs a filter for vehicles which removes all non-steam-locomotives from the game.
```lua
local function myFilter(fileName, data)
if data.metadata.railVehicle and data.metadata.railVehicle.engines and
#data.metadata.railVehicle.engines == 1 and data.metadata.railVehicle.engines[1].type ~= "STEAM" then
return false
end
return true
end
 
addFileFilter("model/vehicle", myFilter)
```
[Base Config](https://wiki.transportfever3.com/doku.php?id=modding:scripting:baseconfig "modding:scripting:baseconfig")
[Game Scripts](https://wiki.transportfever3.com/doku.php?id=modding:scripting:gamescripts "modding:scripting:gamescripts")
+98
View File
@@ -0,0 +1,98 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:start (fetched 2026-10-04) -->
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:menubox_title_modding_manual.png)
New here? Start with a brief [introduction](https://wiki.transportfever3.com/doku.php?id=modding:introduction "modding:introduction") into modding Transport Fever 3.
Transport Fever 3 features full modding support, empowering the community to create, customize, and expand the experience in countless ways. Mods can be easily [shared](https://wiki.transportfever3.com/doku.php?id=modding:general:publishing "modding:general:publishing") and discovered through [mod.io](https://mod.io/g/transportfever3 "https://mod.io/g/transportfever3"), making it simple to connect creators and players across platforms.
We're proud to run a Curated Mods Program, highlighting high-quality creations and supporting standout modders within the community. To help you bring your ideas to life, we provide dedicated modding tools and extensive documentation covering everything from getting started to advanced topics.
Mods are a vital part of Transport Fever 3's ecosystem. They inspire innovation, extend longevity, and strengthen the community. We're incredibly grateful to every creator who contributes their time, talent, and passion.
## General
This chapter introduces the general concepts and fundamentals of Transport Fever 3 modding. It explains mod structure, loading behavior and the used resource types.
* [Syntax](https://wiki.transportfever3.com/doku.php?id=modding:general:syntax "modding:general:syntax")
* [Mod Definition](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition "modding:general:moddefinition")
* [Mod Parameters & Scripts](https://wiki.transportfever3.com/doku.php?id=modding:general:modscripts "modding:general:modscripts")
* [Resource Types & Structure](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes "modding:general:resourcetypes")
* [Models (.mdl)](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl "modding:general:resourcetypes:mdl")
* [Meshes (.msh)](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:msh "modding:general:resourcetypes:msh")
* [Materials (.mtl)](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl "modding:general:resourcetypes:mtl")
* [Guidelines & Requirements](https://wiki.transportfever3.com/doku.php?id=modding:general:guidelines "modding:general:guidelines")
* [Best Practices](https://wiki.transportfever3.com/doku.php?id=modding:general:bestpractice "modding:general:bestpractice")
* [Publish a mod](https://wiki.transportfever3.com/doku.php?id=modding:general:publishing "modding:general:publishing")
## Tools
Documentation of official and third-party tools relevant to the modding workflow. Covers their purpose and typical use cases.
* [Model Editor](https://wiki.transportfever3.com/doku.php?id=modding:tools:modeleditor "modding:tools:modeleditor")
* Ingame Tools (Coming Soon)
* [Terrain Generator Editor](https://wiki.transportfever3.com/doku.php?id=modding:tools:terraingeneratoreditor "modding:tools:terraingeneratoreditor")
* [External Tools](https://wiki.transportfever3.com/doku.php?id=modding:tools:external "modding:tools:external")
## Vehicles
This chapter describes how vehicles are defined. It covers configuration files, models, animations, and gameplay-relevant parameters for all supported transport types.
* [Vehicle Basics](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics "modding:vehicles:basics")
* [Vehicle Types](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:types "modding:vehicles:types")
* [Vehicle Advanced Topics](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:advancedtopics "modding:vehicles:advancedtopics")
* [Repaint Mods](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:repaints "modding:vehicles:repaints")
## Constructions
This chapter describes how constructions such as buildings and stations are defined. It covers configuration options, models, and in-game behavior.
* [Construction Basics](https://wiki.transportfever3.com/doku.php?id=modding:constructions:basics "modding:constructions:basics")
* [Construction Menu](https://wiki.transportfever3.com/doku.php?id=modding:constructions:menu "modding:constructions:menu")
* [Construction Types](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types "modding:constructions:types")
* [Modular Constructions](https://wiki.transportfever3.com/doku.php?id=modding:constructions:modular "modding:constructions:modular")
* [Construction Templates](https://wiki.transportfever3.com/doku.php?id=modding:constructions:templates "modding:constructions:templates")
* [Ground Textures](https://wiki.transportfever3.com/doku.php?id=modding:constructions:groundtextures "modding:constructions:groundtextures")
## Infrastructure
This chapter describes how streets, tracks, and other transport network elements are defined. It covers geometry, parameters, and gameplay behavior.
* [Tracks and Streets](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:tracksstreets "modding:infrastructure:tracksstreets")
* [Bridges and Tunnels](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:bridgestunnels "modding:infrastructure:bridgestunnels")
* [Signals](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:signals "modding:infrastructure:signals")
* [Railroad Crossings](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:railroadcrossings "modding:infrastructure:railroadcrossings")
* [Traffic Lights](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:trafficlights "modding:infrastructure:trafficlights")
* [Edge Addons](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:edgeaddons "modding:infrastructure:edgeaddons")
## Environment
This chapter covers environment-related modding topics such as map generation, terrain materials, vegetation, and other world elements.
* [Climate Zones](https://wiki.transportfever3.com/doku.php?id=modding:environment:climates "modding:environment:climates")
* [Environments](https://wiki.transportfever3.com/doku.php?id=modding:environment:environments "modding:environment:environments")
* [Terrain Generators](https://wiki.transportfever3.com/doku.php?id=modding:environment:terraingenerators "modding:environment:terraingenerators")
* [Terrain Materials](https://wiki.transportfever3.com/doku.php?id=modding:environment:terrainmaterials "modding:environment:terrainmaterials")
* [Animals](https://wiki.transportfever3.com/doku.php?id=modding:environment:animals "modding:environment:animals")
* [Landscape Assets](https://wiki.transportfever3.com/doku.php?id=modding:environment:landscapeassets "modding:environment:landscapeassets")
## Misc
Collection of miscellaneous reference material relevant to Transport Fever 3 modding. Covers features referenced across multiple chapters.
* [Cargo Types](https://wiki.transportfever3.com/doku.php?id=modding:misc:cargo "modding:misc:cargo")
* [People](https://wiki.transportfever3.com/doku.php?id=modding:misc:people "modding:misc:people")
* [Shaders](https://wiki.transportfever3.com/doku.php?id=modding:misc:shaders "modding:misc:shaders")
* [Sound Sets](https://wiki.transportfever3.com/doku.php?id=modding:misc:soundsets "modding:misc:soundsets")
* [Playlists](https://wiki.transportfever3.com/doku.php?id=modding:misc:playlists "modding:misc:playlists")
* [Names](https://wiki.transportfever3.com/doku.php?id=modding:misc:names "modding:misc:names")
* [Localizations](https://wiki.transportfever3.com/doku.php?id=modding:misc:localizations "modding:misc:localizations")
## Scripting
Reference for scripting interfaces available to mods. Covers APIs, execution flow, and interaction with game systems.
* [API Reference](https://wiki.transportfever3.com/doku.php?id=modding:scripting:api "modding:scripting:api")
* [Missions](https://wiki.transportfever3.com/doku.php?id=modding:scripting:missions "modding:scripting:missions")
* Game Scripts (Coming Soon)
* User Interface (Coming Soon)
@@ -0,0 +1,127 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:tools:external (fetched 2026-10-04) -->
### Table of Contents
* [Scripting](#scripting)
+ [VS Code Setup](#vs_code_setup)
- [Install Teal & Extensions](#install_teal_extensions)
- [Preparing Development Workspace](#preparing_development_workspace)
- [Adding your own Teal definitions](#adding_your_own_teal_definitions)
* [3D Modelling](#d_modelling)
* [Texturing](#texturing)
+ [DDS Export](#dds_export)
* [Custom Tools for Transport Fever 3](#custom_tools_for_transport_fever_3)
# External Tools
Every modder has individual preferences which programs he likes to use for modding. Here are some recommendations and hints on tools that are often used in the community. The list below is not exclusive, there are many other compatible programs.
## Scripting
The various [text based](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes#code_files "modding:general:resourcetypes") resouce types can be edited with almost any text editor. This reaches from *Microsoft Editor* to integrated development environments like *Eclipse*. Two commonly used text editors are:
* **Visual Studio Code** is afree text editor that runs under Microsoft Windows, MacOS and Linux. It can be downloaded for free at [code.visualstudio.com](https://code.visualstudio.com/ "https://code.visualstudio.com/").
* **Notepad++** is another free text editor for Microsoft Windows. It can open multiple tabs in several windows and has builtin Lua syntax highlighting. It can be downloaded for free at [notepad-plus-plus.org](https://notepad-plus-plus.org/ "https://notepad-plus-plus.org/").
### VS Code Setup
To ease development of mods with VS Code, it is recommended to follow this setup guide.
#### Install Teal & Extensions
TEAL is an augmentation for LUA that provides additional features. When using TEAL, autocompletion and compile time type checking is possible also in LUA. Download it from the [official repository](https://github.com/teal-language/tl "https://github.com/teal-language/tl").
There are several helpful extensions for autocompletion and syntax highlighting:
* [Lua by sumneko](https://marketplace.visualstudio.com/items?itemName=sumneko.lua "https://marketplace.visualstudio.com/items?itemName=sumneko.lua")
* [Teal by Patrick Desaulniers](https://marketplace.visualstudio.com/items?itemName=pdesaulniers.vscode-teal "https://marketplace.visualstudio.com/items?itemName=pdesaulniers.vscode-teal")
After installing the Teal extension, go to the settings of the extension and configure the path to the Teal executable.
#### Preparing Development Workspace
To create the development workspace, start the [Model Editor](https://wiki.transportfever3.com/doku.php?id=modding:tools:modeleditor "modding:tools:modeleditor") once. It creates the following files in the Staging Area:
* `tlconfig.lua`
* `workspace_def.lua`
* `.vscode/extensions.json`
If this did not work, you can manually copy them from `<tf3_install_folder>/dev-workspace` and adjust `tlconfig.lua` to have proper folders set.
To test the workspace setup, create an example script in your mod called `example.tl` and paste
```lua
local function myFirstTealFunction()
log.warning("Hello World!")
end
```
You should be able to navigate to the definition of the `warning` function via CTRL and click or F12 or right click → Go To Definition.
#### Adding your own Teal definitions
To add your own definitions, create `*.d.tl` files in your mod and add the folder of your mod in the `tlconfig.lua` and `workspace_def.tl`.
As a trial run, you may
1. uncomment `– "example_mod_1"` from both: `tlconfig.lua` and `workspace_def.tl`
2. add a file called `example_mod_1/example_mod_1.d.tl` with the following content:
```lua
global record MyFirstRecord
myFirstField : string
end
```
Now you should be able to use it in the `example.tl`:
```lua
local function myFirstTealFunction(aRecord : MyFirstRecord)
log.warning("Hello World : " .. aRecord.myFirstField)
end
```
## 3D Modelling
Like with the text editors, there are plenty of available software solutions for 3D modelling.
* **Blender** is a free allround software for 3D modelling, animation, … It supports bone animations too and bake texture maps like for ambient occlusion. Models exported as FBX may be imported with the [model editor](https://wiki.transportfever3.com/doku.php?id=modding:tools:modeleditor "modding:tools:modeleditor"). Blender can be downloaded at [www.blender.org](https://www.blender.org/ "https://www.blender.org/").
* **Modo** is a payware 3D modelling software by Foundry. It's the software used by Urban Games to develop the content for Transport Fever 3. The FBX import into the model editor is suited for FBX files exported by Modo. As Foundry stopped the support for Modo, it is not recommended to use it for new mod projects anymore.
* **3ds Max** is a payware 3D modelling software by Autodesk. See [www.autodesk.de/products/3ds-max/overview](https://www.autodesk.de/products/3ds-max/overview "https://www.autodesk.de/products/3ds-max/overview") for more information.
* **Maya** is the other payware 3D modelling software by Autodesk. See [www.autodesk.de/products/maya/overview](https://www.autodesk.de/products/maya/overview "https://www.autodesk.de/products/maya/overview") for more information.
* **Cinema4D** is a payware 3D modelling software by Maxon. See [www.maxon.net/de/produkte/cinema-4d/](https://www.maxon.net/de/produkte/cinema-4d/ "https://www.maxon.net/de/produkte/cinema-4d/") for more information.
As the payware tools have very expensive licenses, it's not recommended to invest in them if you are not an advanced user. Students may get a discount or free license though as long as they do not use the software for professional and commercial projects.
There are other tools that are used regularily. These have some problems regarding the compatibility to Transport Fever 3 that are described below:
* **Sketchup** is a 3D modelling software that many prefer because of its ease of use. However, it does not offer the possibility to map the 3D model itself, so it is not possible to import it directly into Transport Fever 3. The models must first be mapped in another program. In addition, cylindrical objects are often exported very finely, so that the models have high tris numbers.
* **Solidworks** and similar parametrized CAD modelling tools are especially popular with people in the CAD field. They offer the possibility to create systematic models. However, they have the same problems as Sketchup, i.e. it is usually not possible to map the model and curves are implemented very finely.
## Texturing
Beside the modelling tools, there are various texturing tools. These have different advantages and disadvantages and many artists stick to tools they are familiar with.
* **GIMP** is a freeware image editing software that is commonly used. It is capable of loading some Photoshop files too. You can find it on [www.gimp.org](https://www.gimp.org/ "https://www.gimp.org/").
* **Paint.NET** is a freeware image editing software that originally started as an alternative to the classical Microsoft Paint. It can be downloaded at [www.getpaint.net](https://www.getpaint.net/ "https://www.getpaint.net/").
* **Photoshop** is a payware software for professional 2D artists by Adobe. It is available as part of the Adobe Creative Cloud or separate but not with a perpetual license. Find more information about Photoshop at [www.adobe.com/products/photoshop.html](https://www.adobe.com/products/photoshop.html "https://www.adobe.com/products/photoshop.html").
* **Paint Shop Pro** is a payware software by Corel. It can be bought with a perpetual license at [www.paintshoppro.com](https://www.paintshoppro.com/ "https://www.paintshoppro.com/").
* **Substance Painter** is a payware software for professional 3D artists originally developed by Allegorithmic and now maintained and developed further by Adobe. It is used for texturing at Urban Games. In contrast to the software above, the textures can be painted directly on the 3D model. Interactive texture materials can be used with lots of modifiers to accommodate various needs. Get more information at [www.adobe.com/products/substance3d-painter.html](https://www.adobe.com/products/substance3d-painter.html "https://www.adobe.com/products/substance3d-painter.html"). A perpetual lifetime license can be bought on [Steam](https://store.steampowered.com/app/4329260/Substance_3D_Painter_2026/ "https://store.steampowered.com/app/4329260/Substance_3D_Painter_2026/").
### DDS Export
To export DDS files, a plugin or standalone program might be needed. Here is a list of common ones. Be aware that their internal compression algorithms may differ resulting in slightly different output.
* Both **Gimp** and **Paint.net** have included support for exporting DDS textures.
* **NVIDIA Texture Tools** require a NVIDIA CUDA capable GPU. They are available standalone as well as plugin for Adobe Photoshop. Find more information at [developer.nvidia.com/nvidia-texture-tools-exporter](https://developer.nvidia.com/nvidia-texture-tools-exporter "https://developer.nvidia.com/nvidia-texture-tools-exporter").
* **Intel Texture Works** is a Photoshop plugin made by Intel. You can find more information at [software.intel.com/.../intel-texture-works-plugin.html](https://software.intel.com/content/www/us/en/develop/articles/intel-texture-works-plugin.html "https://software.intel.com/content/www/us/en/develop/articles/intel-texture-works-plugin.html").
* **Compressonator** is an AMD based GUI and command line tool that is now hosted on GitHub. See [github.com/GPUOpen-Tools/compressonator](https://github.com/GPUOpen-Tools/compressonator "https://github.com/GPUOpen-Tools/compressonator") for more information.
## Custom Tools for Transport Fever 3
There are fanmade tools available that support the development of mods for Transport Fever 3. Some common ones are:
* An interactive transformation matrix calculator by Xanos can be found at [transportfever.net](https://www.transportfever.net/lexicon/index.php?entry/77-transformationsmatrix/ "https://www.transportfever.net/lexicon/index.php?entry/77-transformationsmatrix/").
+189
View File
@@ -0,0 +1,189 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:tools:ingame (fetched 2026-10-04) -->
### Table of Contents
* [Hotkeys](#hotkeys)
* [Debug Tool Window](#debug_tool_window)
+ [Main](#main)
+ [Framebuffers](#framebuffers)
+ [Textures](#textures)
+ [Draw Calls](#draw_calls)
+ [Animals](#animals)
+ [Environment](#environment)
+ [Dump](#dump)
+ [Crash](#crash)
* [Console](#console)
* [Construction Development Tools](#construction_development_tools)
+ [Set Dressing Assets & Ground Textures](#set_dressing_assets_ground_textures)
This page is not yet adapted for Transport Fever 3. The content may be outdated.
# Ingame Tools
There are several tools available ingame to test and debug content. To activate these features, the debug mode has to be enabled. To do this, change the according `debugMode` value in the settings.lua to `true` or toggle the option in the [game settings](https://wiki.transportfever3.com/doku.php?id=gamemanual:ui:settings#advanced "gamemanual:ui:settings") advanced options.
Once enabled, you have:
* enabled debugging hotkeys to help debugging or testing modifications
* additional debug views and windows
* unlocked access to all campaigns and missions
* several filetypes are automatically refreshed when they changed (this e.g. covers updateFn definitions)
## Hotkeys
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=350&tok=03090b&media=modding:tools:boundingbox.png)
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=350&tok=9db53d&media=modding:tools:lanes.png)
The following hotkeys are available with activated debug mode:
(In the US keyboard layout, the AltGr key is the right Alt)
| Hotkey | Function |
| --- | --- |
| AltGr + A | Reload [sound sets](https://wiki.transportfever3.com/doku.php?id=modding:misc:soundsets "modding:misc:soundsets") |
| AltGr + B | Show bounding boxes |
| AltGr + D | Open Debug Tool Window (see below) |
| AltGr + E | Show chunk refresh |
| AltGr + G | Hide user interface |
| AltGr + I | Show GPU workload statistics and simulation diagrams |
| AltGr + L | Show lanes |
| AltGr + R | Recompile shaders (keep existing when compilation fails) |
| AltGr + Z | Open [camera tool](https://wiki.transportfever3.com/doku.php?id=gamemanual:ui:cameratool "gamemanual:ui:cameratool") |
In general, most of these modes can be switched on and off by repeating the keyboard combination.
## Debug Tool Window
The debug tool window has several tabs with different functions that are described below. It can be opened with AltGr + D.
### Main
The MAIN tab has several options regarding rendering and miscellaneous other tools.
In the **General** section, the current cursor position is shown with all three coordinates.
The **Rendering options** cover several modes that are available as Hotkeys as well as some others:
* **Show Bounding Boxes** is the same as AltGr + B
* **Freeze Render** is the same as AltGr + F
* **Show Lanes** is the same as AltGr + L
* **Grass Prepass** is a rendering technique used to fasten the grass rendering. It is enabled by default.
* **Terrain Prepass** is a rendering technique used to fasten the terrain rendering. It is enabled by default.
* **Terrain Disable Filter** disables the dithering between different textures. The dominationg materials are displayed alone in a grid of 1×1 m².
* **LOD Scale** is a slider to simulate different LOD distances.
* **Terrain Memory Usage** is a slider to adjust the ratio between performance and memory consumption of terrain data.
* **Terrain LOD error multiplier** is a parameter for the terrain LOD generation tolerance.
* **Terrain LOD radius multiplier** is a parameter for the terrain LOD generation radius. The average in this radius is used for the simpler LOD geometry.
The **Simulation options** provide buttons to increase the simulation speed up to 32 compared to the regular maximum of 4.
Finally the **UI Debug tools** have two tools:
* With **Show UI Element (Console)** it is possible to get the type and identifier of the current ui element the cursor is pointing on. The output can be read in the console.
* **Toggle Ruler** enables or disables a small ruler tool that can be used to measure distances on the map.
### Framebuffers
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=350&tok=996d32&media=modding:tools:framebuffer.png)
Some rendering intermediate frames can be reached in the FRAMEBUFFERS tab. These are:
* **Fb color** with color information.
* **Fb normalmap** with the overall normal information.
* **Fb depth** with depth information. As the depth information is stored exponential, zoom in to see some differences.
* **Hdr lum** with light emission and reflecion information.
* **Hdr blur final** and **Hdr small** with light blurring information.
* **Shadow map** is some depth information but from the origin of the sunlight.
* **Ssao** with ssao information.
* **Ssao final** with the blurred ssao information.
* **Reflection** with reflection information.
The views depend on the current main viewport and can be scaled to various sizes.
### Textures
To get an overview over the currently loaded textures, the TEXTURES tab can be used. It shows a complete folder tree with all loaded textures or only the subset with file sizes larger than the **Threshold** that can be set in the text box at the bottom edge.
### Draw Calls
The DRAW CALLS tab lists all current draw calls proceeded by the engine. There are several different properties listed in the top section:
* **Draw calls** is the total number of current draw calls.
* **Tec binds** is the number of different material techniques / shaders used for the objects.
* **Mat binds** is the number of loaded materials.
* **Total instances** is the number of all objects rendered.
* **Total vertices** is the total number of vertices of all these objects.
Below is a sortable table with different columns:
* **#** is an ID in the order of rendering.
* **Type** is `either` opaque for non transparent materials or `transp` for transparent materials.
* **Material** is the reference to the used material.
* **Mesh** is the name of the displayed mesh.
* **Grp** is the number of the submesh of this mesh.
* **Inst** is the number of instances of this mesh.
* **Mode** is usually `tris` for triangle based objects. `points` are very rare.
* **Vert** is the number of vertices of one instance.
* **Inst\*Vert** is the product of the vertices and the number of instances.
* **%** is the percentage of the total workload.
### Animals
There is a dropdown where the [score maps](https://wiki.transportfever3.com/doku.php?id=modding:environment:animals#suitable_areas "modding:environment:animals") for animals can be selected. They are visualized with red and blue colors. The current animal movements and distribution can be shown with the two buttons at **Debug rendering** below.
### Environment
The settings of [environments](https://wiki.transportfever3.com/doku.php?id=modding:environment:environments "modding:environment:environments") can be tested live in the ENVIRONMENT tab. It provides several input fields to test the different properties. See the documentation of environments for more information on their meaning.
At the bottom is a Load From Camera Tool and a Save to Camera Tool button. These can be used to temporarily store an environment setting for the use in the camera tool.
### Dump
A list of component types of an entity can be gathered from the **Entity information dump**. By entering an entity id, confirming it with REFRESH and pressing GOTO, it is possible to jump to the entity on the map.
### Crash
The last tab CRASH provides several buttons that can be used to force a crash of the game. Usually this is not needed for modders, but if you like you can test various ways of forced crashes there.
## Console
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=350&tok=a679bb&media=modding:tools:console.png)
The ingame console can be used to see the output that is written in the stdout.txt as well as to test and execute lua commands. The console can be opened and closed by default with the key below the ESC key. Depending on your keyboard layout it could be either `, § or ^.
You can execute lua commands in the command line by typing them and pressing ENTER. To enter complex statements with several lines of code, simply press ENTER inbetween. The console will execute the commands once syntactically complete, even if they span over many lines.
There are some assisting commands available:
* `\help` shows this list of available shortcuts and assisting commands.
* `\clear` clears the console.
* `\stdout <on|off>` regulates the output in `stdout.txt`. Default is `on`. If set to `off` there will be no output in the `stdout.txt`.
* `\timestamp <on|off>` shows timestamp in front of output if set to `on`. Default is `off`.
* `\scrlk <on|off>` does not jump to the bottom if new output appears if set to `on`. Default is `off`.
* `\history` prints a history of recent sent commands.
You may use the Up and Down keys to recall and cycle through the recent sent commands from the history.
To see a list of available functions, press CTRL + SPACE. This opens the autocomplete menu. You may use the Up and Down keys or the scroll wheel to navigate in the autocomplete menu. Press ENTER or click on a command to add the selected command to the console command line.
If you press TAB in the command line, the command will be completed if it can be completed unambiguously. Pressing TAB twice prints the available commands from the current autocomplete menu view to the console.
## Construction Development Tools
To ease the creation of constructions, there are several assistance tools available.
### Set Dressing Assets & Ground Textures
To decorate constructions, it is recommended to use the available asset models of the base game. The [Asset Placer mod](https://mod.io/g/transportfever3/m/asset-placer "https://mod.io/g/transportfever3/m/asset-placer") provides most of them in the landscaping menu. As they have the `allowSnapToMesh` flag set to true in the `snapPoint` struct, it is possible to snap them directly to a surface of a model, e.g. to a building wall after pressing Alt.
To move around some assets after placing them, the Toggle asset manipulator in the Main tab of the debug tools window can be used. Click on a model after activating it and it will be highlighted in turqoise. The shown axis handles can be used to move it around in 3D space. The menu on the left side of the screen allows manually tuning positions, size and rotation as well as to delete and clone the model.
The terrain can be painted with the brushes in the landscaping menu as well. To define areas that shall not be painted by the construction but what is already there, use a [terrain material](https://wiki.transportfever3.com/doku.php?id=modding:environment:terrainmaterials "modding:environment:terrainmaterials") that has a file name containing `"chroma_key"`. A mod containing such a material is provided below.
After decorating and painting the terrain, both can be exported. For that to work, the construction needs to have a configured [ground texture](https://wiki.transportfever3.com/doku.php?id=modding:constructions:groundtextures "modding:constructions:groundtextures"). Go to the Dump tab of the debug tools window and click on the construction. On the right side, the Print button allows to update the referenced `.gtex.lua` and its referenced index texture of the construction. Additionally, a `<construction_name>_assets.tl` file containing a list of all model instances is generated. Its content can be copied over elsewhere or the file can be required by a `.script.lua`. The `constructionutil.addModels` function can insert such a list to a subconstruction model result.
**Chroma Key Mod**
This mod contains a pink terrain material that will be skipped when exporting the ground texture mask as described above. The mod can be downloaded [here](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:ingame:ug_tf3_chroma_key.zip "modding:tools:ingame:ug_tf3_chroma_key.zip (423.3 KB)").
@@ -0,0 +1,670 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:tools:modeleditor (fetched 2026-10-04) -->
### Table of Contents
* [Setup](#setup)
* [User Interface](#user_interface)
+ [Model Loading and Saving](#model_loading_and_saving)
+ [Model Rendering](#model_rendering)
+ [Animation Preview](#animation_preview)
+ [Model Properties](#model_properties)
- [Model Tree](#model_tree)
- [Metadata Editor](#metadata_editor)
- [Material Editor](#material_editor)
- [Metadata Validation](#metadata_validation)
+ [Options](#options)
- [Rendering](#rendering)
- [Config](#config)
- [Graphics](#graphics)
- [Environment](#environment)
- [Icons](#icons)
- [Im-/ Export](#im-_export)
- [Tools](#tools)
* [Importing Models & Data](#importing_models_data)
+ [Material/Metadata Templates](#material_metadata_templates)
+ [FBX Model Import](#fbx_model_import)
- [File & Folder Names](#file_folder_names)
- [Nodes / Meshes](#nodes_meshes)
- [Materials](#materials)
- [Textures](#textures)
- [Animations](#animations)
- [Metadata](#metadata)
+ [Bulk Import](#bulk_import)
+ [FBX Construction Data Import](#fbx_construction_data_import)
- [Target Construction](#target_construction)
- [Hierarchy](#hierarchy)
- [Collider & Terrain Alignment](#collider_terrain_alignment)
- [Set Dressing](#set_dressing)
# Model Editor
The Model Editor is the main tool to be used when importing and editing models for Transport Fever 3. It features various import and conversion tools as well as editing functionality for the model metadata. Testing of different things like particles, aging, labels and animations is possible too. For presentation in the vehicle store, the Model Editor also includes an option to create screenshots and GUI icons.
## Setup
To start the Model Editor, execute the `ModelEditor.bat` in the installation directory of Transport Fever 2 on Windows, `ModelEditor.sh` in the installation directory of Linux or `ModelEditor.command` in the installation directory of MacOS. This will open two windows, a console window with debug output and the actual user interface window.
On first startup, the Model Editor creates a configuration file in
* `%APPDATA%/Transport Fever 3/` for Windows
* `~/.local/share/Transport Fever 2` for Linux
* `/Users/<username>/Library/Application Support/Transport Fever 3/` for MacOS
and asks the user to fill in the userdata location in this file. The configuration file contains several references to directories. The `userDataPath` is the directory where the userData is located. It is used to find the staging area and screenshot folders. You can find your userdata folder over the game's main menu > SETTINGS > ADVANCED > OPEN USER DATA FOLDER.
Only use forward slashes / in the path, otherwise the model editor may not start properly.
After saving the file, the Model Editor may be started again.
## User Interface
Once the Model Editor has started, the main window is visible with the toolbar at the top and the viewport where the models are rendered below.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_editor_toolbar.png)
The toolbar is split into 5 different groups of interactive elements:
1 dropdowns and buttons to select mods and models and save them
2 model rendering options
3 animation trigger
4 detail windows
5 validation, screenshots and options
As long as no model is loaded, most buttons are deactivated.
The viewport camera is controlled with the mouse:
* Hold the left mouse button and move the mouse to rotate the camera around.
* Hold the right mouse button and move the mouse to move the camera around.
* Scroll the mouse wheel to zoom in and out.
* Hold CTRL and the left mouse button to rotate the skybox around the model.
### Model Loading and Saving
To open a model, click on the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:open.png) button. The open dialog allows browsing through the content of the base game as well as all mods in the staging area. It is possible to search for models with the search field. It covers all subfolders of the currently selected folder. The ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:open_folder.png) button opens the currently selected folder in the file explorer.
A list of previously opened models can be accessed by clicking the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:recent.png) button. It is sorted by date. To close the currently loaded model, use the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:close.png) button.
If you want to save a model, use the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:save.png) button. It will save the currently loaded model to the mod that is selected in the dropdown next to it. Please be aware that there is no extra confirmation in case you overwrite existing files. To only save certain parts of the model, you may deselect the options provided in the dropdown next to the save button:
* MODEL will save the .mdl file
* MESH will save the .msh and .msh.blob files if they were just imported from fbx.
* MATERIAL will save the .mtl files
* ANIMATION will save the .ani files if they were just imported from fbx.
### Model Rendering
The model rendering in the viewport can be influenced by the second group of buttons in the toolbar 2. The three icon buttons are:
* ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_material.png) for the model with the materials as it looks in the game.
* ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_solid.png) for the model in plain white.
* ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_wireframe.png) for the model as a wire mesh.
The level of detail can be selected further to the right. With Auto the used level of detail depends on the zoom level like in the game. 0 and above select specific levels of detail independent of the zoom level.
### Animation Preview
The third button group 3 allows direct playing of one animation. If the model has any animation, it can be selected in the dropdown. Then it can be started with the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:resume.png) button. To loop the animation, activate the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:refresh.png) toggle next to it.
### Model Properties
The fourth group 4 allows accessing detailed windows for the model tree, metadata, materials and animations.
#### Model Tree
A click on the Model button opens the model window.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_editor_model_dialogue.png)
At the top of the window, the full path of the model resource is shown. To the right 1, there are three action buttons:
1. Recalculation of the [bounding box](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#bounding_box "modding:general:resourcetypes:mdl").
2. Recalculation of the [simulation extent](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#extent "modding:general:resourcetypes:mdl").
3. Open the model folder in the file explorer.
In the section below 2, the bounding box of the model can be manually adjusted.
The bottom section of the window is a tab widget 3 that has a tab for each level of detail. For each level of detail, it is possible to adjust the `visibleFrom` and `visibleTo` distances and lods can be marked as [texture lod base](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#level_of_detail "modding:general:resourcetypes:mdl") 4.
Below is a tree 5 that shows the node hierarchy of the model with the node names and mesh file references. If you activate checkboxes on the left or hover over lines, the mesh will be displayed alone in the viewport. This helps with identifying model parts. If you want to manually transform a mesh, e.g. to correct its position, you can open a popup with transformation parameters by clicking on the right button. The columns at the very right tell how many materials and animations are assigned to the node. They are listed in a tooltip when hovering the numbers or by clicking the checkbox Extended View further above.
#### Metadata Editor
To access the metadata editor, press the Metadata button. It presents all the data that is set in the `metadata` struct of a model.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_editor_metadata_dialogue.png)
To add new properties, you can press on the + button in the top right corner 1. There you can select new property types and add them to the metadata of the currently loaded model. To delete all metadata, use the gear button next to it and select Delete all. If you want to reset to the initial loaded values, press Reset.
The properties are collapsed by default 2 . Press the arrow markers on the left to unfold the hierarchy levels of properties. To unfold the complete hierarchy inside of a property, you can hold SHIFT while you click on the arrow marker.
To reset a single property or only a value of the property, you can use the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:recent.png) arrow on the right side 3. To completely remove it, press the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=20&tok=2f215a&media=gamemanual:interface:buttons:bin.png) button instead. Some properties contain lists, e.g. the labels. You can add a new entry by clicking on ![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=20&tok=e4446a&media=gamemanual:interface:buttons:plus.png) button. If you want to add a duplicate of an existing list element, use the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=20&tok=433e43&media=gamemanual:interface:buttons:duplicate.png) button next to it. Both are added to the end of the list.
Depending on the property there are one or more attributes to configure 4. Some require numeric or text input, others are checkboxes or sliders. For some attributes or even whole metadata properties, there are templates. They can be selected with the gear button on the left 5.
#### Material Editor
The MATERIALS button leads you to the materials editor. It provides access to all material files that are used in the model.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:model_editor_material_dialogue.png)
The top section 1 lists all materials used in this model. The ones referenced with relative path are more emphasized than the ones referenced with absolute path to differentiate between materials that are local and materials that are likely shared and should only be edited with caution. On the right side, the short tag of the individual material type is shown. Hovering over a line will hide all other geometry in the viewport.
For the currently selected material, the full material type is shown below the list 2. Its order can be changed on the right side and the material can be converted to another [material type](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl#types "modding:general:resourcetypes:mtl") by pressing the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=20&tok=6bd44f&media=gamemanual:interface:buttons:settings.png). There you can reset a material file to the initially loaded values too by clicking the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:recent.png) button.
Below is a list of all material properties depending on the material type 3. To unfold properties, you can press the arrow button and to reset a single property, you can click on the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:recent.png) arrow on the right side.
You can edit the properties in a similar way as the properties in the metadata editor. Texture references can either be written by hand or selected with a file picker that can be opened with the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:open.png) button on the right side 4.
Some material properties offer templates, they can be accessed as in the metadata editor by clicking on the gear button on the left side 5.
#### Metadata Validation
To check if there are major issues with the configured metadata, you can use the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:validate.png) button in the top bar. It will run a check and informs you as soon as it finds an issue. Run it again after you fixed the issue to check if there are further issues.
Currently the following metadata issues are covered by the validation tool:
| | | |
| --- | --- | --- |
| `loadIndicator` | `Referenced mesh node 'XYZ' not found (key: 'cargoBay.childId' in '<file>')` | The `cargoBay` is not assigned to an existing node in the model. |
| `loadIndicator` | `Area too small for 'loadIndicator.configs[1].cargoBay.bbox' at <file>` | The `cargoBay` bounding box is too small. |
| `loadIndicator` | `The configuration 'loadIndicator.configurations[1][2][3]' references invalid slot 1 at <file>` | The referenced slot in the config does not exist in the model. |
| `roadVehicle` | `roadVehicle/configs/fakeBogies: size (= 2) must match number of lods (= 3) or be empty` | There needs to be a list of `fakeBogies` per LOD. It might be an empty list `{}`. |
| `railVehicle` | `railVehicle/configs/fakeBogies: size (= 2) must match number of lods (= 3) or be empty` | There needs to be a list of `fakeBogies` per LOD. It might be an empty list `{}`. |
| `waterVehicle` | `waterVehicle/availPower: ship will never reach top speed, increase power or reduce area` | The `waterVehicle.area` value is too large in relation to the `availPower`. |
| `airVehicle` | `airVehicle/configs/axles: missing entry (but wheels specified)` | If `wheels` are configured, `axles` need to be configured as well. |
| `airVehicle` | `airVehicle/configs/wheels: missing entry (but axles specified)` | If `axles` are configured, `wheels` need to be configured as well. |
| `airVehicle` | `airVehicle/configs: invalid landing gear configuration; wheels defined but not axles` | If `wheels` are configured, `axles` need to be configured as well. |
| `airVehicle` | `airVehicle/configs: invalid landing gear configuration; axles defined but not wheels` | If `axles` are configured, `wheels` need to be configured as well. |
| `airVehicle` | `airVehicle/configs: invalid landing gear configuration; zero distance between main and front/back gear` | The configured wheels and axles may not be in the same spot. |
| `airVehicle` | `airVehicle/configs: invalid landing gear configuration; aircraft's initial pitch on ground is below <number> deg` | The plane gear results in a too negative pitch on the ground. |
| `airVehicle` | `airVehicle/configs: invalid landing gear configuration; aircraft's initial pitch on ground is above <number> deg` | The plane gear results in a too high pitch on the ground. |
| `seatProvider` | `Referenced mesh node 'XYZ' not found (key: 'seatProvider.seats[10]' in '<file>')` | The `seat` is not assigned to an existing node in the model. |
| `transportVehicle` | `transportVehicle/loadConfigs: list of load configs must not be empty` | The compartment needs at least one load config. |
| `transportVehicle` | `Model has a cargo entry specifying a load indicator '1' but no load indicator metadata` | The cargo configuration is referencing a `loadIndicator` but none are configured. |
| `transportVehicle` | `Model does not have load indicator '2`' | The cargo configuration is referencing a non-existent `loadIndicator`. |
| `transportVehicle` | `Wrong seat index in transportVehicle, seat 3 is invalid, should between 0 and 2 (compartment = 1, loadConfig = 2)` | The cargo configuration is referencing a non-existent `seat`. |
| `transportVehicle` | `transportVehicle/capacities: capacity '-1' not valid` | The capacity may not be negative. |
| `transportVehicle` | `transportVehicle/capacities: 'excluded' cargo type 'cargos/coal/coal.cargo' not found` | The cargo configuration is referencing a non-existent cargo type. |
| `transportVehicle` | `transportVehicle/capacities: cargo class 'BULK' not found` | The cargo configuration is referencing a non-existent cargo class. |
| `categoryList` | `Referenced cargo type not found: cargos/coal/coal.cargo, referenced by <file>` | The model is referencing a non-existent cargo type. |
| `categoryList` | `Referenced cargo format not found: RECT_40x10, referenced by <file>` | The model is referencing a non-existent cargo format. |
The following error messages might appear when there are misconfigured meshes, materials or animations:
| | | |
| --- | --- | --- |
| `Mesh` | `'body' with '<file>' (SKINNING_PHYS_NRML_MAP): Used technique '<file>' requires missing mesh attribute 'XYZ' in mesh {}. Usually this means a model is missing UV1 vertex attributes or a skinned material is used with a unskinned mesh.` | The mesh is not compatible with the material type. |
| `Material` | `'body' with '<file>' (PHYS_TRANSPARENT): map_albedo has missing texture <file>.` | A material is referencing a non-existent texture file. |
| `Material` | `Group 'body' has 2 materials but mesh has 3 groups (they must be equal). The group has been patched but this might lead to unexpected results.` | The number of submeshes needs to match the number of assigned materials. |
| `Material` | `Group 'body' has no materials but mesh has 2 groups` | Materials need to be assigned to the meshes. |
| `Material` | `Group body has 2 skin materials but mesh has 3 groups (they must be equal). The group has been patched but this might lead to unexpected results.` | The number of subgroups needs to match the number of assigned skin materials. |
| `Material` | `Group 'body' has no skin materials but mesh has 2 groups` | Materials need to be assigned to the meshes. |
| `Material` | `Model Group 'body' uses skinned mesh '<file>' with an unskinned material '<file>'. This might lead to unexpected crashes or glitches.` | Meshes with vertex groups should be used with skinned materials. |
| `Animation` | `Animation at body named <file> has incorrent number of times 12 and transforms 10.` | Number of time stamps and transformations need to match. |
| `Animation` | `Animation at body named <file> has no keyframes.` | Animations need to have at least one time stamp. |
| `Animation` | `Animation at body named <file> has keyframes starting after 0: 1200.` | The first time stamp needs to be at time 0. |
### Options
The options window can be opened by a click on the OPTIONS button in the top right. It contains several tabs with different settings. All settings are persisted and loaded when the model editor is opened. Most of the tabs have a RESET TO DEFAULT button to revert them to their default values.
#### Rendering
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=300&tok=ebfe05&media=modding:tools:modeleditor:options_rendering.png)
The Rendering tab provides several general options to show and hide annotations. The **Render Settings** cover general annotations:
* **Axes** renders the three orientation axis that meet at the origin in the 3D space. These mark the global positive directions of the x (red), y (green) and z (blue) dimension.
* **Nodes** renders the hierarchy of nodes in the model node tree as well as their individual orientation.
* **Node Names** renders the assigned names for each node.
* **Bounding Box** renders the [bounding box](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#bounding_box "modding:general:resourcetypes:mdl") used for rendering as it is specified in the `.mdl` file.
* **Extent Box** renders the [extent box](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#extent "modding:general:resourcetypes:mdl") used for simulation as it is specified in the `.mdl` file.
* **Collider** renders the [collider shape](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#collider "modding:general:resourcetypes:mdl") as it is specified in the `.mdl` file.
The **Locator Settings** cover annotations for individual features:
* **Seat Locators** renders small indicators at the positions of defined [passenger seats](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics#seats1 "modding:vehicles:basics").
* **Light Source Locators** renders origins and shapes of defined [light sources](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#lightsourcelist "modding:general:resourcetypes:mdl").
* **Particle System Locators** renders small indicators where particle emitters are located.
* **Label Outlines** enables small markers and rectangles where [text labels](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#labellist "modding:general:resourcetypes:mdl") are placed in the model. The text boxes below can be used to define some demo texts to test the display.
* **Water Line** renders a line that indicates the position of the [water line](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:types#metadata2 "modding:vehicles:types") definition of ships.
* **Cargo Bay Bounding Box** renders the bounding boxes of defined [cargo bays](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics#cargobay "modding:vehicles:basics").
* **Cargo Slot Locators** renders small indicators at the positions of defined [cargo slots](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:advancedtopics#custom_cargo_models "modding:vehicles:advancedtopics").
* **Bogies** renders small indicators where axles and wheels are located.
* **Bogies** renders small indicators where bogies are located.
* **Fake Bogies** renders small indicators where [fake bogies](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:advancedtopics#fake_bogies "modding:vehicles:advancedtopics") are located.
For investigation of meshes, it is also possible to select **Vertex Settings**:
* **Normals** renders the normals of vertices.
* **Tangents** renders the tangents of vertices.
#### Config
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:options_config.png)
The Config tab provides options to test various features of vehicles. In the top section **General Settings** holds various options for general testing:
* **Enable Auto Update** decides whether and when the model should be reloaded whenever one of the source files changes. Be aware that unsaved changes to the model are lost if the model is reloaded. If you encounter black textures or crashes while exporting textures from your graphic program, this might be caused by the Model Editor reloading the texture files before they are completely written by the program. In this case, use the `On Focus` option.
* **Animation Speed Scale** is the speed factor that is used for animations.
* **Lod scale** is a slider that can be used to mimic lower geometry graphic settings.
* **Reference Model** is allows showing a second model of the same mod or the base game as a reference to compare. If a model is referenced, it is visible as static asset. It won't be used in the screenshots. Click on the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:open.png) button and navigate through the file picker to select a model. With the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=20&tok=d0735f&media=gamemanual:interface:buttons:transform.png) button you can transform the reference model and with the ![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:eye.png) button you can toggle its visibility.
* **Color** lets you choose a color for models that have [color blending materials](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl#color_blending_dirt_rust "modding:general:resourcetypes:mtl") in use.
* **Age** is a slider that can be used to simulate the [aging](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl#color_blending_dirt_rust "modding:general:resourcetypes:mtl") of the vehicle. Depending on the age, the dirt and rust layers have different intensities.
The **Load Settings** section allows testing the [cargo](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics#compartment_list "modding:vehicles:basics") and [passengers](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics#seats "modding:vehicles:basics") of vehicles:
* **Passengers** is a slider that lets you choose the amount of passengers that should be displayed.
* **Cargo** is a slider that lets you choose the amount of cargo that should be displayed.
* **Random Seed** is a spinner that can be used to select a seed for the randomisation of cargo slots and configurations.
* **Cargo indicators** is a list of compartments where you can select the cargo type that should be used in the preview.
The **Particle Settings** section allows testing the [particle emitters](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#particlesystem "modding:general:resourcetypes:mdl"):
* **Preview Particles** enables the particle rendering.
* **Reverse Direction** changes the direction of the preview velocity configured with the slider below.
* **Particle Velocity** is a slider to select a preview velocity that mimics movement of the model.
* **Particle Filter Tag** allows restricting the preview to particle emitters with a specific tag.
The **Label Settings** section allows testing the [labels](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#labellist "modding:general:resourcetypes:mdl"). There are text inputs for the different label data types.
#### Graphics
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:options_graphics.png)
The Graphics tab provides graphic settings:
* **Texture Quality** selects the maximum displayed texture mip map level. `Very High` uses the original texture resolution, `High` skips the highest resolution, …
* **Anti-Aliasing Quality** sets the anti-aliasing quality for the rendering.
* **Bloom Quality** sets the quality of the emissive bloom.
* **Enable Bloom** allows disabling or enabling the bloom altogether.
* **Enable Screen Space Reflections (SSR)** toggles the reflections on glossy surfaces based on the actual surroundings.
* **Enable Screen Space Ambient Occlusion (SSAO)** enables the smooth ambient occlusion that is computed by the engine.
* **Enable Shadows** enables shadows that are computed depending on the direction of the sun.
* **Enable Legacy HDR Rendering** switches back to the Transport Fever 2 dynamic range rendering.
* **Enable VSync** toggles the frame rate synchronization.
Some of the graphic settings require a restart of the Model Editor to take effect.
#### Environment
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:options_environment.png)
The Environment tab provides several camera and environment related options. The camera settings are:
* **Field of View (FOV)** allows choosing the field of view. The larger the chosen value is, the wider is the camera angle.
* **Background** can be used to set the rgb values for the background color that should be used when the environment is not rendered (see above). A colorpicker is opened on click to select the color by numbers, hex code or visually.
For the environment, the following settings are available:
* **Enable Environment** activates a gray ground face at height 0 and a simple blue skybox as the background. If unchecked, no ground face is shown.
* **Environment** offers all available [environments](https://wiki.transportfever3.com/doku.php?id=modding:environment:environments "modding:environment:environments") that may be used in the model editor.
* **Time of Day** is a slider to set the current time of the environment.
* **Day of Year** is a slider to set the day of the year. The height of the sun depends on the calendar.
* **Reference Brightness** is a slider to adjust the overall brightness. 10 is the maximum value that results in a darker mood.
* **Sun Intensity** is a slider to adjust the intensity of the sun, affecting the direct light on the planet.
* **Ambient Color (Day)** is an ambient color that tints the whole world slightly during day time.
* **HDR Exposure (Day)** is a number used to scale the hdr exposure during day time.
* **HDR Contrast (Day)** is a number used to scale the hdr contrast during day time.
* **Ambient Color (Night)** is an ambient color that tints the whole world slightly during night time.
* **HDR Exposure (Night)** is a number used to scale the hdr exposure during night time.
* **HDR Contrast (Night)** is a number used to scale the hdr contrast during night time.
#### Icons
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:options_icons.png)
To generate vehicle icons and take representative screenshots of the vehicle models, you can use the SCREENSHOT button on the right side of the top bar. This will generate the necessary ui icons and moves them at the correct position in the mod. Additionally, larger renderings from all sides as well as a perspective view will be generated and saved in the `screenshots/` folder in the specified `userDataPath`.
The Icons tab provides options for the creation of ui icons and other screenshots. The **Screenshot Settings** are general settings for the screenshot generation:
* **Screenshot extension** allows choosing the file extension that is used for the generation. The game requires the `.tga` format, while `.png` might be useful for websites.
* **Use Default Stage Files** switches between the standard stages for tracks/streets and the ones configured below.
* **Stage file (bus, tram, truck)** is the model reference that is used as street segment for screenshots of road vehicles.
* **Stage file (train, wagon)** is the model reference that is used as track segment for screenshots of rail vehicles.
* **Use Viewport Environment** switches between the standard environment for screenshots and the ones selected in the Environment.
* **Use Viewport Animations** switches between the default animation state and the one configured in the Animation Window.
The sections below can be used to fine tune the generated icons and screenshots. The **UI Icons** setting **Max Width** sets the maximum horizontal size for ui icons. The pixel per meter ratio for the icon is set with **Pixels Per Meter**. The same settings are available for the **Large Ortho Screenshot** further down below.
The **3D Screenshot Settings** are used for the diagonal screenshots:
* **Angle** is the horizontal rotation around the model.
* **Height** is the height of the camera. The height is computed automatically with value `-1`.
* **Radius** is the distance of the camera to the center point of the model. The distance is computed automatically with value `-1`.
* **Field of View** is the camera angle that sets the focal length.
* **Size X** sets the maximum horizontal size.
* **Size Y** sets the maximum vertical size.
The **Viewport Screenshot Settings** allow setting the size for viewport screenshots.
Below are several checkboxes allowing to adjust the behavior of the screenshot button in the top bar:
* **Disable Screenshot Button** disables the screenshot button in the top bar so it can't be triggered accidentally.
* **Generate UI Icons** will generate the ui icons for vehicle models in the mod.
* **Generate Cblend Mask** will generate the cblend masks for vehicle models in the mod.
* **Generate 3D Screenshots** will generate the perspective screenshots for vehicle models in the mod.
* **Generate Large Ortho Screenshots** will generate large orthographic screenshots for vehicle models in the mod.
* **Generate Viewport Screenshots** will generate screenshots with the current perspective in the Model Editor.
* **Generate Ortho Screenshots** will generate small orthographic renderings for vehicle models in the mod.
The **Bulk Actions** section allows taking screenshots for multiple models at once. The **Bulk Prefix** allows selecting a folder containing multiple models. The operation is then started with Bulk Generate.
Keep in mind that this may take a long time depending on the number of models in the mod and the hardware of the computer.
#### Im-/ Export
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:options_import.png)
The Im-/Export tab allows configuring the behavior when importing `.fbx` files:
* **Overwrite Meshes** allows overwriting existing meshes when re-importing a `.fbx` file.
* **Overwrite Animations** allows overwriting existing animations when re-importing a `.fbx` file.
* **Overwrite Materials** allows overwriting existing materials when re-importing a `.fbx` file.
* **Overwrite PHYS\_TRANSPARENT\_DIFFUSE Materials** allows overwriting the placeholder materials when re-importing a `.fbx` file.
* **Overwrite Textures** allows overwriting existing textures when re-importing a `.fbx` file.
* **Use Blender Coordinate System** needs to be selected when importing `.fbx` files exported from Blender.
When saving models, the Model Editor automatically converts `.tga` textures to `.dds`. It is possible to adjust the **Texture Conversion Quality** and the compression can be disabled completely with **Skip Compression of Existing Textures**.
#### Tools
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:options_tools.png)
The Tools tab provides several tools to adjust the currently opened model or several models at the same time. The general settings are:
* **Target Mod** allows selecting the mod in which the result of the tool operation will be saved.
* **Bulk Prefix** can be used to select the folder which contains the model that will be affected.
The **Rename** tool allows searching and replacing parts of filenames. There are two inputs:
* **Search Token** is used to specify the string that is searched.
* **Replace Token** can be used to specify the string that shall be inserted where the search token is found. If empty, the search token is removed without replacement.
The **Convert** tool allows migrating Transport Fever 2 models to Transport Fever 3. This covers:
* restructuring files from `res` to the `content` structure. Be aware that the result depends on how consistent the folder structure in the `res` directory are.
* adjusting `.mtl` files to the new structure. This automatically sets the [legacy flag](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl#light_receiver "modding:general:resourcetypes:mtl").
* adjusting the metadata of the mdl. New values need to be added manually.
The **Copy** tool allows creating a copy of a model in another mod.
The **Validate** tool allows validating several models at once.
## Importing Models & Data
The Model Editor supports the import of different things:
* `.import.lua` files for material/metadata templates
* `.fbx` files for 3D models
* `.fbx` files for construction data
### Material/Metadata Templates
It is possible to import a preset containing both info for materials and metadata before importing 3D models. These `.import.lua` files have the following structure with three optional functions:
```lua
fbxLoader = {
predictMaterial = function(params)
return "::physical_nrml_map_op_uv1_ao_op_mask.mat.lua"
end,
overrideProperties = function(params)
return params.properties
end,
getMetadataMap = function()
return {
availability = {
yearFrom = 1900,
yearTo = 0,
},
}
end,
}
```
The `predictMaterial` function is called for every material and allows to provide a custom function to identify the material type. The `params` struct contains:
* `fbxPath` contains the folder path of the `.fbx`, e.g. `C:/Users/<user>/Desktop/stations-water-models+main_building_small`.
* `fbxName` contains the file name of the `.fbx` without the `lodX` suffix and file ending, e.g. `test_`.
* `fbxMaterialName` contains the name of the material in the `.fbx` for which the function is called during the import, e.g. `/buildings/a/r1/4x4_04/mat/a_rs_r_wd01`.
* `textures` contains a list of texture resources that are assigned to the material in the `.fbx`, e.g. `{"<TF3 install path>/content/buildings/a/r1/shared/a_res_l1/a_rs_r_wd01_alb.dds"}`.
Expected return value is either `nil` or a file path to a material type definition, e.g. `phys_transparent_nrml_map_cblend.mat`.
The `overrideProperties` function is called for every material and allows to override the material properties. The `params` struct contains:
* `fbxPath` contains the folder path of the `.fbx`, e.g. `C:/Users/<user>/Desktop/stations-water-models+main_building_small`.
* `fbxName` contains the file name of the `.fbx` without the `lodX` suffix and file ending, e.g. `test_`.
* `fbxMaterialName` contains the name of the material in the `.fbx` for which the function is called during the import, e.g. `/buildings/a/r1/4x4_04/mat/a_rs_r_wd01`.
* `materialType` contains the material type reference, e.g. `::/rendering/physical.mat`.
* `properties` contains the full table with all material properties, similar to the content of the `.mtl` files.
Expected return value is the altered or unaltered list of properties.
The `getMetadataMap` function has no parameters and returns a table that is used as is for the `metadata` of the models that are imported afterwards.
##### Material Override
This example provides a rather complex override template that alters properties of materials. It is available for [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:pnouam_light_soft_overlay_materials.import.lua "modding:tools:modeleditor:pnouam_light_soft_overlay_materials.import.lua (5.6 KB)").
Explanation
The example contains both a `predictMaterial` and a `overrideProperties` function used to alter material types and properties of building windows when importing them.
The `predictMaterial` function checks if the material name ends with `PNOUA` or `PNOL` and returns a specific material type for these. Other materials are untouched by returning nothing.
The `overrideProperties` function overrides several properties, e.g. `map_id`, `map_ao`, `map_op_1`, `map_op_2` and others. Some of them are only altered if the material name has a certain suffix.
##### Metadata Template
This example provides a template that contains the usual metadata for a bus model. It is available for [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:bus.import.lua "modding:tools:modeleditor:bus.import.lua (3.6 KB)").
### FBX Model Import
To import models, the `.fbx` file format is used. To import a single file, drag and drop it from the file explorer into the Model Editor window. To import multiple files at once, do the same with a folder.
As there are some differences in `.fbx` files exported out of software from different vendors, not all files are imported properly. Currently, `.fbx` files from Modo and Blender are supported. See the information about [external tools](https://wiki.transportfever3.com/doku.php?id=modding:tools:external "modding:tools:external") to find out more about the available modelling software.
#### File & Folder Names
Each `.fbx` file may contain the 3D model of one level of detail. The `.fbx` files for the different levels of detail are distinguished by the `_lodX.fbx` ending, where X=0 is mandatory and higher levels of detail are optional. To import several files together in a model, they should be placed in the same folder. The name of the folder will be used to determine the folder structure, where the imported resources will be placed in the mod:
* `<folder1>-<folder2>-<mdlname>/` Use hyphens to define the folder hierarchy. This will place the model and its resources in `folder1/folder2/mdlname/mdlname.mdl`.
* `<folder1>-<folder2>+<mdlname>/` If the folder is set up like this, the model will end up in `folder1/folder2/mdlname.mdl`. This can be used for the import of multiple models into the same folder, e.g. for multiple units.
#### Nodes / Meshes
The names of nodes are structured as following:
```lua
<elementname>[#<comment>]
```
The `<elementname>` is the name of the element that is used for the mesh by default. The element name may not contain `|`, `%` or `#`! Usually, the element names consist of lowercase words seperated with `_`. It will be suffixed with the current LOD automatically, so there is no need to append a `_lodX` manually.
* By default, the meshes will be imported into a `msh` subfolder next to the `.mdl`.
* To reference existing meshes from other models, the element name uses a path with '/' relative to the content root directory, e.g.: `/vehicle/waggon/bay_05/msh/w1`. If the importer can find the mesh at this specified location, it will only be referenced, otherwise the mesh at that position in the folder hierarchy will be generated.
* To reference meshes from another mod, the path is prefixed with `<modid>::` and the other mod needs to be listed as dependency of the current mod in its `mod.json`.
The following examples will have different results:
* `w1` will create a mesh `msh/w1_lodX.msh` in the current mod relative to the directory of the model.
* `/vehicle/waggon/bay_05/msh/w1` will use or create the mesh `vehicle/waggon/bay_05/msh/w1_lodX.msh` relative to the content root of the current mod.
* `::/vehicle/waggon/bay_05/msh/w1` will use the mesh `vehicle/waggon/bay_05/msh/w1_lodX.msh` in the base game `content`.
* `master_mod::/vehicle/waggon/bay_05/msh/w1` will use the mesh `vehicle/waggon/bay_05/msh/w1_lodX.msh` from the mod with the mod id `master_mod`.
If there is a `#` then the rest of the name behind it is ignored. This can be used to mask out `.001` or similar things automatically created by modifiers or to distinguish between different lod level meshes/nodes with the same name.
```lua
middle_interior
middle_interior#borrowed_from_middle
```
These two will both create a node with mesh `…/middle_interior_lodX.msh` and name `middle_interior`.
In Blender, the name of the empty/object is the one that is used for the node name on import. The name of the mesh assigned to the object is the one used for the `.msh` filename.
#### Materials
The names of materials are structured in a similar pattern as the nodes/meshes:
```lua
<materialname>[|<prefix>][#<comment>]
```
The `<materialname>` works the same as the one for meshes. Referencing existing materials in other mods and both relative and absolute is possible. To reference existing materials, use absolute paths starting with `/` like `/infrastructure/tracks/standard/mat/ballast`. If the importer can find the material at this specified location, it will only be referenced, otherwise the material at that position in the folder hierarchy will be generated with the assigned textures.
The material types that should be used by the importer can be specified by appending `|<shortcode>` which is listed in the tables in the [material type documentation](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl#types "modding:general:resourcetypes:mtl").
#### Textures
The import process recognizes albedo textures if they:
* have the same name as the material + the suffix from below or
* are assigned to the material in the fbx.
All other textures are recognized based on the same file name as the albedo textures together with the appropriate suffixes:
| Map | Supported Suffixes |
| --- | --- |
| map\_albedo | `_alb` or `_albedo` |
| map\_albedo\_opacity | `_albo` or `_albedo_opacity` |
| map\_normal | `_nrm` or `_normal` |
| map\_metal\_gloss\_ao | `_mga` or `_metal_gloss_ao` |
| map\_cblend\_dirt\_rust | `_cdr` or `_cblend_dirt_rust` |
| map\_cblend | `_cblend` |
| map\_ao | `_uv1_ao` |
| map\_id | `_id` or `_emissive_ID_lod1` |
| map\_lgt | `_e` |
| map\_op\_1 | `_op1` |
| map\_op\_2 | `_op2` |
#### Animations
Imported models will generate `.ani` files from the key frames that are defined in animations assigned to the meshes.
In Blender, the animations need to be saved as non linear animations (NLA) to have the correct names. It is important that the strips themselves have the correct name of the animation event. To have multiple strips (one for each animated mesh) for the same animation event, it is possible to use `#` again to clip off the `.001` suffixes.
#### Metadata
Some metadata elements can be (partially) imported with the `.fbx`.
##### Light Sources
[Light](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#lightsourcelist "modding:general:resourcetypes:mdl") objects in `.fbx` files can be imported as long as their type is one of the three:
* `Point`
* `Spot`
* `Directional` which will be used as `Parallel` in Transport Fever 3
The following properties will be imported: `lightId` based on the object name, `type` as written above, `transf`, `color`, `strength`, `angleMin` and `angleMax`. The `childId` is derived from the parent to which the light is assigned as a child node in the fbx.
In Blender, the three light types are called `Point`, `Spot` and `Sun`. The `strength` is taken from `Intensity`/`Power` and multiplied with factor 100. For the `Spot` type, the `angleMin` is calculated based on `Angle` and `Blend`.
##### Seats
A locator with `|seat_<anim>` or `|seat_crew_<anim>` is used to define the position, rotation and scaling of [seat](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics#seats1 "modding:vehicles:basics") positions. `<anim>` needs to be replaced by the name of one of the supported character animations `idle`, `sitting`, `driving_upright`, `driving`. If there are meshes attached to the node, they will be removed and not be present in the imported model.
##### Draft Animals
Similar to the seats, it is possible to set locations for draft animals with `|draft_animal_<mdlreference>[(<colorconfigindex>)]`. The `<mdlreference>` is a fully qualified reference to an existing `.mdl`. Optionally, it is possible to append a specific index for a color config behind the model reference. A valid example would be `something|draft_animal_mymod::/animal/draft_horse/draft_horse.mdl(2)#something`. This would pick the second color config of the `draft_horse.mdl`.
##### Particle Emitters
A locator with `emitter` in the name is considered as [particle emitter](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#particlesystem "modding:general:resourcetypes:mdl"). The following properties are imported: `particleId` as the complete name before the `#` comment suffix, `position` and `velocity` based on the scale along x axis. If the name contains `smoke_bright` or `smoke_dark` the properties `colorOverLifetime`, `frequency`, `lifeTime` and `sizeOverLifeTime` are set with fitting standard values.
##### Bounding Box, Extent & Collider
A mesh named with `|bounding_box` will be used to define the size of the [bounding box](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#bounding_box "modding:general:resourcetypes:mdl") for the model. The mesh itself will be removed on import, it’s bounding box will be used for the whole model though.
The same method can be used to set the [extent](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#extent "modding:general:resourcetypes:mdl") with `|extent` and a box [collider](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#collider "modding:general:resourcetypes:mdl") with `|collider_list`.
##### Labels
A [label](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl#label_list "modding:general:resourcetypes:mdl") is defined by two locators with `|label` of which one is the child of the other. The parent defines the bottom left corner, the offset to the nested child at the top right corner defines the size of the label. They
1. need to be rotated in the same way.
2. need to lay in the same plane along their x/y axis.
3. need to have the z axis to look forward.
### Bulk Import
To import many models at once, e.g. for bridges and tunnels, it is possible to drag a `.txt` or `.log` file from the file explorer into the Model Editor. It must have the following content:
```lua
Scene;<scenename>
ModRefName;<modid>
DeleteTargetFolders;<False or True>
Path;<path to folder with fbx>
Path;<another path to folder with fbx>
```
It consists of:
* `Scene`, which is an optional tag to memorize from which source file things were exported.
* `ModRefName`, which contains the mod id of the mod where the models shall be imported to.
* `DeleteTargetFolders`, which is an optional flag that allows overwriting all existing files in the mod.
* `Path`, which contains one folder path with `_lod0.fbx`, `_lod1.fbx`… . It is possible to have any number of such lines in the file.
### FBX Construction Data Import
To import construction data, the `.fbx` file format is used. Drag and drop it from the file explorer into the box of the option window Im-/Export tab.
As there are some differences in `.fbx` files exported out of software from different vendors, not all files are imported properly. Currently, `.fbx` files from Modo and Blender are supported. See the information about [external tools](https://wiki.transportfever3.com/doku.php?id=modding:tools:external "modding:tools:external") to find out more about the available modelling software.
#### Target Construction
The `*.fbx` file needs to have `Empty` nodes on top level that point to the correct `*.script` file. The reference is written in the name, e.g. `/industries/brewery/brewery.script`.
This `*.script` file needs to have the following lines:
```lua
...
--Begin Generated
 
--End Generated
...
```
Anything between these two lines will be replaced on import.
#### Hierarchy
Depending on the used util, different top levels are used, e.g. `base`, `static` and `level1`. They are imported from `Empty` nodes with the name `<name>.id`. Optionally, it is possible to append a comment with `#<comment>` which will be ignored for the import.
All the other elements described below are nested as children of these top level nodes.
#### Collider & Terrain Alignment
The collider of the construction is defined with a `mesh` node with the name `collider.curve[#comment]`.
To define the geometry for the terrain alignment, you can use `mesh` nodes with the names:
* `less.mesh[#comment]` for the mesh that defines the upper limit for the terrain.
* `greater.mesh[#comment]` for the mesh that defines the lower limit for the terrain.
* `equal.mesh` for the mesh that defines the exact terrain alignment.
#### Set Dressing
The position, scale and rotation of models can be defined by adding `mesh` nodes with the name `<mod>::<pathToMdl>/<name>.mdl[#comment]`. The properties of these nodes are taken for the transformation of the models.
There are predefined groups of random assets in the `/scripts/construction/asset_util.tl`. These can be referenced with `<key>.grp`.
To synchronize the randomization of multiple elements, it is possible to set a `seed(<number>`, e.g. `seed(123)|era_a_garden_chair.grp`.
By default, the models will adjust their z coordinate according to the terrain at their position. To enforce the height relative to the construction origin, it is possible to add an `align` tag: `align|<mod>::<pathToMdl>/<name>.mdl[#comment]`.
It is possible to randomly skip set dressing models. This is done by using the keyword `ignore(<propability>)`, e.g. `ignore(50)|::/assets/buildings/ind/boxes/wood_box_long.mdl#` will show the wooden box in 50% of the cases.
If these three properties shall be applied on several models or group instances, it is possible to add an `Empty` node with the property, e.g. `align[#comment]` and nest all affected instances as children.
##### Construction Data Example
An exemplaric `*.fbx` file containing construction data is available for [download](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:modeleditor:construction_data_example.zip "modding:tools:modeleditor:construction_data_example.zip (3.3 MB)").
@@ -0,0 +1,85 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:tools:start (fetched 2026-10-04) -->
Transport Fever 3 Wiki
[Docs](https://wiki.transportfever3.com/)» [Modding Manual](https://wiki.transportfever3.com/doku.php?id=modding:start "modding:start")» [Tools](https://wiki.transportfever3.com/doku.php?id=modding:tools "modding:tools")» modding:tools:start
---
# Permission Denied
Sorry, you don't have enough rights to continue.
[Transport Fever 3 Wiki](https://wiki.transportfever3.com/)
Search
[Game Manual](https://wiki.transportfever3.com/doku.php?id=gamemanual "gamemanual")
●
[Modding Manual](https://wiki.transportfever3.com/doku.php?id=modding "modding")
* [Introduction](https://wiki.transportfever3.com/doku.php?id=modding:introduction "modding:introduction")
* [General](https://wiki.transportfever3.com/doku.php?id=modding:general "modding:general")
+ [Mod Definition](https://wiki.transportfever3.com/doku.php?id=modding:general:moddefinition "modding:general:moddefinition")
+ [Syntax](https://wiki.transportfever3.com/doku.php?id=modding:general:syntax "modding:general:syntax")
+ [Mod Parameters & Scripts](https://wiki.transportfever3.com/doku.php?id=modding:general:modscripts "modding:general:modscripts")
+ [Resource Types & Structure](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes "modding:general:resourcetypes")
- [.mdl](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mdl "modding:general:resourcetypes:mdl")
- [.msh](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:msh "modding:general:resourcetypes:msh")
- [.mtl](https://wiki.transportfever3.com/doku.php?id=modding:general:resourcetypes:mtl "modding:general:resourcetypes:mtl")
+ [Guidelines & Requirements](https://wiki.transportfever3.com/doku.php?id=modding:general:guidelines "modding:general:guidelines")
+ [Best Practices](https://wiki.transportfever3.com/doku.php?id=modding:general:bestpractice "modding:general:bestpractice")
+ [Publish a mod](https://wiki.transportfever3.com/doku.php?id=modding:general:publishing "modding:general:publishing")
* [Tools](https://wiki.transportfever3.com/doku.php?id=modding:tools "modding:tools")
+ [Model Editor](https://wiki.transportfever3.com/doku.php?id=modding:tools:modeleditor "modding:tools:modeleditor")
+ [Terrain Generator Editor](https://wiki.transportfever3.com/doku.php?id=modding:tools:terraingeneratoreditor "modding:tools:terraingeneratoreditor")
+ [External Tools](https://wiki.transportfever3.com/doku.php?id=modding:tools:external "modding:tools:external")
* [Vehicles](https://wiki.transportfever3.com/doku.php?id=modding:vehicles "modding:vehicles")
+ [Vehicle Basics](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:basics "modding:vehicles:basics")
+ [Vehicle Types](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:types "modding:vehicles:types")
+ [Vehicle Advanced Topics](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:advancedtopics "modding:vehicles:advancedtopics")
+ [Repaint Mods](https://wiki.transportfever3.com/doku.php?id=modding:vehicles:repaints "modding:vehicles:repaints")
* [Constructions](https://wiki.transportfever3.com/doku.php?id=modding:constructions "modding:constructions")
+ [Construction Basics](https://wiki.transportfever3.com/doku.php?id=modding:constructions:basics "modding:constructions:basics")
+ [Construction Menu](https://wiki.transportfever3.com/doku.php?id=modding:constructions:menu "modding:constructions:menu")
+ [Construction Types](https://wiki.transportfever3.com/doku.php?id=modding:constructions:types "modding:constructions:types")
+ [Modular Constructions](https://wiki.transportfever3.com/doku.php?id=modding:constructions:modular "modding:constructions:modular")
+ [Construction Templates](https://wiki.transportfever3.com/doku.php?id=modding:constructions:templates "modding:constructions:templates")
+ [Ground Textures](https://wiki.transportfever3.com/doku.php?id=modding:constructions:groundtextures "modding:constructions:groundtextures")
* [Infrastructure](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure "modding:infrastructure")
+ [Tracks and Streets](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:tracksstreets "modding:infrastructure:tracksstreets")
+ [Bridges and Tunnels](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:bridgestunnels "modding:infrastructure:bridgestunnels")
+ [Signals](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:signals "modding:infrastructure:signals")
+ [Railroad Crossings](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:railroadcrossings "modding:infrastructure:railroadcrossings")
+ [Traffic Lights](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:trafficlights "modding:infrastructure:trafficlights")
+ [Edge Addons](https://wiki.transportfever3.com/doku.php?id=modding:infrastructure:edgeaddons "modding:infrastructure:edgeaddons")
* [Environment](https://wiki.transportfever3.com/doku.php?id=modding:environment "modding:environment")
+ [Climate Zones](https://wiki.transportfever3.com/doku.php?id=modding:environment:climates "modding:environment:climates")
+ [Environments](https://wiki.transportfever3.com/doku.php?id=modding:environment:environments "modding:environment:environments")
+ [Terrain Generators](https://wiki.transportfever3.com/doku.php?id=modding:environment:terraingenerators "modding:environment:terraingenerators")
+ [Terrain Materials](https://wiki.transportfever3.com/doku.php?id=modding:environment:terrainmaterials "modding:environment:terrainmaterials")
+ [Animals](https://wiki.transportfever3.com/doku.php?id=modding:environment:animals "modding:environment:animals")
+ [Landscape Assets](https://wiki.transportfever3.com/doku.php?id=modding:environment:landscapeassets "modding:environment:landscapeassets")
* [Misc](https://wiki.transportfever3.com/doku.php?id=modding:misc "modding:misc")
+ [Cargo Types](https://wiki.transportfever3.com/doku.php?id=modding:misc:cargo "modding:misc:cargo")
+ [People](https://wiki.transportfever3.com/doku.php?id=modding:misc:people "modding:misc:people")
+ [Sound Sets](https://wiki.transportfever3.com/doku.php?id=modding:misc:soundsets "modding:misc:soundsets")
+ [Playlists](https://wiki.transportfever3.com/doku.php?id=modding:misc:playlists "modding:misc:playlists")
+ [Names](https://wiki.transportfever3.com/doku.php?id=modding:misc:names "modding:misc:names")
+ [Localizations](https://wiki.transportfever3.com/doku.php?id=modding:misc:localizations "modding:misc:localizations")
* [Scripting](https://wiki.transportfever3.com/doku.php?id=modding:scripting "modding:scripting")
+ [API Reference](https://wiki.transportfever3.com/doku.php?id=modding:scripting:api "modding:scripting:api")
+ [Missions](https://wiki.transportfever3.com/doku.php?id=modding:scripting:missions "modding:scripting:missions")
[Transport Fever 3](https://transportfever3.com) ● [Urban Games](https://urbangames.com) © 2026
@@ -0,0 +1,73 @@
<!-- Source: https://wiki.transportfever3.com/doku.php?id=modding:tools:terraingeneratoreditor (fetched 2026-10-04) -->
### Table of Contents
* [Controls](#controls)
* [Nodes](#nodes)
* [Node Categories](#node_categories)
# Terrain Generator Editor
The Terrain Generator Editor is a node based tool specifically made to edit terrain generators and material distributions. It is embedded in the Map Editor in the game itself after enabling it in the `settings.lua`by setting the `experimentalTerrainGeneratorEditor` property to `true`.
![](https://wiki.transportfever3.com/lib/exe/fetch.php?media=modding:tools:terraingeneratoreditor:terrain_generator_editor.png)
The user interface of the Terrain Generator Editor consists of three sections, a headbar, the main blueprint board and a catalogue with node types 9 at the bottom.
The headbar provides the following options:
1 a toggle to switch between terrain generator and material distribution mode.
2 open dialog to load an existing node tree
3 save dialog to save the node tree
4 close button to reset the terrain generator editor in the empty state
5 undo and redo buttons to revert or redo the last step
6 a cancel button to abort the current operation
7 validation button that triggers a validation based on the selected mode from 1
8 settings dialog to set a custom seed, preview map resolution and a parallelization factor for the preview computation.
## Controls
To control the node graph blueprint board, you can use mouse and keyboard:
* Hold the right mouse button or use W, A, S and D to move the board.
* Use the mouse scroll wheel to zoom in or out.
* Use CTRL+C and CTRL+V to copy and paste nodes.
* Use R to jump back to the initial view.
* Use SHIFT and left mouse to selecte multiple nodes.
## Nodes
![](https://wiki.transportfever3.com/lib/exe/fetch.php?w=300&tok=a92f7f&media=modding:tools:terraingeneratoreditor:node.png)
A node can be added by clicking on one of the buttons in the node catalog 9 at the bottom. The node consists of the following parts:
1 Input connectors that receive various kinds of data from other nodes. The symbols and colors code the different data formats:
* blue is a single number
* red is a pair of numbers, called point
* green is a list of pairs, called point cloud
* yellow is a full map
2 Output connectors provide data which can be forwarded to other nodes. Both input and output connectors have an unlink button to drop all existing connections.
3 Additional Parameters of the node are accessible by clicking the dropdown. For some nodes, as with `From` and `To` in the example on the right, it is possible to define a local parameter instead of connecting something to an input connector.
4 To try the generator up to the point of a certain node, use the Generate Preview button.
5 After calculation, the eye button provides a preview of the test run result.
6 This preview can be exported to .tga files with the export button.
7 Each node can be renamed and recolored.
## Node Categories
The bottom menu provides all defined node types categorized in tabs:
* Special contains the nodes that are usually used at the begin and end of the node trees to manage inputs, parameters and outputs.
* Numbers contains nodes to generate and operate on single decimal number values.
* Points contains nodes to generate and operate on pairs of decimal number values.
* Point Clouds contains nodes to generate and operate on lists of pairs of decimal number values.
* Map Generators contains nodes to generate simple maps from raw data and textures.
* Noise Maps contains nodes to generate various noise patterns as maps.
* Map Operations 1 contains nodes to operate on existing maps.
* Map Operations 2 contains advanced nodes to operate on existing maps.
* Combine Maps contains nodes to combine multiple maps with and without masks.
* Ridges & Rivers contains nodes specifically made for the generation of ridges and rivers.
When hovering over a node type, a brief summary is displayed. When hovering over inputs and outputs, small hints are displayed as well.
+49
View File
@@ -0,0 +1,49 @@
# TpF3 mod tooling and mod structure
Resolves issue #3. Offline copies of sources: `docs/reference/wiki/` (wiki, converted to Markdown) and `docs/reference/script-doc/` (selected script-doc pages; the full site has ~320 pages, we kept `api*`, `app`, and a few `content/` modules). Example mod: `.examples/ug_tf3_example_config_change`.
## Mod folder layout
Source: wiki `modding:general:moddefinition`.
```
<author>_<modname>/ dir name not functional; keep == modId
_metadata/modinfo.json name (<=32 chars), summary (<=100), description, localization, authors, tags, dependencies (mod.io ids), url
_metadata/0.png, N.png 1920x1080 previews; description.html optional
content/ all resources; mirror base-game structure
mod.script.tl optional script functions
mod.json technical definition
strings.json optional translations {lang:{key:text}}
```
Mods are plain directories. Do not edit the install dir. Install by copying into `staging_area/` in the userdata dir (e.g. `...\Steam\userdata\<id>\3493540\local\staging_area\`; userdata folder reachable via Settings > Advanced > Open user data folder). modId precedence if duplicated: Staging Area > Manual install dir > Subscribed.
## mod.json
Keys: `modId` (a-z0-9_, `<author>_<modname>`, no `_3` major suffix as in TpF2), `revision` (int, bump per release), `severityAdd`/`severityRemove` (`None|Warning|Critical`; defaults None/Warning), `visible` (default true), `cosmetic` (true only if no simulation impact; keeps achievements), `dependencies[]` ({mod:{modId,revisionMin,revisionMax}, modInfo:{displayName,url}, loadBefore (default true), optional (default false)}), `incompatibilities[]`, `params[]` (key, name, tooltip, uiType Button|Slider|ComboBox|IconButton|CheckBox, values, numbers, defaultIndex 0-based while Lua/Teal indices are 1-based), and script hooks. Static JSON on purpose so a broken definition cannot prevent game start.
Example mod's mod.json: modId `ug_tf3_config_change`, revision 1, severityAdd None, severityRemove Warning, cosmetic false, `preRunScript.fileName = "ug_tf3_config_change::mod.script@preRunFn"`. modinfo.json there uses tag `script`; wiki sample uses "Script Mod" (see `modding:general:modtags` for the official list).
## Teal vs Lua
Source: wiki `modding:general:syntax`. All scripts are Lua (5.2 manual referenced); scripts "usually use the Teal dialect" (statically typed Lua: `: type` annotations, `record`). Only `local` variables, no globals. Types: any, nil, boolean, integer, number, string. Teal source files are `.tl` (e.g. `mod.script.tl`); wiki examples still also show `.con.lua`/`.script.lua` for constructions, so both coexist. Import: `ug_require "::/scripts/name.lua"`. Resource refs use `<modid>::/path`. Script-doc signatures are written in Teal (`global record Api`, `function(...): T`), so it doubles as the type definition source (BaseConfig, ModelMetadata, etc.).
## Entry points
Source: wiki `modding:general:modscripts`; example `mod.script.tl`.
Referenced from mod.json as `<modid>::<file-no-ext>@<function>`. Base-game functions run first, then mods in activation order.
- `preRunScript` (preRunFn): before resources load. Args `(configDict {{string,string}}, allModParams {string:{string:integer}}, baseConfig BaseConfig)`. Mutate `baseConfig` fields (e.g. example sets `baseConfig.trainAccelerationFactor = 0.5`). Full property list: wiki `modding:scripting:baseconfig`.
- `runScript` (runFn): when mod loads; `(configDict, allModParams)`. Can register file filters / resource modifiers (see `modding:scripting:modifiersfilters`), but postRun preferred.
- `postRunScript` (postRunFn): once after all resources loaded; `(configDict, allModParams)`. Use `api.res.*` (e.g. `modelRep.forEachModelWithMetadata`, `getAsTable`/`setAsTable`/`addAsTable`, `setVisible`). Cannot add new 3D resources, only reference existing `.mdl`.
Own params: `allModParams[getCurrentModId()].<key>`. The example module shape is `local mod = {}; mod.preRunFn = function(...) end; return mod`.
## Logging and debugging
Source: wiki `modding:tools:ingame`, `modding:scripting:api`.
- Enable debug mode: `debugMode = true` in `settings.lua`, or Settings > Advanced. Enables hotkeys, debug views, Debug Tool Window (AltGr+D).
- Ingame console (key below ESC): shows `stdout.txt` output, runs Lua; commands `\clear`, `\stdout <on|off>`, `\history`; CTRL+SPACE autocomplete.
- `debugPrint(<table>)` dumps table structures; wiki recommends testing API calls in the console.
- Model Editor opens a console window with debug output. Script-doc has no dedicated logging module (searched `api*`); `print` use is not documented, so verify empirically.
## Editor tooling
Source: wiki `modding:tools:external`.
- VS Code recommended (Notepad++ alternative, Lua highlighting only). Install Teal (github.com/teal-language/tl) plus extensions `sumneko.lua` (LuaLS) and `pdesaulniers.vscode-teal`. "The Model Editor automatically creates necessary workspace configuration files in the Staging Area" (so editing mod scripts inside the staging area picks up Teal config; the config contents are not documented on the wiki).
- Model Editor (`ModelEditor.bat` in install dir; config under `%APPDATA%/Transport Fever 3/` where `userDataPath` must be set with forward slashes) for models/vehicles. Blender (FBX), GIMP/Paint.NET/Compressonator (DDS) for assets.
- Script reference: https://wiki.transportfever3.com/script-doc/ (root `api` global: res, gui, engine, type, util, cmd, modhub; plus `app`).
## Gaps
Not resolved by sources: exact contents of the auto-generated Teal workspace files, whether `print` reaches stdout.txt, how a `.tl` is compiled (game appears to load Teal directly, undocumented). Wiki pages for baseconfig/modifiersfilters were saved; the 320 script-doc `content/` modules were mostly not.