Creating a mod
Driving rules
Choose targets, speeds, permissions and end conditions for each restriction.
Associate a rule with an indication
This guide completes a model whose rules and images are already defined. AutomaticDriving builds a DrivingRule without reading the game. Return it from driving: creating a rule in a variable does not publish it. Your mod defines speeds and the meaning of every aspect and reason.
| Function | Target and duration |
|---|---|
| stop() | Stop at the current signal without permission to pass it. |
| clear() | Release restrictions whose policy waits for Clear; does not prove a physically clear track. |
| announceStop(…) | Remembered stop one or two signals downstream; target permission can replace it with the passage speed until the head passes. |
| limitAtSignal(…) | Local ceiling at the current signal without a restriction retained after passage. |
| limitUntilClearThenRear(…) | Ceiling at the current signal or one/two signals downstream, retained until the rear clears a Clear signal actually passed. |
| restrictedUntilNextSignal(…) | After entry, a ceiling and braking for physically clear space until the head passes the next signal. |
Colours determine no permission. Two indications sharing an aspect can choose different rules according to their reason. A driving function returning null supplies no rule; null must not be treated as clear().
Choose explicit speeds and permissions
All helper speeds are in metres per second. Convert a chosen km/h value by dividing it by 3.6 once at your configuration boundary. Values must be finite; zero denotes a stop, never an implicit running speed.
| Parameter | Meaning and limits |
|---|---|
| signalsAhead | 1 or 2 for announceStop; 0, 1 or 2 for limitUntilClearThenRear. Zero targets the current signal. |
| passableHere | Current-signal permission under the chosen policy; grants no target-signal permission and removes no other restrictions. |
| followTargetSpeed | Allows following the numeric speed declared by an announcement target. |
| cancelAtNextClear | Announcement option accepted only with followTargetSpeed=true and signalsAhead=2. |
| limitAtSignal | Nonnegative speed; passableHere=true requires a strictly positive speed. |
| limitUntilClearThenRear | Strictly positive speed. Reopening alone does not erase a remembered restriction. |
| restrictedUntilNextSignal | maximumSpeedMps > 0 and 0 ≤ entrySpeedMps ≤ maximumSpeedMps; stopFirst=true requires entrySpeedMps=0. |
With stopFirst, restricted entry waits for evidence of a stop. Without that option, entry speed can be chosen explicitly. Choose the release condition according to your rule: head and rear release do not cover the same train length.
A fictional policy you can test
package wiki.driving
import nimby.*
enum class Aspect { Closed, Warning, Restricted, Open }
// These values are policy chosen by this fictional mod, never SDK defaults.
data class Speeds(val passageKmh: Double, val restrictedKmh: Double) {
init {
require(passageKmh.isFinite() && passageKmh > 0)
require(restrictedKmh.isFinite() && restrictedKmh > 0)
}
}
fun instruction(aspect: Aspect, speeds: Speeds): DrivingRule = when (aspect) {
Aspect.Closed -> AutomaticDriving.stop()
Aspect.Warning -> AutomaticDriving.announceStop(
signalsAhead = 1,
passageSpeedMps = speeds.passageKmh / 3.6,
passableHere = true,
followTargetSpeed = true
)
Aspect.Restricted -> AutomaticDriving.restrictedUntilNextSignal(
entrySpeedMps = speeds.restrictedKmh / 3.6,
maximumSpeedMps = speeds.restrictedKmh / 3.6,
stopFirst = false
)
Aspect.Open -> AutomaticDriving.clear()
}
// In your signalModel builder:
// driving { indication -> instruction(indication.aspect, Speeds(25.0, 12.0)) }
// The model must use this Aspect enum; its Reason enum remains its own.
This file defines the pure instruction function. Use it in a signalModel with this Aspect enum and its own reason enum. Closed requires stopping; Warning announces the next signal; Restricted here chooses entry without a prior stop; Open emits Clear. These choices and speeds belong only to this example.
val speeds = wiki.driving.Speeds(passageKmh = 25.0, restrictedKmh = 12.0)
val rule = wiki.driving.instruction(wiki.driving.Aspect.Warning, speeds)
check(rule.signalsAhead == 1)
check(rule.reopenedSpeedMps == 25.0 / 3.6)
check(DrivingFlag.ApproachPassable in rule.flags)Extend tests with a closed then permitted target, head passage, rear clearance for a HoldToClear restriction, and an observation becoming unknown. Also verify that permission at the current signal does not grant permission at its target.
Understand effects beyond display
maximumLineSpeed is a mod option disabled by default. It allows the material maximum as a cruising ceiling while retaining an active native target; it can also affect trains without your mod’s signal constraints. Enable it only when it is part of the intended, tested behaviour.
drivingPlan takes Vehicle, DrivingSettings, DrivingInput and Constraint values and returns a computational or diagnostic DrivingPlan. This result does not directly control the train. Define signal DrivingRule values for in-game driving; do not assume an advisory plan replaces them.