Add game setting to limit train lookahead to signal aspect

In realistic braking mode when multi-aspect signalling enabled by GRF
pull/400/head
Jonathan G Rennison 2 years ago
parent bdd73a19a1
commit 22caac6529

@ -1269,6 +1269,7 @@ STR_CONFIG_SETTING_ORIGINAL :Original
STR_CONFIG_SETTING_REALISTIC :Realistic
STR_CONFIG_SETTING_TRAIN_BRAKING_REALISTIC :Realistic {PUSH_COLOUR}{RED}(Expert){POP_COLOUR}
STR_CONFIG_SETTING_TRAIN_BRAKING_ASPECT_LIMITED_ON :On {PUSH_COLOUR}{RED}(Expert, Difficult){POP_COLOUR}
STR_CONFIG_SETTING_REALISTIC_BRAKING_SIGNALS_NOT_ALLOWED :{WHITE}Realistic braking can't be enabled. At least one pre-signal or two-way signal is present.
@ -1369,6 +1370,8 @@ STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL :Train accelerat
STR_CONFIG_SETTING_TRAIN_ACCELERATION_MODEL_HELPTEXT :Select the physics model for train acceleration. The "original" model penalises slopes equally for all vehicles. The "realistic" model penalises slopes and curves depending on various properties of the consist, like length and tractive effort
STR_CONFIG_SETTING_TRAIN_BRAKING_MODEL :Train braking model: {STRING2}
STR_CONFIG_SETTING_TRAIN_BRAKING_MODEL_HELPTEXT :Select the physics model for train braking. The "original" model allows trains to stop instantly. In the "realistic" model, trains have a stopping distance and will reserve ahead accordingly, trains cannot stop instantly.{}{}The "realistic" model has many implications for signalling and track layout design, and is therefore an advanced feature which may not be suitable for beginners. In particular pre-signals and two-way signals are not permitted, and PBS is used for all signalling.
STR_CONFIG_SETTING_REALISTIC_BRAKING_ASPECT_LIMITED :Realistic train braking is aspect limited: {STRING2}
STR_CONFIG_SETTING_REALISTIC_BRAKING_ASPECT_LIMITED_HELPTEXT :In the realistic train braking model, limit the look-ahead of trains to the aspect of signals (i.e. do not allow reserving and looking ahead an unlimited number of signals).{}This requires a NewGRF which provides multi-aspect signals.{}{}This is an expert setting which will likely increase gameplay difficulty and will require additional thought about signal types and placement.
STR_CONFIG_SETTING_ROAD_VEHICLE_ACCELERATION_MODEL :Road vehicle acceleration model: {STRING2}
STR_CONFIG_SETTING_ROAD_VEHICLE_ACCELERATION_MODEL_HELPTEXT :Select the physics model for road vehicle acceleration. The "original" model penalises slopes equally for all vehicles. The "realistic" model penalises slopes depending on various properties of the engine, for example 'tractive effort'

