NRF SDK 0.9

Getting started

Your first mod

Build a two-aspect teaching signal with images, a setting and driving behaviour.

Declare the signal in Kotlin

Prerequisite: the project and mod.json from the setup guide. Add the complete file below. This model opens when its block is known clear and its other conditions hold; otherwise it closes. It is a teaching starting point, not a complete signalling system.

src/main/kotlin/Entry.kt
package nimby.mod

import nimby.*

// These names describe your indications, not native codes.
enum class Aspect { Closed, Open }
enum class Reason { Unknown, Disabled, Occupied, Clear }

val firstSignal = signalModel(
    id = "monmod.signal",
    title = "My first signal",
    textures = "mon_premier_signal",
    fallback = Indication(Aspect.Closed, Reason.Unknown)
) {
    construction(states = listOf("closed.svg", "open.svg"))
    val active = checkbox("active", "Enable the signal", defaultValue = true)

    rules {
        when {
            settingsStatus == SettingsStatus.Unavailable -> Indication(Aspect.Closed, Reason.Unknown)
            !enabled(active) -> Indication(Aspect.Closed, Reason.Disabled)
            !fresh || !routeKnown || observation.lampFailed || observation.forcedStop ->
                Indication(Aspect.Closed, Reason.Unknown)
            block == Occupancy.Clear -> Indication(Aspect.Open, Reason.Clear)
            block == Occupancy.Occupied -> Indication(Aspect.Closed, Reason.Occupied)
            else -> Indication(Aspect.Closed, Reason.Unknown)
        }
    }

    // Both SVG files are declared once in construction.
    images { indication ->
        when (indication.aspect) {
            Aspect.Closed -> "closed.svg"
            Aspect.Open -> "open.svg"
        }
    }
    driving { indication ->
        when (indication.aspect) {
            Aspect.Closed -> AutomaticDriving.stop()
            Aspect.Open -> AutomaticDriving.clear()
        }
    }
}

// The mod is the package; the model above retains its own types and rules.
fun createMod(): SignallingMod = signalMod(modInfo) {
    metadata(author = "Your name", description = "My first signalling mod.")
    signal(firstSignal)
}

Add the two images

File names in construction and images are relative to the package root. assets is copied to that root: assets/closed.svg therefore becomes closed.svg. The mod.txt catalogue is generated from the Kotlin declaration.

assets/closed.svg
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="64" viewBox="0 0 32 64"><rect x="6" y="2" width="20" height="48" rx="10" fill="#161616"/><circle cx="16" cy="15" r="7" fill="#ef4444"/><path d="M16 50v14" stroke="#888" stroke-width="4"/></svg>
assets/open.svg
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="64" viewBox="0 0 32 64"><rect x="6" y="2" width="20" height="48" rx="10" fill="#161616"/><circle cx="16" cy="37" r="7" fill="#22c55e"/><path d="M16 50v14" stroke="#888" stroke-width="4"/></svg>

You can replace the artwork. If you change a file name, update all of its Kotlin references. Preserve the order of a catalogue already used by saves.

Build and try the signal

Terminal at the project root
.\gradlew.bat assembleReleaseMod verifyNativeMod

The package is created in build/gradle/mod/release. verifyNativeMod checks loading, stopping and reloading without opening the game. Then add the project to your Hub developer profile, build it with the same kit and activate the profile while the game is closed.

  • Launch a test game with the mod resources enabled and place the signal.
  • On a known clear block, check the green image; with an occupied block or missing data, check closure.
  • Clear “Enable signal”: this example explicitly requests closure. Check the driving behaviour as well as the colour.

Read the model responsibilities

DeclarationResponsibility
signalModelModel identity, aspect types and fallback.
checkbox → enabledA setting declared and then read by a rule.
rules → IndicationA decision explained by an aspect and a reason.
imagesImage matching the decision.
drivingDriving instruction matching the decision.
signalMod → signalAssembly of models into the mod.

Develop the model