diff --git a/src/lang/english.txt b/src/lang/english.txt index 77bfeb5c17..655df00edf 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3442,7 +3442,9 @@ STR_TIMETABLE_TOOLTIP :{BLACK}Timetabl STR_TIMETABLE_NO_TRAVEL :No travel STR_TIMETABLE_NOT_TIMETABLEABLE :Travel (automatic; timetabled by next manual order) STR_TIMETABLE_TRAVEL_NOT_TIMETABLED :Travel (not timetabled) +STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED :Travel with at most {2:VELOCITY} (not timetabled) STR_TIMETABLE_TRAVEL_FOR :Travel for {STRING1} +STR_TIMETABLE_TRAVEL_FOR_SPEED :Travel for {STRING1} with at most {2:VELOCITY} STR_TIMETABLE_STAY_FOR :and stay for {STRING1} STR_TIMETABLE_AND_TRAVEL_FOR :and travel for {STRING1} STR_TIMETABLE_DAYS :{COMMA} day{P "" s} @@ -3466,6 +3468,12 @@ STR_TIMETABLE_WAIT_TIME_TOOLTIP :{BLACK}Change t STR_TIMETABLE_CLEAR_TIME :{BLACK}Clear Time STR_TIMETABLE_CLEAR_TIME_TOOLTIP :{BLACK}Clear the amount of time for the highlighted order +STR_TIMETABLE_CHANGE_SPEED :{BLACK}Change Speed Limit +STR_TIMETABLE_CHANGE_SPEED_TOOLTIP :{BLACK}Change the maximum travel speed of the highlighted order + +STR_TIMETABLE_CLEAR_SPEED :{BLACK}Clear Speed Limit +STR_TIMETABLE_CLEAR_SPEED_TOOLTIP :{BLACK}Clear the maximum travel speed of the highlighted order + STR_TIMETABLE_RESET_LATENESS :{BLACK}Reset Late Counter STR_TIMETABLE_RESET_LATENESS_TOOLTIP :{BLACK}Reset the lateness counter, so the vehicle will be on time diff --git a/src/order_base.h b/src/order_base.h index eaf050cbda..82e1371eec 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -49,8 +49,9 @@ public: uint16 wait_time; ///< How long in ticks to wait at the destination. uint16 travel_time; ///< How long in ticks the journey to this destination should take. + uint16 max_speed; ///< How fast the vehicle may go on the way to the destination. - Order() : refit_cargo(CT_NO_REFIT) {} + Order() : refit_cargo(CT_NO_REFIT), max_speed(UINT16_MAX) {} ~Order(); Order(uint32 packed); diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 824d820453..6846c7af5b 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -241,6 +241,7 @@ Order::Order(uint32 packed) this->refit_subtype = 0; this->wait_time = 0; this->travel_time = 0; + this->max_speed = UINT16_MAX; } /** @@ -281,6 +282,7 @@ void Order::AssignOrder(const Order &other) this->wait_time = other.wait_time; this->travel_time = other.travel_time; + this->max_speed = other.max_speed; } /** diff --git a/src/order_type.h b/src/order_type.h index 0099c67c91..44f93218ba 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -169,6 +169,7 @@ enum OrderDepotAction { enum ModifyTimetableFlags { MTF_WAIT_TIME, ///< Set wait time. MTF_TRAVEL_TIME, ///< Set travel time. + MTF_TRAVEL_SPEED, ///< Set max travel speed. MTF_END }; template <> struct EnumPropsT : MakeEnumPropsT {}; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 0c0157ab12..6a82250502 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -416,7 +416,7 @@ void RoadVehicle::UpdateDeltaXY(Direction direction) */ inline int RoadVehicle::GetCurrentMaxSpeed() const { - if (_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) return this->vcache.cached_max_speed; + if (_settings_game.vehicle.roadveh_acceleration_model == AM_ORIGINAL) return min(this->vcache.cached_max_speed, this->current_order.max_speed * 2); int max_speed = this->vcache.cached_max_speed; @@ -430,7 +430,7 @@ inline int RoadVehicle::GetCurrentMaxSpeed() const } } - return max_speed; + return min(max_speed, this->current_order.max_speed * 2); } /** diff --git a/src/saveload/order_sl.cpp b/src/saveload/order_sl.cpp index e60c35d8da..0437a2d7ac 100644 --- a/src/saveload/order_sl.cpp +++ b/src/saveload/order_sl.cpp @@ -110,6 +110,7 @@ const SaveLoad *GetOrderDescription() SLE_CONDVAR(Order, refit_subtype, SLE_UINT8, 36, SL_MAX_VERSION), SLE_CONDVAR(Order, wait_time, SLE_UINT16, 67, SL_MAX_VERSION), SLE_CONDVAR(Order, travel_time, SLE_UINT16, 67, SL_MAX_VERSION), + SLE_CONDVAR(Order, max_speed, SLE_UINT16, 172, SL_MAX_VERSION), /* Leftover from the minor savegame version stuff * We will never use those free bytes, but we have to keep this line to allow loading of old savegames */ diff --git a/src/saveload/saveload.cpp b/src/saveload/saveload.cpp index eb9c38a289..f768ce6851 100644 --- a/src/saveload/saveload.cpp +++ b/src/saveload/saveload.cpp @@ -235,8 +235,9 @@ * 169 23816 * 170 23826 * 171 23835 + * 172 23947 */ -extern const uint16 SAVEGAME_VERSION = 171; ///< Current savegame version of OpenTTD. +extern const uint16 SAVEGAME_VERSION = 172; ///< Current savegame version of OpenTTD. SavegameType _savegame_type; ///< type of savegame we are loading diff --git a/src/script/api/game/game_window.hpp.sq b/src/script/api/game/game_window.hpp.sq index 395a60c6de..37317fce92 100644 --- a/src/script/api/game/game_window.hpp.sq +++ b/src/script/api/game/game_window.hpp.sq @@ -1107,6 +1107,8 @@ void SQGSWindow_Register(Squirrel *engine) SQGSWindow.DefSQConst(engine, ScriptWindow::WID_VT_SHARED_ORDER_LIST, "WID_VT_SHARED_ORDER_LIST"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_VT_ARRIVAL_DEPARTURE_SELECTION, "WID_VT_ARRIVAL_DEPARTURE_SELECTION"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_VT_EXPECTED_SELECTION, "WID_VT_EXPECTED_SELECTION"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_VT_CHANGE_SPEED, "WID_VT_CHANGE_SPEED"); + SQGSWindow.DefSQConst(engine, ScriptWindow::WID_VT_CLEAR_SPEED, "WID_VT_CLEAR_SPEED"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_PAUSE, "WID_TN_PAUSE"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_FAST_FORWARD, "WID_TN_FAST_FORWARD"); SQGSWindow.DefSQConst(engine, ScriptWindow::WID_TN_SETTINGS, "WID_TN_SETTINGS"); diff --git a/src/script/api/script_window.hpp b/src/script/api/script_window.hpp index b31c738f0d..99f8313f7a 100644 --- a/src/script/api/script_window.hpp +++ b/src/script/api/script_window.hpp @@ -2154,6 +2154,8 @@ public: WID_VT_SHARED_ORDER_LIST = ::WID_VT_SHARED_ORDER_LIST, ///< Show the shared order list. WID_VT_ARRIVAL_DEPARTURE_SELECTION = ::WID_VT_ARRIVAL_DEPARTURE_SELECTION, ///< Disable/hide the arrival departure panel. WID_VT_EXPECTED_SELECTION = ::WID_VT_EXPECTED_SELECTION, ///< Disable/hide the expected selection button. + WID_VT_CHANGE_SPEED = ::WID_VT_CHANGE_SPEED, ///< Change speed limit button. + WID_VT_CLEAR_SPEED = ::WID_VT_CLEAR_SPEED, ///< Clear speed limit button. }; /** Widgets of the #MainToolbarWindow class. */ diff --git a/src/ship_cmd.cpp b/src/ship_cmd.cpp index dc76d9451e..ebb6d9bfff 100644 --- a/src/ship_cmd.cpp +++ b/src/ship_cmd.cpp @@ -342,6 +342,7 @@ static bool ShipAccelerate(Vehicle *v) byte t; spd = min(v->cur_speed + 1, v->vcache.cached_max_speed); + spd = min(spd, v->current_order.max_speed * 2); /* updates statusbar only if speed have changed to save CPU time */ if (spd != v->cur_speed) { diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index d360d81903..5be51c37b5 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -42,6 +42,10 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, order->travel_time = val; break; + case MTF_TRAVEL_SPEED: + order->max_speed = val; + break; + default: NOT_REACHED(); } @@ -58,6 +62,10 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, v->current_order.travel_time = val; break; + case MTF_TRAVEL_SPEED: + v->current_order.max_speed = val; + break; + default: NOT_REACHED(); } @@ -98,6 +106,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u int wait_time = order->wait_time; int travel_time = order->travel_time; + int max_speed = order->max_speed; switch (mtf) { case MTF_WAIT_TIME: wait_time = GB(p2, 0, 16); @@ -107,6 +116,10 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u travel_time = GB(p2, 0, 16); break; + case MTF_TRAVEL_SPEED: + max_speed = GB(p2, 0, 16); + break; + default: NOT_REACHED(); } @@ -125,10 +138,12 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u } if (travel_time != order->travel_time && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; + if (max_speed != order->max_speed && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR; if (flags & DC_EXEC) { if (wait_time != order->wait_time) ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME); if (travel_time != order->travel_time) ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME); + if (max_speed != order->max_speed) ChangeTimetable(v, order_number, max_speed, MTF_TRAVEL_SPEED); } return CommandCost(); diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 287386d72f..41c61d3477 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -162,6 +162,7 @@ struct TimetableWindow : Window { uint deparr_time_width; ///< The width of the departure/arrival time uint deparr_abbr_width; ///< The width of the departure/arrival abbreviation Scrollbar *vscroll; + bool query_is_speed_query; ///< The currently open query window is a speed query and not a time query. TimetableWindow(const WindowDesc *desc, WindowNumber window_number) : Window(), @@ -319,9 +320,12 @@ struct TimetableWindow : Window { disable = order == NULL || ((!order->IsType(OT_GOTO_STATION) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) && !order->IsType(OT_CONDITIONAL)); } } + bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT; this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable); this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable); + this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed); + this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed); this->SetWidgetDisabledState(WID_VT_SHARED_ORDER_LIST, !v->IsOrderListShared()); this->EnableWidget(WID_VT_START_DATE); @@ -331,6 +335,8 @@ struct TimetableWindow : Window { this->DisableWidget(WID_VT_START_DATE); this->DisableWidget(WID_VT_CHANGE_TIME); this->DisableWidget(WID_VT_CLEAR_TIME); + this->DisableWidget(WID_VT_CHANGE_SPEED); + this->DisableWidget(WID_VT_CLEAR_SPEED); this->DisableWidget(WID_VT_RESET_LATENESS); this->DisableWidget(WID_VT_AUTOFILL); this->DisableWidget(WID_VT_SHARED_ORDER_LIST); @@ -391,11 +397,12 @@ struct TimetableWindow : Window { string = STR_TIMETABLE_NOT_TIMETABLEABLE; colour = ((i == selected) ? TC_SILVER : TC_GREY) | TC_NO_SHADE; } else if (order->travel_time == 0) { - string = STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; + string = order->max_speed != UINT16_MAX ? STR_TIMETABLE_TRAVEL_NOT_TIMETABLED_SPEED : STR_TIMETABLE_TRAVEL_NOT_TIMETABLED; } else { SetTimetableParams(0, 1, order->travel_time); - string = STR_TIMETABLE_TRAVEL_FOR; + string = order->max_speed != UINT16_MAX ? STR_TIMETABLE_TRAVEL_FOR_SPEED : STR_TIMETABLE_TRAVEL_FOR; } + SetDParam(2, order->max_speed); DrawString(rtl ? r.left + WD_FRAMERECT_LEFT : middle, rtl ? middle : r.right - WD_FRAMERECT_LEFT, y, string, colour); @@ -490,10 +497,10 @@ struct TimetableWindow : Window { } } - static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected) + static inline uint32 PackTimetableArgs(const Vehicle *v, uint selected, bool speed) { uint order_number = (selected + 1) / 2; - ModifyTimetableFlags mtf = (selected % 2 == 1) ? MTF_TRAVEL_TIME : MTF_WAIT_TIME; + ModifyTimetableFlags mtf = (selected % 2 == 1) ? (speed ? MTF_TRAVEL_SPEED : MTF_TRAVEL_TIME) : MTF_WAIT_TIME; if (order_number >= v->GetNumOrders()) order_number = 0; @@ -540,16 +547,43 @@ struct TimetableWindow : Window { } } + this->query_is_speed_query = false; ShowQueryString(current, STR_TIMETABLE_CHANGE_TIME, 31, this, CS_NUMERAL, QSF_NONE); break; } - case WID_VT_CLEAR_TIME: { // Clear waiting time button. - uint32 p1 = PackTimetableArgs(v, this->sel_index); + case WID_VT_CHANGE_SPEED: { // Change max speed button. + int selected = this->sel_index; + VehicleOrderID real = (selected + 1) / 2; + + if (real >= v->GetNumOrders()) real = 0; + + StringID current = STR_EMPTY; + const Order *order = v->GetOrder(real); + if (order != NULL) { + if (order->max_speed != UINT16_MAX) { + SetDParam(0, ConvertKmhishSpeedToDisplaySpeed(order->max_speed)); + current = STR_JUST_INT; + } + } + + this->query_is_speed_query = true; + ShowQueryString(current, STR_TIMETABLE_CHANGE_SPEED, 31, this, CS_NUMERAL, QSF_NONE); + break; + } + + case WID_VT_CLEAR_TIME: { // Clear waiting time. + uint32 p1 = PackTimetableArgs(v, this->sel_index, false); DoCommandP(0, p1, 0, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); break; } + case WID_VT_CLEAR_SPEED: { // Clear max speed button. + uint32 p1 = PackTimetableArgs(v, this->sel_index, true); + DoCommandP(0, p1, UINT16_MAX, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + break; + } + case WID_VT_RESET_LATENESS: // Reset the vehicle's late counter. DoCommandP(0, v->index, 0, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); break; @@ -580,12 +614,16 @@ struct TimetableWindow : Window { const Vehicle *v = this->vehicle; - uint32 p1 = PackTimetableArgs(v, this->sel_index); + uint32 p1 = PackTimetableArgs(v, this->sel_index, this->query_is_speed_query); - uint64 time = StrEmpty(str) ? 0 : strtoul(str, NULL, 10); - if (!_settings_client.gui.timetable_in_ticks) time *= DAY_TICKS; + uint64 val = StrEmpty(str) ? 0 : strtoul(str, NULL, 10); + if (this->query_is_speed_query) { + val = ConvertDisplaySpeedToKmhishSpeed(val); + } else { + if (!_settings_client.gui.timetable_in_ticks) val *= DAY_TICKS; + } - uint32 p2 = minu(time, UINT16_MAX); + uint32 p2 = minu(val, UINT16_MAX); DoCommandP(0, p1, p2, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); } @@ -628,6 +666,10 @@ static const NWidgetPart _nested_timetable_widgets[] = { NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CHANGE_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CLEAR_TIME), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP), EndContainer(), + NWidget(NWID_VERTICAL, NC_EQUALSIZE), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CHANGE_SPEED), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CHANGE_SPEED, STR_TIMETABLE_CHANGE_SPEED_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_CLEAR_SPEED), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_CLEAR_SPEED, STR_TIMETABLE_CLEAR_SPEED_TOOLTIP), + EndContainer(), NWidget(NWID_VERTICAL, NC_EQUALSIZE), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_START_DATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_STARTING_DATE, STR_TIMETABLE_STARTING_DATE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_RESET_LATENESS), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP), diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 35127ae7f6..4473c5265c 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -404,6 +404,7 @@ int Train::GetCurrentMaxSpeed() const } } + max_speed = min(max_speed, this->current_order.max_speed); return min(max_speed, this->gcache.cached_max_track_speed); } @@ -2748,7 +2749,7 @@ int Train::UpdateSpeed() switch (_settings_game.vehicle.train_acceleration_model) { default: NOT_REACHED(); case AM_ORIGINAL: - return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, this->gcache.cached_max_track_speed); + return this->DoUpdateSpeed(this->acceleration * (this->GetAccelerationStatus() == AS_BRAKE ? -4 : 2), 0, min(this->gcache.cached_max_track_speed, this->current_order.max_speed)); case AM_REALISTIC: return this->DoUpdateSpeed(this->GetAcceleration(), this->GetAccelerationStatus() == AS_BRAKE ? 0 : 2, this->GetCurrentMaxSpeed()); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f40f5070eb..c6abc3f25c 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2202,7 +2202,9 @@ void Vehicle::ShowVisualEffect() const } max_speed = min(max_speed, t->gcache.cached_max_track_speed); + max_speed = min(max_speed, this->current_order.max_speed); } + if (this->type == VEH_ROAD || this->type == VEH_SHIP) max_speed = min(max_speed, this->current_order.max_speed * 2); const Vehicle *v = this; diff --git a/src/widgets/timetable_widget.h b/src/widgets/timetable_widget.h index 1661bb219f..09beb61672 100644 --- a/src/widgets/timetable_widget.h +++ b/src/widgets/timetable_widget.h @@ -29,6 +29,8 @@ enum VehicleTimetableWidgets { WID_VT_SHARED_ORDER_LIST, ///< Show the shared order list. WID_VT_ARRIVAL_DEPARTURE_SELECTION, ///< Disable/hide the arrival departure panel. WID_VT_EXPECTED_SELECTION, ///< Disable/hide the expected selection button. + WID_VT_CHANGE_SPEED, ///< Change speed limit button. + WID_VT_CLEAR_SPEED, ///< Clear speed limit button. }; #endif /* WIDGETS_TIMETABLE_WIDGET_H */