It is a clean, minimal, purple-themed graphic social media banner, serving as the official header for the "Metruvia Knowledge Base" (Metruvia and Metruvia Knowledge Base) Transport Fever mod brand on Mod.io. The banner is set against a solid, deep-violet-purple background with a rich saturation. In the upper-center, a white, simplified, and symmetrical game controller icon is positioned. Directly below this icon, in prominent, white, bold, sans-serif text, is the text "Metruvia Knowledge Base". Below that, in smaller, regular white text, is the tagline "Built by Players. Powered by Knowledge." Symmetrically framing the central text and icon, on the far left and far right sides, are two large, detailed, white game controllers, depicted from a top-down aerial perspective with colorful button inputs (yellow, blue, pink, and green) and analog sticks. Scattered across the remaining purple space are small, white line-art icons that reinforce the gaming and puzzle themes: several puzzle piece outlines, small pixel-art heart outlines, simple bullseye targets, and a single solid white circle (a "knowledge" point). The layout is balanced, professional, and easily recognizable as part of a gaming community and documentation resource. The art style is flat vector graphic. A small, stylized white compass needle icon, derived from the Metruvia brand mark, is visible over the left analog stick of the controller on the right, providing a cohesive brand identity. The text is sharp and legible.

Metruvia Content Creator Series: Stations and Assets

Welcome to the Metruvia Content Creator Stations and Assets Guide. In Transport Fever 2, the transition from static buildings to dynamic, player-customizable architecture was a massive leap for the franchise. Modular constructions allow players to piece together massive transit hubs, complex freight terminals, and sprawling airport concourses exactly to their specifications.

For a modder, however, this system represents a significant escalation in technical complexity. You are no longer just placing a 3D model; you are programming a dynamic construction puzzle. When you target the Mod.io ecosystem to reach PlayStation 5 and Xbox Series X|S players, this complexity is paired with strict hardware and UI limitations. Console players do not have the pixel-precision of a mouse, and the console CPU must calculate terrain deformation and track-snapping logic in real-time without faltering.

This masterclass will deconstruct the .con (construction) scripting environment. We will break down the ConstructWithModules lifecycle, the mathematics of slot coordinates, the mechanics of track-snapping nodes, and the absolute necessities of controller-friendly UI categorization. Master these elements, and your custom stations will become the centerpiece of transit networks across all platforms.

1. The Architecture of a .con File

Unlike a simple vehicle mod that relies on a single .mdl (model) file, a modular asset relies on a primary .con construction script. This file is the blueprint that tells the game engine how to generate the foundation of the building and how to accept modular pieces (like extra platforms or passenger terminals) later.

1.1 The constructionTemplates Block

To begin, your .con file must define the base state of the asset. When a player selects your station from the build menu, the game loads a “Template.”

constructionTemplates = {
{
type = “DYNAMIC”,
constructionType = “STREET_STATION”,
description = {
name = (“Modular Grand Hub”), description = (“A fully customizable central station.”),
icon = “ui/construction/station/street/grand_hub.tga”
},
availability = { yearFrom = 1950, yearTo = 0 },
— Initial modules are defined here
}
}

Console Hygiene Note: As discussed in Article 4, your text descriptions must be culturally compliant and de-branded. Furthermore, the icon specified here must follow standard UI dimensions to prevent blurry upscaling on a 1080p or 4K television screen.

1.2 The updateFn Function

The core of your .con script is the updateFn. This is the function the engine calls every single time the player moves the mouse or adjusts a parameter before they click to build. It calculates the cost, the terrain deformation, the track positions, and the placement of the 3D models.

If your updateFn is bloated with inefficient Lua loops (e.g., iterating through thousands of potential track variations unnecessarily), the game will stutter wildly as the console player tries to position the building. Efficiency here is paramount.

2. The ConstructWithModules Lifecycle

To build a modular station, you must understand the exact sequence of events the game executes. When a modular construction is modified, the engine invokes a hidden ConstructWithModules cycle.

2.1 The Sorting and Fetching Phase

When the player clicks to add a new “Passenger Platform” module to your station, the engine does not just render the new piece. It rebuilds the entire station from scratch in milliseconds.

The engine sorts all currently placed modules in ascending order of their slotId.

