Create: Computing AgesDoc Brass
Périphériques

Trains

La Station de train, le Signal de train et l'Observateur de train de Create : le train à quai, son planning, où il peut aller, l'assemblage, et les signaux de la ligne.

Un ordinateur posé près d'une Station de train de Create sait quel train est à quai, lequel arrive, et où il pourrait aller depuis là. Il renomme les trains, lit et écrit leurs plannings, les assemble ou les démonte. Les Signaux de train et les Observateurs de train lui racontent ce qui se passe le long de la ligne. Avec eux, un programme fait tourner un dépôt, un tableau des arrivées ou tout un service de trains.

Brass
local gare = peripheral.find("station")
if gare.train_present() then
  print(gare.train_name() .. " est à quai à " .. gare.name())
end
Méthodes
station.name()Le nom de la station, tel qu'il est écrit dans son interface.
station.set_name(name)Renomme la station, comme si vous tapiez le nom dans son interface.
station.train_present()Vrai quand un train s'est arrêté à cette station.
station.train_imminent()Vrai quand le train qui vient ici est à moins de 30 blocs par les rails, ou déjà à quai : le moment de faire sonner une cloche ou d'allumer une lampe au bord du quai.
station.train_enroute()Vrai dès qu'un train a cette station pour prochain arrêt, aussi loin soit-il. La réponse reste vraie tant que le train est à quai, et redevient fausse quand il repart.
station.train_name()Le nom du train arrêté à la station. Arrête le programme avec there is no train present quand le quai est vide : un programme qui veut le nom d'un train qui vient de partir doit le garder dans une variable.
station.set_train_name(name)Renomme le train arrêté à la station. Tous les joueurs voient le nouveau nom aussitôt.
station.has_schedule()Si le train arrêté à la station suit un planning.
station.schedule()Le planning du train arrêté à la station. Arrête le programme avec train doesn't have a schedule quand il n'en a pas.
station.set_schedule(schedule)Donne un planning au train arrêté à la station. Il remplace celui que le train avait, et le train le commence aussitôt, par sa première entrée (ou par l'entrée que désigne une clé progress, comptée à partir de 0, quand la table en a une).
station.can_reach(name)Si le train arrêté à la station peut rejoindre une station qui correspond à name.
station.distance_to(name)La distance que parcourrait le train arrêté à la station pour rejoindre une station qui correspond à name.
station.assembling()Si la station est en mode assemblage. Pendant ce temps, aucun train planifié ne vient à elle.
station.set_assembly_mode(on)Entre en mode assemblage, ou en sort.
station.assemble()Assemble le train posé dans la zone d'assemblage de la station, puis sort du mode assemblage : le nouveau train est prêt à partir.
station.disassemble()Démonte le train arrêté à quai, en blocs posés sur le rail.
sig.state()Le feu que montre le signal en ce moment.
sig.forced_red()Si le signal est tenu au rouge, quoi que fassent les trains.
sig.set_forced_red(red)Tient le signal au rouge, comme le ferait un signal de redstone : les trains attendent devant lui.
sig.blocking_trains()Les trains dans la section qui suit le signal : ceux qu'attend un train arrêté au feu rouge.
sig.signal_type()La sorte de signal :
sig.cycle_signal_type()Fait passer le signal à l'autre sorte, comme le fait la Clé de Create.
obs.train_passing()Si un train passe en ce moment. L'observateur reste allumé quelques ticks après le dernier wagon : en demandant toutes les 0,2 seconde, aucun train ne vous échappe.
obs.passing_train()Le nom du train qui passe, ou nil quand aucun ne passe.

Installation

Trois blocs. La Station de train est un périphérique de type station, le Signal de train de type signal, l'Observateur de train de type track_observer. Posez-en un contre une face de l'ordinateur, ou contre un Câble de données de son réseau (à partir du Mini-ordinateur, il s'appelle alors comme station@120,64,-35), puis prenez-le : peripheral.wrap("left"), ou peripheral.find("station"). Voir Périphériques.

Sur un rail. Une station ou un signal ne fonctionne qu'une fois posé contre un rail, comme Create le pose (clic droit sur le rail avec le bloc d'abord). Sinon, la station arrête le programme avec station is not connected to a track, et le signal avec no signal: place it against a track.

Les trains sont partagés. Un train appartient à tout le serveur : un nom ou un planning changé par un programme est vu aussitôt par tous les joueurs et par toutes les autres stations.

Le train à quai. Beaucoup de méthodes agissent sur le train arrêté à la station. Quand il n'y en a pas, elles arrêtent le programme avec there is no train present : demandez d'abord train_present().

Pas d'évènements : demandez en boucle. Les périphériques ComputerCraft de Create envoient des évènements (train_arrival, train_passing...). Brass n'a pas d'évènements de périphériques : un programme redemande sans cesse, avec un court sleep entre deux questions. Une demi-seconde suffit largement pour des trains.

Brass
local gare = peripheral.find("station")
while not gare.train_present() do
  sleep(0.5)
end
print(gare.train_name() .. " est arrivé")

Pour faire autre chose pendant ce temps, voyez la boucle principale. Quand un bloc est cassé, ses méthodes arrêtent le programme avec station removed, signal removed ou track observer removed, et avec peripheral is not loaded quand son chunk n'est pas chargé.

La station

#

station.name()

→ string

Le nom de la station, tel qu'il est écrit dans son interface.

Renvoie
string
le nom de la station
Brass
local gare = peripheral.find("station")
print("Bienvenue à " .. gare.name())

Voir aussi station.set_name()

#

station.set_name(name)

Renomme la station, comme si vous tapiez le nom dans son interface.

Paramètres
name string
le nouveau nom, coupé à 64 caractères

Les plannings désignent leurs stations par leur nom : un train dont le planning demande l'ancien nom ne trouvera plus la station. Une station qui n'est pas sur un rail ne peut pas être renommée : le programme s'arrête avec could not set station name.

Voir aussi station.name()

#

station.train_present()

→ boolean

Vrai quand un train s'est arrêté à cette station.

Renvoie
boolean
vrai tant qu'un train est arrêté à quai

Un train qui passe sans s'arrêter ne compte pas : surveillez-le plutôt avec un Observateur de train.

Voir aussi station.train_imminent() station.train_enroute() station.train_name()

#

station.train_imminent()

→ boolean

Vrai quand le train qui vient ici est à moins de 30 blocs par les rails, ou déjà à quai : le moment de faire sonner une cloche ou d'allumer une lampe au bord du quai.

Renvoie
boolean
vrai quand un train va arriver, ou est déjà à quai
Brass
local gare = peripheral.find("station")
while true do
  rs.set("top", gare.train_imminent())  -- une lampe au-dessus de l'ordinateur
  sleep(0.5)
end

Voir aussi station.train_present()

#

station.train_enroute()

→ boolean

Vrai dès qu'un train a cette station pour prochain arrêt, aussi loin soit-il. La réponse reste vraie tant que le train est à quai, et redevient fausse quand il repart.

Renvoie
boolean
vrai quand un train est en route vers cette station, ou à quai

Les trois questions répondent vrai l'une après l'autre : train_enroute, puis train_imminent, puis train_present.

Voir aussi station.train_present()

#

station.train_name()

→ string

Le nom du train arrêté à la station. Arrête le programme avec there is no train present quand le quai est vide : un programme qui veut le nom d'un train qui vient de partir doit le garder dans une variable.

Renvoie
string
le nom du train à quai

Voir aussi station.set_train_name()

#

station.set_train_name(name)

Renomme le train arrêté à la station. Tous les joueurs voient le nouveau nom aussitôt.

Paramètres
name string
le nouveau nom, coupé à 64 caractères
Brass
local gare = peripheral.find("station")
if gare.train_present() then
  gare.set_train_name("Express du fer")
end

Voir aussi station.train_name()

Plannings

Un train suit son planning, celui écrit dans un Planning de train : une liste d'arrêts, chacun avec les conditions à attendre avant de repartir. Un programme le lit sous forme de table, et en donne un nouveau au train à quai. La table a la forme des données de planning de Create, avec leurs clés en snake_case (la même forme que le périphérique ComputerCraft de Create) :

Brass
local planning = {
  cyclic = true,
  entries = {
    {
      instruction = {id = "create:destination", data = {text = "Mine de fer"}},
      conditions = {
        { {id = "create:delay", data = {value = 20, time_unit = 1}} },
      },
    },
    {
      instruction = {id = "create:destination", data = {text = "Fonderie"}},
      conditions = {
        { {id = "create:idle", data = {value = 5, time_unit = 1}} },
      },
    },
  },
}
  • cyclic : true recommence à la première entrée après la dernière ; false arrête le train après elle.
  • entries : les étapes, dans l'ordre. Chacune a une instruction, {id =, data =}.
  • conditions, pour une destination : une liste de groupes de conditions. Le train repart dès qu'un groupe est rempli, et un groupe est rempli quand ses conditions le sont, l'une après l'autre. L'interface de Create montre chaque groupe comme une colonne. Même une condition seule est dans son propre groupe : { {condition} }. Un arrêt sans aucune condition y garde le train pour de bon.

Les instructions et conditions courantes :

idce qu'elle faitdata
create:destinationaller à une station (* permis dans le nom)text : le nom de la station
create:renamechanger le titre du planning, le texte que montrent les affichagestext : le nouveau titre
create:throttlelimiter la vitesse maximale du trainvalue : en pourcentage
create:delaycondition : attendre un momentvalue, et time_unit : 0 ticks, 1 secondes, 2 minutes
create:idlecondition : la cargaison n'a pas bougé depuis un momentvalue, time_unit
create:poweredcondition : la station reçoit de la redstoneaucune

Pour toutes les autres, réglez-les dans un Planning de train en jeu, donnez-le à un train, et regardez ce que renvoie schedule().

#

station.has_schedule()

→ boolean

Si le train arrêté à la station suit un planning.

Renvoie
boolean
vrai quand le train à quai a un planning
Brass
local gare = peripheral.find("station")
if gare.train_present() and not gare.has_schedule() then
  print(gare.train_name() .. " n'a pas de planning")
end

Un train garde son planning une fois qu'il l'a fini (un planning qui n'est pas cyclique arrête le train, il ne disparaît pas).

Voir aussi station.schedule()

#

station.schedule()

→ table⚙ coût 16

Le planning du train arrêté à la station. Arrête le programme avec train doesn't have a schedule quand il n'en a pas.

Renvoie
table
le planning du train à quai, de la forme décrite plus haut

Lister les arrêts :

Brass
local gare = peripheral.find("station")
local planning = gare.schedule()
for i, etape in ipairs(planning.entries) do
  if etape.instruction.id == "create:destination" then
    print(i .. ". " .. etape.instruction.data.text)
  end
end

Voir aussi station.set_schedule() station.has_schedule()

#

station.set_schedule(schedule)

Donne un planning au train arrêté à la station. Il remplace celui que le train avait, et le train le commence aussitôt, par sa première entrée (ou par l'entrée que désigne une clé progress, comptée à partir de 0, quand la table en a une).

Paramètres
schedule table
un planning de la même forme que celui que renvoie schedule()

La table compte 4096 valeurs au plus, sur 16 niveaux. Sans aucune entrée, le programme s'arrête avec a schedule needs at least one entry. Quand le train n'avait pas de planning, le nouveau se comporte comme un planning donné par une station : un joueur qui le retire du train ne récupère pas de Planning de train.

Brass
local gare = peripheral.find("station")
local planning = gare.schedule()
planning.cyclic = false  -- encore un tour, puis arrêt
gare.set_schedule(planning)

Voir aussi station.schedule()

Où le train peut-il aller ?

Avant d'envoyer un train, un programme peut demander s'il peut seulement arriver : une station renommée, un rail cassé ou un signal manquant suffisent à couper une ligne. Ces deux méthodes regardent toutes les stations du réseau de rails du train dont le nom correspond à un filtre :

  • * remplace n'importe quel texte, même vide : "Mine*" correspond à Mine, Mine 2 et Mine nord ;
  • ? remplace exactement un caractère : "Quai ?" correspond à Quai 1 jusqu'à Quai 9, pas à Quai 10 ;
  • tout le reste doit être identique, majuscules comprises. Un filtre fait 64 caractères au plus.

Les deux répondent pour le train arrêté à quai (sinon there is no train present), selon la façon dont la navigation de Create le conduirait : un train qui ne roule qu'en marche avant peut ne pas rejoindre une station qu'un autre train atteindrait. C'est la recherche dans le réseau qui les rend coûteuses.

#

station.can_reach(name)

→ boolean⚙ coût 64

Si le train arrêté à la station peut rejoindre une station qui correspond à name.

Paramètres
name string
le nom d'une station, * et ? permis
Renvoie
boolean
vrai quand le train à quai peut rejoindre une station de ce nom
Brass
local gare = peripheral.find("station")
if gare.can_reach("Mine de fer") then
  print("la ligne de la mine est ouverte")
else
  print("aucun chemin vers la mine d'ici")
end

Voir aussi station.distance_to()

#

station.distance_to(name)

→ number|nil⚙ coût 64

La distance que parcourrait le train arrêté à la station pour rejoindre une station qui correspond à name.

Paramètres
name string
le nom d'une station, * et ? permis
Renvoie
number|nil
la distance en blocs par les rails, arrondie à 0,01, ou nil quand aucune station de ce nom n'est accessible

Quand plusieurs stations correspondent, c'est la distance jusqu'à celle que choisirait la navigation, en général la plus proche.

Brass
local gare = peripheral.find("station")
local d = gare.distance_to("Carrière*")
if d then
  print("carrière la plus proche : " .. math.floor(d) .. " blocs")
end

Voir aussi station.can_reach()

Assembler les trains

En mode assemblage, une station transforme les bogies et les blocs collés posés sur son rail en un train, et démonte un train en blocs. Ces méthodes font ce que font les boutons de l'interface de la station.

Comme un joueur qui clique sur ces boutons, une station change une fois par tick au plus : un deuxième set_assembly_mode, assemble ou disassemble sur la même station dans le même tick arrête le programme avec the station already changed this tick: sleep(0) waits for the next. Mettez un sleep(0) entre deux d'entre eux.

#

station.assembling()

→ boolean

Si la station est en mode assemblage. Pendant ce temps, aucun train planifié ne vient à elle.

Renvoie
boolean
vrai tant que la station est en mode assemblage

Voir aussi station.set_assembly_mode()

#

station.set_assembly_mode(on)

Entre en mode assemblage, ou en sort.

Paramètres
on boolean
true pour entrer en mode assemblage, false pour en sortir

Y entrer avec un train à quai démonte ce train, comme disassemble(). Demander le mode dans lequel la station est déjà arrête le programme, avec failed to enter assembly mode ou failed to exit assembly mode : demandez d'abord assembling().

Voir aussi station.assembling() station.assemble()

#

station.assemble()

Assemble le train posé dans la zone d'assemblage de la station, puis sort du mode assemblage : le nouveau train est prêt à partir.

Brass
local gare = peripheral.find("station")
if not gare.assembling() then
  gare.set_assembly_mode(true)
  sleep(0)   -- un changement par tick
end
gare.assemble()
print(gare.train_name() .. " est prêt")

Hors du mode assemblage, elle arrête le programme avec station must be in assembly mode. Quand Create refuse d'assembler (pas de Contrôles de train, un bogie qui manque...), elle s'arrête avec failed to assemble train : l'interface de la station dit pourquoi.

Voir aussi station.set_assembly_mode() station.disassemble()

#

station.disassemble()

Démonte le train arrêté à quai, en blocs posés sur le rail.

La station est ensuite en mode assemblage : appelez assemble() pour remonter le train, ou set_assembly_mode(false) pour rouvrir la station aux trains. Un Planning de train qu'un joueur avait donné au train tombe à la station. Erreurs : station must not be in assembly mode, there is no train present, et could not disassemble train quand Create n'y arrive pas.

Voir aussi station.assemble()

Signal de train

Un Signal de train découpe la voie en sections, et ne laisse un train entrer dans la section qui le suit que si elle est libre. Un programme lit son feu, voit quels trains occupent la section, et peut le garder au rouge.

#

sig.state()

→ string

Le feu que montre le signal en ce moment.

Renvoie
string
"red", "yellow", "green" ou "invalid"

"yellow" n'apparaît que sur un signal de croisement : certaines sorties de sa section sont libres, d'autres non. "invalid" (le feu clignote en jeu) veut dire que le signal n'est pas sur un rail, ou qu'il ne sépare pas deux sections, comme sur une boucle sans autre signal.

Brass
local sig = peripheral.find("signal")
if sig.state() == "red" then
  print("la section suivante est occupée")
end

Voir aussi signal.blocking_trains()

#

sig.forced_red()

→ boolean

Si le signal est tenu au rouge, quoi que fassent les trains.

Renvoie
boolean
vrai quand le signal est forcé au rouge, par un programme ou par la redstone

Voir aussi signal.set_forced_red()

#

sig.set_forced_red(red)

Tient le signal au rouge, comme le ferait un signal de redstone : les trains attendent devant lui.

Paramètres
red boolean
true pour tenir le signal au rouge, false pour le libérer

Le changement se fait à la fin du tick du programme. La redstone garde le dernier mot : quand un bloc voisin du signal change, le signal suit de nouveau son entrée de redstone. Ne mettez pas de redstone autour d'un signal qu'un programme pilote.

Brass
local sig = peripheral.find("signal")
sig.set_forced_red(true)   -- travaux sur la ligne
sleep(120)
sig.set_forced_red(false)

Voir aussi signal.forced_red()

#

sig.blocking_trains()

→ table

Les trains dans la section qui suit le signal : ceux qu'attend un train arrêté au feu rouge.

Renvoie
table
les noms des trains dans la section que garde le signal, une liste vide quand elle est libre
Brass
local sig = peripheral.find("signal")
for _, nom in ipairs(sig.blocking_trains()) do
  print(nom .. " bloque le passage")
end

Voir aussi signal.state()

#

sig.signal_type()

→ string

La sorte de signal :

Renvoie
string
"entry_signal" ou "cross_signal"
  • "entry_signal" (signal d'entrée) laisse entrer un train quand la section est inoccupée ;
  • "cross_signal" (signal de croisement) ne laisse entrer un train que s'il peut traverser toute la section, pour qu'aucun train ne s'arrête au milieu d'un aiguillage ou d'un croisement.

Voir aussi signal.cycle_signal_type()

#

sig.cycle_signal_type()

Fait passer le signal à l'autre sorte, comme le fait la Clé de Create.

Voir aussi signal.signal_type()

Observateur de train

Un Observateur de train surveille un point de la voie et donne un signal de redstone pendant qu'un train passe. Quand un objet est réglé dans son filtre, seuls les trains qui transportent cette cargaison comptent.

#

obs.train_passing()

→ boolean

Si un train passe en ce moment. L'observateur reste allumé quelques ticks après le dernier wagon : en demandant toutes les 0,2 seconde, aucun train ne vous échappe.

Renvoie
boolean
vrai pendant qu'un train passe au point surveillé

Voir aussi track_observer.passing_train()

#

obs.passing_train()

→ string|nil

Le nom du train qui passe, ou nil quand aucun ne passe.

Renvoie
string|nil
le nom du train qui passe, ou nil

Compter les trains d'une ligne :

Brass
local obs = peripheral.find("track_observer")
local compte = 0
local avant = false
while true do
  local maintenant = obs.train_passing()
  if maintenant and not avant then
    compte = compte + 1
    print(compte .. " : " .. (obs.passing_train() or "un train"))
  end
  avant = maintenant
  sleep(0.2)
end

Voir aussi track_observer.train_passing()

Exemples

Un tableau des arrivées

Un ordinateur sur le quai, sans moniteur : son propre écran annonce chaque train. L'heure vient de l'horloge du monde (os.day_time()).

startup
local gare = peripheral.find("station")
local dernier_train = "Le train"

local function heure()
  local t = (os.day_time() + 6000) % 24000
  return string.format("%02d:%02d", t // 1000, t % 1000 * 60 // 1000)
end

local function situation()
  if gare.train_present() then return "a_quai" end
  if gare.train_imminent() then return "imminent" end
  if gare.train_enroute() then return "en_route" end
  return "vide"
end

term.clear()
term.set_cursor(1, 1)
print(gare.name())
local avant = "vide"
while true do
  local maintenant = situation()
  if maintenant ~= avant then
    if avant == "a_quai" then
      print(heure() .. "  " .. dernier_train .. " est parti")
    end
    if maintenant == "a_quai" then
      dernier_train = gare.train_name()
      print(heure() .. "  " .. dernier_train .. " est à quai")
    elseif maintenant == "imminent" then
      print(heure() .. "  arrivée : éloignez-vous du bord")
    elseif maintenant == "en_route" then
      print(heure() .. "  un train est en route")
    end
    avant = maintenant
  end
  sleep(0.5)
end

Le nom du train est gardé dans dernier_train tant qu'il est à quai : une fois parti, train_name() arrêterait le programme.

Un dépôt qui n'envoie les trains que là où ils peuvent aller

Chaque train qui s'arrête au dépôt reçoit un nouveau planning : un aller vers la mine suivante, à tour de rôle, puis le retour au dépôt. Une mine dont la ligne est coupée est sautée, et quand aucune mine n'est accessible, le train attend au dépôt qu'une le redevienne.

startup
local gare = peripheral.find("station")
local DEPOT = gare.name()
local MINES = {"Mine de fer", "Mine de charbon", "Carrière*"}
local tour = 1

-- un arrêt : aller à une station, y attendre quelques secondes
local function arret(station, secondes)
  return {
    instruction = {id = "create:destination", data = {text = station}},
    conditions = {
      { {id = "create:delay", data = {value = secondes, time_unit = 1}} },
    },
  }
end

-- la prochaine mine que le train à quai peut rejoindre, ou nil
local function mine_suivante()
  for i = 1, #MINES do
    local mine = MINES[tour]
    tour = tour % #MINES + 1
    if gare.can_reach(mine) then
      return mine
    end
  end
  return nil
end

local servi = false
while true do
  if not gare.train_present() then
    servi = false
  elseif not servi then
    local mine = mine_suivante()
    if mine then
      local blocs = math.floor(gare.distance_to(mine))
      gare.set_schedule({cyclic = false, entries = {arret(mine, 30), arret(DEPOT, 5)}})
      print(gare.train_name() .. " part pour " .. mine .. ", " .. blocs .. " blocs")
      servi = true
    end
  end
  sleep(2)
end

servi retient que le train à quai a reçu ses ordres, pour qu'il ne les reçoive qu'une fois. Quand il revient de la mine, il s'arrête de nouveau au dépôt et le programme l'envoie à la mine suivante. Un train sans mine accessible est redemandé toutes les deux secondes : un can_reach coûte 64 instructions, un tiers d'un tick de Calculateur à tubes, ne le demandez donc pas à chaque tick.