manual:subwaysim:vehicle_development:vehicle_lua
Differences
This shows you the differences between two versions of the page.
| Next revision | Previous revision | ||
| manual:subwaysim:vehicle_development:vehicle_lua [2026/01/12 19:13] – created dcs | manual:subwaysim:vehicle_development:vehicle_lua [2026/01/13 19:57] (current) – dcs | ||
|---|---|---|---|
| Line 32: | Line 32: | ||
| author = " | author = " | ||
| - | -- not required for AI trains | + | emptyMass = 37.0, |
| - | description = "", | + | vmax = 70, |
| - | previewFilename | + | |
| - | + | ||
| - | emptyMass = 37.0, | + | |
| - | vmax = 70, | + | |
| - | length = 12.64, | + | length = 12.64, |
| frontToFirstBogie = 2.53, | frontToFirstBogie = 2.53, | ||
| rearToLastBogie = 2.53, | rearToLastBogie = 2.53, | ||
| Line 174: | Line 170: | ||
| ---@type RailVehicle_DataTable | ---@type RailVehicle_DataTable | ||
| - | local GI1E_b | + | local GI1E_b |
| - | GI1E_b.components | + | GI1E_b.components |
| - | GI1E_b.contentName | + | GI1E_b.contentName |
| - | GI1E_b.blueprintFilename | + | GI1E_b.blueprintFilename |
| - | GI1E_b.lights.headLights = nil; | + | GI1E_b.lights.headLights = nil; |
| - | GI1E_b.lights.tailLights = nil; | + | GI1E_b.lights.tailLights = nil; |
| GI1E_b.couplingFront.couplingBoneName = " | GI1E_b.couplingFront.couplingBoneName = " | ||
| GI1E_b.couplingRear.couplingBoneName = " | GI1E_b.couplingRear.couplingBoneName = " | ||
| Line 273: | Line 269: | ||
| ---- | ---- | ||
| - | ===== Next Steps ===== | + | ===== RailVehicle_DataTable Explained ===== |
| + | |||
| + | Now we will go through the `RailVehicle_DataTable` step by step. | ||
| + | |||
| + | The `RailVehicle_DataTable` contains the core configuration of a vehicle. | ||
| + | It defines all information required so the game can correctly: | ||
| + | * load the vehicle Blueprint, | ||
| + | * place the vehicle on the track, | ||
| + | * apply physics and braking, | ||
| + | * configure couplings, | ||
| + | * and register audio, lights, and additional Lua components. | ||
| + | |||
| + | In this example, the DataTable is stored in a local Lua table called `GI1E_a`. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Creating the Vehicle Data Table ==== | ||
| + | |||
| + | <code lua> | ||
| + | ---@type RailVehicle_DataTable | ||
| + | local GI1E_a = { | ||
| + | </ | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `local GI1E_a = {` | Creates a **local** Lua table called `GI1E_a`. This table holds all vehicle data and will later be registered via the Content Manager. | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Basic Metadata ==== | ||
| + | |||
| + | These fields define what the vehicle is and how it appears inside the content system. | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `contentType = " | ||
| + | | `contentName = " | ||
| + | | `title = " | ||
| + | | `author = " | ||
| + | | `description = "" | ||
| + | | `previewFilename = "" | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Physical Parameters ==== | ||
| + | |||
| + | These values define physical base properties and basic performance. | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `emptyMass = 37.0` | Empty mass of the vehicle in **tons**. | | ||
| + | | `vmax = 70` | Vehicle maximum speed (km/h) that the vehicle can reach by its own traction. | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Dimensions and Bogie Distances ==== | ||
| + | |||
| + | These values are critical for correct placement on the track and correct bogie positioning. | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `length = 12.64` | Total vehicle length from coupling tip to coupling tip (meters). | | ||
| + | | `frontToFirstBogie = 2.53` | Distance from the **front coupling tip** to the **center of the front bogie** (meters). | | ||
| + | | `rearToLastBogie = 2.53` | Distance from the **rear coupling tip** to the **center of the rear bogie** (meters). | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Traction / Engine Settings ==== | ||
| + | |||
| + | These values define the maximum propulsion capability of the vehicle. | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `maxEnginePower = 480` | Maximum engine power (kW). If you have multiple motors per car, sum them up for the full carbody. | | ||
| + | | `maxEngineForce = 50` | Maximum tractive effort / force (kN). | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Blueprint Reference ==== | ||
| + | |||
| + | This defines which Unreal Blueprint is loaded for this vehicle. | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `blueprintFilename = "/ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Coupling Configuration ==== | ||
| + | |||
| + | This section is crucial for: | ||
| + | * correct vehicle placement on the rail, | ||
| + | * correct coupling rotation, | ||
| + | * automatic coupling behavior in consists. | ||
| + | |||
| + | The front coupling: | ||
| + | |||
| + | <code lua> | ||
| + | couplingFront = { | ||
| + | rotationOrigin | ||
| + | couplingOffset | ||
| + | automaticCoupling | ||
| + | skeletalMesh | ||
| + | couplingBoneName | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `rotationOrigin` | Name of the **Scene Component** in your Vehicle Blueprint that defines the front coupling reference point (e.g. `Coupling_F`). | | ||
| + | | `couplingOffset` | Offset distance between the coupling reference point and the coupling rotation pivot (meters). | | ||
| + | | `automaticCoupling` | If `true`, coupling happens automatically when approaching another vehicle. | | ||
| + | | `skeletalMesh` | Name of the Skeletal Mesh Component inside the Blueprint that contains the coupling mesh (e.g. `Exterior`). | | ||
| + | | `couplingBoneName` | Name of the coupling rotation bone inside the skeleton (driven by animation). | | ||
| + | |||
| + | The rear coupling works the same way: | ||
| + | |||
| + | <code lua> | ||
| + | couplingRear = { | ||
| + | rotationOrigin | ||
| + | couplingOffset | ||
| + | automaticCoupling | ||
| + | skeletalMesh | ||
| + | couplingBoneName | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Cameras ==== | ||
| + | |||
| + | Defines exterior cameras if the vehicle supports them. | ||
| + | For AI vehicles, this is typically empty. | ||
| + | |||
| + | <code lua> | ||
| + | cameras = { | ||
| + | exteriorCameras = {}, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Braking Systems ==== | ||
| + | |||
| + | Defines available brake systems and their maximum forces. | ||
| + | |||
| + | <code lua> | ||
| + | brakingSystems = { | ||
| + | pneumatic5bar = { | ||
| + | maxBrakeForce = 60, | ||
| + | }, | ||
| + | electric = { | ||
| + | maxBrakeForce = 50, | ||
| + | maxBrakePower = 480, | ||
| + | }, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `pneumatic5bar.maxBrakeForce` | Maximum brake force of the pneumatic brake (kN). | | ||
| + | | `electric.maxBrakeForce` | Maximum brake force of the electric brake (kN). | | ||
| + | | `electric.maxBrakePower` | Maximum braking power of the electric brake (kW). | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Components ==== | ||
| + | |||
| + | This list defines which Lua components are attached to the vehicle. | ||
| + | |||
| + | <code lua> | ||
| + | components = { | ||
| + | LightManager, | ||
| + | VehicleNumber, | ||
| + | AudioManager, | ||
| + | Berlin_PIS, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ^ Component ^ Purpose ^ | ||
| + | | `LightManager` | Controls vehicle lighting logic. | | ||
| + | | `VehicleNumber` | Handles vehicle number generation and decals. | | ||
| + | | `AudioManager` | Manages audio playback and audio components. | | ||
| + | | `Berlin_PIS` | Controls destination displays and announcements. | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Audio (AI Bogie Audio) ==== | ||
| + | |||
| + | Defines which Blueprint Audio Component is used for AI bogie sound playback. | ||
| + | |||
| + | <code lua> | ||
| + | audio = { | ||
| + | audioBogieAI = { | ||
| + | { | ||
| + | audioComponent = " | ||
| + | }, | ||
| + | }, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `audioComponent = " | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Lights ==== | ||
| + | |||
| + | This section defines headlights and taillights, including: | ||
| + | * which direction they apply to, | ||
| + | * which mesh + material slot is affected, | ||
| + | * which emissive parameters are set, | ||
| + | * and which Light Components are controlled. | ||
| + | |||
| + | ^ Concept ^ Explanation ^ | ||
| + | | `direction = 1` | Applies when the vehicle faces the forward direction. | | ||
| + | | `direction = -1` | Applies when the vehicle faces the reverse direction. | | ||
| + | | `mesh` | Skeletal Mesh Component name used for material manipulation (e.g. `Exterior`). | | ||
| + | | `materialSlot` | The material slot name that contains emissive parameters. | | ||
| + | | `materialParams` | Material parameter values applied when the light is active (usually emissive strength). | | ||
| + | | `lightComponents` | Actual Unreal Light Components to switch on/off and configure (intensity, cone, radius, etc.). | | ||
| + | |||
| + | Headlights example: | ||
| + | |||
| + | <code lua> | ||
| + | headLights = { | ||
| + | { | ||
| + | direction = 1, | ||
| + | headlightElements = { | ||
| + | { | ||
| + | mesh = " | ||
| + | materialSlot = " | ||
| + | materialParams = { | ||
| + | [" | ||
| + | }, | ||
| + | lightComponents = { | ||
| + | {lightComponent =" | ||
| + | {lightComponent =" | ||
| + | }, | ||
| + | }, | ||
| + | }, | ||
| + | }, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | Taillights example: | ||
| + | |||
| + | <code lua> | ||
| + | tailLights = { | ||
| + | { | ||
| + | mesh = " | ||
| + | materialSlot = " | ||
| + | direction = -1, | ||
| + | materialParams = { | ||
| + | [" | ||
| + | }, | ||
| + | }, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== PIS (Destination Displays & Announcements) ==== | ||
| + | |||
| + | The PIS section defines: | ||
| + | * which destination display components exist, | ||
| + | * which announcement audio files are used, | ||
| + | * the audio component for playback, | ||
| + | * and additional announcement logic settings. | ||
| + | |||
| + | Key elements: | ||
| + | * `destinationDisplays` defines display components (e.g. `ZZA`) | ||
| + | * audio lists define announcement file assets | ||
| + | * `audioComponent` defines the Blueprint Audio Component used for announcements | ||
| + | |||
| + | (Your provided PIS block remains valid and can be explained further in the next section.) | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Vehicle Numbers ==== | ||
| + | |||
| + | Defines how vehicle numbers are generated and displayed. | ||
| + | |||
| + | <code lua> | ||
| + | vehicleNumber = { | ||
| + | labels = { | ||
| + | { | ||
| + | mesh = " | ||
| + | materialSlot = " | ||
| + | }, | ||
| + | }, | ||
| + | poolName | ||
| + | poolValueMin = 1070, | ||
| + | poolValueMax = 1094, | ||
| + | }, | ||
| + | </ | ||
| + | |||
| + | ^ Entry ^ Explanation ^ | ||
| + | | `labels.mesh` | Mesh that receives the number decal material. | | ||
| + | | `labels.materialSlot` | Material slot where number decals are applied. | | ||
| + | | `poolName` | Name of the number pool. | | ||
| + | | `poolValueMin / poolValueMax` | Range of numbers the vehicle can randomly receive. | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Creating a Variant Vehicle using deepCopy ===== | ||
| + | |||
| + | In this section, we create a **variant** of an existing vehicle by copying an already defined `RailVehicle_DataTable` and adjusting only the values that differ. | ||
| + | |||
| + | This approach is very common and highly recommended, | ||
| + | * A- and B-cars | ||
| + | * Direction-dependent variants | ||
| + | * Slightly different vehicle bodies sharing the same base configuration | ||
| + | |||
| + | Instead of defining the entire vehicle again, we reuse the existing data. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Copying the Base Vehicle ==== | ||
| + | |||
| + | <code lua> | ||
| + | ---@type RailVehicle_DataTable | ||
| + | local GI1E_b = TableUtil.deepCopy(GI1E_a); | ||
| + | </ | ||
| + | |||
| + | This line creates a **deep copy** of the previously defined vehicle `GI1E_a`. | ||
| + | |||
| + | Important points: | ||
| + | * All values from `GI1E_a` are copied into `GI1E_b` | ||
| + | * Nested tables (lights, brakes, couplings, etc.) are copied as well | ||
| + | * Changes made to `GI1E_b` do **not** affect `GI1E_a` | ||
| + | |||
| + | This gives us a fully independent vehicle configuration that starts as a 1:1 copy. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Reusing Component Definitions ==== | ||
| + | |||
| + | <code lua> | ||
| + | GI1E_b.components = GI1E_a.components; | ||
| + | </ | ||
| + | |||
| + | Here, the component list is explicitly reused. | ||
| + | |||
| + | This means: | ||
| + | * Both vehicles use the same Lua components (LightManager, | ||
| + | * This is safe as long as the component behavior is identical | ||
| + | * The comment `-- TODO` indicates that this could be changed later if needed | ||
| + | |||
| + | This avoids unnecessary duplication of identical component lists. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Changing the Content Name ==== | ||
| + | |||
| + | <code lua> | ||
| + | GI1E_b.contentName = " | ||
| + | </ | ||
| + | |||
| + | Each vehicle must have a **unique `contentName`**. | ||
| + | |||
| + | Even though `GI1E_b` is based on `GI1E_a`, it must be registered as a separate vehicle in the Content Manager. | ||
| + | |||
| + | This name is later used: | ||
| + | * in train compositions | ||
| + | * when spawning vehicles | ||
| + | * internally by the game | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Assigning a Different Blueprint ==== | ||
| + | |||
| + | <code lua> | ||
| + | GI1E_b.blueprintFilename = "/ | ||
| + | </ | ||
| + | |||
| + | The B-car uses a **different Vehicle Blueprint**. | ||
| + | |||
| + | Typical reasons for this: | ||
| + | * Different interior layout | ||
| + | * No driver cab | ||
| + | * Different light setup | ||
| + | * Different coupling setup | ||
| + | |||
| + | Even though most logic is shared, the physical Blueprint can differ. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Removing Head- and Taillights ==== | ||
| + | |||
| + | <code lua> | ||
| + | GI1E_b.lights.headLights = nil; | ||
| + | GI1E_b.lights.tailLights = nil; | ||
| + | </ | ||
| + | |||
| + | In this case, the B-car does **not** have its own headlights or taillights. | ||
| + | |||
| + | By setting these entries to `nil`: | ||
| + | * The light definitions inherited from `GI1E_a` are removed | ||
| + | * The vehicle will not attempt to control head- or taillights | ||
| + | * This prevents duplicated or incorrect lighting inside a train consist | ||
| + | |||
| + | This is typical for intermediate cars in a multiple-unit train. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Adjusting Coupling Bone Names ==== | ||
| + | |||
| + | <code lua> | ||
| + | GI1E_b.couplingFront.couplingBoneName = " | ||
| + | GI1E_b.couplingRear.couplingBoneName | ||
| + | </ | ||
| + | |||
| + | The B-car uses **different coupling bones** than the A-car. | ||
| + | |||
| + | Reasons for this include: | ||
| + | * Different skeleton layout | ||
| + | * Additional coupling bones for internal train connections | ||
| + | * Separate animation channels for different couplers | ||
| + | |||
| + | Only the bone names are changed — all other coupling parameters remain the same. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Summary: Differences Between GI1E_a and GI1E_b ==== | ||
| + | |||
| + | ^ Aspect ^ GI1E_a (A-car) ^ GI1E_b (B-car) ^ | ||
| + | | Base data | Fully defined | Copied from GI1E_a | | ||
| + | | Blueprint | BP_GI1E_A | BP_GI1E_B | | ||
| + | | Lights | Head- and tail lights present | No head- or tail lights | | ||
| + | | Coupling bones | Rotation1 / Rotation2 | Rotation3 / Rotation4 | | ||
| + | | Components | Own list | Reused from GI1E_a | | ||
| + | | contentName | Berlin_GI1E_a | Berlin_GI1E_b | | ||
| + | |||
| + | Using `TableUtil.deepCopy` keeps your Lua files clean, maintainable, | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== Train Compositions and Content Registration ===== | ||
| + | |||
| + | At the end of the file comes one of the most important parts: | ||
| + | **Train compositions** and the registration of all DataTables in the **Content Manager**. | ||
| + | |||
| + | Without these steps, the game would know that the vehicle exists as a data table — but it would not know: | ||
| + | * how to spawn it as a full train consist, | ||
| + | * and it would not load the content at all. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== What is a TrainComposition_DataTable? | ||
| + | |||
| + | A `TrainComposition_DataTable` defines a **complete train consist** (multiple vehicles in a fixed order). | ||
| + | |||
| + | AI trains in SubwaySim 2 are usually spawned using **train compositions**, | ||
| + | |||
| + | That is why this section is essential for AI vehicles. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Example: GI1E_1x (one train unit) ==== | ||
| + | |||
| + | <code lua> | ||
| + | ---@type TrainComposition_DataTable | ||
| + | local GI1E_1x = { | ||
| + | contentType | ||
| + | contentName | ||
| + | |||
| + | title = "GI1e x1", | ||
| + | author | ||
| + | |||
| + | -- not required for AI trains | ||
| + | description | ||
| + | previewFilename | ||
| + | hidden | ||
| + | |||
| + | vehicles = { | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | }, | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | ^ Field ^ Meaning ^ | ||
| + | | `contentType = " | ||
| + | | `contentName` | Internal unique identifier for this consist. Used by the Content Manager and spawners. | | ||
| + | | `title` | Display name (used in menus or debug lists). | | ||
| + | | `author` | Author name. | | ||
| + | | `hidden = true` | Hides this composition from the vehicle selection menu. Very common for AI-only trains. | | ||
| + | | `vehicles` | The ordered list of vehicles that form the train consist. | | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== The vehicles list ==== | ||
| + | |||
| + | The most important part is the `vehicles = { ... }` block. | ||
| + | |||
| + | Each entry uses: | ||
| + | |||
| + | ^ Field ^ Meaning ^ | ||
| + | | `contentName` | Refers to a vehicle previously defined and registered (e.g. `Berlin_GI1E_a`). | | ||
| + | | `forward` | Defines the orientation of that vehicle inside the consist. `true` means forward-facing, | ||
| + | |||
| + | This is where you can clearly see why `contentName` is so important: | ||
| + | It acts as the unique key that links your **vehicle DataTables** into a full train consist. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Example: GI1E_2x (two units coupled) ==== | ||
| + | |||
| + | The 2x consist simply repeats the same unit again, resulting in a longer AI train. | ||
| + | |||
| + | <code lua> | ||
| + | ---@type TrainComposition_DataTable | ||
| + | local GI1E_2x = { | ||
| + | contentType | ||
| + | contentName | ||
| + | |||
| + | title = "GI1e x2", | ||
| + | author | ||
| + | |||
| + | -- not required for AI trains | ||
| + | description | ||
| + | previewFilename | ||
| + | hidden | ||
| + | |||
| + | vehicles = { | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | |||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | { contentName = " | ||
| + | }, | ||
| + | }; | ||
| + | </ | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Registering Content with the Content Manager ==== | ||
| + | |||
| + | At the very end, we must register all content tables so SubwaySim 2 actually loads them. | ||
| + | |||
| + | <code lua> | ||
| + | g_contentManager: | ||
| + | g_contentManager: | ||
| + | g_contentManager: | ||
| + | g_contentManager: | ||
| + | </ | ||
| + | |||
| + | This does two things: | ||
| + | * It tells the game: **" | ||
| + | * It makes the `contentName` entries usable by the spawning and AI systems. | ||
| + | |||
| + | Important: | ||
| + | * We pass the **local Lua variables** here (e.g. `GI1E_a`), not the string names. | ||
| + | * If you forget to register a table, it will not exist in the game content system. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== Summary ==== | ||
| + | |||
| + | * `RailVehicle_DataTable` defines **single vehicles** | ||
| + | * `TrainComposition_DataTable` defines **AI train consists** | ||
| + | * `g_contentManager: | ||
| + | |||
| + | With this section completed, the vehicle and its AI train compositions are fully registered and can be used by SubwaySim 2. | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== What' | ||
| + | |||
| + | This page covered the setup of a **simple vehicle.lua**, | ||
| + | |||
| + | If you want to create more advanced vehicles — such as player-driven trains, vehicles with interactive cabs, advanced systems, or extended logic — you will find further detailed information in the following chapters: | ||
| + | |||
| + | * **[[manual: | ||
| + | Learn how to build advanced vehicle Blueprints with interactive components, cab logic, and extended systems. | ||
| + | |||
| + | * **[[manual: | ||
| + | Explore advanced Lua setups including player controls, extended systems, and complex vehicle behavior. | ||
| + | |||
| + | These chapters expand on the foundations explained here and guide you step by step towards fully-featured, | ||
| - | In the following sections, we will break down this file step by step and explain what each part does: | ||
| - | * Metadata and identifiers (`contentType`, | ||
| - | * Physical parameters (`emptyMass`, | ||
| - | * Blueprint reference (`blueprintFilename`) | ||
| - | * Coupling setup (`couplingFront`, | ||
| - | * Braking systems (`brakingSystems`) | ||
| - | * Basic components, lights and audio | ||
| - | * Train compositions (`trainComposition`) for AI | ||
| {{page> | {{page> | ||
manual/subwaysim/vehicle_development/vehicle_lua.1768241619.txt.gz · Last modified: by dcs
