manual:subwaysim:vehicle_development:vehicle_lua
Differences
This shows you the differences between two versions of the page.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| manual:subwaysim:vehicle_development:vehicle_lua [2026/01/13 08:08] – [Coupling Configuration] dcs | manual:subwaysim:vehicle_development:vehicle_lua [2026/01/13 19:57] (current) – dcs | ||
|---|---|---|---|
| Line 32: | Line 32: | ||
| author = " | author = " | ||
| - | emptyMass = 37.0, | + | emptyMass = 37.0, |
| - | vmax = 70, | + | vmax = 70, |
| - | length = 12.64, | + | length = 12.64, |
| frontToFirstBogie = 2.53, | frontToFirstBogie = 2.53, | ||
| rearToLastBogie = 2.53, | rearToLastBogie = 2.53, | ||
| Line 170: | 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 568: | Line 568: | ||
| ---- | ---- | ||
| - | (Continuation follows: | + | ===== Creating a Variant Vehicle using deepCopy ===== |
| + | |||
| + | In this section, we create a **variant** of an existing vehicle by copying | ||
| + | |||
| + | This approach is very common and highly recommended, | ||
| + | * A- and B-cars | ||
| + | * Direction-dependent | ||
| + | * 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 | ||
| + | |||
| + | 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` | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ===== 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 | ||
| + | |||
| + | ---- | ||
| + | |||
| + | ==== 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's Next? ===== | ||
| + | |||
| + | 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, | ||
| + | |||
| {{page> | {{page> | ||
manual/subwaysim/vehicle_development/vehicle_lua.1768288129.txt.gz · Last modified: by dcs