@ -585,7 +585,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
/* Entrance signal */
lookahead->AddSignal(signal_speed, 0, z);
lookahead->AddSignal(signal_speed, 0, z, 0);
update_z(tile, trackdir, false);
@ -599,7 +599,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
int offset = start_offset - TILE_SIZE;
for (int i = 0; i < signals; i++) {
offset += TILE_SIZE * spacing;
lookahead->AddSignal(signal_speed, offset, chunnel ? LookaheadTileHeightForChunnel(length, i * spacing) : z);
lookahead->AddSignal(signal_speed, offset, chunnel ? LookaheadTileHeightForChunnel(length, i * spacing) : z, 0);
}
/* Exit signal */
@ -613,7 +613,7 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
}
lookahead->AddSignal(signal_speed, end_offset, z);
lookahead->AddSignal(signal_speed, end_offset, z, 0);
lookahead->SetNextExtendPositionIfUnset();
} else {
@ -656,7 +656,11 @@ static PBSTileInfo FollowReservation(Owner o, RailTypes rts, TileIndex tile, Tra
/* Passing through a signal from the front side */
uint16 signal_speed = GetRailTypeInfo(rt)->max_speed;
if (signal_speed == 0 || (speed_restriction != 0 && speed_restriction < signal_speed)) signal_speed = speed_restriction;
lookahead->AddSignal(signal_speed, 0, z);
uint16 signal_flags = 0;
if (_non_aspect_inc_style_mask != 0 && HasBit(_non_aspect_inc_style_mask, GetSignalStyle(tile, TrackdirToTrack(trackdir)))) {
SetBit(signal_flags, TRSLAI_NO_ASPECT_INC);
}
lookahead->AddSignal(signal_speed, 0, z, signal_flags);
lookahead->SetNextExtendPositionIfUnset();
}
}
@ -953,7 +957,7 @@ void TryCreateLookAheadForTrainInTunnelBridge(Train *t)
int offset = -(int)TILE_SIZE;
for (int i = 0; i < signals; i++) {
offset += TILE_SIZE * spacing;
t->lookahead->AddSignal(signal_speed, offset, HasBit(t->lookahead->flags, TRLF_CHUNNEL) ? LookaheadTileHeightForChunnel(length, i * spacing) : z);
t->lookahead->AddSignal(signal_speed, offset, HasBit(t->lookahead->flags, TRLF_CHUNNEL) ? LookaheadTileHeightForChunnel(length, i * spacing) : z, 0);
}
/* Exit signal */
@ -966,7 +970,7 @@ void TryCreateLookAheadForTrainInTunnelBridge(Train *t)
if (signal_speed == 0 || (bridge_speed != 0 && bridge_speed < signal_speed)) signal_speed = bridge_speed;
}
t->lookahead->AddSignal(signal_speed, end_offset, z);
t->lookahead->AddSignal(signal_speed, end_offset, z, 0);
t->lookahead->SetNextExtendPositionIfUnset();
}
@ -979,6 +983,37 @@ void TryCreateLookAheadForTrainInTunnelBridge(Train *t)
}
}
void SetTrainReservationLookaheadEnd(Train *v)
{
if (_settings_game.vehicle.realistic_braking_aspect_limited != TRBALM_ON || _extra_aspects == 0) {
v->lookahead->lookahead_end_position = v->lookahead->reservation_end_position + 1;
return;
}
int32 threshold = v->lookahead->current_position + 24;
uint8 known_signals_ahead = 1;
for (const TrainReservationLookAheadItem &item : v->lookahead->items) {
if (item.type == TRLIT_SIGNAL) {
if (item.start <= threshold) {
/* Signal is within visual range */
uint8 max_aspect = _extra_aspects + (HasBit(item.data_aux, TRSLAI_NO_ASPECT_INC) ? 1 : 2);
if (max_aspect > known_signals_ahead) known_signals_ahead = max_aspect;
}
if (!HasBit(item.data_aux, TRSLAI_NO_ASPECT_INC)) {
known_signals_ahead--;
if (known_signals_ahead == 0) {
if (item.start > v->lookahead->lookahead_end_position) v->lookahead->lookahead_end_position = item.start;
return;
}
}
}
}
/* Didn't need to stop at a signal along the reservation */
v->lookahead->lookahead_end_position = v->lookahead->reservation_end_position;
if (known_signals_ahead > 1) v->lookahead->lookahead_end_position++;
}
void FillTrainReservationLookAhead(Train *v)
{
TileIndex tile;
@ -1033,12 +1068,16 @@ void FillTrainReservationLookAhead(Train *v)
}
if (!(HasAcrossTunnelBridgeReservation(end) && GetTunnelBridgeExitSignalState(end) == SIGNAL_STATE_GREEN && raw_free_tiles == INT_MAX)) {
/* do not attempt to follow through a signalled tunnel/bridge if it is not empty or the far end is not reserved */
SetTrainReservationLookaheadEnd(v);
return;
}
}
}
if (IsRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) return;
if (IsRailDepotTile(tile) && !GetDepotReservationTrackBits(tile)) {
SetTrainReservationLookaheadEnd(v);
return;
}
FollowReservationFlags flags = FRF_NONE;
if (HasBit(v->lookahead->flags, TRLF_TB_EXIT_FREE)) flags |= FRF_TB_EXIT_FREE;
@ -1069,6 +1108,8 @@ void FillTrainReservationLookAhead(Train *v)
v->lookahead->reservation_end_tile = res.tile;
v->lookahead->reservation_end_trackdir = res.trackdir;
SetTrainReservationLookaheadEnd(v);
}
/**

@ -58,11 +58,16 @@ enum TrainReservationLookAheadItemType : byte {
TRLIT_CURVE_SPEED = 5, ///< Curve speed limit
};
enum TrainReservationSignalLookAheadItemFlags {
TRSLAI_NO_ASPECT_INC = 0, ///< This signal does not increase the signal aspect (e.g. banner repeater)
};
struct TrainReservationLookAheadItem {
int32 start;
int32 end;
int16 z_pos;
uint16 data_id;
uint16 data_aux;
TrainReservationLookAheadItemType type;
};
@ -83,6 +88,7 @@ struct TrainReservationLookAhead {
Trackdir reservation_end_trackdir; ///< The reserved trackdir on the end tile.
int32 current_position; ///< Current position of the train on the reservation
int32 reservation_end_position; ///< Position of the end of the reservation
int32 lookahead_end_position; ///< Position of the end of the reservation within the lookahead distance
int32 next_extend_position; ///< Next position to try extending the reservation at the sighting distance of the next mid-reservation signal
int16 reservation_end_z; ///< The z coordinate of the reservation end
int16 tunnel_bridge_reserved_tiles; ///< How many tiles a reservation into the tunnel/bridge currently extends into the wormhole
@ -101,38 +107,38 @@ struct TrainReservationLookAhead {
void AddStation(int tiles, StationID id, int16 z_pos)
{
int end = this->RealEndPosition();
this->items.push_back({ end, end + (((int)TILE_SIZE) * tiles), z_pos, id, TRLIT_STATION });
this->items.push_back({ end, end + (((int)TILE_SIZE) * tiles), z_pos, id, 0, TRLIT_STATION });
}
void AddReverse(int16 z_pos)
{
int end = this->RealEndPosition();
this->items.push_back({ end, end, z_pos, 0, TRLIT_REVERSE });
this->items.push_back({ end, end, z_pos, 0, 0, TRLIT_REVERSE });
}
void AddTrackSpeedLimit(uint16 speed, int offset, int duration, int16 z_pos)
{
int end = this->RealEndPosition();
this->items.push_back({ end + offset, end + offset + duration, z_pos, speed, TRLIT_TRACK_SPEED });
this->items.push_back({ end + offset, end + offset + duration, z_pos, speed, 0, TRLIT_TRACK_SPEED });
}
void AddSpeedRestriction(uint16 speed, int offset, int duration, int16 z_pos)
{
int end = this->RealEndPosition();
this->items.push_back({ end + offset, end + offset + duration, z_pos, speed, TRLIT_SPEED_RESTRICTION });
this->items.push_back({ end + offset, end + offset + duration, z_pos, speed, 0, TRLIT_SPEED_RESTRICTION });
this->speed_restriction = speed;
}
void AddSignal(uint16 target_speed, int offset, int16 z_pos)
void AddSignal(uint16 target_speed, int offset, int16 z_pos, uint16 flags)
{
int end = this->RealEndPosition();
this->items.push_back({ end + offset, end + offset, z_pos, target_speed, TRLIT_SIGNAL });
this->items.push_back({ end + offset, end + offset, z_pos, target_speed, flags, TRLIT_SIGNAL });
}
void AddCurveSpeedLimit(uint16 target_speed, int offset, int16 z_pos)
{
int end = this->RealEndPosition();
this->items.push_back({ end + offset, end + offset, z_pos, target_speed, TRLIT_CURVE_SPEED });
this->items.push_back({ end + offset, end + offset, z_pos, target_speed, 0, TRLIT_CURVE_SPEED });
}
void SetNextExtendPosition();
@ -155,6 +161,7 @@ bool ValidateLookAhead(const Train *v);
PBSTileInfo FollowTrainReservation(const Train *v, Vehicle **train_on_res = nullptr, FollowTrainReservationFlags flags = FTRF_NONE);
void ApplyAvailableFreeTunnelBridgeTiles(TrainReservationLookAhead *lookahead, int free_tiles, TileIndex tile, TileIndex end);
void TryCreateLookAheadForTrainInTunnelBridge(Train *t);
void SetTrainReservationLookaheadEnd(Train *v);
void FillTrainReservationLookAhead(Train *v);
void SetLookAheadNextExtendPosition(Train *v);
void SetLookAheadNextExtendPositionIfUnset(Train *v);

@ -4082,6 +4082,14 @@ bool AfterLoadGame()
_aspect_cfg_hash = 0;
}
if (!SlXvIsFeaturePresent(XSLFI_REALISTIC_TRAIN_BRAKING, 9) && _settings_game.vehicle.train_braking_model == TBM_REALISTIC) {
for (Train *t : Train::Iterate()) {
if (t->lookahead != nullptr) {
t->lookahead->lookahead_end_position = t->lookahead->reservation_end_position + 1;
}
}
}
InitializeRoadGUI();
/* This needs to be done after conversion. */

