Train actions
What it is
The game has trains that run on tracks. This API lets your mod drive them: turn an engine on or off, set how fast the train should go, apply the brakes, and join or separate two cars. You pick a train with its id, which you get from Trains.All() (see the Trains API).
When to use it
- Start a train when something happens in your mod, such as the player buying a ticket.
- Stop a train with the brakes, or send it off at a speed you choose.
- Join two cars so they run together, or split them again.
Quick example
This mod waits until a save is loaded. It then turns on the engine of every train and sets its target speed to 10. It writes to the log which trains started.
using ObenseuerCore;
[ModInfo("example.trainstart", "Train Start", "0.1.0")]
[ModDependency("obenseuer.moddingframework", "0.11.0")]
public sealed class TrainStart : FrameworkMod
{
protected override void OnLoad()
{
Events.WhenReady(() =>
{
foreach (TrackTrainInfo train in Trains.All())
{
bool started = TrainActions.SetEngine(Mod.Id, train.Id, true);
TrainActions.SetTargetSpeed(Mod.Id, train.Id, 10f);
Log.Info("Train " + train.Id + (started ? " started" : " could not be started"));
}
});
}
}
WhenReady waits until a save is loaded before it runs your code. See Using events for more on it.
Common tasks
The snippets below use the ids of two cars (frontCarId and rearCarId) that you got from Trains.All(). Apply the brakes and let them go again.
TrainActions.Brake(Mod.Id, frontCarId, true);
TrainActions.Brake(Mod.Id, frontCarId, false);
Join the back of one car to the front of another, and separate them again. Couple only works when the two cars are already touching.
if (TrainActions.Couple(Mod.Id, frontCarId, TrainEnd.Back, rearCarId, TrainEnd.Front))
Log.Info("The cars are joined.");
TrainActions.Release(Mod.Id, frontCarId, TrainEnd.Back);
TrainActions.SetEngine(Mod.Id, frontCarId, false);
Reference
This is the full list of what the API gives you. Text in brackets at the end of a note is extra detail for experienced modders, and you can skip it on a first read.
TrainActions drive trains and join or split cars
Use these calls to switch a train’s engine, set its target speed, brake it, and couple two trains or release a coupling at one end.
public static class TrainActions
{
public static bool SetEngine(string modId, string trainId, bool on);
public static bool SetTargetSpeed(string modId, string trainId, float speed);
public static bool Brake(string modId, string trainId, bool on);
public static bool Couple(string modId, string trainId, TrainEnd end, string otherTrainId, TrainEnd otherEnd);
public static bool Release(string modId, string trainId, TrainEnd end);
}
| Method | Returns | What it does |
|---|---|---|
SetEngine(modId, trainId, on) | bool | Turns the engine on or off. true once the request is made. |
SetTargetSpeed(modId, trainId, speed) | bool | Sets how fast the train should go. true when it was set. |
Brake(modId, trainId, on) | bool | Applies or releases the brakes. true when the brakes are now in the state you asked for. |
Couple(modId, trainId, end, otherTrainId, otherEnd) | bool | Joins one end of a car to one end of another car. true when they are joined. |
Release(modId, trainId, end) | bool | Separates a car at one end. true when that end is free afterwards. |
TrainEnd the two ends of a car
Couple and Release take this to pick the end of a car to use. Front and Back are the framework’s own values, and they select the game’s frontCoupler or backCoupler.
public enum TrainEnd
{
Front = 0,
Back = 1
}
| Value | Meaning |
|---|---|
Front | The front end of a car. |
Back | The back end of a car. |
Things to know
SetEngineturns an engine on or off. It returnstrueonce the request has been made. A train that belongs to a minitrain (the small train the player rides) starts only after the game’s own start-up delay. The lights and the dashboard follow, as they do when the player starts it. (for a minitrain it uses the game’s ownStartMinitrainandStopMinitrain. Other trains useTrackTrain.Toggle.)SetTargetSpeedsets how fast the train should go, and it can’t go faster than the train allows. The speed is held between the train’s own highest forward and highest reverse speed.Brakeapplies or releases the brakes, and the game’s brake outputs still fire. It returnstrueonly when the brakes are really in the state you asked for. (it calls the game’sApplyBrakes, so the game’s braking outputs still fire.)Couplejoins one end of a train to one end of another, and returnstruewhen they are joined. It does what the game does when two cars touch and their couplers meet. It changes nothing and returnsfalseunless all of these are true:- the two cars are different, and they do not already share a wagon;
- each end has a coupler, and neither end is already joined to something;
- both couplers are ready, which means they are switched on and not in their short pause after a crash;
- the two cars are already touching;
- each car is moving no faster than its own coupler allows, when the coupler has a limit.
- (it calls the game’s
TrainCoupler.Couple. The couplers’ triggers must be active and not in their crash cooldown. “Touching” means the bounds of the two triggers’ colliders overlap. The speed limit is the absolute speed of each train against its own coupler trigger’smaxSpeed, when that is above 0.)
Releaseseparates a car at one end, and returnstruewhen that end is free. It splits the train and plays the uncoupling sound. It returnsfalsewhen nothing is joined at that end. (it calls the game’sTrainCoupler.Release.)- The game finishes the join after
Couple. The wagon list, the engine that drives the string, and the way the cars face are all worked out by the game’s own join and split. After a join, the game moves the car that is not driving until the ends meet.Coupleonly joins cars that are already touching, so that gap is small. A join made by your mod is saved the same way as one the player makes. Couplereturningtruedoes not mean the join lasts. The game can release it on a later update if the gap is over the coupler’s allowed distance. ReadFrontCoupledToorBackCoupledTofromTrains.All()to see whether the join is still there. (the distance is the coupler’smaxDistance, when that is above 0.)Couplecan say yes to cars that only look close. The test uses a box around each coupler, so two boxes can overlap when the shapes inside them do not touch. The game then decides, as it does for its own couplers. (it usesCollider.bounds, which are axis-aligned boxes.)Coupleis a little stricter than the game. It checks each car against its own coupler’s speed limit, using the absolute speed, so it refuses some joins the game allows, such as a train moving backward. (the game’s trigger tests the signed speed.)- Your mod can interfere with a train the player is driving. It can start or stop that train, and it can join or separate a joint the player made. The framework keeps no record of who changed a train, so there is no owner to check.
Limits
- Every call returns
falseand does nothing when it can’t do its job. That is for an empty mod id, an unknown or destroyed train, a speed that is not a real number, and while the game is loading or saving a save.CoupleandReleasealso need a valid mod id: 1 to 64 characters of letters, digits,.,_or-. The other three accept any mod id that is not empty. (the checks areSaveData.Busy, the train lookup inTrainController.TrackTrains, and for the speedfloat.IsNaNandfloat.IsInfinity.) - A train without fuel reports a target speed of 0 in the game, so setting a speed does not move it.
What you can’t do (yet)
- You can’t add a car to a train. The train shop does that.
Coupleonly joins two cars that are already in the world. - You can’t slide a car toward another one.
Coupledoes not move a car. The game’s own correction does that, and only over a gap that is already small. - You can’t handle a crash from this page. Use
Trains.Crashed(see the Trains API).
Details
Target: Obenseuer 0.5.08 (buildid 25969064), Unity 6000.0.77f1 Mono, BepInEx 6 Unity.Mono (be.788, the tested build) or BepInEx 5 (5.4.23 or later). Namespace: ObenseuerCore. Logs go to BepInEx\logs\obenseuer.moddingframework.trainactions.log.
Everything not listed in this reference is internal and may change without notice.
When you can call it
- Call everything from your mod’s normal code (
OnLoad, event handlers,Update), not from a background thread you started. The game only works safely on one thread, the main thread (see the glossary). - Train state belongs to the game, and the game saves it. The framework keeps nothing of its own.
If something fails to set up
This API does not change any game method, so there is no feature group to turn off, and nothing for the framework to set up. If the game fails while one of the calls runs, the framework writes one error to the log and the call returns false.