diff --git a/src/lang/english.txt b/src/lang/english.txt index 5c7350fc0a..4abac89abe 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -5378,6 +5378,7 @@ STR_ERROR_NO_TOWN_ROADTYPES_AVAILABLE_YET_EXPLANATION :{WHITE}Start a STR_ERROR_CAN_T_MAKE_TRAIN_PASS_SIGNAL :{WHITE}Can't make train pass signal at danger... STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN :{WHITE}Can't reverse direction of train... STR_ERROR_TRAIN_START_NO_POWER :Train has no power +STR_ERROR_ROAD_VEHICLE_START_NO_POWER :Road vehicle has no power STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN :{WHITE}Can't make road vehicle turn around... diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 428554d8f6..77cb9432e7 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -42,6 +42,7 @@ #include "road_cmd.h" #include "landscape_cmd.h" #include "rail_cmd.h" +#include "platform_func.h" #include "table/strings.h" #include "table/roadtypes.h" @@ -2476,21 +2477,51 @@ static const uint8_t _roadveh_enter_depot_dir[4] = { TRACKDIR_X_SW, TRACKDIR_Y_NW, TRACKDIR_X_NE, TRACKDIR_Y_SE }; -static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int, int) +static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int x, int y) { if (GetRoadTileType(tile) != ROAD_TILE_DEPOT || v->type != VEH_ROAD) return VETSB_CONTINUE; - RoadVehicle *rv = RoadVehicle::From(v); - if (rv->frame == RVC_DEPOT_STOP_FRAME && - _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) { - rv->state = RVSB_IN_DEPOT; - rv->vehstatus |= VS_HIDDEN; - rv->direction = ReverseDir(rv->direction); - if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); - rv->tile = tile; + if (IsExtendedRoadDepot(tile)) { + v = v->First(); + if (!IsExtendedRoadDepotTile(v->tile)) return VETSB_CONTINUE; + DepotID depot_id = GetDepotIndex(v->tile); + if (!v->current_order.IsType(OT_GOTO_DEPOT) || + v->current_order.GetDestination() != depot_id) { + return VETSB_CONTINUE; + } + for (Vehicle *u = v; u != nullptr; u = u->Next()) { + if (!IsExtendedRoadDepotTile(u->tile) || GetDepotIndex(u->tile) != depot_id) return VETSB_CONTINUE; + if (!IsDiagonalDirection(u->direction)) return VETSB_CONTINUE; + if (DiagDirToAxis(DirToDiagDir(u->direction)) != + DiagDirToAxis(GetRoadDepotDirection(v->tile))) { + return VETSB_CONTINUE; + } + } - InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); - return VETSB_ENTERED_WORMHOLE; + /* Stop position on platform is half the front vehicle length of the road vehicle. */ + int stop_pos = RoadVehicle::From(v)->gcache.cached_veh_length / 2; + DiagDirection dir = DirToDiagDir(v->direction); + int depot_ahead = (GetPlatformLength(tile, dir, GetRoadTramType(RoadVehicle::From(v)->roadtype)) - 1) * TILE_SIZE; + if (depot_ahead > stop_pos) return VETSB_CONTINUE; + + x = v->x_pos & 0xF; + y = v->y_pos & 0xF; + + if (DiagDirToAxis(dir) != AXIS_X) Swap(x, y); + if (dir == DIAGDIR_SE || dir == DIAGDIR_SW) x = TILE_SIZE - x; + if (abs(stop_pos - x) <= 1) return VETSB_ENTERED_DEPOT_PLATFORM; + } else { + RoadVehicle *rv = RoadVehicle::From(v); + if (rv->frame == RVC_DEPOT_STOP_FRAME && + _roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) { + rv->state = RVSB_IN_DEPOT; + rv->vehstatus |= VS_HIDDEN; + rv->direction = ReverseDir(rv->direction); + if (rv->Next() == nullptr) VehicleEnterDepot(rv->First()); + rv->tile = tile; + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); + return VETSB_ENTERED_WORMHOLE; + } } return VETSB_CONTINUE; diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 48ca4b08f3..471485fedd 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -254,11 +254,12 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length) /** * Find an adequate tile for placing an engine. * @param[in,out] tile A tile of the depot. + * @param[in,out] is_exit_facing_south Whether the depot tile is facing south. * @param e Engine to be built. - * @param flags Flags of the command. - * @return CommandCost() or an error message if the depot is not appropriate. + * @param already_built Whether the vehicle already exists (for vehicle replacement). + * @return CommandCost() or an error message if the depot has no appropriate tiles. */ -CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCommandFlag flags) +CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, bool &is_exit_facing_south, const Engine *e, bool already_built) { assert(IsRoadDepotTile(tile)); @@ -269,23 +270,43 @@ CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCo const RoadTypeInfo *rti = GetRoadTypeInfo(rt); if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_DEPOT_WRONG_DEPOT_TYPE); - /* Use same tile if possible when replacing. */ - if (flags & DC_AUTOREPLACE) { - /* Use same tile if possible when replacing. */ - if (HasTileAnyRoadType(tile, rti->powered_roadtypes)) return CommandCost(); + /* Use same tile if possible when replacing or trying to leave the depot. */ + if (HasTileAnyRoadType(tile, rti->powered_roadtypes) && already_built) return CommandCost(); + + for (auto t : dep->depot_tiles) { + if (!HasTileAnyRoadType(t, rti->powered_roadtypes)) continue; + if (!IsExtendedDepot(t)) return CommandCost(); + if (GetDepotReservation(t, is_exit_facing_south) == DEPOT_RESERVATION_EMPTY) { + tile = t; + return CommandCost(); + } else if (GetDepotReservation(t, !is_exit_facing_south) == DEPOT_RESERVATION_EMPTY) { + is_exit_facing_south = !is_exit_facing_south; + tile = t; + return CommandCost(); + } } - tile = INVALID_TILE; - for (auto &depot_tile : dep->depot_tiles) { - if (!HasTileAnyRoadType(depot_tile, rti->powered_roadtypes)) continue; - tile = depot_tile; - break; - } - - assert(tile != INVALID_TILE); - return CommandCost(); + return_cmd_error(STR_ERROR_DEPOT_FULL_DEPOT); } +DiagDirection GetRoadDepotExit(TileIndex tile, RoadTramType rtt, DiagDirection dir) +{ + assert(IsRoadDepot(tile)); + RoadBits rb = GetRoadBits(tile, rtt); + if ((rb & DiagDirToRoadBits(dir)) != ROAD_NONE) return dir; + if (rb & ROAD_SE) return DIAGDIR_SE; + if (rb & ROAD_SW) return DIAGDIR_SW; + if (rb & ROAD_NE) return DIAGDIR_NE; + if (rb & ROAD_NW) return DIAGDIR_NW; + return INVALID_DIAGDIR; +} + +struct RoadDriveEntry { + uint8_t x, y; +}; + +#include "table/roadveh_movement.h" + /** * Build a road vehicle. * @param flags type of operation. @@ -299,24 +320,36 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin assert(IsRoadDepotTile(tile)); RoadType rt = e->u.road.roadtype; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + DiagDirection dir = GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile)); + bool facing_south = IsValidDiagDirection(dir) ? IsDiagDirFacingSouth(dir) : false; - CommandCost check = FindDepotTileForPlacingEngine(tile, e, flags); - if (check.Failed()) return check; + if ((flags & DC_AUTOREPLACE) == 0) { + CommandCost check = FindDepotTileForPlacingEngine(tile, facing_south, e, false); + if (check.Failed()) return check; + dir = GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile)); + } + + if (IsExtendedRoadDepotTile(tile) && facing_south != IsDiagDirFacingSouth(dir)) dir = ReverseDiagDir(dir); if (flags & DC_EXEC) { const RoadVehicleInfo *rvi = &e->u.road; RoadVehicle *v = new RoadVehicle(); *ret = v; - v->direction = DiagDirToDir(GetRoadDepotDirection(tile)); + v->direction = DiagDirToDir(dir); v->owner = _current_company; v->tile = tile; - int x = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; - int y = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; - v->x_pos = x; - v->y_pos = y; - v->z_pos = GetSlopePixelZ(x, y, true); + if (IsExtendedRoadDepotTile(tile)) { + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(rt)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + DiagDirToDiagTrackdir(dir)]; + v->frame = RVC_DEPOT_START_FRAME; + v->x_pos = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); + v->y_pos = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + } else { + v->x_pos = TileX(tile) * TILE_SIZE + TILE_SIZE / 2; + v->y_pos = TileY(tile) * TILE_SIZE + TILE_SIZE / 2; + } + v->z_pos = GetSlopePixelZ(v->x_pos, v->y_pos, true); v->state = RVSB_IN_DEPOT; v->vehstatus = VS_HIDDEN | VS_STOPPED | VS_DEFPAL; @@ -360,6 +393,7 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin for (RoadVehicle *u = v; u != nullptr; u = u->Next()) { u->cargo_cap = u->GetEngine()->DetermineCapacity(u); u->refit_cap = 0; + u->state = RVSB_IN_DEPOT; v->InvalidateNewGRFCache(); u->InvalidateNewGRFCache(); } @@ -369,6 +403,12 @@ CommandCost CmdBuildRoadVehicle(DoCommandFlag flags, TileIndex tile, const Engin v->UpdatePosition(); + if (IsExtendedDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) { + v->vehstatus &= ~VS_HIDDEN; + UpdateExtendedDepotReservation(v, true); + v->UpdateViewport(true, true); + } + CheckConsistencyOfArticulatedVehicle(v); } @@ -654,7 +694,7 @@ struct RoadVehFindData { static Vehicle *EnumCheckRoadVehClose(Vehicle *v, void *data) { - if (v->type != VEH_ROAD || v->IsInDepot()) return nullptr; + if (v->type != VEH_ROAD || (v->vehstatus & VS_HIDDEN) == 0) return nullptr; RoadVehFindData *rvf = (RoadVehFindData*)data; @@ -843,7 +883,7 @@ static Vehicle *EnumFindVehBlockingOvertake(Vehicle *v, void *data) { const OvertakeData *od = (OvertakeData*)data; - return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v) ? v : nullptr; + return (v->type == VEH_ROAD && v->First() == v && v != od->u && v != od->v && ((v->vehstatus & VS_HIDDEN) == 0)) ? v : nullptr; } /** @@ -880,6 +920,9 @@ static void RoadVehCheckOvertake(RoadVehicle *v, RoadVehicle *u) /* Don't overtake in stations */ if (IsTileType(v->tile, MP_STATION) || IsTileType(u->tile, MP_STATION)) return; + /* Don't overtake in road depot platforms. */ + if (IsExtendedRoadDepotTile(v->tile)) return; + /* For now, articulated road vehicles can't overtake anything. */ if (v->HasArticulatedPart()) return; @@ -961,9 +1004,17 @@ static Trackdir RoadFindPathToDest(RoadVehicle *v, TileIndex tile, DiagDirection TrackdirBits trackdirs = TrackStatusToTrackdirBits(ts); if (IsTileType(tile, MP_ROAD)) { - if (IsRoadDepot(tile) && (!IsTileOwner(tile, v->owner) || GetRoadDepotDirection(tile) == enterdir)) { - /* Road depot owned by another company or with the wrong orientation */ - trackdirs = TRACKDIR_BIT_NONE; + if (IsRoadDepot(tile)) { + if (!IsTileOwner(tile, v->owner)) { + trackdirs = TRACKDIR_BIT_NONE; + } else if (IsExtendedRoadDepotTile(tile)) { + if (tile != v->tile) { + RoadBits rb = GetRoadBits(tile, GetRoadTramType(v->roadtype)) & DiagDirToRoadBits(ReverseDiagDir(enterdir)); + if (rb == ROAD_NONE) trackdirs = TRACKDIR_BIT_NONE; + } + } else if (GetRoadDepotDirection(tile) == enterdir) { // Standard depot + trackdirs = TRACKDIR_BIT_NONE; + } } } else if (IsTileType(tile, MP_STATION) && IsBayRoadStopTile(tile)) { /* Standard road stop (drive-through stops are treated as normal road) */ @@ -1065,60 +1116,118 @@ found_best_track:; return best_track; } -struct RoadDriveEntry { - uint8_t x, y; -}; +void HandleRoadVehicleEnterDepot(RoadVehicle *v) +{ + assert(IsRoadDepotTile(v->tile)); -#include "table/roadveh_movement.h" + if (IsExtendedRoadDepot(v->tile)) { + assert(v == v->First()); + for (RoadVehicle *u = v; u != nullptr; u = u->Next()) { + assert(u->direction == v->direction); + assert(IsExtendedRoadDepotTile(u->tile)); + assert(GetDepotIndex(u->tile) == GetDepotIndex(v->tile)); + u->state = RVSB_IN_DEPOT; + u->cur_speed = 0; + u->UpdateViewport(true, true); // revise: probably unneded + } + + v->StartService(); + UpdateExtendedDepotReservation(v, true); + + SetWindowClassesDirty(WC_ROADVEH_LIST); + SetWindowDirty(WC_VEHICLE_VIEW, v->index); + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); + } else { + VehicleEnterDepot(v); + } +} bool RoadVehLeaveDepot(RoadVehicle *v, bool first) { /* Don't leave unless v and following wagons are in the depot. */ for (const RoadVehicle *u = v; u != nullptr; u = u->Next()) { - if (u->state != RVSB_IN_DEPOT || u->tile != v->tile) return false; + if (!u->IsInDepot()) return false; } - DiagDirection dir = GetRoadDepotDirection(v->tile); - v->direction = DiagDirToDir(dir); + bool visible_vehicle = first && (v->vehstatus & VS_HIDDEN) == 0; - Trackdir tdir = DiagDirToDiagTrackdir(dir); - const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; + if (first && (v->vehstatus & VS_HIDDEN) != 0) { + TileIndex new_tile = v->tile; + bool facing_south = IsDiagDirFacingSouth(DirToDiagDir(v->direction)); + if (FindDepotTileForPlacingEngine(new_tile, facing_south, Engine::Get(v->engine_type), true).Failed()) return false; + if (IsExtendedDepot(v->tile)) { + UpdateExtendedDepotReservation(v, false); + v->tile = new_tile; + UpdateExtendedDepotReservation(v, true); + } - int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); - int y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + DiagDirection dir = GetRoadDepotExit(v->tile, RoadTramType(v->roadtype), DirToDiagDir(v->direction)); + if (facing_south != IsDiagDirFacingSouth(dir)) dir = ReverseDiagDir(dir); + assert(dir != INVALID_DIAGDIR); + for (Vehicle *u = v; u != nullptr; u = u->Next()) { + u->direction = DiagDirToDir(dir); + u->tile = v->tile; + } + } + + int x = v->x_pos; + int y = v->y_pos; + Trackdir tdir = v->GetVehicleTrackdir(); + + if ((v->vehstatus & VS_HIDDEN) != 0) { + const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; + + x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); + y = TileY(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].y & 0xF); + } if (first) { /* We are leaving a depot, but have to go to the exact same one; re-enter */ if (v->current_order.IsType(OT_GOTO_DEPOT) && IsRoadDepotTile(v->tile) && v->current_order.GetDestination() == GetDepotIndex(v->tile)) { - VehicleEnterDepot(v); + if (IsExtendedRoadDepot(v->tile)) { + v->StartService(); + } else { + VehicleEnterDepot(v); + } return true; } - - if (RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true; - + if ((v->vehstatus & VS_HIDDEN) != 0 && RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true; VehicleServiceInDepot(v); + v->LeaveUnbunchingDepot(); StartRoadVehSound(v); - - /* Vehicle is about to leave a depot */ + /* Vehicle is about to leave a depot. */ v->cur_speed = 0; } - v->vehstatus &= ~VS_HIDDEN; v->state = tdir; - v->frame = RVC_DEPOT_START_FRAME; - v->x_pos = x; - v->y_pos = y; + if ((v->vehstatus & VS_HIDDEN) != 0) { + v->vehstatus &= ~VS_HIDDEN; + v->x_pos = x; + v->y_pos = y; + v->frame = RVC_DEPOT_START_FRAME; + } else if (v->Next() != nullptr && (v->Next()->vehstatus & VS_HIDDEN) == 0){ + for (RoadVehicle *u = v->Next(); u != nullptr; u = u->Next()) { + u->state = DiagDirToDiagTrackdir(DirToDiagDir(u->direction)); + u->UpdatePosition(); + u->UpdateInclination(true, true); + } + } + v->UpdatePosition(); v->UpdateInclination(true, true); + if (first && IsExtendedDepot(v->tile)) { + UpdateExtendedDepotReservation(v, false); + } + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); - return true; + return !visible_vehicle; } static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicle *prev, TileIndex tile, DiagDirection entry_dir, bool already_reversed) @@ -1138,7 +1247,7 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl if (IsTileType(tile, MP_TUNNELBRIDGE)) { diag_dir = GetTunnelBridgeDirection(tile); } else if (IsRoadDepotTile(tile)) { - diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile)); + diag_dir = ReverseDiagDir(IsExtendedRoadDepot(tile) ? DirToDiagDir(v->direction) : GetRoadDepotDirection(tile)); } if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR; @@ -1206,6 +1315,53 @@ static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadB return ret.Succeeded(); } +/** Check whether there is a close vehicle ahead and act as needed. + * @param v Moving vehicle + * @param x x coordinate to check + * @param y y coordinate to check + * @param dir direction of the vehicle + * @return whether a close vehicle is found. + */ +bool CheckCloseVehicle(RoadVehicle *v, int x, int y, Direction dir) +{ + if (!v->IsFrontEngine() || IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) return false; + /* Vehicle is not in a road stop. + * Check for another vehicle to overtake */ + RoadVehicle *u = RoadVehFindCloseTo(v, x, y, dir); + + if (u == nullptr) return false; + assert(u == u->First()); + + /* There is a vehicle in front overtake it if possible */ + if (v->overtaking == 0) RoadVehCheckOvertake(v, u); + if (v->overtaking == 0) v->cur_speed = u->cur_speed; + + /* In case we are in a road depot platform, why not try to start servicing? */ + if (IsExtendedRoadDepotTile(v->tile) && v->current_order.IsType(OT_GOTO_DEPOT)) { + DepotID depot_id = GetDepotIndex(v->tile); + if (v->current_order.GetDestination() != depot_id) return true; + if (!u->IsInDepot() || GetDepotIndex(u->tile) != depot_id) return true; + for (u = v; u != nullptr; u = u->Next()) { + if (!IsExtendedRoadDepotTile(u->tile) || GetDepotIndex(u->tile) != depot_id) return true; + if (v->direction != u->direction) return true; + } + HandleRoadVehicleEnterDepot(v); + return true; + } + + /* 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->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) && + GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) { + Station *st = Station::GetByTile(v->tile); + v->last_station_visited = st->index; + RoadVehArrivesAt(v, st); + v->BeginLoading(); + } + return true; +} + bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) { if (v->overtaking != 0) { @@ -1264,18 +1420,21 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) (_settings_game.vehicle.road_side << RVS_DRIVE_SIDE)) ^ v->overtaking][v->frame + 1]; if (rd.x & RDE_NEXT_TILE) { - TileIndex tile = v->tile + TileOffsByDiagDir((DiagDirection)(rd.x & 3)); + DiagDirection diag_dir = (DiagDirection)(rd.x & 3); + TileIndex tile = v->tile + TileOffsByDiagDir(diag_dir); Trackdir dir; + bool extended_depot_turn = IsExtendedRoadDepotTile(v->tile) && + (GetRoadBits(v->tile, GetRoadTramType(v->roadtype)) & DiagDirToRoadBits(diag_dir)) == ROAD_NONE; if (v->IsFrontEngine()) { /* If this is the front engine, look for the right path. */ - if (HasTileAnyRoadType(tile, v->compatible_roadtypes)) { - dir = RoadFindPathToDest(v, tile, (DiagDirection)(rd.x & 3)); + if (HasTileAnyRoadType(tile, v->compatible_roadtypes) && !extended_depot_turn) { + dir = RoadFindPathToDest(v, tile, diag_dir); } else { - dir = _road_reverse_table[(DiagDirection)(rd.x & 3)]; + dir = _road_reverse_table[diag_dir]; } } else { - dir = FollowPreviousRoadVehicle(v, prev, tile, (DiagDirection)(rd.x & 3), false); + dir = FollowPreviousRoadVehicle(v, prev, tile, diag_dir, false); } if (dir == INVALID_TRACKDIR) { @@ -1302,7 +1461,10 @@ again: case TRACKDIR_RVREV_SW: needed = ROAD_NE; break; case TRACKDIR_RVREV_NW: needed = ROAD_SE; break; } - if ((v->Previous() != nullptr && v->Previous()->tile == tile) || + if (extended_depot_turn) { + tile = v->tile; + start_frame = RVC_TURN_AROUND_START_FRAME_SHORT_TRAM; + } else if ((v->Previous() != nullptr && v->Previous()->tile == tile) || (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) && HasTileAnyRoadType(tile, v->compatible_roadtypes) && (needed & GetRoadBits(tile, RTT_TRAM)) != ROAD_NONE)) { @@ -1319,8 +1481,9 @@ again: } else if (!v->IsFrontEngine() || !CanBuildTramTrackOnTile(v->owner, tile, v->roadtype, needed) || ((~needed & GetAnyRoadBits(v->tile, RTT_TRAM, false)) == ROAD_NONE)) { /* * Taking the 'small' corner for trams only happens when: - * - We are not the from vehicle of an articulated tram. + * - We are not the front vehicle of an articulated tram. * - Or when the company cannot build on the next tile. + * - Or when the extended depot doesn't have the appropriate tram bit to continue. * * The 'small' corner means that the vehicle is on the end of a * tram track and needs to start turning there. To do this properly @@ -1363,6 +1526,11 @@ again: } uint32_t r = VehicleEnterTile(v, tile, x, y); + if (HasBit(r, VETS_ENTERED_DEPOT_PLATFORM) && v->Next() == nullptr && v == v->First()) { + HandleRoadVehicleEnterDepot(RoadVehicle::From(v)); + return false; + } + if (HasBit(r, VETS_CANNOT_ENTER)) { if (!IsTileType(tile, MP_TUNNELBRIDGE)) { v->cur_speed = 0; @@ -1419,6 +1587,9 @@ again: } v->x_pos = x; v->y_pos = y; + if (prev != nullptr && prev->IsInDepot() && (prev->vehstatus & VS_HIDDEN) == 0) { + v->state = RVSB_IN_DEPOT; + } v->UpdatePosition(); RoadZPosAffectSpeed(v, v->UpdateInclination(true, true)); return true; @@ -1429,7 +1600,7 @@ again: Trackdir dir; uint turn_around_start_frame = RVC_TURN_AROUND_START_FRAME; - if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) { + if (RoadTypeIsTram(v->roadtype) && !IsRoadDepotTile(v->tile) && !IsExtendedRoadDepotTile(v->tile) && HasExactlyOneBit(GetAnyRoadBits(v->tile, RTT_TRAM, true))) { /* * The tram is turning around with one tram 'roadbit'. This means that * it is using the 'big' corner 'drive data'. However, to support the @@ -1495,6 +1666,9 @@ again: v->x_pos = x; v->y_pos = y; + if (prev != nullptr && prev->IsInDepot() && (prev->vehstatus & VS_HIDDEN) == 0) { + v->state = RVSB_IN_DEPOT; + } v->UpdatePosition(); RoadZPosAffectSpeed(v, v->UpdateInclination(true, true)); return true; @@ -1504,8 +1678,10 @@ again: * it's on a depot tile, check if it's time to activate the next vehicle in * the chain yet. */ if (v->Next() != nullptr && IsRoadDepotTile(v->tile)) { - if (v->frame == v->gcache.cached_veh_length + RVC_DEPOT_START_FRAME) { - RoadVehLeaveDepot(v->Next(), false); + if ((v->Next()->vehstatus & VS_HIDDEN)) { + if (v->frame == v->gcache.cached_veh_length + RVC_DEPOT_START_FRAME) { + RoadVehLeaveDepot(v->Next(), false); + } } } @@ -1515,30 +1691,7 @@ again: Direction new_dir = RoadVehGetSlidingDirection(v, x, y); - if (v->IsFrontEngine() && !IsInsideMM(v->state, RVSB_IN_ROAD_STOP, RVSB_IN_ROAD_STOP_END)) { - /* Vehicle is not in a road stop. - * Check for another vehicle to overtake */ - RoadVehicle *u = RoadVehFindCloseTo(v, x, y, new_dir); - - if (u != nullptr) { - assert(u == u->First()); - /* There is a vehicle in front overtake it if possible */ - if (v->overtaking == 0) RoadVehCheckOvertake(v, u); - if (v->overtaking == 0) v->cur_speed = u->cur_speed; - - /* 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->owner == GetTileOwner(v->tile) && !v->current_order.IsType(OT_LEAVESTATION) && - GetRoadStopType(v->tile) == (v->IsBus() ? ROADSTOP_BUS : ROADSTOP_TRUCK)) { - Station *st = Station::GetByTile(v->tile); - v->last_station_visited = st->index; - RoadVehArrivesAt(v, st); - v->BeginLoading(); - } - return false; - } - } + if (CheckCloseVehicle(v, x, y, new_dir)) return false; Direction old_dir = v->direction; if (new_dir != old_dir) { @@ -1634,6 +1787,16 @@ again: v->y_pos = y; v->UpdatePosition(); RoadZPosAffectSpeed(v, v->UpdateInclination(false, true)); + + /* After updating the position, check whether the vehicle can stop in a depot platform. */ + if (IsExtendedRoadDepotTile(v->tile) && v->Next() == nullptr) { + RoadVehicle *first = RoadVehicle::From(v)->First(); + if (HasBit(VehicleEnterTile(first, first->tile, first->x_pos, first->y_pos), VETS_ENTERED_DEPOT_PLATFORM)) { + HandleRoadVehicleEnterDepot(first); + return false; + } + } + return true; } @@ -1655,6 +1818,8 @@ static bool RoadVehController(RoadVehicle *v) return true; } + if (v->ContinueServicing()) return true; + ProcessOrders(v); v->HandleLoading(); @@ -1816,6 +1981,9 @@ Trackdir RoadVehicle::GetVehicleTrackdir() const if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR; if (this->IsInDepot()) { + if (IsExtendedRoadDepot(this->tile)) { + return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); + } /* We'll assume the road vehicle is facing outwards */ return DiagDirToDiagTrackdir(GetRoadDepotDirection(this->tile)); } diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index 1a23c07a23..b2f94fdda5 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -253,6 +253,9 @@ CommandCost CmdSellVehicle(DoCommandFlag flags, VehicleID v_id, bool sell_chain, ret = CommandCost(EXPENSES_NEW_VEHICLES, -front->value); if (flags & DC_EXEC) { + if (front->type == VEH_ROAD && IsExtendedDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) { + UpdateExtendedDepotReservation(v, false); + } if (front->IsPrimaryVehicle() && backup_order) OrderBackup::Backup(front, client_id); delete front; } @@ -610,7 +613,16 @@ CommandCost CmdStartStopVehicle(DoCommandFlag flags, VehicleID veh_id, bool eval } case VEH_SHIP: + break; case VEH_ROAD: + if ((v->vehstatus & VS_STOPPED) && !(flags & DC_AUTOREPLACE) && v->IsStoppedInDepot()) { + Depot *dep = Depot:: GetByTile(v->tile); + + /* Check that the vehicle can drive on some tile of the depot */ + RoadType rt = Engine::Get(v->engine_type)->u.road.roadtype; + const RoadTypeInfo *rti = GetRoadTypeInfo(rt); + if ((dep->r_types.road_types & rti->powered_roadtypes) == 0) return_cmd_error(STR_ERROR_ROAD_VEHICLE_START_NO_POWER); + } break; case VEH_AIRCRAFT: {