diff --git a/src/command.cpp b/src/command.cpp index 3aa93777c5..9c0a714eec 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -179,6 +179,7 @@ DEF_COMMAND(CmdSetGroupReplaceProtection); DEF_COMMAND(CmdMoveOrder); DEF_COMMAND(CmdChangeTimetable); DEF_COMMAND(CmdSetVehicleOnTime); +DEF_COMMAND(CmdAutofillTimetable); /* The master command table */ static const Command _command_proc_table[] = { @@ -335,6 +336,7 @@ static const Command _command_proc_table[] = { {CmdMoveOrder, 0}, /* 127 */ {CmdChangeTimetable, 0}, /* 128 */ {CmdSetVehicleOnTime, 0}, /* 129 */ + {CmdAutofillTimetable, 0}, /* 130 */ }; /* This function range-checks a cmd, and checks if the cmd is not NULL */ diff --git a/src/command.h b/src/command.h index c19027a310..8cb78c9e06 100644 --- a/src/command.h +++ b/src/command.h @@ -155,6 +155,7 @@ enum { CMD_MOVE_ORDER = 127, CMD_CHANGE_TIMETABLE = 128, CMD_SET_VEHICLE_ON_TIME = 129, + CMD_AUTOFILL_TIMETABLE = 130, }; enum { diff --git a/src/lang/english.txt b/src/lang/english.txt index 63849c6998..0bb5536cd7 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2773,6 +2773,8 @@ STR_TIMETABLE_CHANGE_TIME :{BLACK}Change T STR_TIMETABLE_STATUS_ON_TIME :This vehicle is currently running on time STR_TIMETABLE_STATUS_LATE :This vehicle is currently running {STRING1} late STR_TIMETABLE_STATUS_EARLY :This vehicle is currently running {STRING1} early +STR_TIMETABLE_AUTOFILL :{BLACK}Autofill +STR_TIMETABLE_AUTOFILL_TOOLTIP :{BLACK}Fill the timetable automatically with the values from the first journey ##id 0x9000 STR_9000_ROAD_VEHICLE_IN_THE_WAY :{WHITE}Road vehicle in the way diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index ee333cb785..1012212168 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -8,9 +8,32 @@ #include "variables.h" #include "table/strings.h" #include "command.h" +#include "date.h" +#include "player.h" #include "vehicle.h" +static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 time, bool is_journey) +{ + Order *order = GetVehicleOrder(v, order_number); + + if (is_journey) { + order->travel_time = time; + } else { + order->wait_time = time; + } + + if (v->cur_order_index == order_number && HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) { + if (is_journey) { + v->current_order.travel_time = time; + } else { + v->current_order.wait_time = time; + } + } + + InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); +} + /** * Add or remove waiting times from an order. * @param tile Not used. @@ -43,21 +66,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, uint32 flags, uint32 p1, uint32 p } if (flags & DC_EXEC) { - if (is_journey) { - order->travel_time = p2; - } else { - order->wait_time = p2; - } - - if (v->cur_order_index == order_number && HASBIT(v->current_order.flags, OFB_PART_OF_ORDERS)) { - if (is_journey) { - v->current_order.travel_time = p2; - } else { - v->current_order.wait_time = p2; - } - } - - InvalidateWindow(WC_VEHICLE_TIMETABLE, v->index); + ChangeTimetable(v, order_number, p2, is_journey); } return CommandCost(); @@ -87,6 +96,46 @@ CommandCost CmdSetVehicleOnTime(TileIndex tile, uint32 flags, uint32 p1, uint32 return CommandCost(); } +/** + * Start or stop filling the timetable automatically from the time the vehicle + * actually takes to complete it. When starting to autofill the current times + * are cleared and the timetable will start again from scratch. + * @param tile Not used. + * @param flags Operation to perform. + * @param p1 Vehicle index. + * @param p2 Set to 1 to enable, 0 to disable. + */ +CommandCost CmdAutofillTimetable(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) +{ + if (!_patches.timetabling) return CMD_ERROR; + + VehicleID veh = GB(p1, 0, 16); + if (!IsValidVehicleID(veh)) return CMD_ERROR; + + Vehicle *v = GetVehicle(veh); + if (!CheckOwnership(v->owner)) return CMD_ERROR; + + if (flags & DC_EXEC) { + if (p2 == 1) { + /* Start autofilling the timetable, which clears all the current + * timings and clears the "timetable has started" bit. */ + SETBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE); + CLRBIT(v->vehicle_flags, VF_TIMETABLE_STARTED); + + for (Order *order = GetVehicleOrder(v, 0); order != NULL; order = order->next) { + order->wait_time = 0; + order->travel_time = 0; + } + + v->current_order.wait_time = 0; + v->current_order.travel_time = 0; + } else { + CLRBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE); + } + } + + return CommandCost(); +} void UpdateVehicleTimetable(Vehicle *v, bool travelling) { @@ -95,10 +144,37 @@ void UpdateVehicleTimetable(Vehicle *v, bool travelling) v->current_order_time = 0; + if (!_patches.timetabling) return; + + /* Make sure the timetable only starts when the vehicle reaches the first + * order, not when travelling from the depot to the first station. */ + if (v->cur_order_index == 0 && !HASBIT(v->vehicle_flags, VF_TIMETABLE_STARTED)) { + SETBIT(v->vehicle_flags, VF_TIMETABLE_STARTED); + return; + } + + if (!HASBIT(v->vehicle_flags, VF_TIMETABLE_STARTED)) return; + + if (HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)) { + if (timetabled == 0) { + /* Round the time taken up to the nearest day, as this will avoid + * confusion for people who are timetabling in days, and can be + * adjusted later by people who aren't. */ + time_taken = (((time_taken - 1) / DAY_TICKS) + 1) * DAY_TICKS; + + ChangeTimetable(v, v->cur_order_index, time_taken, travelling); + return; + } else if (v->cur_order_index == 0) { + /* Otherwise if we're at the beginning and it already has a value, + * assume that autofill is finished and turn it off again. */ + CLRBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE); + } + } + /* Vehicles will wait at stations if they arrive early even if they are not * timetabled to wait there, so make sure the lateness counter is updated * when this happens. */ - if (!_patches.timetabling || (timetabled == 0 && (travelling || v->lateness_counter >= 0))) return; + if (timetabled == 0 && (travelling || v->lateness_counter >= 0)) return; v->lateness_counter -= (timetabled - time_taken); diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 891f8f0eb4..699084d948 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -67,12 +67,16 @@ static void DrawTimetableWindow(Window *w) } EnableWindowWidget(w, 8); + EnableWindowWidget(w, 9); } else { DisableWindowWidget(w, 6); DisableWindowWidget(w, 7); DisableWindowWidget(w, 8); + DisableWindowWidget(w, 9); } + SetWindowWidgetLoweredState(w, 9, HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); + SetDParam(0, v->index); DrawWindowWidgets(w); @@ -242,6 +246,10 @@ static void TimetableWndProc(Window *w, WindowEvent *we) case 8: /* Reset the vehicle's late counter. */ DoCommandP(0, v->index, 0, NULL, CMD_SET_VEHICLE_ON_TIME | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); break; + + case 9: /* Autofill the timetable. */ + DoCommandP(0, v->index, HASBIT(v->vehicle_flags, VF_AUTOFILL_TIMETABLE) ? 0 : 1, NULL, CMD_AUTOFILL_TIMETABLE | CMD_MSG(STR_CAN_T_TIMETABLE_VEHICLE)); + break; } SetWindowDirty(w); @@ -270,26 +278,27 @@ static void TimetableWndProc(Window *w, WindowEvent *we) static const Widget _timetable_widgets[] = { { WWT_CLOSEBOX, RESIZE_NONE, 14, 0, 10, 0, 13, STR_00C5, STR_018B_CLOSE_WINDOW}, - { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 337, 0, 13, STR_TIMETABLE_TITLE, STR_018C_WINDOW_TITLE_DRAG_THIS}, - { WWT_STICKYBOX, RESIZE_LR, 14, 338, 349, 0, 13, STR_NULL, STR_STICKY_BUTTON}, + { WWT_CAPTION, RESIZE_RIGHT, 14, 11, 387, 0, 13, STR_TIMETABLE_TITLE, STR_018C_WINDOW_TITLE_DRAG_THIS}, + { WWT_STICKYBOX, RESIZE_LR, 14, 388, 399, 0, 13, STR_NULL, STR_STICKY_BUTTON}, - { WWT_PANEL, RESIZE_RB, 14, 0, 337, 14, 95, STR_NULL, STR_TIMETABLE_TOOLTIP}, - { WWT_SCROLLBAR, RESIZE_LRB, 14, 338, 349, 14, 95, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, + { WWT_PANEL, RESIZE_RB, 14, 0, 387, 14, 95, STR_NULL, STR_TIMETABLE_TOOLTIP}, + { WWT_SCROLLBAR, RESIZE_LRB, 14, 388, 399, 14, 95, STR_NULL, STR_0190_SCROLL_BAR_SCROLLS_LIST}, - { WWT_PANEL, RESIZE_RTB, 14, 0, 349, 96, 107, STR_NULL, STR_NULL}, + { WWT_PANEL, RESIZE_RTB, 14, 0, 399, 96, 107, STR_NULL, STR_NULL}, { WWT_PUSHTXTBTN, RESIZE_TB, 14, 0, 109, 108, 119, STR_TIMETABLE_CHANGE_TIME, STR_TIMETABLE_WAIT_TIME_TOOLTIP}, { WWT_PUSHTXTBTN, RESIZE_TB, 14, 110, 219, 108, 119, STR_CLEAR_TIME, STR_TIMETABLE_CLEAR_TIME_TOOLTIP}, { WWT_PUSHTXTBTN, RESIZE_TB, 14, 220, 337, 108, 119, STR_RESET_LATENESS, STR_TIMETABLE_RESET_LATENESS_TOOLTIP}, + { WWT_PUSHTXTBTN, RESIZE_TB, 14, 338, 387, 108, 119, STR_TIMETABLE_AUTOFILL, STR_TIMETABLE_AUTOFILL_TOOLTIP}, - { WWT_PANEL, RESIZE_RTB, 14, 338, 337, 108, 119, STR_NULL, STR_NULL}, - { WWT_RESIZEBOX, RESIZE_LRTB, 14, 338, 349, 108, 119, STR_NULL, STR_RESIZE_BUTTON}, + { WWT_PANEL, RESIZE_RTB, 14, 388, 387, 108, 119, STR_NULL, STR_NULL}, + { WWT_RESIZEBOX, RESIZE_LRTB, 14, 388, 399, 108, 119, STR_NULL, STR_RESIZE_BUTTON}, { WIDGETS_END } }; static const WindowDesc _timetable_desc = { - WDP_AUTO, WDP_AUTO, 350, 120, + WDP_AUTO, WDP_AUTO, 400, 120, WC_VEHICLE_TIMETABLE, WC_VEHICLE_VIEW, WDF_STD_TOOLTIPS | WDF_STD_BTN | WDF_DEF_WIDGET | WDF_UNCLICK_BUTTONS | WDF_STICKY_BUTTON | WDF_RESIZABLE, _timetable_widgets, diff --git a/src/vehicle.h b/src/vehicle.h index d8f039de11..7b2b5cebc4 100644 --- a/src/vehicle.h +++ b/src/vehicle.h @@ -95,6 +95,8 @@ enum VehicleFlags { VF_LOADING_FINISHED, VF_CARGO_UNLOADING, VF_BUILT_AS_PROTOTYPE, + VF_TIMETABLE_STARTED, ///< Whether the vehicle has started running on the timetable yet. + VF_AUTOFILL_TIMETABLE, ///< Whether the vehicle should fill in the timetable automatically. }; /* Effect vehicle types */