diff --git a/src/lang/english.txt b/src/lang/english.txt index ad29b3d596..489ee0e431 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -3771,6 +3771,9 @@ STR_ORDER_REFIT_AUTO_TOOLTIP :{BLACK}Select w STR_ORDER_DROP_REFIT_AUTO :Fixed cargo STR_ORDER_DROP_REFIT_AUTO_ANY :Available cargo +STR_ORDER_REVERSE :{BLACK}Reverse +STR_ORDER_REVERSE_TOOLTIP :{BLACK}Change the reversing behaviour of the highlighted order. + STR_ORDER_SERVICE :{BLACK}Service STR_ORDER_DROP_GO_ALWAYS_DEPOT :Always go STR_ORDER_DROP_SERVICE_DEPOT :Service if needed @@ -3822,6 +3825,8 @@ STR_ORDERS_VEH_WITH_SHARED_ORDERS_LIST_TOOLTIP :{BLACK}Show all # String parts to build the order string STR_ORDER_GO_TO_WAYPOINT :Go via {WAYPOINT} STR_ORDER_GO_NON_STOP_TO_WAYPOINT :Go non-stop via {WAYPOINT} +STR_ORDER_GO_TO_WAYPOINT_REVERSE :Go via and reverse at {WAYPOINT} +STR_ORDER_GO_NON_STOP_TO_WAYPOINT_REVERSE :Go non-stop via and reverse at {WAYPOINT} STR_ORDER_SERVICE_AT :Service at STR_ORDER_SERVICE_NON_STOP_AT :Service non-stop at diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index 57b29f3f53..0cb4924e1d 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -1315,7 +1315,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 break; case OT_GOTO_WAYPOINT: - if (mof != MOF_NON_STOP) return CMD_ERROR; + if (mof != MOF_NON_STOP && mof != MOF_WAYPOINT_FLAGS) return CMD_ERROR; break; case OT_CONDITIONAL: @@ -1397,6 +1397,10 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 case MOF_COND_DESTINATION: if (data >= v->GetNumOrders()) return CMD_ERROR; break; + + case MOF_WAYPOINT_FLAGS: + if (data != (data & OWF_REVERSE)) return CMD_ERROR; + break; } if (flags & DC_EXEC) { @@ -1486,6 +1490,10 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 order->SetConditionSkipToOrder(data); break; + case MOF_WAYPOINT_FLAGS: + order->SetWaypointFlags((OrderWaypointFlags)data); + break; + default: NOT_REACHED(); } diff --git a/src/order_gui.cpp b/src/order_gui.cpp index d4feae35ca..fdd1015399 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -314,10 +314,13 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int } break; - case OT_GOTO_WAYPOINT: - SetDParam(0, (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO_WAYPOINT : STR_ORDER_GO_TO_WAYPOINT); + case OT_GOTO_WAYPOINT: { + StringID str = (order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS) ? STR_ORDER_GO_NON_STOP_TO_WAYPOINT : STR_ORDER_GO_TO_WAYPOINT; + if (order->GetWaypointFlags() & OWF_REVERSE) str += STR_ORDER_GO_TO_WAYPOINT_REVERSE - STR_ORDER_GO_TO_WAYPOINT; + SetDParam(0, str); SetDParam(1, order->GetDestination()); break; + } case OT_CONDITIONAL: SetDParam(1, order->GetConditionSkipToOrder() + 1); @@ -489,6 +492,7 @@ private: /* WID_O_SEL_TOP_LEFT */ DP_LEFT_LOAD = 0, ///< Display 'load' in the left button of the top row of the train/rv order window. DP_LEFT_REFIT = 1, ///< Display 'refit' in the left button of the top row of the train/rv order window. + DP_LEFT_REVERSE = 2, ///< Display 'reverse' in the left button of the top row of the train/rv order window. /* WID_O_SEL_TOP_MIDDLE */ DP_MIDDLE_UNLOAD = 0, ///< Display 'unload' in the middle button of the top row of the train/rv order window. @@ -999,13 +1003,14 @@ public: row_sel->SetDisplayedPlane(DP_ROW_LOAD); } else { train_row_sel->SetDisplayedPlane(DP_GROUNDVEHICLE_ROW_NORMAL); - left_sel->SetDisplayedPlane(DP_LEFT_LOAD); + left_sel->SetDisplayedPlane(DP_LEFT_REVERSE); middle_sel->SetDisplayedPlane(DP_MIDDLE_UNLOAD); right_sel->SetDisplayedPlane(DP_RIGHT_EMPTY); this->EnableWidget(WID_O_NON_STOP); this->SetWidgetLoweredState(WID_O_NON_STOP, order->GetNonStopType() & ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS); + this->EnableWidget(WID_O_REVERSE); + this->SetWidgetLoweredState(WID_O_REVERSE, order->GetWaypointFlags() & OWF_REVERSE); } - this->DisableWidget(WID_O_FULL_LOAD); this->DisableWidget(WID_O_UNLOAD); this->DisableWidget(WID_O_REFIT_DROPDOWN); break; @@ -1287,6 +1292,17 @@ public: } break; + case WID_O_REVERSE: { + VehicleOrderID sel_ord = this->OrderGetSel(); + const Order *order = this->vehicle->GetOrder(sel_ord); + + if (order == NULL) break; + + DoCommandP(this->vehicle->tile, this->vehicle->index + (sel_ord << 20), MOF_WAYPOINT_FLAGS | (order->GetWaypointFlags() ^ OWF_REVERSE) << 4, + CMD_MODIFY_ORDER | CMD_MSG(STR_ERROR_CAN_T_MODIFY_THIS_ORDER)); + break; + } + case WID_O_TIMETABLE_VIEW: ShowTimetableWindow(this->vehicle); break; @@ -1555,6 +1571,8 @@ static const NWidgetPart _nested_orders_train_widgets[] = { SetDataTip(STR_ORDER_TOGGLE_FULL_LOAD, STR_ORDER_TOOLTIP_FULL_LOAD), SetResize(1, 0), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_O_REFIT), SetMinimalSize(93, 12), SetFill(1, 0), SetDataTip(STR_ORDER_REFIT, STR_ORDER_REFIT_TOOLTIP), SetResize(1, 0), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_O_REVERSE), SetMinimalSize(93, 12), SetFill(1, 0), + SetDataTip(STR_ORDER_REVERSE, STR_ORDER_REVERSE_TOOLTIP), SetResize(1, 0), EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_O_SEL_TOP_MIDDLE), NWidget(NWID_BUTTON_DROPDOWN, COLOUR_GREY, WID_O_UNLOAD), SetMinimalSize(93, 12), SetFill(1, 0), diff --git a/src/order_type.h b/src/order_type.h index 8cb31de022..93b941e619 100644 --- a/src/order_type.h +++ b/src/order_type.h @@ -117,6 +117,7 @@ DECLARE_ENUM_AS_BIT_SET(OrderDepotActionFlags) */ enum OrderWaypointFlags { OWF_DEFAULT = 0, ///< Default waypoint behaviour + OWF_REVERSE = 1 << 0, ///< Reverse train at the waypoint }; DECLARE_ENUM_AS_BIT_SET(OrderWaypointFlags) @@ -163,6 +164,7 @@ enum ModifyOrderFlags { MOF_COND_COMPARATOR, ///< A comparator changes. MOF_COND_VALUE, ///< The value to set the condition to. MOF_COND_DESTINATION,///< Change the destination of a conditional order. + MOF_WAYPOINT_FLAGS, ///< Change the waypoint flags MOF_END }; template <> struct EnumPropsT : MakeEnumPropsT {}; diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 9690481154..f245de5e82 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2951,6 +2951,13 @@ bool AfterLoadGame() } } + if (SlXvIsFeatureMissing(XSLFI_REVERSE_AT_WAYPOINT)) { + Train *t; + FOR_ALL_TRAINS(t) { + t->reverse_distance = 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 6e13994e67..29f4162068 100644 --- a/src/saveload/extended_ver_sl.cpp +++ b/src/saveload/extended_ver_sl.cpp @@ -45,6 +45,7 @@ std::vector _sl_xv_discardable_chunk_ids; ///< list of chunks static const uint32 _sl_xv_slxi_chunk_version = 0; ///< current version os SLXI chunk const SlxiSubChunkInfo _sl_xv_sub_chunk_infos[] = { + { XSLFI_REVERSE_AT_WAYPOINT, XSCF_NULL, 1, 1, "reverse_at_waypoint", NULL, NULL, NULL }, { XSLFI_NULL, XSCF_NULL, 0, 0, NULL, NULL, NULL, NULL },// This is the end marker }; diff --git a/src/saveload/extended_ver_sl.h b/src/saveload/extended_ver_sl.h index 39a03478c2..243f183d70 100644 --- a/src/saveload/extended_ver_sl.h +++ b/src/saveload/extended_ver_sl.h @@ -21,6 +21,7 @@ */ enum SlXvFeatureIndex { XSLFI_NULL = 0, ///< Unused value, to indicate that no extended feature test is in use + XSLFI_REVERSE_AT_WAYPOINT, ///< Reverse at waypoint orders XSLFI_SIZE, ///< Total count of features, including null feature }; diff --git a/src/saveload/vehicle_sl.cpp b/src/saveload/vehicle_sl.cpp index ebc5fc4215..5d3a562f9e 100644 --- a/src/saveload/vehicle_sl.cpp +++ b/src/saveload/vehicle_sl.cpp @@ -724,6 +724,7 @@ const SaveLoad *GetVehicleDescription(VehicleType vt) SLE_CONDNULL(2, 2, 19), SLE_CONDVAR(Train, gv_flags, SLE_UINT16, 139, SL_MAX_VERSION), SLE_CONDNULL(11, 2, 143), // old reserved space + SLE_CONDVAR_X(Train, reverse_distance, SLE_UINT16, 0, SL_MAX_VERSION, SlXvFeatureTest(XSLFTO_AND, XSLFI_REVERSE_AT_WAYPOINT)), SLE_END() }; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index eb90c29ab5..b2e2764ef1 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3129,6 +3129,11 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i { if (v->type == VEH_TRAIN) { StationID station_id = GetStationIndex(tile); + if (v->current_order.IsType(OT_GOTO_WAYPOINT) && v->current_order.GetDestination() == station_id && v->current_order.GetWaypointFlags() & OWF_REVERSE) { + Train *t = Train::From(v); + // reverse at waypoint + if (t->reverse_distance == 0) t->reverse_distance = t->gcache.cached_total_length; + } if (!v->current_order.ShouldStopAtStation(v, station_id)) return VETSB_CONTINUE; if (!IsRailStation(tile) || !v->IsFrontEngine()) return VETSB_CONTINUE; diff --git a/src/train.h b/src/train.h index 280d59ebdd..c809cc9d64 100644 --- a/src/train.h +++ b/src/train.h @@ -102,6 +102,8 @@ struct Train FINAL : public GroundVehicle { /** Ticks waiting in front of a signal, ticks being stuck or a counter for forced proceeding through signals. */ uint16 wait_counter; + uint16 reverse_distance; + /** We don't want GCC to zero our struct! It already is zeroed and has an index! */ Train() : GroundVehicleBase() {} /** We want to 'destruct' the right class. */ diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 8f20973b35..48a450b7ad 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -624,6 +624,7 @@ static CommandCost CmdBuildRailWagon(TileIndex tile, DoCommandFlag flags, const v->owner = _current_company; v->track = TRACK_BIT_DEPOT; v->vehstatus = VS_HIDDEN | VS_DEFPAL; + v->reverse_distance = 0; v->SetWagon(); @@ -756,6 +757,7 @@ CommandCost CmdBuildRailVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->refit_cap = 0; v->last_station_visited = INVALID_STATION; v->last_loading_station = INVALID_STATION; + v->reverse_distance = 0; v->engine_type = e->index; v->gcache.first_engine = INVALID_ENGINE; // needs to be set before first callback @@ -1811,6 +1813,8 @@ void ReverseTrainDirection(Train *v) InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile); } + v->reverse_distance = 0; + /* Clear path reservation in front if train is not stuck. */ if (!HasBit(v->flags, VRF_TRAIN_STUCK)) FreeTrainTrackReservation(v); @@ -3104,6 +3108,22 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) Train *prev; bool direction_changed = false; // has direction of any part changed? + if (reverse && v->reverse_distance == 1) { + goto reverse_train_direction; + } + + if (v->reverse_distance > 1) { + v->vehstatus |= VS_TRAIN_SLOWING; + int target_speed; + if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC) { + target_speed = ((v->reverse_distance - 1) * 5) / 2; + } else { + target_speed = (v->reverse_distance - 1) * 10 - 5; + } + uint16 spd = max(0, target_speed); + if (spd < v->cur_speed) v->cur_speed = spd; + } + /* For every vehicle after and including the given vehicle */ for (prev = v->Previous(); v != nomove; prev = v, v = v->Next()) { DiagDirection enterdir = DIAGDIR_BEGIN; @@ -3118,6 +3138,7 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) /* Inside depot */ gp.x = v->x_pos; gp.y = v->y_pos; + v->reverse_distance = 0; } else { /* Not inside depot */ @@ -3354,6 +3375,9 @@ bool TrainController(Train *v, Vehicle *nomove, bool reverse) v->x_pos = gp.x; v->y_pos = gp.y; v->UpdatePosition(); + if (v->reverse_distance > 1) { + v->reverse_distance--; + } /* update the Z position of the vehicle */ int old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false); diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 46f98fe63b..31e03f0a08 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -1362,6 +1362,7 @@ void VehicleEnterDepot(Vehicle *v) t->force_proceed = TFP_NONE; ClrBit(t->flags, VRF_TOGGLE_REVERSE); t->ConsistChanged(CCF_ARRANGE); + t->reverse_distance = 0; break; } diff --git a/src/widgets/order_widget.h b/src/widgets/order_widget.h index 825f791ea8..476ff79cf0 100644 --- a/src/widgets/order_widget.h +++ b/src/widgets/order_widget.h @@ -29,6 +29,7 @@ enum OrderWidgets { WID_O_SERVICE, ///< Select service (at depot). WID_O_EMPTY, ///< Placeholder for refit dropdown when not owner. WID_O_REFIT_DROPDOWN, ///< Open refit options. + WID_O_REVERSE, ///< Select waypoint reverse type WID_O_COND_VARIABLE, ///< Choose condition variable. WID_O_COND_COMPARATOR, ///< Choose condition type. WID_O_COND_VALUE, ///< Choose condition value.