User Tools

Site Tools

Urban Games

Action disabled: revisions
modding:constructionbasics

Construction Basics

The concept of constructions, introduced with Transport Fever, is very powerful. It allows to configure and create a lot of different building types and assets in just one format. This includes stations, depots, industries, town buildings and different kinds of assets. They are stored in .con files in the folder res/construction/.

Overview

The format mainly consists of meta information and an update function. The construction is configured and created with the function. It can return different configurations of the construction depending on the input parameters.

function data()
 
return {
  type = "INDUSTRY",
 
  -- other meta information
 
  params = { },  -- parameters changeable by the player
 
  -- update function, which adjusts the construction according the parameters
  updateFn = function(params)
    local result = { }
 
    result.models = { {
      id = "industry/chemical_plant/building_small.mdl",
      transf = transf.transl(vec3.new(20, -10 , 0))     
    } }
 
    -- terrain alignment, streets, tracks, stock lists, etc.
 
    return result
  end
}
end

Metadata

There are several general properties available for use with constructions:

  ...
  type = "ASSET_DEFAULT",
  description = {
    name = _("Bench"),			  
    description = _("A bench.")
  },
  availability = {
    yearFrom = 1925,
    yearTo = 1925
  },
  ...

The type property is an identifier for the type of construction. See the construction types documentation for further details on their specific specialities and individual properties.

The description block contains a desc property for the description displayed in the menu. The name is set in the name property. Both can be translated in a strings.lua file.

The availability is set by yearFrom for the year from when the construction should be available. Unset or values below 1851 mean from start. The end limit is set by yearTo. It is the year until when the construction should be available. Unset or value 0 means unlimited availability, values below 1850 result in a never available construction.

Parameters

Parameters can be used to let the user set some preferences and influence the outcome of the construction script. They are defined in the params block outside of the updateFn function:

...
  params = {
    {
      key = "postbox_type",
      name = _("Postbox"),
      uiType = "BUTTON",
      values = { _("Germany"), _("Switzerland"), _("Austria"), },
      tooltip = _("Choose the type of postbox you'd like to place."),
      defaultIndex = 0,
    },
    ...
  },
...

Each parameter block has several parameters:

Property Value Default Description
key String mandatory Unique identifier for the parameter
name String “” (empty) Display name for the parameter menu
values {String,String,…} mandatory Display values for the parameter menu, see below
defaultIndex Integer 0 Pre-selected option on first call
uiType “BUTTON” or “SLIDER” or “COMBOBOX”
or “ICON_BUTTON” or “CHECKBOX”
“BUTTON” Type of parameter, see below
yearFrom Integer 0 Parameter is displayed from
yearTo Integer infinite Parameter is displayed until
tooltip String “” (empty) Tooltip that is displayed above the parameter name when hovering

It is recommended to choose key names that are totally unique, not only on mod level. Otherwise it could happen, that a preselected parameter value from another third-party construction is used once your construction is loaded. This could result in unintended behavior, if you do not check for the parameter range.

In the updateFn function of the construction, the selected options can be called up with params.<key>. Numbers starting from 0 are displayed. The first entry of a dropdown, for example, returns a 0. For a checkbox, 0 corresponds to the non-activated state, 1 to the activated state.

The individual characteristics of the different types are shown below.

Button

Simple text buttons are already known from Transport Fever. They are lined up in the menu as a centered list. The labels of the buttons are passed as a string list in the values parameter, they can also be translated in strings.lua.

{
  key = "postbox_type",
  name = _("Postbox"),
  uiType = "BUTTON",
  values = { _("Germany"), _("Switzerland"), _("Austria"), },
  tooltip = _("Choose the type of postbox you'd like to place."),
},

Slider

Especially for many linear values, such as numerical series or size increments, sliders can be used as easy-to-use controllers. The name of the currently selected position is displayed to the right of the slider. The display names of the buttons are passed as a string list in the values parameter, these can also be translated in the strings.lua.

{
  key = "parcelstation_length",
  name = _("Parcel Station Length"),
  uiType = "SLIDER",
  values = { _("5m"), _("8m"), _("10m"), _("12m"), _("15m"), _("20m"), },
  tooltip = _("Choose the length of the parcel station."),
},

Combo Box

If a large number of text options should be available, a simple list of buttons is not sufficient. Then a combo box, also known as a drop-down menu, can be implemented to offer a compact number of options. The entries of the list are passed as a string list in the values parameter, they can also be translated in strings.lua.

