NRF SDK 0.9

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 publicInterprétation
ToolRouteTrack.lengthMLongueur observée en mètres. Une voie sans longueur fiable est omise de ToolTopology, ainsi que ses signaux.
ToolTrackEnd.A / BExtré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 / UnknownUne aiguille demande une politique de route explicite. Unknown ne prouve pas une fin de voie : arrêtez le calcul sans inventer de continuation.
junctionOffsetsMPositions 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é

ToolTopology.kt — fonction pure
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.