It fetches the spatial location and the tag name of each slot.

It prepares an internal addModel function to render the geometry.

2.2 The Module updateFn

Each individual module (e.g., a single staircase or a roof piece) has its own metadata and its own updateFn. The engine calls the module’s updateFn, providing it with the addModel hook.

Because the entire structure is rebuilt every time a piece is added, keeping the polygon count of individual modules low is critical. If your station consists of 50 high-poly modules, the CPU spike required to rebuild the asset upon adding the 51st module will trigger a watchdog timeout on a console, resulting in a crash to the dashboard.

3. Slot Mathematics: The Grid of Possibility

A modular station works because the base construction defines “Slots”—empty spaces where modules are permitted to exist. Defining these slots requires precision coordinate math.

3.1 The Transformation Matrix

Every slot has a transf property. This is a 4×4 transformation matrix that dictates the exact X, Y, and Z position of the slot relative to the center origin of the construction.

If you want a slot for a waiting room to sit exactly 10 meters to the right of the center point and 2 meters elevated above the ground, you must define the translation values in the bottom row of the matrix.

3.2 Spacing and Selection

The spacing property is arguably the most critical variable for console playability. It defines the physical “hitbox” of the slot using a four-value vector: {-x, x, -y, y}.

The Console Problem: Console players use a thumbstick to move a cursor over these slots. If your spacing values overlap with adjacent slots, or if they are mathematically too narrow (e.g., {-0.1, 0.1, -0.1, 0.1}), the gamepad controller will physically struggle to select the correct slot.

The Solution: Always define generous, non-overlapping spacing boundaries. Make the hitboxes large enough that a slightly imprecise thumbstick flick will reliably target the intended module space.

3.3 Slot Types and Restrictions

You must use the type parameter to strictly govern what module can go where. If you use generic types (like type = "any"), a player might accidentally place a “Train Track” module on the roof of a “Passenger Terminal.”

Define strict strings like type = "hub_platform_roof_1" and ensure your roof modules only accept that specific type key.

4. Snapping Nodes & Edge Lists

A station is useless if vehicles cannot connect to it. You must script the tracks, streets, and pedestrian paths directly into the .con file using Edge Lists.

4.1 Defining the edgeLists

In your updateFn return struct, you must provide edgeLists for streets, tracks, or taxiways. Each edge is essentially a curve defined by two points and two tangents (the direction and sharpness of the curve).

result.edgeLists = {
{
type = “TRACK”,
params = { type = “standard.lua”, catenary = true },
edges = {
— Point A (Start), Tangent A, Point B (End), Tangent B
{ { 0, -20, 0 }, { 0, 10, 0 } },
{ { 0, 20, 0 }, { 0, 10, 0 } }
},
snapNodes = { 0, 1 } — Which ends can the player connect external tracks to?
}
}

4.2 The Snapping Node Logic

The snapNodes array is vital. It tells the game engine which vertices of your internal track can be physically connected to the player’s external rail network.

If you forget to declare snapNodes, the station will place tracks, but the player will get a “Construction Not Possible” collision error whenever they try to attach a rail line to it.

Free Nodes: You can define freeNodes in the params block. These are nodes that are not permanently locked to the construction. This allows players to modify or bulldoze the edges independently of the main station building.

4.3 Terminal Groups

For a station to function, it needs “Terminals” where vehicles stop and load cargo/passengers. Your script must explicitly map a specific edge (track) to a specific terminal group. If the mapping is off by a single coordinate, trains will arrive at Track 1, but passengers will wait invisibly at Track 3, causing your transit rating to plummet.

5. Terrain Alignment & Bounding Boxes

When a player places a 300-meter-long train station on a hill, the game engine must terraform the environment to make it flat. If your script fails to tell the engine how to do this, your station will float in the air or be buried underground.

5.1 The terrainAlignmentLists

You must feed the engine an array of polygons outlining the footprint of your station. The engine uses this to calculate the excavation and embankment required.

EQUAL: Forces the terrain to perfectly match the Z-height of the construction.

LESS: Allows the terrain to slope down away from the station.

GREATER: Allows the terrain to slope up away from the station.

Performance Warning: Complex, jagged terrain alignment polygons require massive CPU calculations. Keep your terrainAlignmentLists as simple geometric rectangles.