{
  key = "post_assets",
  name = _("Post Assets"),
  uiType = "COMBOBOX",
  values = { _("Few Parcels"), _("Many Parcels"), _("Oversized Parcels"), _("Letterboxes"), _("Empty"), },
  tooltip = _("Choose the decoration that should lay around."),
},

Icon Button

Unlike the text buttons, the icon buttons display small images in TGA format. Unlike the text buttons, the list is currently left-aligned. If the list of buttons becomes wider than the parameter menu, it will wrap to another line. It is therefore advisable that the images for the buttons all have the same height. Also, the border of the buttons should be transparent so that you can see which option is currently selected. The file paths to the images are passed as a string list in the values parameter, the paths are relative to res/textures/. The tooltip is used for hovering over the parameter name. It is currently not possible to add tooltips for the individual icons.

{
  key = "post_horns",
  name = _("Post Horns"),
  uiType = "ICON_BUTTON",
  values = { "ui/parameters/post_de.tga", "ui/parameters/post_ch.tga", "ui/parameters/post_at.tga", },
  tooltip = _("Choose the post horn that should be displayed."),
},

Checkbox

If only one value is assigned with yes or no, a checkbox is also suitable as parameter type. Only the name is displayed, but the values parameter must still be defined with two values, but it's values are never shown.

{
  key = "post_box_open",
  name = _("post box is open"),
  uiType = "CHECKBOX",
  values = {"0", "1"},
  tooltip = _("Choose if the post box is shown in opened state."),
},

UpdateFn

In this function, the construction is configured and specified. You can use the content of the table params to configure the construction. It also contains the selection of the user parameters you have specified in the params list above. See the list of construction types to find out, which additional properties are available in the params table depending on the construction type.

The updateFn returns a data struct with several mandatory and many optional properties. They are described below. Please note, that the return data struct is referred as result.

Models

It is possible and very common to add models to a construction. Almost every model can be added by adding it to the list of models in the result data struct.

result.models = { {
  id = "industry/chemical_plant/building_small.mdl",
  transf = transf.transl(vec3.new(20, -10 , 0))
} }
 
-- alternative with dynamic index depending on the already existing list
result.models = { }
...
result.models[#result.models + 1] = {
  id = "industry/chemical_plant/building_small.mdl",
  transf = transf.transl(vec3.new(20, -10 , 0)),
  tag = "office",
}

Each entry contains some properties:

  • id is the path to the .mdl file relative to res/models/model/.
  • transf is the transformation matrix relative to the point where the player clicked. It may either be provided as a 16 value list or by use of one of the functions from transf.lua that can be found in res/scripts/.
  • tag is an optional property to label the model with a certain string. It can be used as reference in code elsewhere.

There is no limitation in the number of models a construction can contain and the order they were added does not matter.

There are two additional properties params.paramX and params.paramY that are controlled by keyboard keys. Per default the keys are O/P and Ü/+ or [/] (depending on keyboard layout). They each have steps of 2π/32 so that 32 steps can be mapped to a full rotation around an axis.

To support rotation around all three axis, encapsulate the transformation matrix of the model with the rotateTransf function transf = constructionutil.rotateTransf(params, { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }) and make sure to import the constructionutil with local constructionutil = require “constructionutil” at the begin of the file (before function data()).

Edge Lists

Edge lists are used to add streets and tracks to a construction. The following code adds a street segment and a track segment to the construction:

result.edgeLists = {
  -- specify a street segment
  {
    type = "STREET",                             
    params = {
      type = "standard/country_new_small.lua",    
      tramTrackType = "YES"                      
    },
    edgeType = "BRIDGE",                          -- optional
    edgeTypeName = "cement.lua",                  -- optional
    edges = {
      -- one entry refers to a position and a tangent
      { { .0, -79.0,  .0 },  { .0, 15.0, .0 } },  -- node 0 (snap node)
      { { .0, -64.0,  .0 },  { .0, 15.0, .0 } }   -- node 1
    },
    snapNodes = { 0 } , -- node 0 is allowed to snap to other edges of the same type
    freeNodes = {},
    tag2Nodes = {},
  },
 
  -- specify a track segment
  {
    type = "TRACK",
    params = {
      type = "standard.lua",                    
      catenary = true
    },
    edgeType = "BRIDGE",                          -- optional
    edgeTypeName = "cement.lua",                  -- optional
    edges = {
      { { -19.1, .0, .0 }, { -20.0, .0, .0 } },  -- node 0 
      { { -39.1, .0, .0 }, { -20.0, .0, .0 } },  -- node 1 (snap node)
    },
    snapNodes = { 1 },  -- node 1 is allowed to snap to other edges of the same type
    freeNodes = {},
  }
}

