Modding reference

Add vehicles, change terrain and fares, translate the game, or write your own gameplay rules.

Search the modding APILook up hooks, methods, and file options.

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."
    }
  }
}
FieldValue
name, descDisplay name and optional shop description.
kindrail, road, water, or air.
look or modelBorrow 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.
cargosA nonempty list of existing cargo IDs. Examples include pax, mail, coal, wood, goods, and food.
max_wagons or capmax_wagons is required for locomotives. Other vehicles use cap for how much they carry.
speedTiles per game day. Multiply by 30 for the speed in km/h shown in the shop.
cost, runPurchase price and annual running cost, in whole dollars. Wagons add to a train’s price.
yearFirst year it appears in the shop. Omit for a vehicle available from the start.
relOptional starting reliability between 0 and 1. The default depends on the vehicle’s year.
electricSet 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 look use 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"]
}
  • cargo takes existing cargo IDs. rate is the fare for one unit over 25 tiles, and days is how long it pays the full rate. Both must be positive. Press Y to see the result in Cargo fares.
  • town_names combines one prefix and one suffix for generated town names. Use replace: true to swap the stock name lists for your own.
  • company_names supplies 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 %s and %d in the same order. Use \n for 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 English key to give the language its own name in Settings. For example, use nl: Nederlands below > 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")
HookWhen 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) → boolBefore 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. Use good, info, warn, or bad for kind. Supply a tile in at to 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. Use info for metadata such as id, name, and version.

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 a road or rail line through consecutive adjacent tiles. Pass a typed Array[Vector2i].
  • sim.build_station(kind, pts, axis, who = null) builds or extends a road, rail, dock, or air station. Pass its tiles as an Array[Vector2i] and the platform axis for road or rail. On success, the result contains station.
  • 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. consist can override the cargo and wagon count for a mixed train. transfer_at lists stop indices where cargo is left for another vehicle. On success, the result contains vehicle.
  • sim.town_action(town, action, who = null) performs advertise, trees, or fund and returns the action’s cost.
  • sim.company.earn(amount) credits the player’s company. sim.company.spend(cost, category) records an expense and returns false if the company cannot afford it. Examples of categories are construction and vehicles.
  • 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 and sim.day_int to 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.