Créer un mod
Parcourir les voies avec ToolTopology
Distances, raccordements, aiguilles et sens de circulation sans interpréter le moteur du jeu.
Préparer une copie de travail
Prérequis : un service d’outil et un signal source connu. L’objectif est de trouver une position à une distance donnée devant ce signal, ou de refuser lorsque les données ne permettent pas de conclure. Dans le callback, lisez network() puis appelez topology() une fois. Réutilisez cette copie pendant le calcul et les renouvellements d’aperçu ; elle ne garantit pas que la partie reste inchangée.
| Élément public | Interprétation |
|---|---|
| ToolRouteTrack.lengthM | Longueur observée en mètres. Une voie sans longueur fiable est omise de ToolTopology, ainsi que ses signaux. |
| ToolTrackEnd.A / B | Extrémités géométriques de la voie, indépendantes du sens du train. |
| ToolTrackConnection.Join(trackId, entry) | Raccord réciproque vers une voie connue ; entry indique par quelle extrémité elle est abordée. En entrant par A, le parcours continue vers B, et inversement. |
| ToolTrackConnection.Junction / Unknown | Une aiguille demande une politique de route explicite. Unknown ne prouve pas une fin de voie : arrêtez le calcul sans inventer de continuation. |
| junctionOffsetsM | Positions des aiguilles depuis A, en mètres, triées. Contrôlez aussi celles qui se trouvent à l’intérieur de la voie, pas seulement les raccords aux extrémités. |
| ToolSignal.travelDirection / placementAt(...) | Le sens normalisé vaut +1 de A vers B et −1 de B vers A. placementAt convertit ce sens pour le modèle source et produit une position utilisable par l’aperçu et la pose. Ne testez pas kind pour retourner vous-même la direction. |
Un parcours volontairement borné
package wiki.topology
import nimby.*
// Suivre une chaîne sans ambiguïté. Arrêt aux aiguilles, liens inconnus et cycles.
// Cette règle ne choisit ni ne réserve un itinéraire et ne prouve pas sa liberté.
fun positionAhead(network: ToolNetwork, sourceId: Long, distanceM: Double): SignalPosition? {
require(distanceM.isFinite() && distanceM > 0)
val topology = network.topology()
val source = topology.signal(sourceId) ?: return null
var track = topology.track(source.track) ?: return null
var direction = source.travelDirection
var offset = source.fraction * track.lengthM
var remaining = distanceM
val visited = HashSet<Long>()
repeat(4096) {
if (!visited.add(track.id)) return null
val toEnd = if (direction > 0) track.lengthM - offset else offset
val stop = minOf(remaining, toEnd)
if (track.junctionOffsetsM.any { val ahead = (it - offset) * direction; ahead >= 0 && ahead <= stop }) return null
if (remaining < toEnd) return source.placementAt(track.id, (offset + direction * remaining) / track.lengthM, direction)
if (remaining == toEnd) return null // Aucune pose exactement à une extrémité.
val connection = track.connection(if (direction > 0) ToolTrackEnd.B else ToolTrackEnd.A)
if (connection !is ToolTrackConnection.Join) return null
remaining -= toEnd
track = topology.track(connection.trackId) ?: return null
direction = if (connection.entry == ToolTrackEnd.A) 1 else -1
offset = if (direction > 0) 0.0 else track.lengthM
}
return null
}
L’exemple cherche une position à une distance donnée devant la source. Il suit seulement les raccords simples, refuse les extrémités exactes, s’arrête à toute aiguille ou donnée inconnue et limite le nombre de voies visitées. Il retourne null si cette politique ne permet pas de conclure. Il ne vérifie ni occupation, ni espacement avec d’autres signaux, ni validité d’une commande de construction ; ajoutez ces règles à votre planificateur.