diff --git a/src/autoreplace_cmd.cpp b/src/autoreplace_cmd.cpp index c6bc9017666e6..0822b0553f5a9 100644 --- a/src/autoreplace_cmd.cpp +++ b/src/autoreplace_cmd.cpp @@ -805,6 +805,7 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1 if (v->type == VEH_TRAIN) { train_placement.LiftTrain(Train::From(v), flags); } else if (IsBigDepotTile(v->tile)) { + if (v->type == VEH_ROAD) ChangeNumDepotVehicles(v->tile, -1); SetBigDepotReservation(v, false); } @@ -853,6 +854,7 @@ CommandCost CmdAutoreplaceVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1 } train_placement.PlaceTrain(Train::From(v), flags); } else if (IsBigDepotTile(v->tile)) { + if (v->type == VEH_ROAD) ChangeNumDepotVehicles(v->tile, 1); SetBigDepotReservation(v, true); } diff --git a/src/depot.cpp b/src/depot.cpp index f4be6e8b1fbda..57381457c210b 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -169,6 +169,31 @@ void Depot::RescanDepotTiles() } } +DepotReservation GetDepotReservationStatus(TileIndex tile) { + assert(IsBigDepotTile(tile) && IsRoadDepot(tile)); + uint num_veh = GetNumDepotVehicles(tile); + switch (num_veh) { + case 0: + return DEPOT_RESERVATION_EMPTY; + default: + assert(num_veh < ROAD_DEPOT_CAPACITY); + return DEPOT_RESERVATION_IN_USE; + case ROAD_DEPOT_CAPACITY: + num_veh = 0; + for (const Vehicle *u : Vehicle::Iterate()) { + if (u->type != VEH_ROAD) continue; + if (!u->IsFrontEngine()) continue; + if (!u->IsInDepot()) continue; + if (u->tile != tile) continue; + if ((u->vehstatus & VS_STOPPED) == 0) continue; + num_veh++; + } + assert(num_veh <= ROAD_DEPOT_CAPACITY); + return num_veh == ROAD_DEPOT_CAPACITY ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } +} + /** * Fix tile reservations on big depots and vehicle changes. * @param v Vehicle to be revised. @@ -180,11 +205,15 @@ void SetBigDepotReservation(Vehicle *v, bool reserve) assert(IsBigDepotTile(v->tile)); DepotReservation res_type = DEPOT_RESERVATION_EMPTY; - res_type = (v->vehstatus & VS_STOPPED) ? - DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + if (reserve && v->type != VEH_ROAD) { + res_type = (v->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } switch (v->type) { case VEH_ROAD: + res_type = GetDepotReservationStatus(v->tile); + SetDepotReservation(v->tile, res_type); break; case VEH_SHIP: diff --git a/src/economy_type.h b/src/economy_type.h index 85a1bbe4fceba..ef99d3185d5e9 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -222,8 +222,6 @@ static const int INVALID_PRICE_MODIFIER = MIN_PRICE_MODIFIER - 1; static const uint TUNNELBRIDGE_TRACKBIT_FACTOR = 4; /** Multiplier for how many regular track bits a level crossing counts. */ static const uint LEVELCROSSING_TRACKBIT_FACTOR = 2; -/** Multiplier for how many regular track bits a road depot counts. */ -static const uint ROAD_DEPOT_TRACKBIT_FACTOR = 2; /** Multiplier for how many regular track bits a bay stop counts. */ static const uint ROAD_STOP_TRACKBIT_FACTOR = 2; /** Multiplier for how many regular tiles a lock counts. */ diff --git a/src/lang/english.txt b/src/lang/english.txt index adc667ae54e44..64b0012318046 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2518,7 +2518,8 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION :{BLACK}Build ro STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}Build tramway section. Ctrl toggles build/remove for tramway construction. Shift toggles building/showing cost estimate STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Build road section using the Autoroad mode. Ctrl toggles build/remove for road construction. Shift toggles building/showing cost estimate STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}Build tramway section using the Autotram mode. Ctrl toggles build/remove for tramway construction. Shift toggles building/showing cost estimate -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build road vehicle depot (for buying and servicing vehicles). Shift toggles building/showing cost estimate +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build one-tile road vehicle depot (for buying and servicing vehicles). Shift toggles building/showing cost estimate +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_BIG_DEPOT :{BLACK}Build multi-tile road vehicle depots (for buying and servicing vehicles). Ctrl enables joining depots. Shift toggles building/showing cost estimate STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build tram vehicle depot (for buying and servicing vehicles). Shift toggles building/showing cost estimate STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Build bus station. Ctrl enables joining stations. Shift toggles building/showing cost estimate STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION :{BLACK}Build passenger tram station. Ctrl enables joining stations. Shift toggles building/showing cost estimate @@ -2777,7 +2778,8 @@ STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT_EXTENDED :Extended railwa STR_LAI_ROAD_DESCRIPTION_ROAD :Road STR_LAI_ROAD_DESCRIPTION_ROAD_WITH_STREETLIGHTS :Road with street lights STR_LAI_ROAD_DESCRIPTION_TREE_LINED_ROAD :Tree-lined road -STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT :Road vehicle depot +STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_STANDARD :Standard road vehicle depot +STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED :Extended road vehicle depot STR_LAI_ROAD_DESCRIPTION_ROAD_RAIL_LEVEL_CROSSING :Road/rail level crossing STR_LAI_ROAD_DESCRIPTION_TRAMWAY :Tramway @@ -4587,7 +4589,7 @@ STR_ERROR_CANT_START_PLATFORM_LONG :{WHITE}{VEHICLE STR_ERROR_DEPOT_FULL_DEPOT :There is no free depot compatible with this type of vehicle STR_ADVICE_PLATFORM_TYPE :{WHITE}{VEHICLE} cannot leave depot because there is no compatible platform for this type of train STR_ADVICE_PLATFORM_LONG :{WHITE}{VEHICLE} cannot leave depot because compatible platforms are not long enough -STR_ADVICE_TRAIN_HAS_NO_POWER :{WHITE}{VEHICLE} cannot leave depot because it has no power in any tile of the depot +STR_ADVICE_VEHICLE_HAS_NO_POWER :{WHITE}{VEHICLE} cannot leave depot because it has no power in any tile of the depot STR_ADVICE_PLATFORM_FREE_PLATFORM :{WHITE}{VEHICLE} cannot leave depot because compatible platforms are occupied STR_ADVICE_PLATFORM_SIGNALS :{WHITE}{VEHICLE} cannot leave depot because the segments of the compatible free platforms are occupied. Check the signaling of those segments. @@ -4748,6 +4750,7 @@ STR_ERROR_NO_VEHICLES_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/pathfinder/follow_track.hpp b/src/pathfinder/follow_track.hpp index 440b4fa5e471a..e931ea935c2ec 100644 --- a/src/pathfinder/follow_track.hpp +++ b/src/pathfinder/follow_track.hpp @@ -277,12 +277,14 @@ struct CFollowTrackT /* road depots can be also left in one direction only */ if (IsRoadTT() && IsDepotTypeTile(m_old_tile, TT())) { - DiagDirection exitdir = GetRoadDepotDirection(m_old_tile); - if (exitdir != m_exitdir) { + RoadTramType rtt = IsTram() ? RTT_TRAM : RTT_ROAD; + RoadBits rb = GetRoadBits(m_old_tile, rtt); + if ((rb & DiagDirToRoadBits(m_exitdir)) == ROAD_NONE) { m_err = EC_NO_WAY; return false; } } + return true; } @@ -309,16 +311,17 @@ struct CFollowTrackT /* road and rail depots can also be entered from one direction only */ if (IsRoadTT() && IsDepotTypeTile(m_new_tile, TT())) { - DiagDirection exitdir = GetRoadDepotDirection(m_new_tile); - if (ReverseDiagDir(exitdir) != m_exitdir) { - m_err = EC_NO_WAY; - return false; - } /* don't try to enter other company's depots */ if (GetTileOwner(m_new_tile) != m_veh_owner) { m_err = EC_OWNER; return false; } + RoadTramType rtt = IsTram() ? RTT_TRAM : RTT_ROAD; + RoadBits rb = GetRoadBits(m_new_tile, rtt); + if ((rb & DiagDirToRoadBits(ReverseDiagDir(m_exitdir))) == ROAD_NONE) { + m_err = EC_NO_WAY; + return false; + } } if (IsRailTT() && IsSmallRailDepotTile(m_new_tile)) { DiagDirection exitdir = GetRailDepotDirection(m_new_tile); @@ -407,9 +410,12 @@ struct CFollowTrackT if (IsBigRailDepot(m_old_tile)) return false; exitdir = GetRailDepotDirection(m_old_tile); break; - case TRANSPORT_ROAD: - exitdir = GetRoadDepotDirection(m_old_tile); + case TRANSPORT_ROAD: { + RoadBits rb = GetRoadBits(m_old_tile, IsTram() ? RTT_TRAM : RTT_ROAD) & DiagDirToRoadBits(m_exitdir); + if (rb != ROAD_NONE) return false; + exitdir = ReverseDiagDir(m_exitdir); break; + } default: NOT_REACHED(); } diff --git a/src/pathfinder/npf/npf.cpp b/src/pathfinder/npf/npf.cpp index 25da2e1e5a6dd..71d4f1d4a69d6 100644 --- a/src/pathfinder/npf/npf.cpp +++ b/src/pathfinder/npf/npf.cpp @@ -368,7 +368,25 @@ static int32 NPFRoadPathCost(AyStar *as, AyStarNode *current, OpenListNode *pare case MP_ROAD: cost = NPF_TILE_LENGTH; /* Increase the cost for level crossings */ - if (IsLevelCrossing(tile)) cost += _settings_game.pf.npf.npf_crossing_penalty; + if (IsLevelCrossing(tile)) { + cost += _settings_game.pf.npf.npf_crossing_penalty; + } else if (IsRoadDepot(tile) && IsBigDepot(tile)) { + /* Check if vehicle is heading towards this depot. */ + NPFFindStationOrTileData *fstd = (NPFFindStationOrTileData*)as->user_target; + bool dest_depot = fstd->v->current_order.IsType(OT_GOTO_DEPOT) && GetDepotIndex(tile) == fstd->v->current_order.GetDestination(); + switch (GetDepotReservation(tile)) { + case DEPOT_RESERVATION_FULL_STOPPED_VEH: + cost += dest_depot ? NPF_INFINITE_PENALTY : (8 * NPF_TILE_LENGTH); + break; + case DEPOT_RESERVATION_IN_USE: + cost += 4 * NPF_TILE_LENGTH; + break; + case DEPOT_RESERVATION_EMPTY: + cost += NPF_TILE_LENGTH; + break; + default: NOT_REACHED(); + } + } break; case MP_STATION: { @@ -643,7 +661,7 @@ static int32 NPFFindStationOrTile(const AyStar *as, const OpenListNode *current) (fstd->not_articulated || IsDriveThroughStopTile(tile))) return AYSTAR_FOUND_END_NODE; } else if (IsDepotTypeTile(tile, user->type) && GetDepotIndex(tile) == fstd->depot_index) { - return AYSTAR_FOUND_END_NODE; + if (fstd->v->type != VEH_ROAD || !IsFullDepot(tile)) return AYSTAR_FOUND_END_NODE; } return AYSTAR_DONE; @@ -825,7 +843,7 @@ static DiagDirection GetSingleTramBit(TileIndex tile) */ static DiagDirection GetTileSingleEntry(TileIndex tile, TransportType type, uint subtype) { - if (type != TRANSPORT_WATER && IsDepotTypeTile(tile, type) && !IsBigRailDepotTile(tile)) return GetDepotDirection(tile, type); + if (type != TRANSPORT_WATER && IsDepotTypeTile(tile, type) && !IsBigDepotTile(tile)) return GetDepotDirection(tile, type); if (type == TRANSPORT_ROAD) { if (IsStandardRoadStopTile(tile)) return GetRoadStopDir(tile); diff --git a/src/pathfinder/yapf/yapf_road.cpp b/src/pathfinder/yapf/yapf_road.cpp index ae0ff71323f07..d4f406609a791 100644 --- a/src/pathfinder/yapf/yapf_road.cpp +++ b/src/pathfinder/yapf/yapf_road.cpp @@ -66,6 +66,19 @@ class CYapfCostRoadT /* Increase the cost for level crossings */ if (IsLevelCrossing(tile)) { cost += Yapf().PfGetSettings().road_crossing_penalty; + } else if (IsRoadDepot(tile) && IsBigDepot(tile)) { + switch (GetDepotReservation(tile)) { + case DEPOT_RESERVATION_FULL_STOPPED_VEH: + cost += 8 * YAPF_TILE_LENGTH; + break; + case DEPOT_RESERVATION_IN_USE: + cost += 4 * YAPF_TILE_LENGTH; + break; + case DEPOT_RESERVATION_EMPTY: + cost += YAPF_TILE_LENGTH; + break; + default: NOT_REACHED(); + } } break; @@ -133,7 +146,7 @@ class CYapfCostRoadT } /* stop if we have just entered the depot */ - if (IsRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) { + if (IsRoadDepotTile(tile) && !IsBigRoadDepotTile(tile) && trackdir == DiagDirToDiagTrackdir(ReverseDiagDir(GetRoadDepotDirection(tile)))) { /* next time we will reverse and leave the depot */ break; } @@ -199,12 +212,12 @@ class CYapfDestinationAnyDepotRoadT /** Called by YAPF to detect if node ends in the desired destination */ inline bool PfDetectDestination(Node &n) { - return IsRoadDepotTile(n.m_segment_last_tile); + return PfDetectDestinationTile(n.m_segment_last_tile, n.m_segment_last_td); } inline bool PfDetectDestinationTile(TileIndex tile, Trackdir trackdir) { - return IsRoadDepotTile(tile); + return IsRoadDepotTile(tile) && !IsFullDepot(tile); } /** @@ -288,7 +301,8 @@ class CYapfDestinationTileRoadT if (m_dest_depot != INVALID_DEPOT) { return IsRoadDepotTile(tile) && - GetDepotIndex(tile) == m_dest_depot; + GetDepotIndex(tile) == m_dest_depot && + !IsFullDepot(tile); } return tile == m_destTile && HasTrackdir(m_destTrackdirs, trackdir); diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 6b2f6c5ce2d4f..5bb5554d4c493 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -317,6 +317,22 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R return CommandCost(); } +void UpdateRoadDepotDir(TileIndex tile) +{ + assert(IsBigRoadDepot(tile)); + RoadBits rb = GetAllRoadBits(tile); + DiagDirection dir = DIAGDIR_NE; + if (rb & ROAD_SE) { + dir = DIAGDIR_SE; + } else if (rb & ROAD_SW) { + dir = DIAGDIR_SW; + } else if (rb & ROAD_NW) { + dir = DIAGDIR_NW; + } else { + assert(rb & ROAD_NE); + } + SetRoadDepotDirection(tile, dir); +} /** * Delete a piece of road. @@ -527,16 +543,29 @@ static CommandCost RemoveRoad(TileIndex tile, DoCommandFlag flags, RoadBits piec } case ROAD_TILE_DEPOT: { - if (!HasRoadTypeRoad(tile) || !HasRoadTypeTram(tile)) return CMD_ERROR; - if (flags & DC_EXEC) { + /* Depot must have at least one road bit. */ + RoadBits new_rb = (GetRoadBits(tile, rtt) & ~pieces); + if (new_rb == ROAD_NONE && GetRoadType(tile, OtherRoadTramType(rtt)) == INVALID_ROADTYPE) return CMD_ERROR; + + uint num_removed_bits = CountBits(pieces & GetRoadBits(tile, rtt)); + CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD] * num_removed_bits); + + if ((flags & DC_EXEC) && num_removed_bits != 0) { + SetRoadBits(tile, new_rb, rtt); + Company *c = Company::GetIfValid(GetTileOwner(tile)); - c->infrastructure.road[GetRoadType(tile, rtt)] -= ROAD_DEPOT_TRACKBIT_FACTOR; + c->infrastructure.road[GetRoadType(tile, rtt)] -= num_removed_bits; DirtyCompanyInfrastructureWindows(c->index); - SetRoadType(tile, rtt, INVALID_ROADTYPE); - Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), false); + + if (new_rb == ROAD_NONE) { + SetRoadType(tile, rtt, INVALID_ROADTYPE); + Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), false); + } + + if (IsBigRoadDepot(tile)) UpdateRoadDepotDir(tile); MarkTileDirtyByTile(tile); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); + return cost; } default: NOT_REACHED(); @@ -732,7 +761,16 @@ CommandCost CmdBuildRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 break; case ROAD_TILE_DEPOT: - if (DiagDirToRoadBits(GetRoadDepotDirection(tile)) == pieces) { + if (IsBigRoadDepot(tile)) { + RoadType tile_rt = GetRoadType(tile, rtt); + if (tile_rt != INVALID_ROADTYPE && rt != tile_rt) return CMD_ERROR; + Axis axis = DiagDirToAxis(GetRoadDepotDirection(tile)); + RoadBits rb = (axis == AXIS_X ? ROAD_X : ROAD_Y) & pieces; + if (rb != pieces) return CMD_ERROR; + if ((pieces & ~existing) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * CountBits(pieces & ~existing)); + break; + } else if (GetRoadBits(tile, OtherRoadTramType(rtt)) == pieces) { /* Check if we can add a new road/tram type if none present. */ if (HasTileRoadType(tile, rtt)) { return_cmd_error(STR_ERROR_ALREADY_BUILT); @@ -911,7 +949,13 @@ do_clear:; RoadTileType rttype = GetRoadTileType(tile); if (rttype == ROAD_TILE_DEPOT) { SetRoadType(tile, rtt, rt); - UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR); + if (IsBigRoadDepot(tile)) { + SetRoadBits(tile, pieces | GetRoadBits(tile, rtt), rtt); + /* Do not add or remove to company infrastructure for depots. Already acounted for. */ + UpdateRoadDepotDir(tile); + } else { + SetRoadBits(tile, GetRoadBits(tile, OtherRoadTramType(rtt)), rtt); + } Depot::GetByTile(tile)->AfterAddRemove(TileArea(tile), true); break; } else if (existing == ROAD_NONE || rttype == ROAD_TILE_CROSSING) { @@ -1174,7 +1218,7 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 return had_success ? cost : last_error; } - +#include "debug.h" /** * Build a road depot. * @param tile tile where to build the depot @@ -1182,7 +1226,11 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 * @param p1 bit 0..1 : entrance direction (DiagDirection) * bit 2..7 : road type * bit 8 : allow adjacent depots - * bits 9-15 : unused + * bit 9 : 0 for normal depots, 1 for big depots + * bit 10 : start tile starts in the 2nd half of tile (p2 & 1). Only used if bit 6 is set or if we are building a single tile + * bit 11 : end tile starts in the 2nd half of tile (p2 & 2). Only used if bit 6 is set or if we are building a single tile + * bit 12 : direction: 0 = along x-axis, 1 = along y-axis (p2 & 4) + * bits 10-15 : unused * bits 16-31 : DepotID to join to * @param p2 end tile * @param text unused @@ -1193,45 +1241,135 @@ CommandCost CmdRemoveLongRoad(TileIndex start_tile, DoCommandFlag flags, uint32 */ CommandCost CmdBuildRoadDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { - DiagDirection dir = Extract(p1); + TileIndex end_tile = p2; + DiagDirection orig_dir = Extract(p1); + Axis axis = DiagDirToAxis(orig_dir); RoadType rt = Extract(p1); + RoadTramType rtt = GetRoadTramType(rt); bool adjacent = HasBit(p1, 8); + bool build_big_depot = HasBit(p1, 9); DepotID join_to = GB(p1, 16, 16); + uint start_coord; + uint end_coord; + bool start_only_half = HasBit(p1, 10); + bool end_only_half = HasBit(p1, 11); + + DiagDirection dir = orig_dir; + if (build_big_depot) { + start_coord = axis == AXIS_X ? TileX(tile) : TileY(tile); + end_coord = axis == AXIS_X ? TileX(end_tile) : TileY(end_tile); + + dir = AxisToDiagDir(axis); + + /* Swap direction, also the half-tile drag var (bit 0 and 1) */ + if (start_coord > end_coord || (start_coord == end_coord && HasBit(p2, 10))) { + dir = ReverseDiagDir(dir); + start_only_half = !start_only_half; + end_only_half = !end_only_half; + } + } TileArea ta(tile, p2); - assert(ta.w == 1 || ta.h == 1); + assert(build_big_depot || ta.w == 1 || ta.h == 1); if (!ValParamRoadType(rt)) return CMD_ERROR; + if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.road_depot_types, build_big_depot)) { + return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE); + } + /* Create a new depot or find a depot to join to. */ Depot *depot = nullptr; CommandCost ret = FindJoiningDepot(ta, VEH_ROAD, join_to, depot, adjacent, flags); if (ret.Failed()) return ret; CommandCost cost(EXPENSES_CONSTRUCTION); + int allowed_z = -1; + uint num_new_pieces = 0; + uint invalid_dirs = build_big_depot ? 5 << axis : 1 << dir; TILE_AREA_LOOP(tile, ta) { if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - Slope tileh = GetTileSlope(tile); + int z; + Slope tileh = GetTileSlope(tile, &z); + int flat_z = z + GetSlopeMaxZ(tileh); + if (tileh != SLOPE_FLAT) { if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) { return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); } + + if (build_big_depot && IsSteepSlope(tileh)) return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + /* Forbid building if the tile faces a slope in a invalid direction. */ + for (DiagDirection dir = DIAGDIR_BEGIN; dir != DIAGDIR_END; dir++) { + if (HasBit(invalid_dirs, dir) && !CanBuildDepotByTileh(dir, tileh)) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } + } + cost.AddCost(_price[PR_BUILD_FOUNDATION]); } - cost.AddCost(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR)); - if (cost.Failed()) return cost; + /* The level of this tile must be equal to allowed_z. */ + if (allowed_z < 0) { + /* First tile. */ + allowed_z = flat_z; + } else if (allowed_z != flat_z) { + return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED); + } + + if (!IsRoadDepotTile(tile) || GetDepotIndex(tile) != join_to) { + cost.AddCost(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR)); + if (cost.Failed()) return cost; + } + + /* Check which road bits to build. */ + RoadBits rb = ROAD_NONE; + if (build_big_depot) { + uint axis_coord = axis == AXIS_X ? TileX(tile) : TileY(tile); + /* Road parts only have to be built at the start tile or at the end tile. */ + if (!end_only_half && axis_coord == end_coord) { + rb = DiagDirToRoadBits(ReverseDiagDir(dir)); + } + if (start_only_half && axis_coord == start_coord) { + rb = DiagDirToRoadBits(dir); + } + if (rb == ROAD_NONE) { + rb = AxisToRoadBits(axis); + } + assert(rb != ROAD_NONE); + if (IsRoadDepotTile(tile)) { + RoadBits old_rb = GetRoadBits(tile, rtt); + if ((rb & ~old_rb) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + num_new_pieces += CountBits(rb & ~old_rb); + rb |= old_rb; + } else { + num_new_pieces += CountBits(rb); + } + } else { + rb = DiagDirToRoadBits(orig_dir); + if (IsRoadDepotTile(tile)) { + RoadBits old_rb = GetRoadBits(tile, rtt); + if ((rb & ~old_rb) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + } + num_new_pieces += 1; + } if (flags & DC_EXEC) { - MakeRoadDepot(tile, _current_company, depot->index, dir, rt); + MakeRoadDepot(tile, _current_company, depot->index, orig_dir, rt); + SetRoadBits(tile, rb, rtt); + if (build_big_depot) { + SB(_m[tile].m5, 5, 1, true); + UpdateRoadDepotDir(tile); + } + MarkTileDirtyByTile(tile); } } if (flags & DC_EXEC) { /* A road depot has two road bits. */ - UpdateCompanyRoadInfrastructure(rt, _current_company, ROAD_DEPOT_TRACKBIT_FACTOR); + UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_pieces); depot->AfterAddRemove(ta, true); if (join_to == NEW_DEPOT) MakeDefaultName(depot); @@ -1260,10 +1398,8 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) Depot *depot = Depot::GetByTile(tile); Company *c = Company::GetIfValid(depot->company); if (c != nullptr) { - /* A road depot has two road bits. */ - RoadType rt = GetRoadTypeRoad(tile); - if (rt != INVALID_ROADTYPE) c->infrastructure.road[rt] -= ROAD_DEPOT_TRACKBIT_FACTOR; - if (tt != INVALID_ROADTYPE) c->infrastructure.road[tt] -= ROAD_DEPOT_TRACKBIT_FACTOR; + /* A road depot has two road types. */ + c->infrastructure.road[rt] -= (CountBits(GetRoadBits(tile, RTT_ROAD)) + CountBits(GetRoadBits(tile, RTT_TRAM))); DirtyCompanyInfrastructureWindows(c->index); } @@ -1316,7 +1452,8 @@ static CommandCost ClearTile_Road(TileIndex tile, DoCommandFlag flags) return ret; } - default: + default: NOT_REACHED(); + case ROAD_TILE_DEPOT: if (flags & DC_AUTO) { return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); @@ -1499,18 +1636,13 @@ void DrawRoadCatenary(const TileInfo *ti) if (IsTileType(ti->tile, MP_ROAD)) { switch (GetRoadTileType(ti->tile)) { case ROAD_TILE_NORMAL: + case ROAD_TILE_DEPOT: road = GetRoadBits(ti->tile, RTT_ROAD); tram = GetRoadBits(ti->tile, RTT_TRAM); break; case ROAD_TILE_CROSSING: tram = road = (GetCrossingRailAxis(ti->tile) == AXIS_Y ? ROAD_X : ROAD_Y); break; - case ROAD_TILE_DEPOT: { - DiagDirection dir = GetRoadDepotDirection(ti->tile); - road = DiagDirToRoadBits(dir); - tram = DiagDirToRoadBits(dir); - break; - } default: NOT_REACHED(); } } else if (IsTileType(ti->tile, MP_STATION)) { @@ -1797,7 +1929,8 @@ static void DrawTile_Road(TileInfo *ti) break; } - default: + default: NOT_REACHED(); + case ROAD_TILE_DEPOT: { if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); @@ -1809,8 +1942,8 @@ static void DrawTile_Road(TileInfo *ti) const RoadTypeInfo *main_rti = tram_rti != nullptr ? tram_rti : road_rti; DiagDirection dir = GetRoadDepotDirection(ti->tile); - uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); - uint tram_offset = GetRoadSpriteOffset(SLOPE_FLAT, DiagDirToRoadBits(dir)); + uint road_offset = GetRoadSpriteOffset(SLOPE_FLAT, GetRoadBits(ti->tile, RTT_ROAD)); + uint tram_offset = GetRoadSpriteOffset(SLOPE_FLAT, GetRoadBits(ti->tile, RTT_TRAM)); PaletteID pal = PAL_NONE; @@ -1818,6 +1951,8 @@ static void DrawTile_Road(TileInfo *ti) DrawGroundSprite(dts->ground.sprite, pal); DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset); + /* Revise: it will be necessary to have proper road bits drawn here. */ + if (road_rti != nullptr && !road_rti->UsesOverlay()) DrawGroundSprite(SPR_ROAD_Y + road_offset, PALETTE_CRASH); int relocation = GetCustomRoadSprite(main_rti, ti->tile, ROTSG_DEPOT); bool default_gfx = relocation == 0; if (default_gfx) { @@ -1888,6 +2023,9 @@ void DrawRoadDepotSprite(int x, int y, DiagDirection dir, RoadType rt) } else if (rtt == RTT_TRAM) { DrawSprite(SPR_TRAMWAY_TRAM + road_offset, PAL_NONE, x, y); DrawSprite(SPR_TRAMWAY_OVERLAY + road_offset, PAL_NONE, x, y); + } else { + /* Revise: will need appropriate road bits sprites. */ + DrawSprite(SPR_ROAD_Y + road_offset, PALETTE_CRASH, x, y); } if (default_gfx) { @@ -2092,6 +2230,8 @@ static const TrackBits _road_trackbits[16] = { TRACK_BIT_ALL, // ROAD_ALL }; +#include "debug.h" + static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) { TrackdirBits trackdirbits = TRACKDIR_BIT_NONE; @@ -2127,13 +2267,14 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u break; } - default: + default: NOT_REACHED(); + case ROAD_TILE_DEPOT: { - DiagDirection dir = GetRoadDepotDirection(tile); + Axis axis = DiagDirToAxis(GetRoadDepotDirection(tile)); - if (side != INVALID_DIAGDIR && side != dir) break; + if (side != INVALID_DIAGDIR && axis != DiagDirToAxis(side)) break; - trackdirbits = TrackBitsToTrackdirBits(DiagDirToDiagTrackBits(dir)); + trackdirbits = TrackBitsToTrackdirBits(AxisToTrackBits(axis)); break; } } @@ -2190,7 +2331,7 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td) } case ROAD_TILE_DEPOT: - td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT; + td->str = IsBigDepot(tile) ? STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED : STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_STANDARD; td->build_date = Depot::GetByTile(tile)->build_date; break; @@ -2236,19 +2377,36 @@ static VehicleEnterTileStatus VehicleEnter_Road(Vehicle *v, TileIndex tile, int case ROAD_TILE_DEPOT: { if (v->type != VEH_ROAD) break; - 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; + if (IsBigRoadDepot(tile)) { + if (!v->current_order.IsType(OT_GOTO_DEPOT) || + v->current_order.GetDestination() != GetDepotIndex(tile)) { + return VETSB_CONTINUE; + } + RoadVehicle *rv = RoadVehicle::From(v); + if (rv->frame == RVC_DEPOT_STOP_FRAME && + ((_roadveh_enter_depot_dir[GetRoadDepotDirection(tile)] == rv->state) || + (_roadveh_enter_depot_dir[ReverseDiagDir(GetRoadDepotDirection(tile))] == rv->state))) { + rv->state = RVSB_IN_DEPOT; + if (rv->Next() == nullptr) HandleRoadVehicleEnterDepot(rv->First()); + rv->tile = tile; + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); + return VETSB_CONTINUE; + } + } 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) HandleRoadVehicleEnterDepot(rv->First()); + rv->tile = tile; + + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(rv->tile)); + return VETSB_ENTERED_WORMHOLE; + } + break; } - break; } default: break; @@ -2264,14 +2422,14 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne if (new_owner == INVALID_OWNER) { DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR); } else { - /* A road depot has two road bits. No need to dirty windows here, we'll redraw the whole screen anyway. */ - RoadType rt = GetRoadTypeRoad(tile); - if (rt == INVALID_ROADTYPE) rt = GetRoadTypeTram(tile); - Company::Get(old_owner)->infrastructure.road[rt] -= 2; - Company::Get(new_owner)->infrastructure.road[rt] += 2; - SetTileOwner(tile, new_owner); FOR_ALL_ROADTRAMTYPES(rtt) { + RoadType rt = GetRoadTypeRoad(tile); + if (rt != INVALID_ROADTYPE) { + uint pieces = CountBits(GetRoadBits(tile, rtt)); + Company::Get(old_owner)->infrastructure.road[rt] -= pieces; + Company::Get(new_owner)->infrastructure.road[rt] += pieces; + } if (GetRoadOwner(tile, rtt) == old_owner) { SetRoadOwner(tile, rtt, new_owner); } @@ -2320,7 +2478,10 @@ static CommandCost TerraformTile_Road(TileIndex tile, DoCommandFlag flags, int z break; case ROAD_TILE_DEPOT: - if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile))) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]); + if (AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRoadDepotDirection(tile)) && + (!IsBigRoadDepot(tile) || AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(GetRoadDepotDirection(tile))))) { + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]); + } break; case ROAD_TILE_NORMAL: { @@ -2504,7 +2665,7 @@ CommandCost CmdConvertRoad(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 if (tt == MP_STATION && IsStandardRoadStopTile(tile)) { num_pieces *= ROAD_STOP_TRACKBIT_FACTOR; } else if (tt == MP_ROAD && IsRoadDepot(tile)) { - num_pieces *= ROAD_DEPOT_TRACKBIT_FACTOR; + num_pieces = CountBits(GetRoadBits(tile, RTT_ROAD)) + CountBits(GetRoadBits(tile, RTT_TRAM)); } found_convertible_road = true; diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 53ca6bd7aa7dd..b03e7bc7d40a3 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -298,7 +298,11 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); - if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + if (_game_mode == GM_NORMAL && + ((this->HasWidget(WID_ROT_DEPOT) && this->IsWidgetLowered(WID_ROT_DEPOT)) || + (this->HasWidget(WID_ROT_BIG_DEPOT) && this->IsWidgetLowered(WID_ROT_BIG_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } if (_settings_client.gui.link_terraform_toolbar) DeleteWindowById(WC_SCEN_LAND_GEN, 0, false); } @@ -316,6 +320,7 @@ struct BuildRoadToolbarWindow : Window { bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt); this->SetWidgetsDisabledState(!can_build, WID_ROT_DEPOT, + WID_ROT_BIG_DEPOT, WID_ROT_BUS_STATION, WID_ROT_TRUCK_STATION, WIDGET_LIST_END); @@ -328,7 +333,8 @@ struct BuildRoadToolbarWindow : Window { if (_game_mode != GM_EDITOR) { if (!can_build) { /* Show in the tooltip why this button is disabled. */ - this->GetWidget(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); + if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget(WID_ROT_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); + if (this->HasWidget(WID_ROT_BIG_DEPOT)) this->GetWidget(WID_ROT_BIG_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_ROT_BUS_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_ROT_TRUCK_STATION)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); } else { @@ -356,7 +362,8 @@ struct BuildRoadToolbarWindow : Window { this->GetWidget(WID_ROT_ROAD_Y)->widget_data = rti->gui_sprites.build_y_road; this->GetWidget(WID_ROT_AUTOROAD)->widget_data = rti->gui_sprites.auto_road; if (_game_mode != GM_EDITOR) { - this->GetWidget(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot; + if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget(WID_ROT_DEPOT)->widget_data = rti->gui_sprites.build_depot; + if (this->HasWidget(WID_ROT_BIG_DEPOT)) this->GetWidget(WID_ROT_BIG_DEPOT)->widget_data = rti->gui_sprites.build_depot; } this->GetWidget(WID_ROT_CONVERT_ROAD)->widget_data = rti->gui_sprites.convert_road; this->GetWidget(WID_ROT_BUILD_TUNNEL)->widget_data = rti->gui_sprites.build_tunnel; @@ -466,7 +473,8 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: - if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) { + case WID_ROT_BIG_DEPOT: + if (HandlePlacePushButton(this, widget, this->rti->cursor.depot, HT_RECT)) { ShowRoadDepotPicker(this); this->last_started_action = widget; } @@ -557,9 +565,17 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: + case WID_ROT_BIG_DEPOT: { + _place_road_flag = (DiagDirToAxis(_road_depot_orientation) == AXIS_X) ? RF_DIR_X : RF_DIR_Y; + if (_tile_fract_coords.x >= 8) _place_road_flag |= RF_START_HALFROAD_X; + if (_tile_fract_coords.y >= 8) _place_road_flag |= RF_START_HALFROAD_Y; + VpSetPlaceSizingLimit(_settings_game.station.station_spread); - VpStartPlaceSizing(tile, (DiagDirToAxis(_road_depot_orientation) == 0) ? VPM_X_LIMITED : VPM_Y_LIMITED, DDSP_BUILD_DEPOT); + ViewportPlaceMethod vpm = VPM_X_AND_Y_LIMITED; + if (this->last_started_action == WID_ROT_DEPOT) vpm = (DiagDirToAxis(_road_depot_orientation) == AXIS_X) ? VPM_X_LIMITED : VPM_Y_LIMITED; + VpStartPlaceSizing(tile, vpm, DDSP_BUILD_DEPOT); break; + } case WID_ROT_BUS_STATION: PlaceRoad_BusStation(tile); @@ -590,7 +606,11 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode != GM_EDITOR && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); - if (_game_mode == GM_NORMAL && this->IsWidgetLowered(WID_ROT_DEPOT)) SetViewportHighlightDepot(INVALID_DEPOT, true); + if (_game_mode == GM_NORMAL && + ((this->HasWidget(WID_ROT_DEPOT) && this->IsWidgetLowered(WID_ROT_DEPOT)) || + (this->HasWidget(WID_ROT_BIG_DEPOT) && this->IsWidgetLowered(WID_ROT_BIG_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } this->RaiseButtons(); this->SetWidgetDisabledState(WID_ROT_REMOVE, true); @@ -642,7 +662,12 @@ struct BuildRoadToolbarWindow : Window { /* Set dir = Y */ _place_road_flag |= RF_DIR_Y; } + break; + case DDSP_BUILD_DEPOT: + _place_road_flag &= ~(RF_END_HALFROAD_Y | RF_END_HALFROAD_X); + if (pt.y & 8) _place_road_flag |= RF_END_HALFROAD_Y; + if (pt.x & 8) _place_road_flag |= RF_END_HALFROAD_X; break; default: @@ -710,9 +735,15 @@ struct BuildRoadToolbarWindow : Window { break; case DDSP_BUILD_DEPOT: { + /* Flag description: + * Use the first three bits (0x07) if dir == Y + * else use the last 2 bits (X dir has + * not the 3rd bit set) */ + _place_road_flag = (RoadFlags)((_place_road_flag & RF_DIR_Y) ? (_place_road_flag & 0x07) : (_place_road_flag >> 3)); + /* Build depot. */ CommandContainer cmdcont = {start_tile, (uint32)(_cur_roadtype << 2 | _road_depot_orientation | (_ctrl_pressed << 8) | - (INVALID_DEPOT << 16)), end_tile, + ((last_started_action == WID_ROT_BIG_DEPOT) << 9) | (INVALID_DEPOT << 16) | _place_road_flag << 10), end_tile, (uint32)(CMD_BUILD_ROAD_DEPOT | CMD_MSG(this->rti->strings.err_depot)), CcRoadDepot, ""}; ShowSelectDepotIfNeeded(cmdcont, TileArea(start_tile, end_tile), VEH_ROAD); @@ -814,6 +845,29 @@ static Hotkey tramtoolbar_hotkeys[] = { }; HotkeyList BuildRoadToolbarWindow::tram_hotkeys("tramtoolbar", tramtoolbar_hotkeys, TramToolbarGlobalHotkeys); +/** + * Add the depot icons depending on availability of construction. + * @param biggest_index Storage for collecting the biggest index used in the returned tree. + * @return Panel with company buttons. + * @post \c *biggest_index contains the largest used index in the tree. + */ +static NWidgetBase *MakeNWidgetRoadDepot(int *biggest_index) +{ + NWidgetHorizontal *hor = new NWidgetHorizontal(); + + if (HasBit(_settings_game.depot.road_depot_types, 0)) { + /* Add the widget for building small road depots. */ + hor->Add(new NWidgetLeaf(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT)); + } + + if (HasBit(_settings_game.depot.road_depot_types, 1)) { + /* Add the widget for building big road depots. */ + hor->Add(new NWidgetLeaf(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BIG_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_BIG_DEPOT)); + } + + *biggest_index = WID_ROT_BIG_DEPOT; + return hor; +} static const NWidgetPart _nested_build_road_widgets[] = { NWidget(NWID_HORIZONTAL), @@ -830,8 +884,7 @@ static const NWidgetPart _nested_build_road_widgets[] = { SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOROAD, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT), - SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT), + NWidgetFunction(MakeNWidgetRoadDepot), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION), @@ -873,8 +926,7 @@ static const NWidgetPart _nested_build_tramway_widgets[] = { SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_AUTOTRAM, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEMOLISH), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_DYNAMITE, STR_TOOLTIP_DEMOLISH_BUILDINGS_ETC), - NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_DEPOT), - SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT), + NWidgetFunction(MakeNWidgetRoadDepot), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_BUS_STATION, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_TRUCK_STATION), diff --git a/src/road_map.cpp b/src/road_map.cpp index 9954e4299370e..611ee866f982e 100644 --- a/src/road_map.cpp +++ b/src/road_map.cpp @@ -38,9 +38,11 @@ RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_b case MP_ROAD: switch (GetRoadTileType(tile)) { default: - case ROAD_TILE_NORMAL: return GetRoadBits(tile, rtt); - case ROAD_TILE_CROSSING: return GetCrossingRoadBits(tile); - case ROAD_TILE_DEPOT: return DiagDirToRoadBits(GetRoadDepotDirection(tile)); + case ROAD_TILE_NORMAL: + case ROAD_TILE_DEPOT: + return GetRoadBits(tile, rtt); + case ROAD_TILE_CROSSING: + return GetCrossingRoadBits(tile); } case MP_STATION: diff --git a/src/road_map.h b/src/road_map.h index 22d0fa54d372a..3e5e15abf2a1b 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -117,6 +117,87 @@ static inline bool IsRoadDepotTile(TileIndex t) return IsTileType(t, MP_ROAD) && IsRoadDepot(t); } +/** + * Return whether a road depot tile is a big one. + * @param t Tile to query. + * @return True if big road depot tile. + */ +static inline bool IsBigRoadDepot(TileIndex t) +{ + assert(IsTileType(t, MP_ROAD)); + assert(IsRoadDepot(t)); + return HasBit(_m[t].m5, 5); +} + +/** + * Return whether a tile is a big road depot tile. + * @param t Tile to query. + * @return True if big road depot tile. + */ +static inline bool IsBigRoadDepotTile(TileIndex t) +{ + return IsTileType(t, MP_ROAD) && IsRoadDepot(t) && IsBigRoadDepot(t); +} + +const int ROAD_DEPOT_CAPACITY = 3; + +/** + * Get the number of vehicles inside this depot. + * @param t The tile to query. + * @pre IsBigRoadDepotTile(t) + * @return The number of vehicles (at most 3 per tile). + */ +static inline int GetNumDepotVehicles(TileIndex t) +{ + assert(IsBigRoadDepotTile(t)); + return GB(_me[t].m6, 3, 3); +} + +/** + * Modify the number of vehicles inside this depot. + * @param t The tile to query. + * @param delta 1 if add a vehicle, -1 if remove a vehicle + * @pre IsRoadDepotTile(t) && (!IsBigRoadDepot(t) || delta == 1 || delta == -1) + * @note No effect for small depots + */ +static inline void ChangeNumDepotVehicles(TileIndex t, int delta) +{ + assert(IsRoadDepotTile(t)); + if (!IsBigRoadDepot(t)) return; + assert(delta == 1 || delta == -1); + int num = GetNumDepotVehicles(t); + assert(num > 0 || delta > 0); + num +=delta; + assert(num <= ROAD_DEPOT_CAPACITY); + SB(_me[t].m6, 3, 3, num); +} + +/** + * Check if depot has reached its maximum capacity. + * @param t The tile to query. + * @return true if depot is full. + */ +static inline bool IsFullDepot(TileIndex t) +{ + assert(IsRoadDepotTile(t)); + if (!IsBigRoadDepot(t)) return false; + assert(GetNumDepotVehicles(t) <= ROAD_DEPOT_CAPACITY); + return GetNumDepotVehicles(t) == ROAD_DEPOT_CAPACITY; +} + +/** + * Check if depot is empty. + * @param t The tile to query. + * @return true if depot is empty. + */ +static inline bool IsEmptyDepot(TileIndex t) +{ + assert(IsRoadDepotTile(t)); + if (!IsBigRoadDepot(t)) return false; + assert(GetNumDepotVehicles(t) <= ROAD_DEPOT_CAPACITY); + return GetNumDepotVehicles(t) == 0; +} + /** * Get the present road bits for a specific road type. * @param t The tile to query. @@ -126,7 +207,7 @@ static inline bool IsRoadDepotTile(TileIndex t) */ static inline RoadBits GetRoadBits(TileIndex t, RoadTramType rtt) { - assert(IsNormalRoad(t)); + assert(IsNormalRoad(t) || IsRoadDepotTile(t)); if (rtt == RTT_TRAM) return (RoadBits)GB(_m[t].m3, 0, 4); return (RoadBits)GB(_m[t].m5, 0, 4); } @@ -151,7 +232,7 @@ static inline RoadBits GetAllRoadBits(TileIndex tile) */ static inline void SetRoadBits(TileIndex t, RoadBits r, RoadTramType rtt) { - assert(IsNormalRoad(t)); // XXX incomplete + assert(IsNormalRoad(t) || IsRoadDepotTile(t)); if (rtt == RTT_TRAM) { SB(_m[t].m3, 0, 4, r); } else { @@ -556,19 +637,28 @@ static inline void TerminateRoadWorks(TileIndex t) SB(_me[t].m7, 0, 4, 0); } +/** + * Set the direction of the exit of a road depot for extended road depots. + * @param t The tile to query. + * @return Diagonal direction of the depot exit. + */ +static inline void SetRoadDepotDirection(TileIndex t, DiagDirection dir) +{ + assert(IsBigRoadDepot(t)); + SB(_me[t].m6, 6, 2, dir); +} /** - * Get the direction of the exit of a road depot. + * Get the direction of the exit of a road depot (or the image of the depot for extended road depots). * @param t The tile to query. * @return Diagonal direction of the depot exit. */ static inline DiagDirection GetRoadDepotDirection(TileIndex t) { assert(IsRoadDepot(t)); - return (DiagDirection)GB(_m[t].m5, 0, 2); + return (DiagDirection)GB(_me[t].m6, 6, 2); } - RoadBits GetAnyRoadBits(TileIndex tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false); /** @@ -687,8 +777,9 @@ static inline void MakeRoadDepot(TileIndex t, Owner owner, DepotID did, DiagDire _m[t].m2 = did; _m[t].m3 = 0; _m[t].m4 = INVALID_ROADTYPE; - _m[t].m5 = ROAD_TILE_DEPOT << 6 | dir; - SB(_me[t].m6, 2, 4, 0); + _m[t].m5 = ROAD_TILE_DEPOT << 6; + SB(_me[t].m6, 0, 6, 0); + SB(_me[t].m6, 6, 2, dir); _me[t].m7 = owner; _me[t].m8 = INVALID_ROADTYPE << 6; SetRoadType(t, GetRoadTramType(rt), rt); diff --git a/src/roadveh.h b/src/roadveh.h index 028af9decb71a..8d480a05fd69b 100644 --- a/src/roadveh.h +++ b/src/roadveh.h @@ -319,4 +319,6 @@ struct RoadVehicle FINAL : public GroundVehicle { } }; +void HandleRoadVehicleEnterDepot(RoadVehicle *v); + #endif /* ROADVEH_H */ diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp index 91356899d1b18..89b431927dcee 100644 --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -248,7 +248,7 @@ void RoadVehUpdateCache(RoadVehicle *v, bool same_length) v->vcache.cached_max_speed = (max_speed != 0) ? max_speed * 4 : RoadVehInfo(v->engine_type)->max_speed; } -CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, DoCommandFlag flags) +CommandCost FindDepotTileForPlacingEngine(TileIndex &tile, const Engine *e, bool already_built) { assert(IsRoadDepotTile(tile)); @@ -259,23 +259,35 @@ 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 || !IsFullDepot(tile))) return CommandCost(); TileIndex best_tile = INVALID_TILE; for (std::vector::iterator it = dep->depot_tiles.begin(); it != dep->depot_tiles.end(); ++it) { tile = *it; if (!HasTileAnyRoadType(tile, rti->powered_roadtypes)) continue; + if (!IsBigDepot(tile)) return CommandCost(); + if (!already_built && IsFullDepot(tile)) continue; best_tile = *it; } + if (best_tile == INVALID_TILE) return_cmd_error(STR_ERROR_DEPOT_FULL_DEPOT); tile = best_tile; return CommandCost(); } +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; +} + /** * Build a road vehicle. * @param tile tile of the depot where road vehicle is built. @@ -291,15 +303,17 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin RoadType rt = e->u.road.roadtype; const RoadTypeInfo *rti = GetRoadTypeInfo(rt); - CommandCost check = FindDepotTileForPlacingEngine(tile, e, flags); - if (check.Failed()) return check; + if ((flags & DC_AUTOREPLACE) == 0) { + CommandCost check = FindDepotTileForPlacingEngine(tile, e, (flags & DC_AUTOREPLACE) != 0); + if (check.Failed()) return check; + } if (flags & DC_EXEC) { const RoadVehicleInfo *rvi = &e->u.road; RoadVehicle *v = new RoadVehicle(); *ret = v; - v->direction = DiagDirToDir(GetRoadDepotDirection(tile)); + v->direction = DiagDirToDir(GetRoadDepotExit(tile, RoadTramType(rt), GetRoadDepotDirection(tile))); v->owner = _current_company; v->tile = tile; @@ -359,6 +373,11 @@ CommandCost CmdBuildRoadVehicle(TileIndex tile, DoCommandFlag flags, const Engin v->UpdatePosition(); + if (IsBigDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) { + ChangeNumDepotVehicles(v->tile, 1); + SetBigDepotReservation(v, true); + } + CheckConsistencyOfArticulatedVehicle(v); } @@ -918,9 +937,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 (IsBigRoadDepotTile(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) && IsStandardRoadStopTile(tile)) { /* Standard road stop (drive-through stops are treated as normal road) */ @@ -1032,17 +1059,66 @@ struct RoadDriveEntry { #include "table/roadveh_movement.h" +static const uint16 JUST_REVERSE_ON_DEPOT = UINT16_MAX - 2; + +void HandleRoadVehicleEnterDepot(RoadVehicle *v) +{ + assert(IsRoadDepotTile(v->tile)); + + if (IsBigRoadDepot(v->tile)) { + assert(GetNumDepotVehicles(v->tile) <= ROAD_DEPOT_CAPACITY); + v->cur_speed = 0; + v->UpdateViewport(true, true); + SetWindowClassesDirty(WC_ROADVEH_LIST); + SetWindowDirty(WC_VEHICLE_VIEW, v->index); + + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); + if (!IsFullDepot(v->tile)) { + v->StartService(); + ChangeNumDepotVehicles(v->tile, 1); + SetBigDepotReservation(v, true); + } else { + v->wait_counter = JUST_REVERSE_ON_DEPOT; + } + } else { + VehicleEnterDepot(v); + } +} +#include "debug.h" static bool RoadVehLeaveDepot(RoadVehicle *v, bool first) { + DEBUG(misc, 0, "Trying to leave"); /* 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 stop_here = (v->wait_counter != JUST_REVERSE_ON_DEPOT); + + TileIndex new_tile = v->tile; + FindDepotTileForPlacingEngine(new_tile, Engine::Get(v->engine_type), true); + if (new_tile == INVALID_TILE) return false; + + if (IsBigDepot(v->tile)) { + ChangeNumDepotVehicles(v->tile, -1); + SetBigDepotReservation(v, false); + } + v->tile = new_tile; + if (IsBigDepot(v->tile)) { + ChangeNumDepotVehicles(new_tile, 1); + SetBigDepotReservation(v, true); + } + + DiagDirection dir = GetRoadDepotExit(v->tile, RoadTramType(v->roadtype), DirToDiagDir(v->direction)); + assert(dir != INVALID_DIAGDIR); + + for (Vehicle *u = v; u != nullptr; u = u->Next()) { + u->direction = DiagDirToDir(dir); + u->tile = v->tile; + } Trackdir tdir = DiagDirToDiagTrackdir(dir); + DEBUG(misc, 0, "Trying to leave 2"); const RoadDriveEntry *rdp = _road_drive_data[GetRoadTramType(v->roadtype)][(_settings_game.vehicle.road_side << RVS_DRIVE_SIDE) + tdir]; int x = TileX(v->tile) * TILE_SIZE + (rdp[RVC_DEPOT_START_FRAME].x & 0xF); @@ -1050,30 +1126,42 @@ static bool RoadVehLeaveDepot(RoadVehicle *v, bool first) 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) && v->tile == v->dest_tile) { - VehicleEnterDepot(v); + if (stop_here && v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) { + if (IsBigRoadDepot(v->tile)) { + v->StartService(); + } else { + VehicleEnterDepot(v); + } return true; } - + DEBUG(misc, 0, "Trying to leave 3"); if (RoadVehFindCloseTo(v, x, y, v->direction, false) != nullptr) return true; - - VehicleServiceInDepot(v); - - StartRoadVehSound(v); - - /* Vehicle is about to leave a depot */ + DEBUG(misc, 0, "Trying to leave 4"); + if (stop_here) { + VehicleServiceInDepot(v); + StartRoadVehSound(v); + } + DEBUG(misc, 0, "Trying to leave 5"); + /* Vehicle is about to leave a depot. */ v->cur_speed = 0; } - v->vehstatus &= ~VS_HIDDEN; + if ((v->vehstatus & VS_HIDDEN) != 0) { + v->vehstatus &= ~VS_HIDDEN; + v->x_pos = x; + v->y_pos = y; + if (!IsBigDepot(v->tile)) v->frame = RVC_DEPOT_START_FRAME; + } v->state = tdir; - v->frame = RVC_DEPOT_START_FRAME; - v->x_pos = x; - v->y_pos = y; v->UpdatePosition(); v->UpdateInclination(true, true); + if (v->IsPrimaryVehicle() && IsBigDepot(v->tile)) { + ChangeNumDepotVehicles(v->tile, -1); + SetBigDepotReservation(v, false); + } + InvalidateWindowData(WC_VEHICLE_DEPOT, GetDepotIndex(v->tile)); return true; @@ -1084,6 +1172,7 @@ static Trackdir FollowPreviousRoadVehicle(const RoadVehicle *v, const RoadVehicl if (prev->tile == v->tile && !already_reversed) { /* If the previous vehicle is on the same tile as this vehicle is * then it must have reversed. */ + DEBUG(misc, 0, "Lookup"); return _road_reverse_table[entry_dir]; } @@ -1096,7 +1185,11 @@ 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)); + if (IsBigRoadDepot(tile)) { + diag_dir = ReverseDiagDir(DirToDiagDir(v->direction)); + } else { + diag_dir = ReverseDiagDir(GetRoadDepotDirection(tile)); + } } if (diag_dir == INVALID_DIAGDIR) return INVALID_TRACKDIR; @@ -1163,9 +1256,10 @@ static bool CanBuildTramTrackOnTile(CompanyID c, TileIndex t, RoadType rt, RoadB cur_company.Restore(); return ret.Succeeded(); } - +#include "debug.h" bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) { + DEBUG(misc, 0, "Starting controller"); if (v->overtaking != 0) { if (IsTileType(v->tile, MP_STATION)) { /* Force us to be not overtaking! */ @@ -1179,11 +1273,13 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) } } } + DEBUG(misc, 0, "Starting controller 2"); /* If this vehicle is in a depot and we've reached this point it must be * one of the articulated parts. It will stay in the depot until activated * by the previous vehicle in the chain when it gets to the right place. */ if (v->IsInDepot()) return true; + DEBUG(misc, 0, "Starting controller 3"); if (v->state == RVSB_WORMHOLE) { /* Vehicle is entering a depot or is on a bridge or in a tunnel */ @@ -1212,6 +1308,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) if ((v->vehstatus & VS_HIDDEN) == 0) v->Vehicle::UpdateViewport(true); return true; } + DEBUG(misc, 0, "Starting controller 4"); /* Get move position data for next frame. * For a drive-through road stop use 'straight road' move data. @@ -1260,7 +1357,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) case TRACKDIR_RVREV_NW: needed = ROAD_SE; break; } if ((v->Previous() != nullptr && v->Previous()->tile == tile) || - (v->IsFrontEngine() && IsNormalRoadTile(tile) && !HasRoadWorks(tile) && + (v->IsFrontEngine() && ((IsNormalRoadTile(tile) && !HasRoadWorks(tile)) || IsBigRoadDepotTile(tile)) && HasTileAnyRoadType(tile, v->compatible_roadtypes) && (needed & GetRoadBits(tile, RTT_TRAM)) != ROAD_NONE)) { /* @@ -1295,6 +1392,8 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) } else if (IsNormalRoadTile(v->tile) && GetDisallowedRoadDirections(v->tile) != DRD_NONE) { v->cur_speed = 0; return false; + } else if (IsBigRoadDepotTile(v->tile) && (GetRoadBits(v->tile, RTT_ROAD) & DiagDirToRoadBits(DirToDiagDir(v->direction))) == ROAD_NONE) { + tile = v->tile; } else { tile = v->tile; } @@ -1382,7 +1481,7 @@ bool IndividualRoadVehicleController(RoadVehicle *v, const RoadVehicle *prev) 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) && !IsBigRoadDepotTile(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 @@ -1607,6 +1706,8 @@ static bool RoadVehController(RoadVehicle *v) return true; } + if (v->ContinueServicing()) return true; + ProcessOrders(v); v->HandleLoading(); @@ -1763,6 +1864,9 @@ Trackdir RoadVehicle::GetVehicleTrackdir() const if (this->IsInDepot()) { /* We'll assume the road vehicle is facing outwards */ + if (IsBigRoadDepot(this->tile)) { + return DiagDirToDiagTrackdir(DirToDiagDir(this->direction)); + } return DiagDirToDiagTrackdir(GetRoadDepotDirection(this->tile)); } diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 8509c41bb0672..cdd63b0ee4e90 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2785,6 +2785,16 @@ bool AfterLoadGame() break; } } + + for (TileIndex t = 0; t < map_size; t++) { + if (!IsRoadDepotTile(t)) continue; + DiagDirection dir = (DiagDirection)GB(_m[t].m5, 0, 2); + SB(_m[t].m5, 0, 6, 0); + RoadBits rb = DiagDirToRoadBits(dir); + SetRoadBits(t, rb, HasRoadTypeRoad(t) ? RTT_ROAD : RTT_TRAM); + SB(_me[t].m6, 6, 2, dir); + if (IsBigRoadDepot(t)) NOT_REACHED(); + } } /* In old versions it was possible to remove an airport while a plane was diff --git a/src/saveload/company_sl.cpp b/src/saveload/company_sl.cpp index fb2742a18621c..7a2c247e2e5f9 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -132,8 +132,8 @@ void AfterLoadCompanyStats() RoadType rt = GetRoadType(tile, rtt); if (rt == INVALID_ROADTYPE) continue; c = Company::GetIfValid(IsRoadDepot(tile) ? GetTileOwner(tile) : GetRoadOwner(tile, rtt)); - /* A level crossings and depots have two road bits. */ - if (c != nullptr) c->infrastructure.road[rt] += IsNormalRoad(tile) ? CountBits(GetRoadBits(tile, rtt)) : 2; + /* Level crossings have two road bits. */ + if (c != nullptr) c->infrastructure.road[rt] += (IsNormalRoad(tile) || IsRoadDepot(tile)) ? CountBits(GetRoadBits(tile, rtt)) : 2; } break; } diff --git a/src/script/api/script_road.cpp b/src/script/api/script_road.cpp index 3eb9cb0551eb6..4ca4ee78755aa 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -528,7 +528,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0); - return ScriptObject::DoCommand(tile, entrance_dir | (ScriptObject::GetRoadType() << 2) | (NEW_DEPOT << 16), tile, CMD_BUILD_ROAD_DEPOT); + return ScriptObject::DoCommand(tile, entrance_dir | (ScriptObject::GetRoadType() << 2) | (DiagDirToAxis((DiagDirection)entrance_dir) << 12) | (NEW_DEPOT << 16), tile, CMD_BUILD_ROAD_DEPOT); } /* static */ bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id) diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 43f3523f8e38f..f91327f16bc10 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1142,7 +1142,7 @@ static bool CanRoadContinueIntoNextTile(const Town *t, const TileIndex tile, con /* If the next tile is a road depot, allow if it's facing the right way. */ if (IsTileType(next_tile, MP_ROAD)) { - return IsRoadDepot(next_tile) && GetRoadDepotDirection(next_tile) == ReverseDiagDir(road_dir); + return IsRoadDepot(next_tile) && (GetRoadBits(next_tile, RTT_ROAD) & DiagDirToRoadBits(ReverseDiagDir(road_dir))) != ROAD_NONE; } /* If the next tile is a railroad track, check if towns are allowed to build level crossings. diff --git a/src/vehicle_cmd.cpp b/src/vehicle_cmd.cpp index b63e1251d30a9..acacbd91625d9 100644 --- a/src/vehicle_cmd.cpp +++ b/src/vehicle_cmd.cpp @@ -239,6 +239,10 @@ CommandCost CmdSellVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3 ret = CommandCost(EXPENSES_NEW_VEHICLES, -front->value); if (flags & DC_EXEC) { + if (front->type == VEH_ROAD && IsBigDepot(v->tile) && (flags & DC_AUTOREPLACE) == 0) { + ChangeNumDepotVehicles(v->tile, -1); + SetBigDepotReservation(v, false); + } if (front->IsPrimaryVehicle() && p1 & MAKE_ORDER_BACKUP_FLAG) OrderBackup::Backup(front, p2); delete front; } @@ -597,7 +601,16 @@ CommandCost CmdStartStopVehicle(TileIndex tile, DoCommandFlag flags, uint32 p1, } 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: { diff --git a/src/widgets/road_widget.h b/src/widgets/road_widget.h index 3d49e9ae9db06..391eb5e608010 100644 --- a/src/widgets/road_widget.h +++ b/src/widgets/road_widget.h @@ -19,6 +19,7 @@ enum RoadToolbarWidgets { WID_ROT_AUTOROAD, ///< Autorail. WID_ROT_DEMOLISH, ///< Demolish. WID_ROT_DEPOT, ///< Build depot. + WID_ROT_BIG_DEPOT, ///< Build a multi-tile depot. WID_ROT_BUS_STATION, ///< Build bus station. WID_ROT_TRUCK_STATION, ///< Build truck station. WID_ROT_ONE_WAY, ///< Build one-way road.