Each block in the edgeList has several properties:

  • type selects if the edges should be “STREET” or “TRACK” edges.
  • params is a block with several properties depending on the type selected above.
  • edgeType decides if the segment is built as a “BRIDGE”, “TUNNEL” or normal (unset).
  • edgeTypeName selects the type of bridge or tunnel to be used. It is relative to res/config/bridge/ or res/config/tunnel/.
  • edges is a list. Every two entries form an edge. The first entry is the start node of the edge, the second entry is the end node of the edge. Every node has two vectors with three values:
    • the first vector is the point relative to the construction origin with values for x, y and z axis.
    • the second vector is the tangent in this point with values for x, y, and z axis.
  • snapNodes is a list of node indizes from the edges list. These nodes can be used to snap streets or tracks while manually building later.
  • freeNodes is a list of nodes, that are not part of the construction. Edges with two free nodes can be deleted or modified independently.

The parameters for streets in the params block are:

  • type is a reference to a tracktype relative to res/config/track/.
  • hasBus is a boolean value. If set to false, no bus lane will be built. If set to true, a bus lane will be built.
  • tramTrackType is a string value. If set to “NO”, no tramtracks will be built. If set to “YES”, tram tracks will be built. If set to “ELECTRIC”, tram tracks with catenary will be built.

The parameters for tracks in the params block are:

  • type is a reference to a tracktype relative to res/config/track/.
  • catenary is a boolean value. If set to true, the track will be electrified.

Edge Objects

Some game objects can be attached to edges, for example signals and waypoints. This is not only possible by the player, but also possible in constructions for edges contained by the constructions, as shown in the following code sample:

-- we need some edges to attach edge objects to them
taxiway = { }
 
