diff --git a/src/lang/english.txt b/src/lang/english.txt index a835ddca22..512499c061 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4689,6 +4689,8 @@ STR_TIMETABLE_EXPECTED :{BLACK}Expected STR_TIMETABLE_SCHEDULED :{BLACK}Scheduled STR_TIMETABLE_EXPECTED_TOOLTIP :{BLACK}Switch between expected and scheduled +STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP :{BLACK}Lock/unlock the amount of time for the highlighted order (Ctrl+Click set lock state for all orders).{}When locked the time will not be changed by timetable autofill or automate. + STR_TIMETABLE_ARRIVAL_ABBREVIATION :A: STR_TIMETABLE_DEPARTURE_ABBREVIATION :D: diff --git a/src/order_base.h b/src/order_base.h index 620c927168..261e91703d 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -423,6 +423,15 @@ public: */ inline void SetMaxSpeed(uint16 speed) { this->max_speed = speed; } + /** Does this order have a fixed wait time? */ + inline bool IsWaitFixed() const { return HasBit(this->GetXFlags(), 1); } + + /** Set if the wait time is fixed */ + inline void SetWaitFixed(bool fixed) + { + if (!this->IsType(OT_CONDITIONAL) && fixed != IsWaitFixed()) SB(this->GetXFlagsRef(), 1, 1, fixed ? 1 : 0); + } + /** * Get the occupancy value * @return occupancy diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 6934224bd6..4fa9f62672 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -732,6 +732,8 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int if (GetOrderDistance(order, next, v) > Aircraft::From(v)->acache.cached_max_range_sqr) SetDParam(10, STR_ORDER_OUT_OF_RANGE); } + bool timetable_wait_time_valid = false; + switch (order->GetType()) { case OT_DUMMY: SetDParam(0, STR_INVALID_ORDER); @@ -760,6 +762,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_STAY_FOR : STR_TIMETABLE_STAY_FOR_ESTIMATED); SetTimetableParams(6, order->GetWaitTime()); } + timetable_wait_time_valid = true; } else { SetDParam(3, (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) ? STR_EMPTY : _station_load_types[order->IsRefit()][unload][load]); if (order->IsRefit()) { @@ -812,6 +815,7 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int SetDParam(5, order->IsWaitTimetabled() ? STR_TIMETABLE_STAY_FOR : STR_TIMETABLE_STAY_FOR_ESTIMATED); SetTimetableParams(6, order->GetWaitTime()); } + timetable_wait_time_valid = !(order->GetDepotActionType() & ODATFB_HALT); } break; @@ -887,7 +891,20 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int default: NOT_REACHED(); } - DrawString(rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT, colour); + int edge = DrawString(rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT, colour); + + if (timetable && timetable_wait_time_valid && order->IsWaitFixed()) { + Dimension lock_d = GetSpriteSize(SPR_LOCK); + DrawPixelInfo tmp_dpi; + if (FillDrawPixelInfo(&tmp_dpi, rtl ? left : middle, y, rtl ? middle - left : right - middle, lock_d.height)) { + DrawPixelInfo *old_dpi = _cur_dpi; + _cur_dpi = &tmp_dpi; + + DrawSprite(SPR_LOCK, PAL_NONE, rtl ? edge - 3 - lock_d.width - left : edge + 3 - middle, 0); + + _cur_dpi = old_dpi; + } + } } /** diff --git a/src/order_type.h b/src/order_type.h index 1ed6712e18..83f1e96b7b 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -203,6 +203,7 @@ enum ModifyTimetableFlags { MTF_WAIT_TIME, ///< Set wait time. MTF_TRAVEL_TIME, ///< Set travel time. MTF_TRAVEL_SPEED, ///< Set max travel speed. + MTF_SET_WAIT_FIXED,///< Set wait time fixed flag state. MTF_END }; template <> struct EnumPropsT : MakeEnumPropsT {}; diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 5c90487dec..66bbaea1a7 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -80,7 +80,7 @@ const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { { XSLFI_SCHEDULED_DISPATCH, XSCF_NULL, 1, 1, "scheduled_dispatch", NULL, NULL, NULL }, { XSLFI_MORE_TOWN_GROWTH_RATES, XSCF_NULL, 1, 1, "more_town_growth_rates", NULL, NULL, NULL }, { XSLFI_MULTIPLE_DOCKS, XSCF_NULL, 1, 1, "multiple_docks", NULL, NULL, "DOCK" }, - { XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 1, 1, "timetable_extra", NULL, NULL, "ORDX" }, + { XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 2, 2, "timetable_extra", NULL, NULL, "ORDX" }, { XSLFI_TRAIN_FLAGS_EXTRA, XSCF_NULL, 1, 1, "train_flags_extra", NULL, NULL, NULL }, { XSLFI_TRAIN_THROUGH_LOAD, XSCF_NULL, 2, 2, "train_through_load", NULL, NULL, NULL }, { XSLFI_ORDER_EXTRA_DATA, XSCF_NULL, 1, 1, "order_extra_data", NULL, NULL, NULL }, diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 2af271b4c7..e4e34471fb 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -34,8 +34,9 @@ * @param val The new data of the timetable entry. * @param mtf Which part of the timetable entry to change. * @param timetabled If the new value is explicitly timetabled. + * @param ignore_lock If the change should be applied even if the value is locked. */ -static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, ModifyTimetableFlags mtf, bool timetabled) +static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, ModifyTimetableFlags mtf, bool timetabled, bool ignore_lock = false) { Order *order = v->GetOrder(order_number); int total_delta = 0; @@ -43,6 +44,7 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, switch (mtf) { case MTF_WAIT_TIME: + if (!ignore_lock && order->IsWaitFixed()) return; if (!order->IsType(OT_CONDITIONAL)) { total_delta = val - order->GetWaitTime(); timetable_delta = (timetabled ? val : 0) - order->GetTimetabledWait(); @@ -65,6 +67,10 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, order->SetMaxSpeed(val); break; + case MTF_SET_WAIT_FIXED: + order->SetWaitFixed(val != 0); + break; + default: NOT_REACHED(); } @@ -88,6 +94,10 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, v->current_order.SetMaxSpeed(val); break; + case MTF_SET_WAIT_FIXED: + v->current_order.SetWaitFixed(val != 0); + break; + default: NOT_REACHED(); } @@ -133,6 +143,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u int wait_time = order->GetWaitTime(); int travel_time = order->GetTravelTime(); int max_speed = order->GetMaxSpeed(); + bool wait_fixed = order->IsWaitFixed(); switch (mtf) { case MTF_WAIT_TIME: wait_time = GB(p2, 0, 16); @@ -149,6 +160,10 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (max_speed == 0) max_speed = UINT16_MAX; // Disable speed limit. break; + case MTF_SET_WAIT_FIXED: + wait_fixed = GB(p2, 0, 16) != 0; + break; + default: NOT_REACHED(); } @@ -171,26 +186,33 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u if (travel_time != order->GetTravelTime() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (max_speed != order->GetMaxSpeed() && (order->IsType(OT_CONDITIONAL) || v->type == VEH_AIRCRAFT)) return CMD_ERROR; + if (wait_fixed != order->IsWaitFixed() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (flags & DC_EXEC) { switch (mtf) { case MTF_WAIT_TIME: /* Set time if changing the value or confirming an estimated time as timetabled. */ if (wait_time != order->GetWaitTime() || (clear_field == order->IsWaitTimetabled())) { - ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME, !clear_field); + ChangeTimetable(v, order_number, wait_time, MTF_WAIT_TIME, !clear_field, true); } break; case MTF_TRAVEL_TIME: /* Set time if changing the value or confirming an estimated time as timetabled. */ if (travel_time != order->GetTravelTime() || (clear_field == order->IsTravelTimetabled())) { - ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME, !clear_field); + ChangeTimetable(v, order_number, travel_time, MTF_TRAVEL_TIME, !clear_field, true); } break; case MTF_TRAVEL_SPEED: if (max_speed != order->GetMaxSpeed()) { - ChangeTimetable(v, order_number, max_speed, MTF_TRAVEL_SPEED, max_speed != UINT16_MAX); + ChangeTimetable(v, order_number, max_speed, MTF_TRAVEL_SPEED, max_speed != UINT16_MAX, true); + } + break; + + case MTF_SET_WAIT_FIXED: + if (wait_fixed != order->IsWaitFixed()) { + ChangeTimetable(v, order_number, wait_fixed ? 1 : 0, MTF_SET_WAIT_FIXED, false, true); } break; diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 7045188326..8a07b66d0b 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -244,7 +244,7 @@ struct TimetableWindow : Window { case WID_VT_ARRIVAL_DEPARTURE_SELECTION: case WID_VT_TIMETABLE_PANEL: - resize->height = FONT_HEIGHT_NORMAL; + resize->height = max(FONT_HEIGHT_NORMAL, GetSpriteSize(SPR_LOCK).height); size->height = WD_FRAMERECT_TOP + 8 * resize->height + WD_FRAMERECT_BOTTOM; break; @@ -357,19 +357,25 @@ struct TimetableWindow : Window { if (v->owner == _local_company) { bool disable = true; + bool wait_lockable = false; + bool wait_locked = false; if (selected != -1) { const Order *order = v->GetOrder(((selected + 1) / 2) % v->GetNumOrders()); if (selected % 2 == 1) { + /* Travel time */ disable = order != NULL && (order->IsType(OT_CONDITIONAL) || order->IsType(OT_IMPLICIT)); } else { + /* Wait time */ disable = (order == NULL) || ((!(order->IsType(OT_GOTO_STATION) || (order->IsType(OT_GOTO_DEPOT) && !(order->GetDepotActionType() & ODATFB_HALT))) || (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) && !order->IsType(OT_CONDITIONAL)); + wait_lockable = !disable; + wait_locked = wait_lockable && order->IsWaitFixed(); } } bool disable_speed = disable || selected % 2 != 1 || v->type == VEH_AIRCRAFT; - this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable || HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)); + this->SetWidgetDisabledState(WID_VT_CHANGE_TIME, disable || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !wait_locked)); this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable || HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)); this->SetWidgetDisabledState(WID_VT_CHANGE_SPEED, disable_speed); this->SetWidgetDisabledState(WID_VT_CLEAR_SPEED, disable_speed); @@ -381,6 +387,8 @@ struct TimetableWindow : Window { this->SetWidgetDisabledState(WID_VT_AUTO_SEPARATION, HasBit(v->vehicle_flags, VF_SCHEDULED_DISPATCH)); this->EnableWidget(WID_VT_AUTOMATE); this->EnableWidget(WID_VT_ADD_VEH_GROUP); + this->SetWidgetDisabledState(WID_VT_LOCK_ORDER_TIME, !wait_lockable); + this->SetWidgetLoweredState(WID_VT_LOCK_ORDER_TIME, wait_locked); } else { this->DisableWidget(WID_VT_START_DATE); this->DisableWidget(WID_VT_CHANGE_TIME); @@ -393,6 +401,7 @@ struct TimetableWindow : Window { this->DisableWidget(WID_VT_AUTO_SEPARATION); this->DisableWidget(WID_VT_SHARED_ORDER_LIST); this->DisableWidget(WID_VT_ADD_VEH_GROUP); + this->DisableWidget(WID_VT_LOCK_ORDER_TIME); } this->SetWidgetLoweredState(WID_VT_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); @@ -422,6 +431,8 @@ struct TimetableWindow : Window { case WID_VT_TIMETABLE_PANEL: { int y = r.top + WD_FRAMERECT_TOP; int i = this->vscroll->GetPosition(); + Dimension lock_d = GetSpriteSize(SPR_LOCK); + int line_height = max(FONT_HEIGHT_NORMAL, lock_d.height); VehicleOrderID order_id = (i + 1) / 2; bool final_order = false; @@ -478,7 +489,7 @@ struct TimetableWindow : Window { } i++; - y += FONT_HEIGHT_NORMAL; + y += line_height; } break; } @@ -766,6 +777,23 @@ struct TimetableWindow : Window { break; } + case WID_VT_LOCK_ORDER_TIME: { // Toggle order wait time lock state. + bool locked = false; + + int selected = this->sel_index; + VehicleOrderID order_number = (selected + 1) / 2; + if (order_number >= v->GetNumOrders()) order_number = 0; + + const Order *order = v->GetOrder(order_number); + if (order != NULL) { + locked = order->IsWaitFixed(); + } + + uint32 p1 = v->index | (order_number << 20) | (MTF_SET_WAIT_FIXED << 28); + DoCommandP(0, p1, locked ? 0 : 1, (_ctrl_pressed ? CMD_BULK_CHANGE_TIMETABLE : 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; @@ -950,7 +978,7 @@ static const NWidgetPart _nested_timetable_widgets[] = { NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VT_SHARED_ORDER_LIST), SetFill(0, 1), SetDataTip(SPR_SHARED_ORDERS_ICON, STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_ADD_VEH_GROUP), SetFill(0, 1), SetDataTip(STR_BLACK_PLUS, STR_ORDERS_NEW_GROUP_TOOLTIP), EndContainer(), - NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), + NWidget(WWT_PUSHIMGBTN, COLOUR_GREY, WID_VT_LOCK_ORDER_TIME), SetFill(0, 1), SetDataTip(SPR_LOCK, STR_TIMETABLE_LOCK_ORDER_TIME_TOOLTIP), NWidget(WWT_RESIZEBOX, COLOUR_GREY), SetFill(0, 1), EndContainer(), EndContainer(), diff --git a/src/widgets/timetable_widget.h b/src/widgets/timetable_widget.h index e80b819c44..c20d563f65 100644 --- a/src/widgets/timetable_widget.h +++ b/src/widgets/timetable_widget.h @@ -37,6 +37,7 @@ enum VehicleTimetableWidgets { WID_VT_CHANGE_SPEED, ///< Change speed limit button. WID_VT_CLEAR_SPEED, ///< Clear speed limit button. WID_VT_SCHEDULED_DISPATCH, ///< Scheduled Dispatch button. + WID_VT_LOCK_ORDER_TIME, ///< Lock order time button. }; #endif /* WIDGETS_TIMETABLE_WIDGET_H */