@ -151,7 +151,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = {
{ XSLFI_ANIMATED_TILE_EXTRA, XSCF_NULL, 1, 1, "animated_tile_extra", nullptr, nullptr, nullptr },
{ XSLFI_NEWGRF_INFO_EXTRA, XSCF_NULL, 1, 1, "newgrf_info_extra", nullptr, nullptr, nullptr },
{ XSLFI_INDUSTRY_CARGO_ADJ, XSCF_IGNORABLE_UNKNOWN, 1, 1, "industry_cargo_adj", nullptr, nullptr, nullptr },
{ XSLFI_REALISTIC_TRAIN_BRAKING,XSCF_NULL, 8, 8, "realistic_train_braking", nullptr, nullptr, "VLKA" },
{ XSLFI_REALISTIC_TRAIN_BRAKING,XSCF_NULL, 9, 9, "realistic_train_braking", nullptr, nullptr, "VLKA" },
{ XSLFI_INFLATION_FIXED_DATES, XSCF_IGNORABLE_ALL, 1, 1, "inflation_fixed_dates", nullptr, nullptr, nullptr },
{ XSLFI_WATER_FLOODING, XSCF_NULL, 2, 2, "water_flooding", nullptr, nullptr, nullptr },
{ XSLFI_MORE_HOUSES, XSCF_NULL, 2, 2, "more_houses", nullptr, nullptr, nullptr },

@ -1387,6 +1387,7 @@ const SaveLoadTable GetVehicleLookAheadDescription()
SLE_VAR(TrainReservationLookAhead, reservation_end_trackdir, SLE_UINT8),
SLE_VAR(TrainReservationLookAhead, current_position, SLE_INT32),
SLE_VAR(TrainReservationLookAhead, reservation_end_position, SLE_INT32),
SLE_CONDVAR_X(TrainReservationLookAhead, lookahead_end_position, SLE_INT32, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 9)),
SLE_VAR(TrainReservationLookAhead, reservation_end_z, SLE_INT16),
SLE_VAR(TrainReservationLookAhead, tunnel_bridge_reserved_tiles, SLE_INT16),
SLE_VAR(TrainReservationLookAhead, flags, SLE_UINT16),
@ -1406,6 +1407,7 @@ const SaveLoadTable GetVehicleLookAheadItemDescription()
SLE_VAR(TrainReservationLookAheadItem, end, SLE_INT32),
SLE_VAR(TrainReservationLookAheadItem, z_pos, SLE_INT16),
SLE_VAR(TrainReservationLookAheadItem, data_id, SLE_UINT16),
SLE_CONDVAR_X(TrainReservationLookAheadItem, data_aux, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REALISTIC_TRAIN_BRAKING, 9)),
SLE_VAR(TrainReservationLookAheadItem, type, SLE_UINT8),
};