-- edge 0
taxiway[#taxiway + 1] = { { 180.0,  20.0,  .0 },  {    .0, -20.0, .0 } } 
taxiway[#taxiway + 1] = { { 180.0,   0.0,  .0 },  {    .0, -20.0, .0 } }
 
-- edge 1
taxiway[#taxiway + 1] = { { 180.0,   0.0,  .0 },  {    .0, -47.1, .0 } }
taxiway[#taxiway + 1] = { { 150.0, -30.0,  .0 },  { -47.1,   0.0, .0 } }
 
-- add taxi way to edge lists...
 
-- attach a signal to one of the above defined edges
result.edgeObjects = {
  {
    edge = 1,                                              -- attach object to edge 1
    param = .5,                                            -- param along the edge
    left = false,
    model = "station/airport/asset/signal_runway_old.mdl"  -- see res/models/model/
  }
}

The edgeObjects list contains all object attachments with properties:

  • edge is the number of the edge. Edge 0 is between node 0 and 1, edge 1 is between node 2 and 3, …
  • param is the offset along the edge from the start node in meter.
  • left is set to false, if the object should be placed on the right side of the edge. true leads to objects on the left side.
  • model is a reference to a .mdl file relative to res/models/model.

Please be aware that it is not possible to add more than one object per edge by script.

Terrain Alignment

The adjustment of terrain height to the construction is done with the terrain alignment. If the required alignment can't be done due to other nearby constructions or infrastructure, the construction can't be built.

result.terrainAlignmentLists = {
  {
    type = "EQUAL",               -- accepted values: "EQUAL", "LESS" and "GREATER"
    faces = { { { -10, -10, 0 }, { 10, -10, 0 }, { 10, 10, 0 } } }, -- a list of polygons
    slopeLow = 0.3,
    slopeHigh = 0.6,
    triangles = { { -10, -10, 0 }, { 10, -10, 0 }, { 10, 10, 0 }, }
    optional = false,
  },
  ...
}

The terrainAlignmentLists list contains 0 or more blocks with several properties:

  • type is the policy that should be used for terrain alignment. With “EQUAL” the terrain is aligned exactly to the specified faces, with “LESS” only higher areas are taken down, with “GREATER” areas below the faces will be filled up.
  • faces is a list of polygons. Each polygon consists of at least three points with three coordinates relative to the construction origin each.
  • slopeLow is the gradient for the slope on the outside of the construction that is needed to make the transition to the modified terrain when the surrounding terrain is lower or higher than the construction area.
  • slopeHigh is the steeper gradient for the slope if the height difference is too big and the slope would get too wide or when another object constraints the sloping.
  • triangles is a list of points. The number of points needs to be a multiple of 3. Every three points are considered as the corners of triangles. This property is an alternative to the faces property.
  • optional should be set to true if the alignment should not throw collision errors when competing against other terrain alignments in the same construction.

Ground Faces

Ground faces are used to paint the terrain below the construction to blend in with the construction.

result.groundFaces = {
  {  
    face = { { -10, -10, 0 }, { 10, -10, 0 }, { 10, 10, 0 } },
    modes = {
      {
        type = "FILL",               
        key = "industry_floor.lua"
      }
    },
    loop = true,
    alignmentOffsetMode = "OBJECT",
    alignmentDirMode = "OBJECT",
    alignmentOffset = { -2.0, -1.0 },
 
  },
  ...
}

The groundFaces list contains 0 or more blocks with the following attributes:

  • face is the list of coordinates that span the surface which should be painted. Each point has a value relative to the construction origin for x,y and z axis, but the z axis is ignored. At least three points are required.
  • modes is a list of paint modes that should be applied to the face:
    • type is the actual painting mode. “FILL” fills the whole face, “STROKE” paints a line with soft sides along the face edge, “STROKE_INNER” paints a line with a soft side to the inside of the face and “STROKE_OUTER” paints a line with a soft side to the outside of the face.
    • key is the reference to the ground texture that should be used relative to res/config/ground_texture/.
  • loop is a boolean value used to describe if the face is closed. If set to false, the edge between last and first coordinate will not get a stroke paint.
  • alignmentOffsetMode specifies whether the offset is applied relative to the construction orientation (“OBJECT”) or the game world orientation (“WORLD”).
  • alignmentOffset contains the offset values in x and y direction.
  • alignmentDirMode specifies if the uv direction setting is applied relative to the construction orientation (“OBJECT”) or the game world orientation (“WORLD”).
  • alignmentDir contains the direction rotation values in x and y axis. They are computed as tan2(y,x) to calculate the actual rotation.

Collider

Colliders are used to define the areas where no other object can be built. This is useful e.g. to reserve space for industry expansions with later level upgrades.

  result.colliders = {
    params = {
      halfExtents = { 1.5, 1.5, 1.5, },
    },
    transf = { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, },
    type = "BOX",
  },
  {
    colliderutil.createBox({ 0, 0, 0 }, { 1.5, 1.5, 1.5 })
  }

The colliders list contains 0 or more blocks with the following properties:

  • params is a list of parameters. These depend on the type used.
    • For “BOX” the required parameter is provided in halfExtents. That is a 3 value vector with the half box lengths for all three axis.
    • For “CYLINDER” it is provided in halfExtents too. The 3 values represent the half sizes for all three axis.
    • For “POINT_CLOUD”, a list of points is provided in the points parameter. Each point has three values for the position on all three axis relative to the construction origin. The transf parameter is ignored.
  • transf is transformation matrix that point on the center of the collider. It is relative to the construction origin.
  • type is the type of collider. It can be either “BOX”, “CYLINDER” or “POINT_CLOUD” for constructions.

The colliderutil.lua in res/scripts/ provides some useful functions to prepare the collider definition based on some parameters.

Costs

There are three properties that can be used to set the costs of the construction:

  • cost is paid when the construction is built.
  • bulldozeCost is paid when the construction is demolished.
  • maintenanceCost is paid monthly to maintain the infrastructure.

Label List

For models with “CUSTOM” typed labels, the content can be provided here:

  result.labelText = {
    [12] = { "Label", "SomeText"}
  }

The labelText list contains a mapping of keys to value lists. The key number equals the id of the model in result.models that should be provided with some custom label texts. The list contains the strings. The first string is for the first label of the model, the second for the second label of this model and so on.

To improve performance, for simple constructions that do not contain ground faces, terrain alignments, edges or depot/station/industry/town building definitions, there is no construction placed in the game but just the seperate mdls. These can not be configured afterwards or be deleted all at once. Furthermore these types of assets do not support the labelText custom texts from constructions.


modding/constructionbasics.txt · Last modified: 2021/07/04 01:11 by yoshi