From 4575f61cc8d8919571a72c9023b7cd584c81cba1 Mon Sep 17 00:00:00 2001
From: Piotr Warszawski
Date: Fri, 4 Sep 2026 15:52:38 +0200
Subject: [PATCH 1/3] whois: add lookahead mode for the timetable entry after
the next stop
Dispatcher scripts that route trains at a station based on where they
go afterwards (e.g. a big terminal throat with several outbound
directions) have no way to read that: whois +40 only exposes the
current/next timetable entry (NextStop()/IsStop()), which while the
train is at that very station is that station itself.
Adds flags::whois_ahead (bit 6, previously unused) plus
TTrainParameters::StationNameAhead(Offset)/StationIsStopAhead(Offset),
returning the raw TimeTable[] entry Offset positions past StationIndex
(name and whether it's a booked stop, "none"/false past the end of the
timetable). The new whois branch calls both with Offset=1, mirroring
the existing +40 branch's UpdateValues()/WriteLog() shape.
Logic-verified with an isolated harness mirroring the touched types
(TTrainParameters/TController/TMemCell shapes and the exact function
bodies); not yet build-tested against the full engine locally - relying
on CI for that.
---
world/Event.cpp | 31 ++++++++++++++++++++++++++++++-
world/Event.h | 1 +
world/mtable.cpp | 16 ++++++++++++++++
world/mtable.h | 4 ++++
4 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/world/Event.cpp b/world/Event.cpp
index eb679e720..e47dbea23 100644
--- a/world/Event.cpp
+++ b/world/Event.cpp
@@ -1018,13 +1018,42 @@ whois_event::run_() {
auto *targetcell { static_cast( std::get( target ) ) };
if( targetcell == nullptr ) { continue; }
// event effect code
+ // +64: station after the next one, unused, stop there or passthrough
// +40: next station name, unused, stop at next station (duplicate of +0 2nd numeric value)
// +32: vehicle name
// +24: vehicle type, consist brake level, obstacle distance
// +16: load type, load amount, max load amount
// +8: destination, direction, engine power
// +0: train name, station count, stop on next station
- if( m_input.flags & flags::whois_name ) {
+ if( m_input.flags & flags::whois_ahead ) {
+ // +64
+ // the timetable entry one past the next stop, and whether the train stops there
+ auto const *owner { (
+ m_activator->Mechanik != nullptr && m_activator->Mechanik->primary() ?
+ m_activator->Mechanik :
+ m_activator->ctOwner ) };
+ auto const aheadname { (
+ owner != nullptr ?
+ owner->TrainTimetable().StationNameAhead( 1 ) :
+ "none" ) };
+ auto const aheadstop { (
+ owner != nullptr && owner->TrainTimetable().StationIsStopAhead( 1 ) ?
+ 1 :
+ 0 ) };
+
+ targetcell->UpdateValues(
+ aheadname, // station after the next one
+ 0, // unused
+ aheadstop, // stop there or passthrough
+ m_input.flags & ( flags::text | flags::value1 | flags::value2 ) );
+
+ WriteLog(
+ "Type: WhoIs (" + std::to_string( m_input.flags ) + ") - "
+ + "[station after next: " + aheadname + "], "
+ + "[X], "
+ + "[stop there: " + ( aheadstop != 0 ? "yes" : "no" ) + "]" );
+ }
+ else if( m_input.flags & flags::whois_name ) {
// +32 or +40
// next station name
if( m_input.flags & flags::mode_alt ) {
diff --git a/world/Event.h b/world/Event.h
index 52ee3468d..55be65de1 100644
--- a/world/Event.h
+++ b/world/Event.h
@@ -37,6 +37,7 @@ public:
mode_alt = 1 << 3,
whois_load = 1 << 4,
whois_name = 1 << 5,
+ whois_ahead = 1 << 6,
// condition values
track_busy = 1 << 3,
track_free = 1 << 4,
diff --git a/world/mtable.cpp b/world/mtable.cpp
index cec54f870..75e6c8ad0 100644
--- a/world/mtable.cpp
+++ b/world/mtable.cpp
@@ -77,6 +77,22 @@ bool TTrainParameters::IsLastStop() const {
return StationIndex >= StationCount;
}
+std::string TTrainParameters::StationNameAhead( int Offset ) const
+{ // nazwa wpisu rozkladu Offset pozycji za aktualnym (bez pomijania przelotow)
+ int const idx { StationIndex + Offset };
+ if( ( idx < 1 ) || ( idx > StationCount ) )
+ return "none";
+ return TimeTable[idx].StationName;
+}
+
+bool TTrainParameters::StationIsStopAhead( int Offset ) const
+{ // czy wpis Offset pozycji za aktualnym to zaplanowany postoj
+ int const idx { StationIndex + Offset };
+ if( ( idx < 1 ) || ( idx > StationCount ) )
+ return false;
+ return TimeTable[idx].Ah >= 0; //-1 to brak postoju
+}
+
bool TTrainParameters::IsMaintenance() const {
if( StationIndex <= StationCount )
diff --git a/world/mtable.h b/world/mtable.h
index c58d120ff..701b0c6ca 100644
--- a/world/mtable.h
+++ b/world/mtable.h
@@ -70,6 +70,10 @@ class TTrainParameters
std::string ShowRelation() const;
double WatchMTable(double DistCounter);
std::string NextStop() const;
+ // nazwa wpisu rozkladu Offset pozycji za StationIndex (Offset>=1); "none" poza koncem rozkladu
+ std::string StationNameAhead( int Offset ) const;
+ // true, gdy ten wpis to zaplanowany postoj (ma godzine przyjazdu), false dla przelotu / poza koncem
+ bool StationIsStopAhead( int Offset ) const;
sound_source next_stop_sound() const;
sound_source last_stop_sound() const;
bool IsStop() const;
From 5705062cd29826b9bb1386eb2ccfa5be258a3204 Mon Sep 17 00:00:00 2001
From: Piotr Warszawski
Date: Fri, 4 Sep 2026 20:28:28 +0200
Subject: [PATCH 2/3] whois: select the lookahead mode via +48 instead of a new
bit
Per review: +48 (whois_name | whois_load, mode_alt clear) was an
unused combination of the existing mode bits - use that instead of
claiming a new bit 6. +56 (same, with mode_alt set) stays free for a
future mode. No new flags::* entry needed; mtable.h/.cpp unchanged.
---
world/Event.cpp | 8 +++++---
world/Event.h | 1 -
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/world/Event.cpp b/world/Event.cpp
index e47dbea23..ebfc73958 100644
--- a/world/Event.cpp
+++ b/world/Event.cpp
@@ -1018,15 +1018,17 @@ whois_event::run_() {
auto *targetcell { static_cast( std::get( target ) ) };
if( targetcell == nullptr ) { continue; }
// event effect code
- // +64: station after the next one, unused, stop there or passthrough
+ // +48: station after the next one, unused, stop there or passthrough
// +40: next station name, unused, stop at next station (duplicate of +0 2nd numeric value)
// +32: vehicle name
// +24: vehicle type, consist brake level, obstacle distance
// +16: load type, load amount, max load amount
// +8: destination, direction, engine power
// +0: train name, station count, stop on next station
- if( m_input.flags & flags::whois_ahead ) {
- // +64
+ if( ( m_input.flags & ( flags::whois_name | flags::whois_load | flags::mode_alt ) )
+ == ( flags::whois_name | flags::whois_load ) ) {
+ // +48 (the whois_name | whois_load combination was unused; +56, same with mode_alt
+ // set, stays free for a future mode)
// the timetable entry one past the next stop, and whether the train stops there
auto const *owner { (
m_activator->Mechanik != nullptr && m_activator->Mechanik->primary() ?
diff --git a/world/Event.h b/world/Event.h
index 55be65de1..52ee3468d 100644
--- a/world/Event.h
+++ b/world/Event.h
@@ -37,7 +37,6 @@ public:
mode_alt = 1 << 3,
whois_load = 1 << 4,
whois_name = 1 << 5,
- whois_ahead = 1 << 6,
// condition values
track_busy = 1 << 3,
track_free = 1 << 4,
From 93463a3850e50f8818d12c042a6ef7e3639b84f3 Mon Sep 17 00:00:00 2001
From: Piotr Warszawski
Date: Fri, 4 Sep 2026 21:24:01 +0200
Subject: [PATCH 3/3] whois: consolidate the two lookahead accessors into one
Per review: StationNameAhead()/StationIsStopAhead() duplicated the same
bounds check for no reason. Replaced with a single
TimeTableEntryAhead(Offset) returning the TMTableLine itself; the whois
branch reads .StationName/.Ah off it directly.
---
world/Event.cpp | 12 +++++-------
world/mtable.cpp | 17 +++++------------
world/mtable.h | 7 +++----
3 files changed, 13 insertions(+), 23 deletions(-)
diff --git a/world/Event.cpp b/world/Event.cpp
index ebfc73958..319ad0591 100644
--- a/world/Event.cpp
+++ b/world/Event.cpp
@@ -1034,14 +1034,12 @@ whois_event::run_() {
m_activator->Mechanik != nullptr && m_activator->Mechanik->primary() ?
m_activator->Mechanik :
m_activator->ctOwner ) };
- auto const aheadname { (
+ auto const *entry { (
owner != nullptr ?
- owner->TrainTimetable().StationNameAhead( 1 ) :
- "none" ) };
- auto const aheadstop { (
- owner != nullptr && owner->TrainTimetable().StationIsStopAhead( 1 ) ?
- 1 :
- 0 ) };
+ &owner->TrainTimetable().TimeTableEntryAhead( 1 ) :
+ nullptr ) };
+ auto const aheadname { ( entry != nullptr ? entry->StationName : "none" ) };
+ auto const aheadstop { ( entry != nullptr && entry->Ah >= 0 ? 1 : 0 ) };
targetcell->UpdateValues(
aheadname, // station after the next one
diff --git a/world/mtable.cpp b/world/mtable.cpp
index 75e6c8ad0..dd84b6c23 100644
--- a/world/mtable.cpp
+++ b/world/mtable.cpp
@@ -77,20 +77,13 @@ bool TTrainParameters::IsLastStop() const {
return StationIndex >= StationCount;
}
-std::string TTrainParameters::StationNameAhead( int Offset ) const
-{ // nazwa wpisu rozkladu Offset pozycji za aktualnym (bez pomijania przelotow)
+TMTableLine const &TTrainParameters::TimeTableEntryAhead( int Offset ) const
+{ // wpis rozkladu Offset pozycji za aktualnym (bez pomijania przelotow);
+ // poza zakresem 1..StationCount zwraca techniczny wpis zerowy
int const idx { StationIndex + Offset };
if( ( idx < 1 ) || ( idx > StationCount ) )
- return "none";
- return TimeTable[idx].StationName;
-}
-
-bool TTrainParameters::StationIsStopAhead( int Offset ) const
-{ // czy wpis Offset pozycji za aktualnym to zaplanowany postoj
- int const idx { StationIndex + Offset };
- if( ( idx < 1 ) || ( idx > StationCount ) )
- return false;
- return TimeTable[idx].Ah >= 0; //-1 to brak postoju
+ return TimeTable[0];
+ return TimeTable[idx];
}
diff --git a/world/mtable.h b/world/mtable.h
index 701b0c6ca..2d20a27bb 100644
--- a/world/mtable.h
+++ b/world/mtable.h
@@ -70,10 +70,9 @@ class TTrainParameters
std::string ShowRelation() const;
double WatchMTable(double DistCounter);
std::string NextStop() const;
- // nazwa wpisu rozkladu Offset pozycji za StationIndex (Offset>=1); "none" poza koncem rozkladu
- std::string StationNameAhead( int Offset ) const;
- // true, gdy ten wpis to zaplanowany postoj (ma godzine przyjazdu), false dla przelotu / poza koncem
- bool StationIsStopAhead( int Offset ) const;
+ // wpis rozkladu Offset pozycji za StationIndex (Offset>=1); poza zakresem 1..StationCount
+ // zwraca techniczny wpis zerowy (TimeTable[0], StationName=="nowhere", Ah==-1)
+ TMTableLine const &TimeTableEntryAhead( int Offset ) const;
sound_source next_stop_sound() const;
sound_source last_stop_sound() const;
bool IsStop() const;