@ -1138,6 +1138,7 @@ static void TrainBrakingModelChanged(int32 new_value)
UpdateAllBlockSignals();
InvalidateWindowData(WC_BUILD_SIGNAL, 0);
InvalidateWindowClassesData(WC_GAME_OPTIONS);
}
/**

@ -2018,6 +2018,7 @@ static SettingsContainer &GetSettingsTree()
{
physics->Add(new SettingEntry("vehicle.train_acceleration_model"));
physics->Add(new SettingEntry("vehicle.train_braking_model"));
physics->Add(new ConditionallyHiddenSettingEntry("vehicle.realistic_braking_aspect_limited", []() -> bool { return GetGameSettings().vehicle.train_braking_model != TBM_REALISTIC; }));
physics->Add(new SettingEntry("vehicle.train_slope_steepness"));
physics->Add(new SettingEntry("vehicle.wagon_speed_limits"));
physics->Add(new SettingEntry("vehicle.train_speed_adaptation"));

@ -603,6 +603,7 @@ struct VehicleSettings {
uint8 smoke_amount; ///< amount of smoke/sparks locomotives produce
uint8 train_acceleration_model; ///< realistic acceleration for trains
uint8 train_braking_model; ///< braking model for trains
uint8 realistic_braking_aspect_limited; ///< realistic braking lookahead is aspect limited
uint8 roadveh_acceleration_model; ///< realistic acceleration for road vehicles
uint8 train_slope_steepness; ///< Steepness of hills for trains when using realistic acceleration
uint8 roadveh_slope_steepness; ///< Steepness of hills for road vehicles when using realistic acceleration

@ -225,6 +225,9 @@ class NIHVehicle : public NIHelper {
};
b = buffer + seprintf(buffer, lastof(buffer), " Position: current: %d, z: %d, end: %d, remaining: %d", l.current_position, stats.z_pos, l.reservation_end_position, l.reservation_end_position - l.current_position);
if (l.lookahead_end_position <= l.reservation_end_position) {
b += seprintf(b, lastof(buffer), ", (lookahead: end: %d, remaining: %d)", l.lookahead_end_position, l.lookahead_end_position - l.current_position);
}
if (l.next_extend_position > l.current_position) {
b += seprintf(b, lastof(buffer), ", next extend position: %d (dist: %d)", l.next_extend_position, l.next_extend_position - l.current_position);
}
@ -273,7 +276,12 @@ class NIHVehicle : public NIHelper {
if (item.data_id > 0) print_braking_speed(item.start, item.data_id, item.z_pos);
break;
case TRLIT_SIGNAL:
b += seprintf(b, lastof(buffer), "signal: target speed: %u", item.data_id);
b += seprintf(b, lastof(buffer), "signal: target speed: %u, flags:", item.data_id);
if (HasBit(item.data_aux, TRSLAI_NO_ASPECT_INC)) b += seprintf(b, lastof(buffer), "n");
if (_settings_game.vehicle.realistic_braking_aspect_limited == TRBALM_ON && l.lookahead_end_position == item.start) {
b += seprintf(b, lastof(buffer), ", lookahead end");
print_braking_speed(item.start, 0, item.z_pos);
}
break;
case TRLIT_CURVE_SPEED:
b += seprintf(b, lastof(buffer), "curve speed: %u", item.data_id);

@ -125,6 +125,12 @@ static const SettingDescEnumEntry _train_braking_model[] = {
{ 0, STR_NULL }
};
static const SettingDescEnumEntry _realistic_braking_aspect_limited[] = {
{ TRBALM_OFF, STR_CONFIG_SETTING_OFF },
{ TRBALM_ON, STR_CONFIG_SETTING_TRAIN_BRAKING_ASPECT_LIMITED_ON },
{ 0, STR_NULL }
};
static const SettingDescEnumEntry _station_delivery_mode[] = {
{ SD_NEAREST_FIRST, STR_CONFIG_SETTING_ORIGINAL},
{ SD_BALANCED, STR_CONFIG_SETTING_DELIVERY_BALANCED},
@ -1327,6 +1333,16 @@ post_cb = TrainBrakingModelChanged
cat = SC_EXPERT
patxname = ""realistic_braking.vehicle.train_braking_model""
[SDT_ENUM]
var = vehicle.realistic_braking_aspect_limited
type = SLE_UINT8
def = TRBALM_OFF
enumlist = _realistic_braking_aspect_limited
str = STR_CONFIG_SETTING_REALISTIC_BRAKING_ASPECT_LIMITED
strhelp = STR_CONFIG_SETTING_REALISTIC_BRAKING_ASPECT_LIMITED_HELPTEXT
cat = SC_EXPERT
patxname = ""realistic_braking.vehicle.realistic_braking_aspect_limited""
[SDT_VAR]
var = vehicle.roadveh_acceleration_model
type = SLE_UINT8

@ -958,6 +958,9 @@ static void ApplyLookAheadItem(const Train *v, const TrainReservationLookAheadIt
break;
case TRLIT_SIGNAL:
if (_settings_game.vehicle.realistic_braking_aspect_limited == TRBALM_ON && v->lookahead->lookahead_end_position == item.start) {
limit_advisory_speed(item.start, 0, item.z_pos);
}
break;
case TRLIT_CURVE_SPEED:
@ -982,6 +985,7 @@ static void AdvanceLookAheadPosition(Train *v)
const int32 old_position = v->lookahead->current_position;
v->lookahead->current_position = 0;
v->lookahead->reservation_end_position -= old_position;
v->lookahead->lookahead_end_position -= old_position;
v->lookahead->next_extend_position -= old_position;
for (TrainReservationLookAheadItem &item : v->lookahead->items) {
item.start -= old_position;
@ -1008,6 +1012,7 @@ static void AdvanceLookAheadPosition(Train *v)
}
if (v->lookahead->current_position == v->lookahead->next_extend_position) {
SetTrainReservationLookaheadEnd(v);
TryLongReserveChooseTrainTrackFromReservationEnd(v, true);
v->lookahead->SetNextExtendPositionIfUnset();
}
@ -3990,6 +3995,8 @@ static bool IsReservationLookAheadLongEnough(const Train *v, const ChooseTrainTr
if (v->lookahead->reservation_end_position >= v->lookahead->current_position + v->reverse_distance - 1) return true;
}
if (v->lookahead->lookahead_end_position <= v->lookahead->reservation_end_position && _settings_game.vehicle.realistic_braking_aspect_limited == TRBALM_ON) return true;
TrainDecelerationStats stats(v, v->lookahead->cached_zpos);
bool found_signal = false;
@ -4985,6 +4992,7 @@ static bool IsTooCloseBehindTrain(Train *t, TileIndex tile, uint16 distance, boo
TileIndex end = GetOtherTunnelBridgeEnd(t->tile);
const int raw_free_tiles = GetAvailableFreeTilesInSignalledTunnelBridge(t->tile, end, tile);
ApplyAvailableFreeTunnelBridgeTiles(t->lookahead.get(), raw_free_tiles + ((raw_free_tiles != INT_MAX) ? DistanceManhattan(t->tile, tile) : 0), t->tile, end);
SetTrainReservationLookaheadEnd(t);
if (!LookaheadWithinCurrentTunnelBridge(t)) {
/* Try to extend the reservation beyond the tunnel/bridge exit */

@ -108,6 +108,12 @@ enum TrainBrakingModel {
TBM_REALISTIC,
};
/** Train realistic braking aspect limited mode. */
enum TrainRealisticBrakingAspectLimitedMode {
TRBALM_OFF,
TRBALM_ON,
};
/** Visualisation contexts of vehicles and engines. */
enum EngineImageType {
EIT_ON_MAP = 0x00, ///< Vehicle drawn in viewport.

Loading…
Cancel
Save