Modular Tram Station Framework for Transport Fever 2 THIS PROJECT AND THIS DOCUMENTATION ARE WIP
Themes are sets of modules of parts of a station which the user can select. Themes can be added by mods.
The definition of a theme is taking place in the modutram table in the metadata. Each module MUST have too values: theme: name of the theme themeType: type of the thing which this module represents
example:
metadata = {
modutram = {
theme = "hamburg",
themeType = "bench"
}
}
You can use a module for multiple theme or theme types.
If you want to use a module for multiple themes use the themes
key instead of theme
and use an array with themes as value
If you want to use a module for multiple themeTypes use the themeTypes
key instead of themeType
and use an array with theme types as value
These list contains all theme types which are used in the base modular tram station mod. Ohter mods may have different theme types.
Theme Type | Module type | Description |
---|---|---|
bench | Asset | A single bench |
benches | Asset | More than one bench |
fence | Asset | Fence at the single platforms |
lighting | Asset | lamps on platforms |
billboard_small | Asset | small billboards on platforms |
station_name_sign | Asset | station name sign |
shelter_small | Asset | small shelter |
shelter_medium | Asset | medium size shelter |
shelter_large | Asset | large shelter |
destination_display | Asset | destination display |
bus_station_sign | Asset | bus stop signs at the platforms ends |
tram_bidirectional_left | Grid Module | bidirectional tram lane (left hand traffic) |
tram_bidirectional_right | Grid Module | bidirectional tram lane (right hand traffic) |
tram_up | Grid Module | oneway tram lane (to the top) |
tram_down | Grid Module | oneway tram lane (to the bottom) |
platform_island_ramp | Grid Module | island platform |
platform_left_ramp | Grid Module | ramp for left platform |
platform_right_ramp | Grid Module | ramp for right platform |
platform_right | Grid Module | platform with tram stop on the right side |
platform_island | Grid Module | island platform |
platform_left | Grid Module | platform with tram stop on the left side |
Additional theme options are optional and only needs to be set in one module of the theme
When a module in the construction is missing in a theme the module will used from the default theme. You can define another theme where missing modules should be used.
example:
metadata = {
modutram = {
theme = "hamburg_1950",
themeType = "bench",
extends = "hamburg"
}
}
Excludes contains a list of theme types which should not be placed. The value is an array of themeTypes. This array MUST NOT contain grid module theme types.
example:
metadata = {
modutram = {
theme = "hamburg_1850",
themeType = "bench",
themeExtends = "hamburg",
themeExcludes = { "destination_display" }
}
}
The theme name will be displayed in the theme selection by default. To use another name (for translation) you can change it. The themeTranslations are a key value pairs with the theme name as key and the translation name as value
metadata = {
modutram = {
themes = { "hamburg_1850", "hamburg_1900" },
themeType = "bench",
themeExtends = "hamburg",
themeExcludes = { "destination_display" },
themeTranslations = {
hamburg_1850 = _("Hamburg at 1850"),
hamburg_1900 = _("Hamburg at 1900")
}
}
}
There are two main module types.
Grid modules are mainly track and platform modules.
Asset modules are modules you are placing on other modules.
A module will be defined by it's type. The type of every module must start with modutram
. If your module is for a station with specific namespace you have to append the namespace. E.g. when the station has the namespace berlin
your module type must start with modutram_berlin
.
After the modutram and namespace prefix the type must include the slot type. See at the table below which slot type you need for your module.
Example modutram_asset
or modutram_berlin_asset
for a specific namespace.
Grid module must have the module width in cm at the and of the type string. The width postfix must have the format 280cm
.
The whole type looks like this: modutram_berlin_tram_up_280cm
Asset and decoration modules may have a postfix when you want to specify special asset or decoration slots at your modules.
For example you can define a type modutram_asset_bench
if you have a specific slot for benches.
Module Type | Slot type | constant | id | description |
---|---|---|---|---|
Grid Module | tram_bidirectional_left | t.TRAM_BIDIRECTIONAL_LEFT | 8 | Tram Tracks in both directions (left hand traffic) |
Grid Module | tram_bidirectional_right | t.TRAM_BIDIRECTIONAL_RIGHT | 9 | Tram Tracks in both directions (right hand traffic) |
Grid Module | tram_up | t.TRAM_UP | 10 | One way tram track up |
Grid Module | tram_down | t.TRAM_DOWN | 11 | One way tram track down |
Grid Module | bus_bidirectional_left | t.BUS_BIDIRECTIONAL_LEFT | 16 | Bus Tracks in both directions (left hand traffic) |
Grid Module | bus_bidirectional_right | t.BUS_BIDIRECTIONAL_LEFT | 16 | Bus Tracks in both directions (left hand traffic) |
Grid Module | bus_up | t.BUS_UP | 18 | One way bus track up |
Grid Module | bus_down | t.BUS_DOWN | 19 | One way bus track down |
Grid Module | train | t.TRAIN | 24 | Train track |
Grid Module | platform_none | t.PLATFORM_NONE | 32 | Platform without waiting lanes (passengerEdge) |
Grid Module | platform_island | t.PLATFORM_ISLAND | 33 | Platform with stop at both sides |
Grid Module | platform_left | t.PLATFORM_LEFT | 34 | Platform with stop at the left side |
Grid Module | platform_right | t.PLATFORM_RIGHT | 35 | Platform with stop at the right side |
Asset Module | asset | t.ASSET | 40 | Asset Module |
Asset Module | decoration | t.DECORATION | 48 | Decoration Module ("Asset" placed on asset module) |
You have access to modutram from your module by calling
updateFn = function(result, transform, tag, slotId)
local modutram = result.modutram
end
in your module updateFn().
You have access to the current module in your module file by calling the getModule()
with the given slotId
as param
updateFn = function(result, transform, tag, slotId)
local module = result.modutram:getModule(slotId)
end
You will get access to the neighbor modules by using following methods
updateFn = function(result, transform, tag, slotId)
local module = result.modutram:getModule(slotId)
local leftNeightbor = module:getNeighborLeft() -- left from current module
local rightNeighbor = module:getNeighborRight() -- right from current module
local neighborTop = module:getNeighborTop() -- above current module
local neighborBottom = module:getNeighborBottom() -- below current module
-- checking whether there is a neighbor
module:hasNeighborLeft() -- false in this case
module:hasNeighborRight() -- false in this case
module:hasNeighborTop() -- false in this case
module:hasNeighborBottom() -- false in this case
end
You will get access to any module by grid coordinates
-- getting the module which is three steps left from current module
updateFn = function(result, transform, tag, slotId)
local module = result.modutram:getModule(slotId)
local otherModule = result.modutram:getModuleAt(module:getGridX() - 3, module:getGridY())
-- checking for a module at grid coordinate
result.modutram:isModuleAt(module:getGridX() - 3, module:getGridY()) -- false in this case
end
If you will access to a module which not exists via neighbor function or getModuleAt()
you will get a void module which is like a dummy.
You can use the neighbor function like a normal module
To check whether there is a void module you can call module:isBlank()
Access the slot id of the module
Access the module type. The module type is represented by an integer value. It is not possible to define own types.
You can import a table with all type with local t = require("modutram.types")
. All types are referenced at the List of slot types.
Access the X-Cooridinate (Horizontal) of the grid (not the real position)
Access the Y-Cooridinate (Vertical) of the grid (not the real position)
Access to the x position of the grid module
Access to the y position of the grid module
Access to the z position of the grid module
ATTENTION: Don't add tracks or street in the handleTerminals()
or handleLanes()
function.
To add a street to you module, you can use the funtion modutram:addStreet(streetType, tram, edges, relativeSnapNodes)
Params:
streetType: street lua file (e.g. 'country_new_small.lua'),
tram: 'YES', 'NO', or 'ELECTRIC' - Tram rails on the street
edges: street edges (see the official modding documentation for more details)
relativeSnapNodes (snap node relative to your own nodes - snap node of 0 means the first node of your edges)
To add tracks to your module, you can use the function modutram:addTrack(trackType, catenary, edges, relativeSnapNodes)
Params:
trackType: rail track lua file (e.g. 'high_speed.lua'),
catenary: true or false - tracks has catenary
edges: street edges (see the official modding documentation for more details)
relativeSnapNodes (snap node relative to your own nodes - snap node of 0 means the first node of your edges)
Lanes are models with pathes for road vehicles (including tram), passengers and cargo. How lane models structured and works you can see in the official transport fever 2 modding documentation. I recommmend to separate lane models from "real" models because lanes are place conditionalle how modules are placed.
Terminals are lane models which has a vehicle node (node where a vehicle stops), passenger edge (edge where passenger waits), passenger node (node where the passengers exits a vehicle) or combined.
Modutram offers two function to handle lanes and terminals
The handleTerminals()
function will be called when you module is part of a terminal group. A terminal group include the module where a vehicle stops and all modules where passengers leaves the vehicle or are waiting for it.
The handleTerminals()
function has one parameter terminalGroup
. Terminal Models MUST to be added via the terminalGroup
model. Otherwise there won't be bind each other to a valid terminal group.
The handleTerminals()
may be called twice (for each side). The game will crash when two same lane models are place. So be aware of it.
You can check if terminals already placed eather with hasTerminals()
or with hasTerminalsLeft
and hasTerminalsRight
The terminalGroup
has three properties:
- trackDirection ('left' or 'right'): relevant for platforms - Direction of the current handled tracks - the
handleTerminals()
my be called twice for each trackDirection on islandPlatforms. - platformDirection ('left' or 'right'): relevant for tracks - Direction of the current handled platform - the
handleTerminals()
my be called twice for each track on bidirectional track modules. - vehicleStopAlignment ('top' or 'middle'): relevant for tracks - Position of the stop node relative to the track module to create a stop node at the center of the whole terminal group.
To add a passenger or a cargo terminal you have to call terminalGroup:addTerminalModel(modelId, modelTransformation, modelTerminalId)
on a platform module
params:
modelId: Id (filename of the mdl file) of the terminal model
transformation: tranformation matrix (pivot center of the station) of the terminal model
modelTerminalId (optional, default = 0): Id of the terminal defined in the terminal model
Return: position in the result.models (starting from 0)
if you makes a cargo module you have to add load = "cargo"
to the module modutram metadata.
Example
module:handleTerminals(function (terminalGroup)
terminalGroup:addTerminalModel('station/tram/modular_tram_station/path/passenger_terminal.mdl', transform)
end)
You can use an existing model as terminal using terminalGroup:attachTerminal(modelPosition, modelTerminalId)
params:
modelPosition: position of the model in the result.models (starting from 0)
modelTerminalId (optional, default = 0): Id of the terminal defined in the terminal model
To add a vehicle terminal you have to call terminalGroup:addVehicleTerminalModel(modelId, modelTransformation, modelTerminalId)
on a track module.
The pareters are the same as passenger/cargo modules
Example:
module:handleTerminals(function (terminalGroup)
if terminalGroup.vehicleStopAlignment == 'top' then
terminalGroup:addVehicleTerminalModel("station/tram/modular_tram_station/path/tram_stop_top.mdl", transform)
else
terminalGroup:addVehicleTerminalModel("station/tram/modular_tram_station/path/tram_stop_middle.mdl", transform)
end
end)
You can use an existing model as vehicle terminal using terminalGroup:attachVehicleTerminal(modelPosition, modelTerminalId)
The pareters are the same as passenger/cargo modules
To add a vehicle and passenger/cargo terminal you can call terminalGroup:addVehicleAndPassengerTerminalModel(modelId, modelTransformation, modelTerminalId)
(Parameters like the other above)
ATTENTION don't create tracks in the handleTerminals() function. It may crash the game
the addTrack()
function returns an index. This index is the first node of the passed in edges.
You can use this index to add a vehicleNode on this track.
To add a vehicle node use terminalGroup:attachVehicleTrackNode(edgeListType, catenaryOrTram, nodeIndex)
Params
- edgeListType: lua file of track or street (e.g. high_speed.lua)
- catenaryOrTram: true or false if track; 'NO', 'YES', 'ELECTRIC' if street
- nodeIndex: node index (returned by the
addTrack()
/addStreet()
function)
If you want to add the first node of your edges add the node position to the index.
Example:
local nodeIndex = modutram:addTrack("high_speed.lua", true, {
{{-10, 0, 0}, {10, 0, 0,}},
{{0, 0, 0}, {10, 0, 0,}},
{{0, 0, 0}, {10, 0, 0,}}, -- node to attach
{{10, 0, 0}, {10, 0, 0,}},
})
module:handleTerminals(function (terminalGroup)
terminalGroup:attachVehicleTrackNode("high_speed.lua", true, nodeIndex + 2)
end)
You can disable terminal handling for you module a track or platform by adding 'hasTerminals = false' to the modutram metradata.
metadata = {
modutram = {
hasTerminals = false
}
},
The handleLanes()
function will be called in all grid modules. These models will be added at the normal way by useing the addModelFn().
Assets are modules which are placable on grid modules. Assets are e.g. buildings, street connections, roofes or decoration stuff.
To add an asset slot to a grid module you have to call the function gridModule:addAssetSlot(result, assetId, slotType, transformation, spacing, shape)
Params:
result: the result param from the updateFn()
assetId: The asset id (Choosen number between 1 and 255). Each asset slot at the grid element should have different asset ids. Look at the reference for common asset slot ids (@todo).
slotType: Slot type of the asset slot ('modutram_asset' is the general asset slot type)
transformation: slot transformation
spacings: slot spacings (see official modding documentation)
shape: slot shape (see official modding documentation)
You can access the grid module where the asset is bind by calling the function asset:getParentGridModule()
You gets the asset id by calling the function asset:getAssetId()
Decorations are modules you can fix on assets. For example a clock on the station building.
You may define decoration slots on assets. To add an decoration slot to a asset you have to call the function asset:addDecorationSlot(result, decorationId, slotType, transformation, spacing, shape)
decorationId
is like the asset id on asset slots. All other ids are the same like the addAssetSlot()
function
You can access the asset by calling getParentAsset()
. You has access to the grid module by calling getParentGridModule()
Utils are tiny various functions which make module building easier.
local makeGroundFaceFromGridModule = require("modutram.utils.makeGroundFaceFromGridModule")
Makes a ground face (necessary for groundFaces and terrainAlignment). The size of the ground face is the size of grid module.
Params:
gridModule
: Module which position and sizes are should be used for
transform
: Module pivot point transformation
Return: the ground face (as table)
local module = result.modutram:getModule(slotId)
local groundFace = makeGroundFaceFromGridModule(module, transform)
local makeLot = require("modutram.utils.makeLot")
Makes ground faces and terraign alignment and adds it to the result table
Params:
result
: Result table to add
groundFace
: Ground face to create the lot
fillTexture
(optional): Ground Texture for the plain
strokeTexture
(optional): Ground Texture for the stroke
local module = result.modutram:getModule(slotId)
local groundFace = makeGroundFaceFromGridModule(module, transform)
makeLot(result, groundFace, "shared/asphalt_01.gtex.lua", "street_border.lua")
If your modules doesn't to the standard station, you can define a station with our own namespace and change the properties that you need to.
Your module types must start with the prefix modutra_your_namespace
.
In following examples I use the namespace my_tram_station
The updateFn() of your construction has to be empty, because it will be overriden by postRunFn().
-- ...
updateFn = function() {
}
-- ...
To create the nessecarry modul list you must add this code to your mod.lua.
local modulesForMyTramStation = {}
local modulesInGame = api.res.moduleRep.getAll()
for _, moduleFileName in ipairs(modulesInGame) do
local module = api.res.moduleRep.get(api.res.moduleRep.find(moduleFileName))
if ModuleRepository.isModutramItem(module, 'my_tram_station') then
table.insert(modulesForMyTramStation, ModuleRepository.convertModule(module, 'my_tram_station'))
end
end
local motrasStation = api.res.constructionRep.get(api.res.constructionRep.find('station/tram/my_tram_station.con'))
motrasStation.updateScript.fileName = "construction/station/tram/my_tram_station.updateFn"
motrasStation.updateScript.params = {modules = modulesForMyTramStation}
Create a file construction/station/tram/my_tram_station.script
and put folliwing code in it
local Modutram = require('modutram.modutram')
function data()
return {
updateFn = function (params, paramsFromModLua)
local config = {
gridModuleLength = 18, -- length (size on y axis) of a module
baseHeight = 0, -- offset of the ground level compared to the ground level of the game
defaultAssetSlotSpacing = {1, 1, 1, 1},
-- @todo put config example here
}
local result = { }
Modutram.initialize(params, paramsFromModLua, config):bindToResult(result)
return result
end
}
end