Leave the last vehicle's coupler at 0 so the train gets its end signals

The simulator hangs end signals only on a last vehicle with coupler 0; every
new car came with 3 and the pass that couples neighbours never touched the
last one, nor did taking a car off the end. The coupler editor says so for
the last vehicle.
This commit is contained in:
maj00r
2026-10-04 14:28:40 +02:00
parent e0901ae0ab
commit 6232930f4c
8 changed files with 93 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
using StarterNG.Classes;
using StarterNG.Domain;
using StarterNG.Domain.Vehicles;
using StarterNG.Tests.Fakes;
namespace StarterNG.Tests;
// The simulator puts end signals only on a last vehicle whose coupler is 0.
public class ConsistTailTests
{
private readonly Consist _consist;
private readonly VehicleCatalog _catalog;
public ConsistTailTests()
{
var installation = new TestInstallation(new InMemoryFileSystem()
.WithFile(TestInstallation.At("dynamic", "pkp", "b111", "textures.txt"),
"!=b\nb111-01.mat=b111,B111\nb111-02.mat=b111,B111"));
installation.Vehicles.Load(installation.Library.Vehicles);
_catalog = installation.Library.Vehicles;
_consist = new Consist(_catalog, new VehicleInfo(_catalog, installation.Physics));
}
[Fact]
public void A_new_last_car_is_left_uncoupled_at_the_back()
{
var first = Append("b111-01");
var last = Append("b111-02");
_consist.AutoConnectAll();
Assert.NotEqual(0, first.Cars[0].Coupling.Flags);
Assert.Equal(0, last.Cars[0].Coupling.Flags);
}
[Fact]
public void Taking_off_the_last_car_frees_the_one_now_at_the_back()
{
var first = Append("b111-01");
var last = Append("b111-02");
_consist.AutoConnectAll();
_consist.Remove(last);
Assert.Equal(0, first.Cars[0].Coupling.Flags);
}
[Fact]
public void Freeing_the_back_keeps_the_brake_setting()
{
var car = Append("b111-01");
car.Cars[0].Coupling = Coupling.Parse("3.BP");
_consist.AutoConnectAll();
Assert.Equal("0.BP", car.Cars[0].Coupling.ToString());
}
private ConsistItem Append(string skin)
{
var item = new ConsistItem
{
Cars = new List<Dynamic> { _consist.MakeDynamic(_catalog.TextureForSkin(skin)!, null) }
};
_consist.Insert(_consist.Count, item);
return item;
}
}

View File

@@ -324,6 +324,10 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
int gap = _items.IndexOf(item);
_items.Remove(item);
// The car now at the end still holds the coupler of the one taken off it.
if (gap == _items.Count)
FreeTail();
if (ReferenceEquals(Selected, item))
Selected = _items.Count == 0
? null
@@ -551,6 +555,8 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
left.Coupling.Flags = left.Coupling.Locked ? -common : common;
}
FreeTail();
}
public bool CanFormUnit(ConsistItem left, ConsistItem right)
@@ -598,6 +604,18 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
public static Dynamic HeadCar(ConsistItem item) =>
item.Flipped ? item.Cars[^1] : item.Cars[0];
/// <summary>
/// Clears the coupler at the back of the consist. The simulator hangs the end
/// signals only on a last vehicle whose coupler is 0 - anything else couples it
/// to whatever stands behind - and every new car comes with 3. Its parameters,
/// the brake setting among them, stay.
/// </summary>
private void FreeTail()
{
if (_items.Count > 0 && _items[^1].Cars.Count > 0)
TailCar(_items[^1]).Coupling.Flags = 0;
}
/// <summary>
/// Whether a car runs turned. Its own sign is the truth - it is what the export
/// writes - and decides which coupler is at which end; a card's flag agrees

View File

@@ -179,6 +179,8 @@ public sealed class VehicleDetails
return;
bool isLast = unitIdx >= _consist.Count - 1;
if (isLast)
couplerPanel.Children.Add(PanelNote(App.Loc["CouplingLastNote"]));
var d = Consist.TailCar(item);

View File

@@ -280,6 +280,7 @@
<String key="TipSplit">Rozdělit jednotku na jednotlivé vozy</String>
<String key="AppliesToUnit">Společné pro celou jednotku</String>
<String key="CouplingUnitRear">Spřáhlo na konci jednotky</String>
<String key="CouplingLastNote">Poslední vozidlo: spřáhlo 0 dává koncovky. Jiný kód spojí soupravu s tím, co stojí za ní, a simulátor koncovky nenasadí.</String>
<String key="Join">Spojit</String>
<String key="TipJoin">Spojit toto vozidlo s následujícím do jedné jednotky</String>
<String key="TipReorder">Přetáhněte pro změnu pořadí</String>

View File

@@ -280,6 +280,7 @@
<String key="TipSplit">Split the unit into individual cars</String>
<String key="AppliesToUnit">Shared by the whole unit</String>
<String key="CouplingUnitRear">Coupler at the rear of the unit</String>
<String key="CouplingLastNote">Last vehicle: coupler 0 gets the end signals. Any other code couples the train to whatever stands behind it, and the simulator puts up no end signals.</String>
<String key="Join">Join</String>
<String key="TipJoin">Couple this vehicle with the next one into a single unit</String>
<String key="TipReorder">Drag to reorder</String>

View File

@@ -280,6 +280,7 @@
<String key="TipSplit">Az egység szétbontása külön kocsikra</String>
<String key="AppliesToUnit">Az egész egységre közös</String>
<String key="CouplingUnitRear">Az egység végén lévő kapcsolókészülék</String>
<String key="CouplingLastNote">Utolsó jármű: a 0-s kapcsolat adja a zárjelzőket. Más kód a mögötte álló járműhöz kapcsolja a szerelvényt, és a szimulátor nem tesz fel zárjelzőt.</String>
<String key="Join">Összekapcsolás</String>
<String key="TipJoin">A jármű összekapcsolása a következővel egy egységgé</String>
<String key="TipReorder">Húzza az átrendezéshez</String>

View File

@@ -280,6 +280,7 @@
<String key="TipSplit">Rozdziel zespół na pojedyncze człony</String>
<String key="AppliesToUnit">Ustawienie wspólne dla całego zespołu</String>
<String key="CouplingUnitRear">Sprzęg na końcu zespołu</String>
<String key="CouplingLastNote">Ostatni pojazd: sprzęg 0 daje końcówki. Inny kod połączy skład z tym, co stoi za nim, a symulator nie założy końcówek.</String>
<String key="Join">Złącz</String>
<String key="TipJoin">Połącz ten pojazd z następnym w jeden zespół</String>
<String key="TipReorder">Przeciągnij, aby zmienić kolejność</String>

View File

@@ -280,6 +280,7 @@
<String key="TipSplit">Разделить сцеп на отдельные вагоны</String>
<String key="AppliesToUnit">Общее для всего состава</String>
<String key="CouplingUnitRear">Сцепка в конце состава</String>
<String key="CouplingLastNote">Последняя единица: сцепка 0 даёт хвостовые сигналы. Другой код сцепит состав с тем, что стоит за ним, и симулятор не поставит хвостовые сигналы.</String>
<String key="Join">Соединить</String>
<String key="TipJoin">Соединить этот вагон со следующим в один состав</String>
<String key="TipReorder">Перетащите для изменения порядка</String>