Mod files and IDs
Every mod is a folder with mod.json inside. Include only the other files your mod uses.
my_mod/
mod.json
data.json
lang/*.lang.txt
maps/
models/*.glb
main.gd
Here is a complete mod.json. Only id is required. Pick an ID unique to your mod and keep it when publishing updates, since saves use it to identify the mod.
{
"id": "north_line_mail",
"name": "North Line Mail",
"version": "1.0",
"author": "Your name",
"description": "A diesel for overnight mail trains."
}
Open Mods on the title screen and choose Open the mods folder. Put the folder or ZIP there, click Look again, then tick the mod. While editing, keep the mod unpacked so you can save changes and scan again. Changes apply when you next start or load a game.
Vehicles
In data.json, put each vehicle under vehicles using a unique vehicle ID. For example, north_line_mail_diesel adds a new locomotive that borrows the stock diesel’s appearance.
{
"vehicles": {
"north_line_mail_diesel": {
"name": "North Line Mail Diesel",
"kind": "rail",
"look": "diesel",
"cargos": ["mail", "pax"],
"max_wagons": 6,
"speed": 6.2,
"cost": 88000,
"run": 7600,
"year": 1962,
"desc": "A fast diesel for overnight mail."
}
}
}
| Field | Value |
|---|---|
name, desc | Display name and optional shop description. |
kind | rail, road, water, or air. |
look or model | Borrow a stock model of the same kind or give a path to your own model. Example stock IDs include steam, diesel, bus, truck, ferry, freighter, prop, and jet. |
cargos | A nonempty list of existing cargo IDs. Examples include pax, mail, coal, wood, goods, and food. |
max_wagons or cap | max_wagons is required for locomotives. Other vehicles use cap for how much they carry. |
speed | Tiles per game day. Multiply by 30 for the speed in km/h shown in the shop. |
cost, run | Purchase price and annual running cost, in whole dollars. Wagons add to a train’s price. |
year | First year it appears in the shop. Omit for a vehicle available from the start. |
rel | Optional starting reliability between 0 and 1. The default depends on the vehicle’s year. |
electric | Set to true for a locomotive that needs overhead wires. |
Installed mods add new vehicle IDs. If Fourways reports a duplicate ID, choose one specific to your mod.
Custom 3D models
Export your model as binary glTF (.glb) and put it in models/. In the vehicle definition, use "model": "models/mail.glb" in place of look, and set length to the body length in tiles.
- Model at one unit per tile. A bus is about 0.46 units long and a ship about 0.9.
- Center the model at the origin, place its base at Y = 0, and face it toward −Z.
- Custom models retain their exported materials. Models borrowed with
lookuse company colors. - A locomotive model covers the engine. Fourways supplies the wagons.
Cargo fares and names
Add any of these sections to data.json alongside your vehicle definitions, or use them on their own.
{
"cargo": {
"mail": {"rate": 52, "days": 10}
},
"town_names": {
"prefixes": ["Vila", "Sant", "Mont"],
"suffixes": ["nova", "bella", "serrat"],
"replace": false
},
"company_names": ["Ferrocarrils del Nord", "Costa Brava Lines"]
}
cargotakes existing cargo IDs.rateis the fare for one unit over 25 tiles, anddaysis how long it pays the full rate. Both must be positive. Press Y to see the result in Cargo fares.town_namescombines one prefix and one suffix for generated town names. Usereplace: trueto swap the stock name lists for your own.company_namessupplies names for rivals. The game uses these before drawing on its stock names.
Heightmaps and presets
Place a grayscale PNG or JPEG in maps/. Black marks the lowest terrain, typically sea, and white marks the highest. Fourways scales the image to the world size and lists it under Map in New game.
Give great_valley.png a companion great_valley.json to set its name, description, and starting options.
{
"name": "Great Valley",
"description": "A wide valley between two ranges.",
"settings": {"w": 512, "h": 256, "towns": 1, "industries": 2, "year": 1970}
}
- Width and height can each be 64, 128, 256, 512, or 1024 tiles.
- Town and industry density range from 0 (few) through 3 (crowded).
- Start years are 1950, 1960, 1970, 1980, 1990, and 2000. Players can adjust the presets before starting.
Translations and new languages
Put plain-text files ending in .lang.txt in lang/. Start a block with > and the exact English wording. Follow it with one line per language code. This works for both stock text and your mod’s vehicle names and descriptions.
> Cost %s
es: Coste %s
ca: Cost %s
fr: Coût %s
- Keep placeholders like
%sand%din the same order. Use\nfor line breaks and double quotes around text with leading or trailing spaces. - For a new language, add translations under its code and include a translation of the
Englishkey to give the language its own name in Settings. For example, usenl: Nederlandsbelow> English. - In scripts, wrap text in
L.t("English text")so players can translate it with the same files.
Gameplay scripts
Create main.gd in the mod folder and start it with extends Mod. Override the hooks you need. For example, this pays the player a $500 monthly grant and posts a news item.
extends Mod
func _on_month(sim: Sim) -> void:
sim.company.earn(500)
news(sim, L.t("Monthly transport grant"), "good")
| Hook | When Fourways calls it |
|---|---|
_on_load() | When active mods are applied, including when you rescan from the Mods screen. There is no active game. |
_on_new_game(sim)_on_game_loaded(sim) | When a world is created or a save finishes loading. |
_on_day(sim)_on_month(sim)_on_year(sim, year) | Each day, at month-end, and when the year changes. year is the year that just ended. |
_on_delivery(sim, vehicle, station, amount) | After a vehicle unloads and is paid. |
_on_vehicle_bought(sim, vehicle)_on_station_built(sim, station) | After a purchase or after a station is built or extended. |
_on_news(sim, item) → bool | Before an item is posted. The dictionary contains text, kind, pos, and day. Return false to hide it or true to let it through. |
_save(sim) → Dictionary_load(sim, data) | Return a dictionary of plain data from _save. Fourways calls _load with that data before _on_game_loaded if the save contains data for this mod. |
The sim object gives scripts access to companies, vehicles, towns, industries, and stations. Use sim.rng for repeatable random events and game methods such as sim.build_station() or sim.buy_vehicle() to perform player actions. The Mod class provides news() to post news, note() to log a message, and path() to find files in the mod folder.
Mod helpers
news(sim, text, kind = "info", at = Vector2(-1, -1))posts a news item. Usegood,info,warn, orbadforkind. Supply a tile inatto make the news jump there when clicked.note(text)writes a log entry tagged with the mod’s ID. You can call it from_on_load().path(file)returns the path to a file relative to the mod folder. Useinfofor metadata such asid,name, andversion.
Common simulation actions
These are game methods you can call from a hook that receives sim. An omitted who uses the player’s company. For calls returning a dictionary, check ok before using the result. On failure, read reason.
sim.build_line(kind, pts, who = null)builds aroadorrailline through consecutive adjacent tiles. Pass a typedArray[Vector2i].sim.build_station(kind, pts, axis, who = null)builds or extends aroad,rail,dock, orairstation. Pass its tiles as anArray[Vector2i]and the platform axis for road or rail. On success, the result containsstation.sim.buy_vehicle(model_id, cargo, wagons, stops, full_load_first, who = null, consist = [], transfer_at = [])buys a vehicle using an existing model ID and an array of stations.consistcan override the cargo and wagon count for a mixed train.transfer_atlists stop indices where cargo is left for another vehicle. On success, the result containsvehicle.sim.town_action(town, action, who = null)performsadvertise,trees, orfundand returns the action’scost.sim.company.earn(amount)credits the player’s company.sim.company.spend(cost, category)records an expense and returnsfalseif the company cannot afford it. Examples of categories areconstructionandvehicles.town.rating(company_id)reads a council rating.town.change_rating(company_id, delta, why = "", day = -1)adjusts it within 0–100. Pass a reason andsim.day_intto log the change in the town window.
All hooks and helpers defined by Mod are listed here. Scripts can also call other game and Godot methods. Those follow the game and engine source.
Script mods run with the game’s permissions. Enable scripts from authors you trust.
Saves and sharing
To share a mod, ZIP its folder. Fourways also accepts ZIPs with mod.json at the top level. Keep the mod ID and vehicle IDs steady between releases so existing saves can find them.
The save records which mods were enabled and can store script data. If a mod is missing or switched off when you load, Fourways warns you and omits vehicles with unavailable models. Re-enable the original mods before loading a save that relies on them.
New cargo types, industries, town buildings, per-item construction costs, and replacements for stock models or sounds are outside the built-in file formats. Interface changes require working with the game’s code.