From 6578d94e6361b56bdc80204fbbcb450742eeba9e Mon Sep 17 00:00:00 2001 From: Jonathan G Rennison Date: Tue, 14 Aug 2018 12:35:05 +0100 Subject: [PATCH] Add support for timetabled wait times at waypoints --- src/order_base.h | 6 ++--- src/order_cmd.cpp | 7 ++++-- src/order_gui.cpp | 5 ++++ src/roadveh_cmd.cpp | 4 ++-- src/saveload/extended_ver_sl.cpp | 2 +- src/station_cmd.cpp | 17 ++++++++++--- src/timetable_cmd.cpp | 1 + src/timetable_gui.cpp | 19 +++++++++++---- src/train_cmd.cpp | 41 +++++++++++++++++++++++++------- src/vehicle.cpp | 3 ++- 10 files changed, 81 insertions(+), 24 deletions(-) diff --git a/src/order_base.h b/src/order_base.h index f95784b44d..e42f9cf085 100644 --- a/src/order_base.h +++ b/src/order_base.h @@ -296,7 +296,7 @@ public: /** What are we going to do when in the depot. */ inline OrderDepotActionFlags GetDepotActionType() const { return (OrderDepotActionFlags)GB(this->flags, 4, 3); } /** What waypoint flags? */ - inline OrderWaypointFlags GetWaypointFlags() const { return (OrderWaypointFlags)GB(this->flags, 0, 8); } + inline OrderWaypointFlags GetWaypointFlags() const { return (OrderWaypointFlags)GB(this->flags, 0, 3); } /** What variable do we have to compare? */ inline OrderConditionVariable GetConditionVariable() const { return (OrderConditionVariable)GB(this->dest, 11, 5); } /** What is the comparator to use? */ @@ -355,7 +355,7 @@ public: /** Set what we are going to do in the depot. */ inline void SetDepotActionType(OrderDepotActionFlags depot_service_type) { SB(this->flags, 4, 3, depot_service_type); } /** Set waypoint flags. */ - inline void SetWaypointFlags(OrderWaypointFlags waypoint_flags) { SB(this->flags, 0, 8, waypoint_flags); } + inline void SetWaypointFlags(OrderWaypointFlags waypoint_flags) { SB(this->flags, 0, 3, waypoint_flags); } /** Set variable we have to compare. */ inline void SetConditionVariable(OrderConditionVariable condition_variable) { SB(this->dest, 11, 5, condition_variable); } /** Set the comparator to use. */ @@ -462,7 +462,7 @@ public: */ inline void SetOccupancy(uint8 occupancy) { this->occupancy = occupancy; } - bool ShouldStopAtStation(const Vehicle *v, StationID station) const; + bool ShouldStopAtStation(const Vehicle *v, StationID station, bool waypoint) const; bool CanLeaveWithCargo(bool has_cargo, CargoID cargo) const; TileIndex GetLocation(const Vehicle *v, bool airport = false) const; diff --git a/src/order_cmd.cpp b/src/order_cmd.cpp index ac0c0bd676..cfe0d6872b 100644 --- a/src/order_cmd.cpp +++ b/src/order_cmd.cpp @@ -2604,7 +2604,8 @@ bool ProcessOrders(Vehicle *v) bool may_reverse = v->current_order.IsType(OT_NOTHING); /* Check if we've reached a 'via' destination. */ - if (((v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) || v->current_order.IsType(OT_GOTO_WAYPOINT)) && + if (((v->current_order.IsType(OT_GOTO_STATION) && (v->current_order.GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)) || + (v->current_order.IsType(OT_GOTO_WAYPOINT) && !v->current_order.IsWaitTimetabled())) && IsTileType(v->tile, MP_STATION) && v->current_order.GetDestination() == GetStationIndex(v->tile)) { v->DeleteUnreachedImplicitOrders(); @@ -2673,10 +2674,12 @@ bool ProcessOrders(Vehicle *v) * based on this order and the non-stop settings. * @param v the vehicle that might be stopping. * @param station the station to stop at. + * @param waypoint if station is a waypoint. * @return true if the vehicle should stop. */ -bool Order::ShouldStopAtStation(const Vehicle *v, StationID station) const +bool Order::ShouldStopAtStation(const Vehicle *v, StationID station, bool waypoint) const { + if (waypoint) return this->IsType(OT_GOTO_WAYPOINT) && this->dest == station; if (this->IsType(OT_LOADING_ADVANCE) && this->dest == station) return true; bool is_dest_station = this->IsType(OT_GOTO_STATION) && this->dest == station; diff --git a/src/order_gui.cpp b/src/order_gui.cpp index ba5d2178d3..e07c7d82aa 100644 --- a/src/order_gui.cpp +++ b/src/order_gui.cpp @@ -824,6 +824,11 @@ void DrawOrderString(const Vehicle *v, const Order *order, int order_index, int if (order->GetWaypointFlags() & OWF_REVERSE) str += STR_ORDER_GO_TO_WAYPOINT_REVERSE - STR_ORDER_GO_TO_WAYPOINT; SetDParam(0, str); SetDParam(1, order->GetDestination()); + if (timetable && order->IsWaitTimetabled()) { + SetDParam(5, STR_TIMETABLE_STAY_FOR); + SetTimetableParams(6, order->GetWaitTime()); + timetable_wait_time_valid = true; + } break; } diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 9043b5bb47..b1b3983920 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -1472,7 +1472,7 @@ again: /* In case an RV is stopped in a road stop, why not try to load? */ if (v->cur_speed == 0 && IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && - v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) && + v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile), false) && IsInfraTileUsageAllowed(VEH_ROAD, v->owner, v->tile) && !v->current_order.IsType(OT_LEAVESTATION) && GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) { Station *st = Station::GetByTile(v->tile); @@ -1505,7 +1505,7 @@ again: if (v->IsFrontEngine() && ((IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END) && _road_stop_stop_frame[v->state - RVSB_IN_ROAD_STOP + (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)] == v->frame) || (IsInsideMM(v->state, RVSB_IN_DT_ROAD_STOP, RVSB_IN_DT_ROAD_STOP_END) && - v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile)) && + v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile), false) && IsInfraTileUsageAllowed(VEH_ROAD, v->owner, v->tile) && GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK) && v->frame == RVC_DRIVE_THROUGH_STOP_FRAME))) { diff --git a/src/saveload/extended_ver_sl.cpp b/src/saveload/extended_ver_sl.cpp index d63868273e..c1aea48106 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, 4, 4, "timetable_extra", NULL, NULL, "ORDX" }, + { XSLFI_TIMETABLE_EXTRA, XSCF_NULL, 5, 5, "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/station_cmd.cpp b/src/station_cmd.cpp index 6b05f8a232..3dac4d04ba 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -3306,14 +3306,25 @@ static VehicleEnterTileStatus VehicleEnter_Station(Vehicle *v, TileIndex tile, i 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 (t->reverse_distance == 0) { + t->reverse_distance = t->gcache.cached_total_length; + if (t->current_order.IsWaitTimetabled()) { + t->DeleteUnreachedImplicitOrders(); + UpdateVehicleTimetable(t, true); + t->last_station_visited = station_id; + SetWindowDirty(WC_VEHICLE_VIEW, t->index); + t->current_order.MakeWaiting(); + t->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION); + return VETSB_CONTINUE; + } + } } if (HasBit(Train::From(v)->flags, VRF_BEYOND_PLATFORM_END)) return VETSB_CONTINUE; Train *front = Train::From(v)->First(); if (!front->IsFrontEngine()) return VETSB_CONTINUE; if (!(v == front || HasBit(Train::From(v)->Previous()->flags, VRF_BEYOND_PLATFORM_END))) return VETSB_CONTINUE; - if (!IsRailStation(tile)) return VETSB_CONTINUE; - if (!front->current_order.ShouldStopAtStation(front, station_id)) return VETSB_CONTINUE; + if (!HasStationTileRail(tile)) return VETSB_CONTINUE; + if (!front->current_order.ShouldStopAtStation(front, station_id, IsRailWaypoint(tile))) return VETSB_CONTINUE; int station_ahead; int station_length; diff --git a/src/timetable_cmd.cpp b/src/timetable_cmd.cpp index 49d876a26a..93ab7c7d60 100644 --- a/src/timetable_cmd.cpp +++ b/src/timetable_cmd.cpp @@ -199,6 +199,7 @@ CommandCost CmdChangeTimetable(TileIndex tile, DoCommandFlag flags, uint32 p1, u break; case OT_GOTO_DEPOT: + case OT_GOTO_WAYPOINT: break; case OT_CONDITIONAL: diff --git a/src/timetable_gui.cpp b/src/timetable_gui.cpp index 1bd1500162..7f9cdd17f1 100644 --- a/src/timetable_gui.cpp +++ b/src/timetable_gui.cpp @@ -359,6 +359,7 @@ struct TimetableWindow : Window { bool disable = true; bool wait_lockable = false; bool wait_locked = false; + bool clearable_when_wait_locked = false; if (selected != -1) { const Order *order = v->GetOrder(((selected + 1) / 2) % v->GetNumOrders()); if (selected % 2 == 1) { @@ -368,9 +369,19 @@ struct TimetableWindow : Window { wait_locked = wait_lockable && order->IsTravelFixed(); } 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)); + if (order != NULL) { + if (order->IsType(OT_GOTO_WAYPOINT)) { + disable = false; + clearable_when_wait_locked = true; + } else if (order->IsType(OT_CONDITIONAL)) { + disable = true; + } else { + disable = (!(order->IsType(OT_GOTO_STATION) || (order->IsType(OT_GOTO_DEPOT) && !(order->GetDepotActionType() & ODATFB_HALT))) || + (order->GetNonStopType() & ONSF_NO_STOP_AT_DESTINATION_STATION)); + } + } else { + disable = true; + } wait_lockable = !disable; wait_locked = wait_lockable && order->IsWaitFixed(); } @@ -378,7 +389,7 @@ struct TimetableWindow : Window { 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) && !wait_locked)); - this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable || HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE)); + this->SetWidgetDisabledState(WID_VT_CLEAR_TIME, disable || (HasBit(v->vehicle_flags, VF_AUTOMATE_TIMETABLE) && !(wait_locked && clearable_when_wait_locked))); 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() || _settings_client.gui.enable_single_veh_shared_order_gui)); diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 915fca9440..bf7aa97dc6 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -367,9 +367,13 @@ int GetTileMarginInFrontOfTrain(const Train *v, int x_pos, int y_pos) int GetTrainStopLocation(StationID station_id, TileIndex tile, Train *v, int *station_ahead, int *station_length, int x_pos, int y_pos) { Train *front = v->First(); - const Station *st = Station::Get(station_id); - *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE; - *station_length = st->GetPlatformLength(tile) * TILE_SIZE; + if (IsRailWaypoint(tile)) { + *station_ahead = *station_length = TILE_SIZE; + } else { + const Station *st = Station::Get(station_id); + *station_ahead = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE; + *station_length = st->GetPlatformLength(tile) * TILE_SIZE; + } /* Default to the middle of the station for stations stops that are not in * the order list like intermediate stations when non-stop is disabled */ @@ -378,6 +382,8 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, Train *v, int *st osl = front->current_order.GetStopLocation(); } else if (front->current_order.IsType(OT_LOADING_ADVANCE) && front->current_order.GetDestination() == station_id) { osl = OSL_PLATFORM_THROUGH; + } else if (front->current_order.IsType(OT_GOTO_WAYPOINT) && front->current_order.GetDestination() == station_id) { + osl = OSL_PLATFORM_FAR_END; } int overhang = front->gcache.cached_total_length - *station_length; int adjust = 0; @@ -559,9 +565,9 @@ int Train::GetCurrentMaxSpeed() const if (_settings_game.vehicle.train_acceleration_model == AM_REALISTIC) { Train *v_platform = const_cast(this->GetStationLoadingVehicle()); TileIndex platform_tile = v_platform->tile; - if (IsRailStationTile(platform_tile)) { + if (HasStationTileRail(platform_tile)) { StationID sid = GetStationIndex(platform_tile); - if (this->current_order.ShouldStopAtStation(this, sid)) { + if (this->current_order.ShouldStopAtStation(this, sid, IsRailWaypoint(platform_tile))) { int station_ahead; int station_length; int stop_at = GetTrainStopLocation(sid, platform_tile, v_platform, &station_ahead, &station_length); @@ -2404,6 +2410,9 @@ static void CheckNextTrainTile(Train *v) /* Exit if we are inside a depot. */ if (v->track == TRACK_BIT_DEPOT) return; + /* Exit if we are on a station tile and are going to stop. */ + if (HasStationTileRail(v->tile) && v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile), IsRailWaypoint(v->tile))) return; + switch (v->current_order.GetType()) { /* Exit if we reached our destination depot. */ case OT_GOTO_DEPOT: @@ -2425,8 +2434,6 @@ static void CheckNextTrainTile(Train *v) default: break; } - /* Exit if we are on a station tile and are going to stop. */ - if (IsRailStationTile(v->tile) && v->current_order.ShouldStopAtStation(v, GetStationIndex(v->tile))) return; Trackdir td = v->GetVehicleTrackdir(); @@ -3351,8 +3358,21 @@ static void TrainEnterStation(Train *v, StationID station) { v->last_station_visited = station; + BaseStation *bst = BaseStation::Get(station); + + if (Waypoint::IsExpected(bst)) { + v->DeleteUnreachedImplicitOrders(); + UpdateVehicleTimetable(v, true); + v->last_station_visited = station; + v->force_proceed = TFP_NONE; + SetWindowDirty(WC_VEHICLE_VIEW, v->index); + v->current_order.MakeWaiting(); + v->current_order.SetNonStopType(ONSF_NO_STOP_AT_ANY_STATION); + return; + } + /* check if a train ever visited this station before */ - Station *st = Station::Get(station); + Station *st = Station::From(bst); if (!(st->had_vehicle_of_type & HVOT_TRAIN)) { st->had_vehicle_of_type |= HVOT_TRAIN; SetDParam(0, st->index); @@ -4775,6 +4795,11 @@ static bool TrainLocoHandler(Train *v, bool mode) if (CheckTrainStayInDepot(v)) return true; + if (v->current_order.IsType(OT_WAITING) && v->reverse_distance == 0) { + v->HandleWaiting(false); + if (v->current_order.IsType(OT_WAITING)) return true; + } + if (!mode) v->ShowVisualEffect(); /* We had no order but have an order now, do look ahead. */ diff --git a/src/vehicle.cpp b/src/vehicle.cpp index 187dc2470e..2fa114281e 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -3007,6 +3007,7 @@ void Vehicle::HandleWaiting(bool stop_waiting) UpdateVehicleTimetable(this, false); this->IncrementImplicitOrderIndex(); this->current_order.MakeDummy(); + if (this->type == VEH_TRAIN) Train::From(this)->force_proceed = TFP_NONE; break; } @@ -3294,7 +3295,7 @@ void Vehicle::ShowVisualEffect() const * - is approaching a reversing point and its speed is equal to maximum approach speed */ if (HasBit(t->flags, VRF_REVERSING) || - (IsRailStationTile(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile)) && + (HasStationTileRail(t->tile) && t->IsFrontEngine() && t->current_order.ShouldStopAtStation(t, GetStationIndex(t->tile), IsRailWaypoint(t->tile)) && t->cur_speed >= max_speed) || (t->reverse_distance >= 1 && t->cur_speed >= ReversingDistanceTargetSpeed(t))) { return;