diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index b5c2d326a6..4ff0510889 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -385,6 +385,7 @@ CommandCost CopyHeadSpecificThings(Vehicle *old_head, Vehicle *new_head, DoComma ChangeVehicleNews(old_head->index, new_head->index); if (old_head->type == VEH_TRAIN) { + Train::From(new_head)->speed_restriction = Train::From(old_head)->speed_restriction; /* Transfer any acquired trace restrict slots to the new vehicle */ if (HasBit(Train::From(old_head)->flags, VRF_HAVE_SLOT)) { TraceRestrictTransferVehicleOccupantInAllSlots(old_head->index, new_head->index); diff --git a/src/lang/english.txt b/src/lang/english.txt index 6063181486..85a50d7e2a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2748,6 +2748,7 @@ STR_TRACE_RESTRICT_COMPANY :Company STR_TRACE_RESTRICT_UNDEFINED_COMPANY :Undefined company STR_TRACE_RESTRICT_SLOT_OP :Slot operation STR_TRACE_RESTRICT_REVERSE :Reverse +STR_TRACE_RESTRICT_SPEED_RESTRICTION :Speed restriction STR_TRACE_RESTRICT_SLOT_ACQUIRE_WAIT :Acquire or wait STR_TRACE_RESTRICT_SLOT_TRY_ACQUIRE :Try to acquire STR_TRACE_RESTRICT_SLOT_RELEASE_FRONT :Release (front) @@ -2778,6 +2779,8 @@ STR_TRACE_RESTRICT_TRAIN_STATUS_LOST :lost STR_TRACE_RESTRICT_TRAIN_STATUS_REQUIRES_SERVICE :requires service STR_TRACE_RESTRICT_REVERSE_SIG :Reverse behind signal STR_TRACE_RESTRICT_REVERSE_SIG_CANCEL :Cancel reverse behind signal +STR_TRACE_RESTRICT_SET_SPEED_RESTRICTION :Restrict train speed to: {VELOCITY} +STR_TRACE_RESTRICT_REMOVE_SPEED_RESTRICTION :Remove train speed restriction STR_TRACE_RESTRICT_VALUE_CAPTION :{WHITE}Value STR_TRACE_RESTRICT_CAPTION :{WHITE}Routefinding restriction STR_TRACE_RESTRICT_CAPTION_SHARED :{WHITE}Routefinding restriction - shared by {COMMA} signals @@ -4566,6 +4569,8 @@ STR_VEHICLE_INFO_CAPACITY_CAPACITY :{BLACK}Capacity STR_VEHICLE_INFO_FEEDER_CARGO_VALUE :{BLACK}Transfer Credits: {LTBLUE}{CURRENCY_LONG} +STR_VEHICLE_INFO_SPEED_RESTRICTION :{BLACK}Speed restriction: {LTBLUE}{VELOCITY} + STR_VEHICLE_DETAILS_SERVICING_INTERVAL_DAYS :{BLACK}Servicing interval: {LTBLUE}{COMMA}{NBSP}days{BLACK} Last service: {LTBLUE}{DATE_LONG} STR_VEHICLE_DETAILS_SERVICING_INTERVAL_PERCENT :{BLACK}Servicing interval: {LTBLUE}{COMMA}%{BLACK} Last service: {LTBLUE}{DATE_LONG} STR_VEHICLE_DETAILS_INCREASE_SERVICING_INTERVAL_TOOLTIP :{BLACK}Increase servicing interval by 10. Ctrl+Click increases servicing interval by 5 diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 4af3c9afbf..757f7814d3 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3357,6 +3357,13 @@ bool AfterLoadGame() } } + if (SlXvIsFeatureMissing(XSLFI_SPEED_RESTRICTION)) { + Train *t; + FOR_ALL_TRAINS(t) { + t->speed_restriction = 0; + } + } + /* * Only keep order-backups for network clients (and when replaying). * If we are a network server or not networking, then we just loaded a previously diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 710ff5792b..f622fa2fc9 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -110,6 +110,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_STATE_CHECKSUM, XSCF_NULL, 1, 1, "state_checksum", nullptr, nullptr, nullptr }, { XSLFI_DEBUG, XSCF_IGNORABLE_ALL, 1, 1, "debug", nullptr, nullptr, "DBGL" }, { XSLFI_FLOW_STAT_FLAGS, XSCF_NULL, 1, 1, "flow_stat_flags", nullptr, nullptr, nullptr }, + { XSLFI_SPEED_RESTRICTION, XSCF_NULL, 1, 1, "speed_restriction", nullptr, nullptr, "VESR" }, { XSLFI_NULL, XSCF_NULL, 0, 0, nullptr, nullptr, nullptr, nullptr },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index ce11d1df25..0e22829e0d 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -76,6 +76,7 @@ enum SlXvFeatureIndex { XSLFI_STATE_CHECKSUM, ///< State checksum XSLFI_DEBUG, ///< Debugging info XSLFI_FLOW_STAT_FLAGS, ///< FlowStat flags + XSLFI_SPEED_RESTRICTION, ///< Train speed restrictions XSLFI_RIFF_HEADER_60_BIT, ///< Size field in RIFF chunk header is 60 bit XSLFI_HEIGHT_8_BIT, ///< Map tile height is 8 bit instead of 4 bit, but savegame version may be before this became true in trunk diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index 2326ad1fde..47976a3cae 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -796,6 +796,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt) SLE_CONDVAR(Train, gv_flags, SLE_UINT16, SLV_139, SL_MAX_VERSION), SLE_CONDNULL(11, SLV_2, SLV_144), // old reserved space SLE_CONDVAR_X(Train, reverse_distance, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REVERSE_AT_WAYPOINT)), + SLE_CONDVAR_X(Train, speed_restriction, SLE_UINT16, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_SPEED_RESTRICTION)), SLE_CONDVAR_X(Train, critical_breakdown_count, SLE_UINT8, SL_MIN_VERSION, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_IMPROVED_BREAKDOWNS, 2)), SLE_END() @@ -1076,7 +1077,46 @@ void Load_VEOX() } } +const SaveLoad *GetVehicleSpeedRestrictionDescription() +{ + static const SaveLoad _vehicle_speed_restriction_desc[] = { + SLE_VAR(PendingSpeedRestrictionChange, distance, SLE_UINT16), + SLE_VAR(PendingSpeedRestrictionChange, new_speed, SLE_UINT16), + SLE_VAR(PendingSpeedRestrictionChange, prev_speed, SLE_UINT16), + SLE_VAR(PendingSpeedRestrictionChange, flags, SLE_UINT16), + SLE_END() + }; + + return _vehicle_speed_restriction_desc; +} + +void Save_VESR() +{ + Train *t; + FOR_ALL_TRAINS(t) { + if (HasBit(t->flags, VRF_PENDING_SPEED_RESTRICTION)) { + auto range = pending_speed_restriction_change_map.equal_range(t->index); + for (auto it = range.first; it != range.second; ++it) { + SlSetArrayIndex(t->index); + PendingSpeedRestrictionChange *ptr = &(it->second); + SlObject(ptr, GetVehicleSpeedRestrictionDescription()); + } + } + } +} + +void Load_VESR() +{ + int index; + while ((index = SlIterateArray()) != -1) { + auto iter = pending_speed_restriction_change_map.insert({ index, {} }); + PendingSpeedRestrictionChange *ptr = &(iter->second); + SlObject(ptr, GetVehicleSpeedRestrictionDescription()); + } +} + extern const ChunkHandler _veh_chunk_handlers[] = { { 'VEHS', Save_VEHS, Load_VEHS, Ptrs_VEHS, nullptr, CH_SPARSE_ARRAY}, - { 'VEOX', Save_VEOX, Load_VEOX, nullptr, nullptr, CH_SPARSE_ARRAY | CH_LAST}, + { 'VEOX', Save_VEOX, Load_VEOX, nullptr, nullptr, CH_SPARSE_ARRAY}, + { 'VESR', Save_VESR, Load_VESR, nullptr, nullptr, CH_SPARSE_ARRAY | CH_LAST}, }; diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 40680fe72c..3146a1956e 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -635,6 +635,12 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp } break; + case TRIT_SPEED_RESTRICTION: { + out.speed_restriction = GetTraceRestrictValue(item); + out.flags |= TRPRF_SPEED_RETRICTION_SET; + break; + } + default: NOT_REACHED(); } @@ -804,6 +810,10 @@ CommandCost TraceRestrictProgram::Validate(const std::vector actions_used_flags |= TRPAUF_REVERSE; break; + case TRIT_SPEED_RESTRICTION: + actions_used_flags |= TRPAUF_SPEED_RESTRICTION; + break; + default: return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION); } diff --git a/src/tracerestrict.h b/src/tracerestrict.h index 2a2d7122f7..24791d7602 100644 --- a/src/tracerestrict.h +++ b/src/tracerestrict.h @@ -142,6 +142,7 @@ enum TraceRestrictItemType { TRIT_COND_END = 48, ///< End (exclusive) of conditional item types, note that this has the same value as TRIT_REVERSE TRIT_REVERSE = 48, ///< Reverse behind signal + TRIT_SPEED_RESTRICTION = 49, ///< Speed restriction /* space up to 63 */ }; @@ -308,6 +309,7 @@ enum TraceRestrictProgramResultFlags { TRPRF_WAIT_AT_PBS = 1 << 3, ///< Wait at PBS signal is set TRPRF_PBS_RES_END_WAIT = 1 << 4, ///< PBS reservations ending at this signal wait is set TRPRF_REVERSE = 1 << 5, ///< Reverse behind signal + TRPRF_SPEED_RETRICTION_SET = 1 << 6, ///< Speed restriction field set }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramResultFlags) @@ -325,11 +327,12 @@ enum TraceRestrictProgramActionsUsedFlags { TRPAUF_PBS_RES_END_WAIT = 1 << 7, ///< PBS reservations ending at this signal wait action is present TRPAUF_PBS_RES_END_SLOT = 1 << 8, ///< PBS reservations ending at this signal slot action is present TRPAUF_REVERSE = 1 << 9, ///< Reverse behind signal + TRPAUF_SPEED_RESTRICTION = 1 << 10, ///< Speed restriction }; DECLARE_ENUM_AS_BIT_SET(TraceRestrictProgramActionsUsedFlags) /** - * Enumeration for TraceRestrictProgram::actions_used_flags + * Enumeration for TraceRestrictProgramInput::permitted_slot_operations */ enum TraceRestrictProgramInputSlotPermissions { TRPISP_ACQUIRE = 1 << 0, ///< Slot acquire is permitted @@ -364,6 +367,7 @@ struct TraceRestrictProgramInput { struct TraceRestrictProgramResult { uint32 penalty; ///< Total additional pathfinder penalty TraceRestrictProgramResultFlags flags; ///< Flags of other actions to take + uint16 speed_restriction; ///> Speed restriction to apply (if TRPRF_SPEED_RETRICTION_SET flag present) TraceRestrictProgramResult() : penalty(0), flags(static_cast(0)) { } @@ -703,6 +707,8 @@ static inline TraceRestrictTypePropertySet GetTraceRestrictTypeProperties(TraceR out.value_type = TRVT_SLOT_INDEX; } else if (GetTraceRestrictType(item) == TRIT_REVERSE) { out.value_type = TRVT_REVERSE; + } else if (GetTraceRestrictType(item) == TRIT_SPEED_RESTRICTION) { + out.value_type = TRVT_SPEED; } else { out.value_type = TRVT_NONE; } diff --git a/src/tracerestrict_gui.cpp b/src/tracerestrict_gui.cpp index 68af7c3979..8af9a1d21d 100644 --- a/src/tracerestrict_gui.cpp +++ b/src/tracerestrict_gui.cpp @@ -151,6 +151,7 @@ static const StringID _program_insert_str[] = { STR_TRACE_RESTRICT_WAIT_AT_PBS, STR_TRACE_RESTRICT_SLOT_OP, STR_TRACE_RESTRICT_REVERSE, + STR_TRACE_RESTRICT_SPEED_RESTRICTION, INVALID_STRING_ID }; static const uint32 _program_insert_else_hide_mask = 8; ///< disable bitmask for else @@ -159,6 +160,7 @@ static const uint32 _program_insert_else_if_hide_mask = 2; ///< disable bitm static const uint32 _program_wait_pbs_hide_mask = 0x100; ///< disable bitmask for wait at PBS static const uint32 _program_slot_hide_mask = 0x200; ///< disable bitmask for slot static const uint32 _program_reverse_hide_mask = 0x400; ///< disable bitmask for reverse +static const uint32 _program_speed_res_hide_mask = 0x800; ///< disable bitmask for speed restriction static const uint _program_insert_val[] = { TRIT_COND_UNDEFINED, // if block TRIT_COND_UNDEFINED | (TRCF_ELSE << 16), // elif block @@ -171,6 +173,7 @@ static const uint _program_insert_val[] = { TRIT_WAIT_AT_PBS, // wait at PBS signal TRIT_SLOT, // slot operation TRIT_REVERSE, // reverse + TRIT_SPEED_RESTRICTION, // speed restriction }; /** insert drop down list strings and values */ @@ -370,6 +373,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG STR_TRACE_RESTRICT_WAIT_AT_PBS, STR_TRACE_RESTRICT_SLOT_OP, STR_TRACE_RESTRICT_REVERSE, + STR_TRACE_RESTRICT_SPEED_RESTRICTION, INVALID_STRING_ID, }; static const uint val_action[] = { @@ -380,6 +384,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG TRIT_WAIT_AT_PBS, TRIT_SLOT, TRIT_REVERSE, + TRIT_SPEED_RESTRICTION, }; static const TraceRestrictDropDownListSet set_action = { str_action, val_action, @@ -441,7 +446,7 @@ static const TraceRestrictDropDownListSet *GetTypeDropDownListSet(TraceRestrictG if (_settings_client.gui.show_adv_tracerestrict_features) { *hide_mask = 0; } else { - *hide_mask = is_conditional ? 0xE0000 : 0x70; + *hide_mask = is_conditional ? 0xE0000 : 0xF0; } } return is_conditional ? &set_cond : &set_action; @@ -1230,6 +1235,15 @@ static void DrawInstructionString(const TraceRestrictProgram *prog, TraceRestric } break; + case TRIT_SPEED_RESTRICTION: + if (GetTraceRestrictValue(item) != 0) { + SetDParam(0, GetTraceRestrictValue(item)); + instruction_string = STR_TRACE_RESTRICT_SET_SPEED_RESTRICTION; + } else { + instruction_string = STR_TRACE_RESTRICT_REMOVE_SPEED_RESTRICTION; + } + break; + default: NOT_REACHED(); break; @@ -1353,7 +1367,7 @@ public: if (ElseIfInsertionDryRun(false)) disabled &= ~_program_insert_or_if_hide_mask; } } - if (!_settings_client.gui.show_adv_tracerestrict_features) hidden |= _program_slot_hide_mask | _program_wait_pbs_hide_mask | _program_reverse_hide_mask; + if (!_settings_client.gui.show_adv_tracerestrict_features) hidden |= _program_slot_hide_mask | _program_wait_pbs_hide_mask | _program_reverse_hide_mask | _program_speed_res_hide_mask; this->ShowDropDownListWithValue(&_program_insert, 0, true, TR_WIDGET_INSERT, disabled, hidden, 0); break; diff --git a/src/train.h b/src/train.h index 18e7d806aa..f7ecb3f96a 100644 --- a/src/train.h +++ b/src/train.h @@ -44,6 +44,7 @@ enum VehicleRailFlags { VRF_ADVANCE_IN_PLATFORM = 18, VRF_CONSIST_BREAKDOWN = 19,///< one or more vehicles in this consist have a breakdown of some sort (breakdown_ctr != 0) VRF_CONSIST_SPEED_REDUCTION = 20,///< one or more vehicles in this consist may be in a depot or on a bridge (may be false positive but not false negative) + VRF_PENDING_SPEED_RESTRICTION = 21,///< This vehicle has one or more pending speed restriction changes VRF_IS_BROKEN = (1 << VRF_BREAKDOWN_POWER) | (1 << VRF_BREAKDOWN_SPEED) | (1 << VRF_BREAKDOWN_STOPPED), ///< Bitmask of all flags that indicate a broken train (braking is not included) }; @@ -120,6 +121,7 @@ struct Train FINAL : public GroundVehicle { uint16 reverse_distance; uint16 tunnel_bridge_signal_num; + uint16 speed_restriction; /** We don't want GCC to zero our struct! It already is zeroed and has an index! */ Train() : GroundVehicleBase() {} diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index d62ad7a142..7d0a788dff 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -617,6 +617,9 @@ int Train::GetCurrentMaxSpeed() const if (HasBit(this->flags, VRF_BREAKDOWN_SPEED)) { max_speed = min(max_speed, this->GetBreakdownSpeed()); } + if (this->speed_restriction != 0) { + max_speed = min(max_speed, this->speed_restriction); + } if (this->current_order.IsType(OT_LOADING_ADVANCE)) max_speed = min(max_speed, 15); @@ -824,6 +827,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const v->track = TRACK_BIT_DEPOT; v->vehstatus = VS_HIDDEN | VS_DEFPAL; v->reverse_distance = 0; + v->speed_restriction = 0; v->SetWagon(); @@ -969,6 +973,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->last_station_visited = INVALID_STATION; v->last_loading_station = INVALID_STATION; v->reverse_distance = 0; + v->speed_restriction = 0; v->engine_type = e->index; v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback @@ -1668,6 +1673,7 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3 /* Copy other important data from the front engine */ new_head->CopyVehicleConfigAndStatistics(first); + new_head->speed_restriction = first->speed_restriction; GroupStatistics::CountVehicle(new_head, 1); // after copying over the profit } else if (v->IsPrimaryVehicle() && data & (MAKE_ORDER_BACKUP_FLAG >> 20)) { OrderBackup::Backup(v, user); @@ -2268,6 +2274,20 @@ void ReverseTrainDirection(Train *v) TryPathReserve(re_reserve_trains[i], true); } + if (HasBit(v->flags, VRF_PENDING_SPEED_RESTRICTION)) { + auto range = pending_speed_restriction_change_map.equal_range(v->index); + for (auto it = range.first; it != range.second;) { + it->second.distance = (v->gcache.cached_total_length + (HasBit(it->second.flags, PSRCF_DIAGONAL) ? 8 : 4)) - it->second.distance; + if (it->second.distance == 0) { + v->speed_restriction = it->second.prev_speed; + it = pending_speed_restriction_change_map.erase(it); + } else { + std::swap(it->second.prev_speed, it->second.new_speed); + ++it; + } + } + } + /* If we are inside a depot after reversing, don't bother with path reserving. */ if (v->track == TRACK_BIT_DEPOT) { /* Can't be stuck here as inside a depot is always a safe tile. */ @@ -4099,7 +4119,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) const Trackdir dir = FindFirstTrackdir(trackdirbits); if (HasSignalOnTrack(gp.new_tile, TrackdirToTrack(dir))) { const TraceRestrictProgram *prog = GetExistingTraceRestrictProgram(gp.new_tile, TrackdirToTrack(dir)); - if (prog && prog->actions_used_flags & (TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_RELEASE_FRONT | TRPAUF_REVERSE)) { + if (prog && prog->actions_used_flags & (TRPAUF_SLOT_ACQUIRE | TRPAUF_SLOT_RELEASE_FRONT | TRPAUF_REVERSE | TRPAUF_SPEED_RESTRICTION)) { TraceRestrictProgramResult out; TraceRestrictProgramInput input(gp.new_tile, dir, nullptr, nullptr); input.permitted_slot_operations = TRPISP_ACQUIRE | TRPISP_RELEASE_FRONT; @@ -4109,6 +4129,17 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) v->reverse_distance = v->gcache.cached_total_length + (IsDiagonalTrack(TrackdirToTrack(dir)) ? 16 : 8); SetWindowDirty(WC_VEHICLE_VIEW, v->index); } + if (out.flags & TRPRF_SPEED_RETRICTION_SET) { + SetBit(v->flags, VRF_PENDING_SPEED_RESTRICTION); + auto range = pending_speed_restriction_change_map.equal_range(v->index); + for (auto it = range.first; it != range.second; ++it) { + if ((uint16) (out.speed_restriction + 0xFFFF) < (uint16) (it->second.new_speed + 0xFFFF)) it->second.new_speed = out.speed_restriction; + } + uint16 flags = 0; + if (IsDiagonalTrack(TrackdirToTrack(dir))) SetBit(flags, PSRCF_DIAGONAL); + pending_speed_restriction_change_map.insert({ v->index, { (uint16) (v->gcache.cached_total_length + (HasBit(flags, PSRCF_DIAGONAL) ? 8 : 4)), out.speed_restriction, v->speed_restriction, flags } }); + if ((uint16) (out.speed_restriction + 0xFFFF) < (uint16) (v->speed_restriction + 0xFFFF)) v->speed_restriction = out.speed_restriction; + } } } } @@ -4370,6 +4401,18 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) if (v->reverse_distance > 1) { v->reverse_distance--; } + if (HasBit(v->flags, VRF_PENDING_SPEED_RESTRICTION)) { + auto range = pending_speed_restriction_change_map.equal_range(v->index); + if (range.first == range.second) ClrBit(v->flags, VRF_PENDING_SPEED_RESTRICTION); + for (auto it = range.first; it != range.second;) { + if (--it->second.distance == 0) { + v->speed_restriction = it->second.new_speed; + it = pending_speed_restriction_change_map.erase(it); + } else { + ++it; + } + } + } /* update the Z position of the vehicle */ int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false, v->track == TRACK_BIT_WORMHOLE); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index b91b8624ad..6e2a1c3b32 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -103,6 +103,8 @@ INSTANTIATE_POOL_METHODS(Vehicle) static btree::btree_set _vehicles_to_pay_repair; static btree::btree_set _vehicles_to_sell; +std::unordered_multimap pending_speed_restriction_change_map; + /** * Determine shared bounds of all sprites. * @param[out] bounds Shared bounds. @@ -985,6 +987,10 @@ void Vehicle::PreDestructor() TraceRestrictRemoveVehicleFromAllSlots(this->index); ClrBit(Train::From(this)->flags, VRF_HAVE_SLOT); } + if (this->type == VEH_TRAIN && HasBit(Train::From(this)->flags, VRF_PENDING_SPEED_RESTRICTION)) { + pending_speed_restriction_change_map.erase(this->index); + ClrBit(Train::From(this)->flags, VRF_PENDING_SPEED_RESTRICTION); + } if (this->Previous() == nullptr) { InvalidateWindowData(WC_VEHICLE_DEPOT, this->tile); @@ -1049,6 +1055,14 @@ Vehicle::~Vehicle() DeleteNewGRFInspectWindow(GetGrfSpecFeature(this->type), this->index); } +/** + * Vehicle pool is about to be cleaned + */ +void Vehicle::PreCleanPool() +{ + pending_speed_restriction_change_map.clear(); +} + /** * Adds a vehicle to the list of vehicles that visited a depot this tick * @param *v vehicle to add diff --git a/src/vehicle_base.h b/src/vehicle_base.h index d5c89ae119..0dfab92823 100644 --- a/src/vehicle_base.h +++ b/src/vehicle_base.h @@ -25,6 +25,7 @@ #include "network/network.h" #include #include +#include CommandCost CmdRefitVehicle(TileIndex, DoCommandFlag, uint32, uint32, const char*); @@ -202,6 +203,18 @@ struct VehicleSpriteSeq { void Draw(int x, int y, PaletteID default_pal, bool force_pal) const; }; +enum PendingSpeedRestrictionChangeFlags { + PSRCF_DIAGONAL = 0, +}; + +struct PendingSpeedRestrictionChange { + uint16 distance; + uint16 new_speed; + uint16 prev_speed; + uint16 flags; +}; +extern std::unordered_multimap pending_speed_restriction_change_map; + /** A vehicle pool for a little over 1 million vehicles. */ typedef Pool VehiclePool; extern VehiclePool _vehicle_pool; @@ -237,6 +250,8 @@ public: friend void AfterLoadVehicles(bool part_of_load); ///< So we can set the #previous and #first pointers while loading friend bool LoadOldVehicle(LoadgameState *ls, int num); ///< So we can set the proper next pointer while loading + static void PreCleanPool(); + TileIndex tile; ///< Current tile index /** diff --git a/src/vehicle_gui.cpp b/src/vehicle_gui.cpp index 48f596d16a..386a2f902b 100644 --- a/src/vehicle_gui.cpp +++ b/src/vehicle_gui.cpp @@ -2364,6 +2364,7 @@ struct VehicleDetailsWindow : Window { bool vehicle_group_line_shown; bool vehicle_weight_ratio_line_shown; bool vehicle_slots_line_shown; + bool vehicle_speed_restriction_line_shown; /** Initialize a newly created vehicle details window */ VehicleDetailsWindow(WindowDesc *desc, WindowNumber window_number) : Window(desc) @@ -2460,6 +2461,12 @@ struct VehicleDetailsWindow : Window { return HasBit(Train::From(v)->flags, VRF_HAVE_SLOT); } + bool ShouldShowSpeedRestrictionLine(const Vehicle *v) const + { + if (v->type != VEH_TRAIN) return false; + return Train::From(v)->speed_restriction != 0; + } + void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize) override { switch (widget) { @@ -2469,10 +2476,12 @@ struct VehicleDetailsWindow : Window { this->vehicle_group_line_shown = ShouldShowGroupLine(v); this->vehicle_weight_ratio_line_shown = ShouldShowWeightRatioLine(v); this->vehicle_slots_line_shown = ShouldShowSlotsLine(v); + this->vehicle_speed_restriction_line_shown = ShouldShowSpeedRestrictionLine(v); int lines = 4; if (this->vehicle_group_line_shown) lines++; if (this->vehicle_weight_ratio_line_shown) lines++; if (this->vehicle_slots_line_shown) lines++; + if (this->vehicle_speed_restriction_line_shown) lines++; size->height = WD_FRAMERECT_TOP + lines * FONT_HEIGHT_NORMAL + WD_FRAMERECT_BOTTOM; for (uint i = 0; i < 5; i++) SetDParamMaxValue(i, INT16_MAX); @@ -2723,9 +2732,17 @@ struct VehicleDetailsWindow : Window { y += FONT_HEIGHT_NORMAL; } + bool should_show_speed_restriction = this->ShouldShowSpeedRestrictionLine(v); + if (should_show_speed_restriction) { + SetDParam(0, Train::From(v)->speed_restriction); + DrawString(r.left + WD_FRAMERECT_LEFT, r.right - WD_FRAMERECT_RIGHT, y, STR_VEHICLE_INFO_SPEED_RESTRICTION); + y += FONT_HEIGHT_NORMAL; + } + if (this->vehicle_weight_ratio_line_shown != should_show_weight_ratio || this->vehicle_weight_ratio_line_shown != should_show_weight_ratio || - this->vehicle_slots_line_shown != should_show_slots) { + this->vehicle_slots_line_shown != should_show_slots || + this->vehicle_speed_restriction_line_shown != should_show_speed_restriction) { const_cast(this)->ReInit(); } break;