diff --git a/src/economy.cpp b/src/economy.cpp index dcb69c20d1..62d30d5e28 100644 --- a/src/economy.cpp +++ b/src/economy.cpp @@ -2029,7 +2029,8 @@ static void LoadUnloadVehicle(Vehicle *front) /* We loaded less cargo than possible for all cargo types and it's not full * load and we're not supposed to wait any longer: stop loading. */ if (!anything_unloaded && full_load_amount == 0 && reservation_left == 0 && !has_full_load_order && - front->current_order_time >= (uint)max(front->current_order.GetTimetabledWait() - front->lateness_counter, 0)) { + (front->current_order_time >= (uint)max(front->current_order.GetTimetabledWait() - front->lateness_counter, 0) || + front->current_order.GetLeaveType() == OLT_LEAVE_EARLY)) { SetBit(front->vehicle_flags, VF_STOP_LOADING); } diff --git a/src/lang/english.txt b/src/lang/english.txt index cdf5fe65fe..2ea3174400 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4749,6 +4749,13 @@ STR_TIMETABLE_EXPECTED_TOOLTIP :{BLACK}Switch b 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_LEAVE_EARLY_ORDER :[leave early] +STR_TIMETABLE_LEAVE_NORMAL :Leave as timetabled +STR_TIMETABLE_LEAVE_EARLY :Leave early + +STR_TIMETABLE_EXTRA_DROP_DOWN :{BLACK}Extra +STR_TIMETABLE_EXTRA_DROP_DOWN_TOOLTIP :{BLACK}Extra Options{}{}Leave as timetabled: Vehicles wait until their timetabled departure time before leaving (default).{}Leave Early: Vehicles leave as soon as loading/unloading is done, possibly before the timetabled departure time. + STR_TIMETABLE_ARRIVAL_ABBREVIATION :A: STR_TIMETABLE_DEPARTURE_ABBREVIATION :D: diff --git a/src/order_base.h b/src/order_base.h index 261e91703d..d43e294244 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -432,6 +432,15 @@ public: if (!this->IsType(OT_CONDITIONAL) && fixed != IsWaitFixed()) SB(this->GetXFlagsRef(), 1, 1, fixed ? 1 : 0); } + /** Get the leave type */ + inline OrderLeaveType GetLeaveType() const { return (OrderLeaveType)GB(this->GetXFlags(), 2, 2); } + + /** Set the leave type */ + inline void SetLeaveType(OrderLeaveType leave_type) + { + if (leave_type != this->GetLeaveType()) SB(this->GetXFlagsRef(), 2, 2, leave_type); + } + /** * Get the occupancy value * @return occupancy diff --git a/src/order_gui.cpp b/src/order_gui.cpp index 4fa9f62672..ba5d2178d3 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -893,6 +893,10 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int int edge = DrawString(rtl ? left : middle, rtl ? middle : right, y, STR_ORDER_TEXT, colour); + if (timetable && timetable_wait_time_valid && order->GetLeaveType() == OLT_LEAVE_EARLY) { + edge = DrawString(rtl ? left : edge + 3, rtl ? edge - 3 : right, y, STR_TIMETABLE_LEAVE_EARLY_ORDER, colour); + } + if (timetable && timetable_wait_time_valid && order->IsWaitFixed()) { Dimension lock_d = GetSpriteSize(SPR_LOCK); DrawPixelInfo tmp_dpi; diff --git a/src/order_type.h b/src/order_type.h index 83f1e96b7b..f28a2605cf 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -196,6 +196,15 @@ enum OrderDepotAction { DA_END }; +/** + * When to leave the station/waiting point. + */ +enum OrderLeaveType { + OLT_NORMAL = 0, ///< Leave when timetabled + OLT_LEAVE_EARLY = 1, ///< Leave as soon as possible + OLT_END +}; + /** * Enumeration for the data to set in #CmdChangeTimetable. */ @@ -204,9 +213,10 @@ enum ModifyTimetableFlags { MTF_TRAVEL_TIME, ///< Set travel time. MTF_TRAVEL_SPEED, ///< Set max travel speed. MTF_SET_WAIT_FIXED,///< Set wait time fixed flag state. + MTF_SET_LEAVE_TYPE,///< Passes an OrderLeaveType. MTF_END }; -template <> struct EnumPropsT : MakeEnumPropsT {}; +template <> struct EnumPropsT : MakeEnumPropsT {}; /** Clone actions. */ diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index 288d036f13..a647b030ac 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, 2, 2, "timetable_extra", NULL, NULL, "ORDX" }, + { XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 3, 3, "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 e4e34471fb..7db6843889 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -71,6 +71,10 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, order->SetWaitFixed(val != 0); break; + case MTF_SET_LEAVE_TYPE: + order->SetLeaveType((OrderLeaveType)val); + break; + default: NOT_REACHED(); } @@ -113,8 +117,8 @@ static void ChangeTimetable(Vehicle *v, VehicleOrderID order_number, uint16 val, * @param p1 Various bitstuffed elements * - p1 = (bit 0-19) - Vehicle with the orders to change. * - p1 = (bit 20-27) - Order index to modify. - * - p1 = (bit 28-29) - Timetable data to change (@see ModifyTimetableFlags) - * - p1 = (bit 30) - 0 to set timetable wait/travel time, 1 to clear it + * - p1 = (bit 28-30) - Timetable data to change (@see ModifyTimetableFlags) + * - p1 = (bit 31) - 0 to set timetable wait/travel time, 1 to clear it * @param p2 The amount of time to wait. * - p2 = (bit 0-15) - The data to modify as specified by p1 bits 28-29. * 0 to clear times, UINT16_MAX to clear speed limit. @@ -135,15 +139,16 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u Order *order = v->GetOrder(order_number); if (order == NULL || order->IsType(OT_IMPLICIT)) return CMD_ERROR; - ModifyTimetableFlags mtf = Extract(p1); + ModifyTimetableFlags mtf = Extract(p1); if (mtf >= MTF_END) return CMD_ERROR; - bool clear_field = GB(p1, 30, 1) == 1; + bool clear_field = GB(p1, 31, 1) == 1; int wait_time = order->GetWaitTime(); int travel_time = order->GetTravelTime(); int max_speed = order->GetMaxSpeed(); bool wait_fixed = order->IsWaitFixed(); + OrderLeaveType leave_type = order->GetLeaveType(); switch (mtf) { case MTF_WAIT_TIME: wait_time = GB(p2, 0, 16); @@ -164,11 +169,16 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u wait_fixed = GB(p2, 0, 16) != 0; break; + case MTF_SET_LEAVE_TYPE: + leave_type = (OrderLeaveType)GB(p2, 0, 16); + if (leave_type >= OLT_END) return CMD_ERROR; + break; + default: NOT_REACHED(); } - if (wait_time != order->GetWaitTime()) { + if (wait_time != order->GetWaitTime() || leave_type != order->GetLeaveType()) { switch (order->GetType()) { case OT_GOTO_STATION: if (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION) return_cmd_error(STR_ERROR_TIMETABLE_NOT_STOPPING_HERE); @@ -187,6 +197,7 @@ 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 (leave_type != order->GetLeaveType() && order->IsType(OT_CONDITIONAL)) return CMD_ERROR; if (flags & DC_EXEC) { switch (mtf) { @@ -216,6 +227,12 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u } break; + case MTF_SET_LEAVE_TYPE: + if (leave_type != order->GetLeaveType()) { + ChangeTimetable(v, order_number, leave_type, MTF_SET_LEAVE_TYPE, true); + } + break; + default: break; } @@ -231,8 +248,8 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u * @param p1 Various bitstuffed elements * - p1 = (bit 0-19) - Vehicle with the orders to change. * - p1 = (bit 20-27) - unused - * - p1 = (bit 28-29) - Timetable data to change (@see ModifyTimetableFlags) - * - p1 = (bit 30) - 0 to set timetable wait/travel time, 1 to clear it + * - p1 = (bit 28-30) - Timetable data to change (@see ModifyTimetableFlags) + * - p1 = (bit 31) - 0 to set timetable wait/travel time, 1 to clear it * @param p2 The amount of time to wait. * - p2 = (bit 0-15) - The data to modify as specified by p1 bits 28-29. * 0 to clear times, UINT16_MAX to clear speed limit. @@ -249,7 +266,7 @@ CommandCost CmdBulkChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p CommandCost ret = CheckOwnership(v->owner); if (ret.Failed()) return ret; - ModifyTimetableFlags mtf = Extract(p1); + ModifyTimetableFlags mtf = Extract(p1); if (mtf >= MTF_END) return CMD_ERROR; if (v->GetNumOrders() == 0) return CMD_ERROR; diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 856ac71e70..17c5e98bc4 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -389,6 +389,7 @@ struct TimetableWindow : Window { 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); + this->SetWidgetDisabledState(WID_VT_EXTRA, disable || (selected % 2 != 0)); } else { this->DisableWidget(WID_VT_START_DATE); this->DisableWidget(WID_VT_CHANGE_TIME); @@ -402,6 +403,7 @@ struct TimetableWindow : Window { this->DisableWidget(WID_VT_SHARED_ORDER_LIST); this->DisableWidget(WID_VT_ADD_VEH_GROUP); this->DisableWidget(WID_VT_LOCK_ORDER_TIME); + this->DisableWidget(WID_VT_EXTRA); } this->SetWidgetLoweredState(WID_VT_AUTOFILL, HasBit(v->vehicle_flags, VF_AUTOFILL_TIMETABLE)); @@ -673,7 +675,7 @@ struct TimetableWindow : Window { if (order_number >= v->GetNumOrders()) order_number = 0; - return v->index | (order_number << 20) | (mtf << 28) | (clear ? 1 << 30 : 0); + return v->index | (order_number << 20) | (mtf << 28) | (clear ? 1 << 31 : 0); } virtual void OnClick(Point pt, int widget, int click_count) @@ -840,11 +842,41 @@ struct TimetableWindow : Window { ShowQueryString(STR_EMPTY, STR_GROUP_RENAME_CAPTION, MAX_LENGTH_GROUP_NAME_CHARS, this, CS_ALPHANUMERAL, QSF_ENABLE_DEFAULT | QSF_LEN_IN_CHARS); break; } + + case WID_VT_EXTRA: { + VehicleOrderID real = (this->sel_index + 1) / 2; + if (real >= this->vehicle->GetNumOrders()) real = 0; + const Order *order = this->vehicle->GetOrder(real); + bool leave_type_disabled = (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)); + DropDownList *list = new DropDownList(); + *list->Append() = new DropDownListStringItem(STR_TIMETABLE_LEAVE_NORMAL, OLT_NORMAL, leave_type_disabled); + *list->Append() = new DropDownListStringItem(STR_TIMETABLE_LEAVE_EARLY, OLT_LEAVE_EARLY, leave_type_disabled); + ShowDropDownList(this, list, order != NULL ? order->GetLeaveType() : -1, WID_VT_EXTRA); + break; + } } this->SetDirty(); } + virtual void OnDropdownSelect(int widget, int index) OVERRIDE + { + switch (widget) { + case WID_VT_EXTRA: { + VehicleOrderID order_number = (this->sel_index + 1) / 2; + if (order_number >= this->vehicle->GetNumOrders()) order_number = 0; + + uint32 p1 = this->vehicle->index | (order_number << 20) | (MTF_SET_LEAVE_TYPE << 28); + DoCommandP(0, p1, index, CMD_CHANGE_TIMETABLE | CMD_MSG(STR_ERROR_CAN_T_TIMETABLE_VEHICLE)); + } + + default: + break; + } + } + virtual void OnQueryTextFinished(char *str) { if (str == NULL) return; @@ -963,12 +995,13 @@ static const NWidgetPart _nested_timetable_widgets[] = { 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_SCHEDULED_DISPATCH), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_SCHEDULED_DISPATCH, STR_TIMETABLE_SCHEDULED_DISPATCH_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_AUTOMATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_AUTOMATE, STR_TIMETABLE_AUTOMATE_TOOLTIP), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_AUTO_SEPARATION), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_AUTO_SEPARATION, STR_TIMETABLE_AUTO_SEPARATION_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), + NWidget(WWT_DROPDOWN, COLOUR_GREY, WID_VT_EXTRA), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_EXTRA_DROP_DOWN, STR_TIMETABLE_EXTRA_DROP_DOWN_TOOLTIP), EndContainer(), NWidget(NWID_VERTICAL, NC_EQUALSIZE), - NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_AUTOMATE), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_AUTOMATE, STR_TIMETABLE_AUTOMATE_TOOLTIP), + NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_SCHEDULED_DISPATCH), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_TIMETABLE_SCHEDULED_DISPATCH, STR_TIMETABLE_SCHEDULED_DISPATCH_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), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_VT_EXPECTED_SELECTION), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_VT_EXPECTED), SetResize(1, 0), SetFill(1, 1), SetDataTip(STR_BLACK_STRING, STR_TIMETABLE_EXPECTED_TOOLTIP), NWidget(WWT_PANEL, COLOUR_GREY), SetResize(1, 0), SetFill(1, 1), EndContainer(), diff --git a/src/vehicle.cpp b/src/vehicle.cpp index b26ffc4d42..187dc2470e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -2961,7 +2961,7 @@ void Vehicle::HandleLoading(bool mode) if (!mode && this->type != VEH_TRAIN) PayStationSharingFee(this, Station::Get(this->last_station_visited)); /* Not the first call for this tick, or still loading */ - if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || this->current_order_time < wait_time) { + if (mode || !HasBit(this->vehicle_flags, VF_LOADING_FINISHED) || (this->current_order_time < wait_time && this->current_order.GetLeaveType() != OLT_LEAVE_EARLY)) { if (!mode && this->type == VEH_TRAIN && HasBit(Train::From(this)->flags, VRF_ADVANCE_IN_PLATFORM)) this->AdvanceLoadingInStation(); return; } @@ -2999,7 +2999,7 @@ void Vehicle::HandleWaiting(bool stop_waiting) case OT_WAITING: { uint wait_time = max(this->current_order.GetTimetabledWait() - this->lateness_counter, 0); /* Vehicles holds on until waiting Timetabled time expires. */ - if (!stop_waiting && this->current_order_time < wait_time) { + if (!stop_waiting && this->current_order_time < wait_time && this->current_order.GetLeaveType() != OLT_LEAVE_EARLY) { return; } diff --git a/src/widgets/timetable_widget.h b/src/widgets/timetable_widget.h index c20d563f65..55fea3ed32 100644 --- a/src/widgets/timetable_widget.h +++ b/src/widgets/timetable_widget.h @@ -38,6 +38,7 @@ enum VehicleTimetableWidgets { WID_VT_CLEAR_SPEED, ///< Clear speed limit button. WID_VT_SCHEDULED_DISPATCH, ///< Scheduled Dispatch button. WID_VT_LOCK_ORDER_TIME, ///< Lock order time button. + WID_VT_EXTRA, ///< Extra drop down menu. }; #endif /* WIDGETS_TIMETABLE_WIDGET_H */