5.2 groundFaces and Painting

To make the station look grounded, you use groundFaces to “paint” the terrain beneath it. You can paint concrete, gravel, or asphalt over the vanilla grass. This is virtually “free” in terms of performance (costing zero polygons) and should be used aggressively to hide awkward seams between your 3D models and the ground.

5.3 The Collider

The collider is the physical bounding box of the asset. It dictates where other buildings or roads can be placed. If your collider is smaller than your 3D model, the game will allow AI towns to build houses clipping directly through your station walls. Always define a collider box that safely encapsulates your entire .mdl geometry.

6. UI Categorization and Controller Limits

Transport Fever 2 offers a Modding API to alter the UI, but this is the primary trap for modular station creators targeting the Mod.io console release.

6.1 The Custom UI Prohibition

As outlined in the console modding guidelines, altering the UI with new interactable components from the UI modding API is strictly prohibited.

If you write a Lua script that injects a custom, bespoke menu panel to manage your station’s modules, Sony and Microsoft will reject the mod. Why? Because gamepad controllers lack the free-roaming cursor required to navigate unmapped UI space. Your custom window will appear on screen, but the player will have no way to highlight or click the buttons.

6.2 Using the Vanilla Module Tabs

You must rely entirely on the vanilla .con parameter UI types (BUTTON, COMBOBOX, SLIDER) and the vanilla Module menu structure.

In your module metadata, you use the category block:

category = {
categories = { “passenger_hubs”, “roofs”, “stairs” },
}

This automatically sorts your modules into the game’s existing, controller-mapped tab system. Do not attempt to bypass this. Stick to the vanilla UI grid.

7. The Performance & File Limits for Mod.io

Modular stations are inherently heavy. A player might place 200 modules to build a mega-hub. You must respect the console performance budgets.

7.1 The 200MB Unpacked Limit

Your entire station mod—including all variations of roofs, stairs, tracks, and textures—must remain under 200MB unpacked.

Texture Atlasing: You cannot afford a unique 2048×2048 texture for every single module type. You must UV-unwrap your roofs, walls, and floors onto a single, shared “Station Atlas” .dds file.

7.2 The 1MB Blob Limit

Every 3D mesh you export compiles into a .blob file. Urban Games mandates that the lowest Level of Detail (LOD) .blob file for any mesh must be under 1MB. For modular pieces, this is usually easy to achieve (a low-poly bench or roof segment is tiny). However, if you include a massive, single-piece “Glass Atrium” module, you must aggressively decimate its geometry for its furthest LOD to pass certification.

7.3 Standalone Functionality

You cannot upload a “Station Base Script” mod and require players to download a separate “Station Textures” mod. All console-compatible mods must be 100% standalone. Bundle all necessary files into a single, cohesive upload.

8. Lifecycle Management: The severityRemove Flag

As with industry mods, stations fundamentally alter the pathfinding graph of the save file.

If a console player builds your modular station, routes five train lines to it, and then deletes the mod from their load order, the save game will attempt to calculate pathfinding to an edgeList that no longer exists in the game code. This will cause an unrecoverable crash.

You must tag your modular station mod in the mod.lua file:

severityRemove = "WARNING" (or "CRITICAL" if your station introduces custom script behaviors).

This prompts the console UI to warn the player before they disable the mod, shifting the responsibility of save-game management away from Urban Games and ensuring your mod remains compliant with QA standards.

9. Summary: The Pre-Flight Station Audit

Before pushing your modular masterpiece to the Mod.io API, run this final technical checklist:

Slot Hitboxes: Are your spacing coordinates generous enough for a thumbstick to easily select them without overlapping?

Node Snapping: Are your snapNodes correctly indexed in the edgeLists so tracks can attach?

UI Compliance: Did you use vanilla categories instead of attempting to inject custom Lua UI panels?

VRAM Budget: Are all modules sharing a central texture atlas to respect the 200MB file limit?

Terrain Logic: Are your terrainAlignmentLists restricted to simple geometry to prevent CPU spikes during placement?

By engineering your .con scripts with these console-first principles, you elevate your work from a simple 3D asset to a professional, platform-agnostic gameplay expansion.