Reading and acting
Connect trains, lines and tags
Read the game’s lines and distinguish their declared tags from tags inherited from parents.
Request the catalogue you need
This guide builds on train queries. Request includeLines to display a line; request includeTags for its tags, which already includes lines. A train may have a lineId even when the catalogue was not requested: a missing join does not mean that the train has no line.
| Information | Native | JVM |
|---|---|---|
| Train’s line identity | train.service?.line?.lineId | record.service?.line |
| Look up the line | snapshot.line(lineId) | snapshot.line(lineId) |
| Observed parent | line.parentLineId | line.parentId |
| Depot | line.isDepot | line.type == LineType.Depot |
LineType distinguishes Depot and Other. Other means neither “passenger” nor “freight”. Name is a presentation property; keep LineId as the key even after renaming. A Line record exposes neither commercial frequency nor a calculated list of upcoming departures.
Read explicitly declared tags
Tag associates a TagId with a label. Line.declaredTags describes only tags on that line. A supplied empty list means no local declaration; null means the declaration was not supplied. To test membership, compare identities rather than a name that may change.
Resolve inheritance without hiding unknowns
snapshot.tagsForLine(lineId) combines tags from the line and its parents without duplicates. Use line.declaredTags for local tags only. These reads operate on the copied batch; they do not silently reread the game to complete an incomplete catalogue.
| Case | Interpretation |
|---|---|
| parentInformationAvailable = false | Parent relationship unknown, even if parentId is null. |
| Parent available and null | Observed root: no parent declared. |
| Parent missing from catalogue, cycle or limit reached | Incomplete resolution: the result remains null. |
| Non-null empty result | No tags in the requested resolved chain. |
Keep membership three-valued: true, false or null. Reducing null to false would turn an incomplete read into a definite absence of a tag, potentially reversing a mod rule.
Compare local and inherited tags
package wiki.trainlines
import nimby.*
data class LineTags(
val line: Line,
val declared: List<Tag>?,
val includingParents: List<Tag>?,
)
fun readLineTags(context: ToolContext, id: LineId): LineTags? {
val snapshot = context.trains(TrainQuery(
includeService = false,
includeLocations = false,
includeTags = true,
))
val line = snapshot.line(id) ?: return null
return LineTags(line, line.declaredTags, snapshot.tagsForLine(id))
}
// Three-state result: true, false or unknown membership.
fun hasTag(tags: List<Tag>?, id: TagId): Boolean? =
tags?.any { it.id == id }
package wiki.jvmtrainlines
import fr.nimby.sdk.*
data class LineTags(
val line: Line,
val declared: List<Tag>?,
val includingParents: List<Tag>?,
)
fun readLineTags(game: Game, id: LineId): LineTags? {
val snapshot = game.trains.snapshot(query = TrainQuery(
includeService = false,
includeLocations = false,
includeTags = true,
))
val line = snapshot.line(id) ?: return null
return LineTags(line, line.declaredTags, snapshot.tagsForLine(id))
}
// A translated or renamed label does not replace the tag identity.
fun hasTag(tags: List<Tag>?, id: TagId): Boolean? =
tags?.any { it.id == id }
The card returns the line, its local declarations and the resolved set. Call hasTag with the TagId selected in your configuration. If the result is null, the screen can explain “tags unavailable” and the business rule can explicitly apply its unknown-data behaviour.