From 718b92539a158edd94014301d3b8dfc9b35e1da3 Mon Sep 17 00:00:00 2001 From: J0anJosep Date: Sat, 26 Aug 2023 19:54:33 +0200 Subject: [PATCH] Feature: Add construction of extended road depots. --- regression/regression/result.txt | 6 +- src/depot.cpp | 35 +++- src/economy_type.h | 2 - src/lang/english.txt | 9 +- src/road_cmd.cpp | 282 ++++++++++++++++++++++++------- src/road_cmd.h | 4 +- src/road_gui.cpp | 155 ++++++++++++++--- src/road_map.cpp | 8 +- src/road_map.h | 52 ++++-- src/saveload/afterload.cpp | 9 + src/saveload/company_sl.cpp | 4 +- src/script/api/script_road.cpp | 2 +- src/town_cmd.cpp | 2 +- src/widgets/road_widget.h | 1 + 14 files changed, 458 insertions(+), 113 deletions(-) diff --git a/regression/regression/result.txt b/regression/regression/result.txt index 27a81675a9..7aaf66b68a 100644 --- a/regression/regression/result.txt +++ b/regression/regression/result.txt @@ -7364,7 +7364,7 @@ ERROR: IsEnd() is invalid as Begin() is never called IsBuoyTile(): false IsLockTile(): false IsCanalTile(): false - GetBankBalance(): 1999979304 + GetBankBalance(): 1999979244 BuildWaterDepot(): true BuildDock(): true BuildBuoy(): true @@ -7377,7 +7377,7 @@ ERROR: IsEnd() is invalid as Begin() is never called IsBuoyTile(): true IsLockTile(): true IsCanalTile(): true - GetBankBalance(): 1999964680 + GetBankBalance(): 1999964620 --AIWaypointList(BUOY)-- Count(): 1 @@ -7396,7 +7396,7 @@ ERROR: IsEnd() is invalid as Begin() is never called IsBuoyTile(): false IsLockTile(): false IsCanalTile(): false - GetBankBalance(): 1999959285 + GetBankBalance(): 1999959225 BuildWaterDepot(): true BuildDock(): true BuildBuoy(): true diff --git a/src/depot.cpp b/src/depot.cpp index 1796310cf8..08eb0af1f1 100644 --- a/src/depot.cpp +++ b/src/depot.cpp @@ -283,12 +283,41 @@ void UpdateExtendedDepotReservation(Vehicle *v, bool reserve) assert(IsExtendedDepotTile(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) { + res_type = (v->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } switch (v->type) { - case VEH_ROAD: + case VEH_ROAD: { + assert(v == v->First()); + assert(IsDiagonalDirection(v->direction)); + bool is_facing_south = IsDiagDirFacingSouth(DirToDiagDir(v->direction)); + TileArea ta; + for (Vehicle *u = v; u != nullptr; u = u->Next()) ta.Add(u->tile); + for (TileIndex t : ta) { + res_type = DEPOT_RESERVATION_EMPTY; + + if (reserve) { + res_type = (v->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } + for (Vehicle *rv : Vehicle::Iterate()) { + if (res_type == DEPOT_RESERVATION_FULL_STOPPED_VEH) break; + if (rv->IsInDepot() && ta.Contains(rv->tile) && rv->First() != v) { + assert(rv->type == v->type); + [[maybe_unused]] DiagDirection diag_dir = DirToDiagDir(rv->direction); + assert(DiagDirToAxis(DirToDiagDir(v->direction)) == DiagDirToAxis(diag_dir)); + if (is_facing_south == IsDiagDirFacingSouth(DirToDiagDir(rv->direction))) { + res_type = (rv->vehstatus & VS_STOPPED) ? + DEPOT_RESERVATION_FULL_STOPPED_VEH : DEPOT_RESERVATION_IN_USE; + } + } + } + SetDepotReservation(t, res_type, is_facing_south); + } break; + } case VEH_SHIP: SetDepotReservation(v->tile, res_type); diff --git a/src/economy_type.h b/src/economy_type.h index e4d0ea077a..a4c08c4919 100644 --- a/src/economy_type.h +++ b/src/economy_type.h @@ -241,8 +241,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 3c8b2a95c9..5c7350fc0a 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -2919,8 +2919,10 @@ STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_SECTION :{BLACK}Build ro STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAMWAY_SECTION :{BLACK}Build tramway section. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOROAD :{BLACK}Build road section using the Autoroad mode. Ctrl+Click to remove road section. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_BUILD_AUTOTRAM :{BLACK}Build tramway section using the Autotram mode. Ctrl+Click to remove tramway section. Also press Shift to show cost estimate only -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build road vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only -STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build tram vehicle depot (for buying and servicing vehicles). Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT :{BLACK}Build standard road vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT :{BLACK}Build extended road vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT :{BLACK}Build standard tram vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only +STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAM_VEHICLE_DEPOT :{BLACK}Build extended tram vehicle depot (for buying and servicing vehicles). Ctrl+Click to select another depot to join. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT :{BLACK}Build waypoint on road. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT :{BLACK}Build waypoint on tramway. Ctrl+Click to select another waypoint to join. Also press Shift to show cost estimate only STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION :{BLACK}Build bus station. Ctrl+Click to select another station to join. Also press Shift to show cost estimate only @@ -3181,7 +3183,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 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 diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 9718f12134..428554d8f6 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -313,6 +313,22 @@ CommandCost CheckAllowRemoveRoad(TileIndex tile, RoadBits remove, Owner owner, R return CommandCost(); } +void UpdateRoadDepotDir(TileIndex tile) +{ + assert(IsExtendedRoadDepot(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. @@ -525,16 +541,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 (IsExtendedRoadDepot(tile)) UpdateRoadDepotDir(tile); MarkTileDirtyByTile(tile); } - return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); + return cost; } default: NOT_REACHED(); @@ -722,8 +751,24 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R if (HasTileRoadType(tile, rtt)) return_cmd_error(STR_ERROR_ALREADY_BUILT); break; - case ROAD_TILE_DEPOT: - if (DiagDirToRoadBits(GetRoadDepotDirection(tile)) == pieces) { + case ROAD_TILE_DEPOT: { + Owner owner = GetRoadOwner(tile, rtt); + if (owner != OWNER_NONE) { + CommandCost ret = CheckOwnership(owner, tile); + if (ret.Failed()) return ret; + } + + if (IsExtendedRoadDepot(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; + existing = GetRoadBits(tile, rtt); + if ((rb & ~existing) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * CountBits(rb & ~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); @@ -732,6 +777,7 @@ CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, R cost.AddCost(_price[PR_BUILD_DEPOT_ROAD]); break; } + } goto do_clear; @@ -908,7 +954,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 (IsExtendedRoadDepot(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) { @@ -1165,8 +1217,11 @@ std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex * @param tile tile where to build the depot * @param flags operation to perform * @param rt road type - * @param dir entrance direction + * @param orig_dir entrance direction * @param adjacent allow adjacent depots + * @param extended build extended depot + * @param half_start build only one trackbit in start tile if building an extended depot + * @param half_end build only one trackbit in end tile if building an extended depot * @param depot_id depot to join to * @param end_tile end tile of the depot to be built * @return the cost of this operation or an error @@ -1174,12 +1229,36 @@ std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex * @todo When checking for the tile slope, * distinguish between "Flat land required" and "land sloped in wrong direction" */ -CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile) +CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection orig_dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile) { - if (!ValParamRoadType(rt) || !IsValidDiagDirection(dir)) return CMD_ERROR; + if (!ValParamRoadType(rt) || !IsValidDiagDirection(orig_dir)) return CMD_ERROR; + + if (Company::IsValidHumanID(_current_company) && !HasBit(_settings_game.depot.road_depot_types, extended)) { + return_cmd_error(STR_ERROR_DEPOT_TYPE_NOT_AVAILABLE); + } TileArea ta(tile, end_tile); - assert(ta.w == 1 || ta.h == 1); + assert(extended || ta.w == 1 || ta.h == 1); + + Axis axis = DiagDirToAxis(orig_dir); + RoadTramType rtt = GetRoadTramType(rt); + uint start_coord = 0; + uint end_coord = 0; + + DiagDirection dir = orig_dir; + if (extended) { + 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) { + dir = ReverseDiagDir(dir); + half_start = !half_start; + half_end = !half_end; + } + } /* Create a new depot or find a depot to join to. */ Depot *depot = nullptr; @@ -1187,58 +1266,135 @@ CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, if (ret.Failed()) return ret; uint8_t num_new_depot_tiles = 0; - uint8_t num_rotated_depot_tiles = 0; + uint8_t num_overbuilt_depot_tiles = 0; CommandCost cost(EXPENSES_CONSTRUCTION); + int allowed_z = -1; + uint num_new_pieces = 0; + uint invalid_dirs = extended ? 5 << axis : 1 << dir; for (Tile t : ta) { + RoadBits rb = ROAD_NONE; if (IsBridgeAbove(t)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST); - Slope tileh = GetTileSlope(t); + auto [tileh, z] = GetTileSlopeZ(t); + 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 (extended && 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]); } - /* Check whether a depot tile exists and it needs to be rotated. */ - if (IsRoadDepotTile(t) && - GetDepotIndex(t) == join_to && - (HasRoadTypeTram(t) ? rt == GetRoadTypeTram(t) : rt == GetRoadTypeRoad(t))) { - if (dir == GetRoadDepotDirection(t)) continue; + /* 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); + } - ret = EnsureNoVehicleOnGround(t); - if (ret.Failed()) return ret; + /* Check whether this is a compatible depot tile. */ + if (IsRoadDepotTile(t) && GetDepotIndex(t) == join_to && rt == GetRoadType(t, rtt)) { + if (extended) { + if (IsExtendedRoadDepotTile(t) && + axis == DiagDirToAxis(GetRoadDepotDirection(t))) { + /* Already exists and has the right axis: Check new roadbits. */ + goto rb_for_extended_depot; + } + } else { + if (!IsExtendedRoadDepotTile(t)) { + if (dir == GetRoadDepotDirection(t)) continue; - num_rotated_depot_tiles++; - if (flags & DC_EXEC) { - SetRoadDepotExitDirection(t, dir); - MarkTileDirtyByTile(t); + /* If another roadtype exists (road/tram), depot cannot be rotated. */ + if (GetRoadTypeRoad(t) != INVALID_ROADTYPE && GetRoadTypeTram(t) != INVALID_ROADTYPE) { + return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); + } + + /* Overbuild the depot tile and change its exit direction. */ + num_overbuilt_depot_tiles++; + if (flags & DC_EXEC) { + rb = DiagDirToRoadBits(orig_dir); + SetRoadBits(t, rb, rtt); + SetRoadDepotDirection(t, orig_dir); + MarkTileDirtyByTile(t); + } + continue; + } } + } + cost.AddCost(Command::Do(flags, t)); + if (cost.Failed()) return cost; + + /* Check which road bits to build. */ + if (extended) { +rb_for_extended_depot: + uint axis_coord = axis == AXIS_X ? TileX(t) : TileY(t); + /* Road parts only have to be built at the start tile or at the end tile. */ + if (!half_end && axis_coord == end_coord) { + rb = DiagDirToRoadBits(ReverseDiagDir(dir)); + } + if (half_start && axis_coord == start_coord) { + rb = DiagDirToRoadBits(dir); + } + if (rb == ROAD_NONE) { + rb = AxisToRoadBits(axis); + } + assert(rb != ROAD_NONE); + if (IsRoadDepotTile(t)) { + RoadType old_rt = GetRoadType(t, rtt); + if (old_rt != INVALID_ROADTYPE && old_rt != rt) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); + RoadBits old_rb = GetAllRoadBits(t); + if ((old_rb & AxisToRoadBits(axis)) != old_rb) return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED); + old_rb = GetRoadBits(t, rtt); + if ((rb & ~old_rb) == ROAD_NONE) return_cmd_error(STR_ERROR_ALREADY_BUILT); + num_new_pieces += CountBits(rb & ~old_rb); + num_overbuilt_depot_tiles++; + rb |= old_rb; + } else { + num_new_pieces += CountBits(rb); + num_new_depot_tiles++; + } } else { - cost.AddCost(Command::Do(flags, t)); - if (cost.Failed()) return cost; - + rb = DiagDirToRoadBits(orig_dir); + num_new_pieces += 1; num_new_depot_tiles++; - if (flags & DC_EXEC) { - MakeRoadDepot(t, _current_company, depot->index, dir, rt); - MarkTileDirtyByTile(t); + } + + if (flags & DC_EXEC) { + if (!IsRoadDepotTile(t)) MakeRoadDepot(t, _current_company, depot->index, orig_dir, rt); + if (GetRoadType(t, rtt) == INVALID_ROADTYPE) SetRoadType(t, rtt, rt); + SetRoadBits(t, rb, rtt); + if (extended) { + SB(t.m5(), 5, 1, true); + UpdateRoadDepotDir(t); } + + MarkTileDirtyByTile(t); } } - if (num_new_depot_tiles + num_rotated_depot_tiles == 0) return CommandCost(); + if (num_new_depot_tiles + num_overbuilt_depot_tiles == 0) return CommandCost(); + + cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_overbuilt_depot_tiles)); if (flags & DC_EXEC) { - /* A road depot has two road bits. */ - UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_depot_tiles * ROAD_DEPOT_TRACKBIT_FACTOR); + UpdateCompanyRoadInfrastructure(rt, _current_company, num_new_pieces); depot->AfterAddRemove(ta, true); if (join_to == NEW_DEPOT) MakeDefaultName(depot); } - cost.AddCost(_price[PR_BUILD_DEPOT_ROAD] * (num_new_depot_tiles + num_rotated_depot_tiles)); return cost; } @@ -1255,16 +1411,16 @@ static CommandCost RemoveRoadDepot(TileIndex tile, DoCommandFlag flags) CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_ROAD]); RoadType rt = GetRoadTypeRoad(tile); RoadType tt = GetRoadTypeTram(tile); - if (rt != INVALID_ROADTYPE && tt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); + if (rt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); + if (tt != INVALID_ROADTYPE) cost.AddCost(_price[PR_CLEAR_DEPOT_ROAD]); if (flags & DC_EXEC) { Depot *depot = Depot::GetByTile(tile); Company *c = Company::GetIfValid(depot->owner); 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. */ + if (rt != INVALID_ROADTYPE) c->infrastructure.road[rt] -= CountBits(GetRoadBits(tile, RTT_ROAD)); + if (tt != INVALID_ROADTYPE) c->infrastructure.road[tt] -= CountBits(GetRoadBits(tile, RTT_TRAM)); DirtyCompanyInfrastructureWindows(c->index); } @@ -1500,18 +1656,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)) { @@ -1883,13 +2034,15 @@ 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; const DrawTileSprites *dts = &_road_depot[dir]; - DrawGroundSprite(dts->ground.sprite, pal); + SpriteID image = SPR_ROAD_Y + (road_rti == nullptr ? tram_offset : road_offset) - 19; + DrawGroundSprite(image, pal); + DrawRoadOverlays(ti, pal, road_rti, tram_rti, road_offset, tram_offset); int relocation = GetCustomRoadSprite(main_rti, ti->tile, ROTSG_DEPOT); @@ -1962,6 +2115,8 @@ 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 { + DrawSprite(SPR_ROAD_Y + road_offset - 19, PAL_NONE, x, y); } if (default_gfx) { @@ -2220,11 +2375,11 @@ static TrackStatus GetTileTrackStatus_Road(TileIndex tile, TransportType mode, u default: 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; } } @@ -2281,7 +2436,7 @@ static void GetTileDesc_Road(TileIndex tile, TileDesc *td) } case ROAD_TILE_DEPOT: - td->str = STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT; + td->str = IsExtendedDepot(tile) ? STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT_EXTENDED : STR_LAI_ROAD_DESCRIPTION_ROAD_VEHICLE_DEPOT; td->build_date = Depot::GetByTile(tile)->build_date; break; @@ -2349,14 +2504,14 @@ static void ChangeTileOwner_Road(TileIndex tile, Owner old_owner, Owner new_owne if (new_owner == INVALID_OWNER) { Command::Do(DC_EXEC | DC_BANKRUPT, tile); } 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 (RoadTramType rtt : _roadtramtypes) { + 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); } @@ -2405,7 +2560,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)) && + (!IsExtendedRoadDepot(tile) || AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(GetRoadDepotDirection(tile))))) { + return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]); + } break; case ROAD_TILE_NORMAL: { @@ -2582,8 +2740,6 @@ CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_s uint num_pieces = CountBits(GetAnyRoadBits(tile, rtt)); if (tt == MP_STATION && IsBayRoadStopTile(tile)) { num_pieces *= ROAD_STOP_TRACKBIT_FACTOR; - } else if (tt == MP_ROAD && IsRoadDepot(tile)) { - num_pieces *= ROAD_DEPOT_TRACKBIT_FACTOR; } found_convertible_road = true; diff --git a/src/road_cmd.h b/src/road_cmd.h index 2ae7e170e6..b7a54c5c4a 100644 --- a/src/road_cmd.h +++ b/src/road_cmd.h @@ -23,7 +23,7 @@ void UpdateNearestTownForRoadTiles(bool invalidate); CommandCost CmdBuildLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, DisallowedRoadDirections drd, bool start_half, bool end_half, bool is_ai); std::tuple CmdRemoveLongRoad(DoCommandFlag flags, TileIndex end_tile, TileIndex start_tile, RoadType rt, Axis axis, bool start_half, bool end_half); CommandCost CmdBuildRoad(DoCommandFlag flags, TileIndex tile, RoadBits pieces, RoadType rt, DisallowedRoadDirections toggle_drd, TownID town_id); -CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); +CommandCost CmdBuildRoadDepot(DoCommandFlag flags, TileIndex tile, RoadType rt, DiagDirection dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile); CommandCost CmdConvertRoad(DoCommandFlag flags, TileIndex tile, TileIndex area_start, RoadType to_type); DEF_CMD_TRAIT(CMD_BUILD_LONG_ROAD, CmdBuildLongRoad, CMD_AUTO | CMD_NO_WATER | CMD_DEITY, CMDT_LANDSCAPE_CONSTRUCTION) @@ -34,7 +34,7 @@ DEF_CMD_TRAIT(CMD_CONVERT_ROAD, CmdConvertRoad, 0, CommandCallback CcPlaySound_CONSTRUCTION_OTHER; CommandCallback CcBuildRoadTunnel; -void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, DepotID join_to, TileIndex end_tile); +void CcRoadDepot(Commands cmd, const CommandCost &result, TileIndex start_tile, RoadType rt, DiagDirection dir, bool adjacent, bool extended, bool half_start, bool half_end, DepotID join_to, TileIndex end_tile); void CcRoadStop(Commands cmd, const CommandCost &result, TileIndex tile, uint8_t width, uint8_t length, RoadStopType, bool is_drive_through, DiagDirection dir, RoadType, RoadStopClassID spec_class, uint16_t spec_index, StationID, bool); #endif /* ROAD_CMD_H */ diff --git a/src/road_gui.cpp b/src/road_gui.cpp index 53a71d0d2c..4b99c39f84 100644 --- a/src/road_gui.cpp +++ b/src/road_gui.cpp @@ -52,7 +52,7 @@ #include "safeguards.h" static void ShowRVStationPicker(Window *parent, RoadStopType rs); -static void ShowRoadDepotPicker(Window *parent); +static void ShowRoadDepotPicker(Window *parent, bool extended_depot); static void ShowBuildRoadWaypointPicker(Window *parent); static bool _remove_button_clicked; @@ -171,16 +171,45 @@ void ConnectRoadToStructure(TileIndex tile, DiagDirection direction) } } -void CcRoadDepot(Commands, const CommandCost &result, TileIndex start_tile, RoadType, DiagDirection dir, bool, DepotID, TileIndex end_tile) +void CcRoadDepot(Commands , const CommandCost &result, TileIndex start_tile, RoadType, DiagDirection orig_dir, bool, bool extended, bool half_start, bool half_end, DepotID, TileIndex end_tile) { if (result.Failed()) return; if (_settings_client.sound.confirm) SndPlayTileFx(SND_1F_CONSTRUCTION_OTHER, start_tile); if (!_settings_client.gui.persistent_buildingtools) ResetObjectToPlace(); + Axis axis = DiagDirToAxis(orig_dir); + uint start_coord; + uint end_coord; + bool build_start = true; + bool build_end = false; + + DiagDirection dir = orig_dir; + if (extended) { + build_start = half_end; + build_end = !half_start; + start_coord = axis == AXIS_X ? TileX(start_tile) : TileY(start_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) { + dir = ReverseDiagDir(dir); + build_start = !build_start; + build_end = !build_end; + } + } + + TileArea ta(start_tile, end_tile); for (TileIndex tile : ta) { - ConnectRoadToStructure(tile, dir); + if (build_start && !ta.Contains(tile + TileOffsByDiagDir(dir))) { + ConnectRoadToStructure(tile, dir); + } + if (build_end && !ta.Contains(tile + TileOffsByDiagDir(ReverseDiagDir(dir)))) { + ConnectRoadToStructure(tile, ReverseDiagDir(dir)); + } } } @@ -373,7 +402,13 @@ struct BuildRoadToolbarWindow : Window { { if (_game_mode == GM_NORMAL && (this->IsWidgetLowered(WID_ROT_BUS_STATION) || this->IsWidgetLowered(WID_ROT_TRUCK_STATION))) SetViewportCatchmentStation(nullptr, true); if (_settings_client.gui.link_terraform_toolbar) CloseWindowById(WC_SCEN_LAND_GEN, 0, false); - 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_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_ROT_EXTENDED_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } + this->Window::Close(); } @@ -390,6 +425,7 @@ struct BuildRoadToolbarWindow : Window { bool can_build = CanBuildVehicleInfrastructure(VEH_ROAD, rtt); this->SetWidgetsDisabledState(!can_build, WID_ROT_DEPOT, + WID_ROT_EXTENDED_DEPOT, WID_ROT_BUILD_WAYPOINT, WID_ROT_BUS_STATION, WID_ROT_TRUCK_STATION); @@ -403,12 +439,14 @@ 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_EXTENDED_DEPOT)) this->GetWidget(WID_ROT_EXTENDED_DEPOT)->SetToolTip(STR_TOOLBAR_DISABLED_NO_VEHICLE_AVAILABLE); this->GetWidget(WID_ROT_BUILD_WAYPOINT)->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 { - this->GetWidget(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT); + if (this->HasWidget(WID_ROT_DEPOT)) this->GetWidget(WID_ROT_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRAM_VEHICLE_DEPOT); + if (this->HasWidget(WID_ROT_EXTENDED_DEPOT)) this->GetWidget(WID_ROT_EXTENDED_DEPOT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_TRAM_VEHICLE_DEPOT); this->GetWidget(WID_ROT_BUILD_WAYPOINT)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT : STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT); this->GetWidget(WID_ROT_BUS_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_BUS_STATION : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_PASSENGER_TRAM_STATION); this->GetWidget(WID_ROT_TRUCK_STATION)->SetToolTip(rtt == RTT_ROAD ? STR_ROAD_TOOLBAR_TOOLTIP_BUILD_TRUCK_LOADING_BAY : STR_ROAD_TOOLBAR_TOOLTIP_BUILD_CARGO_TRAM_STATION); @@ -433,7 +471,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_EXTENDED_DEPOT)) this->GetWidget(WID_ROT_EXTENDED_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; @@ -544,8 +583,9 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: - if (HandlePlacePushButton(this, WID_ROT_DEPOT, this->rti->cursor.depot, HT_RECT)) { - ShowRoadDepotPicker(this); + case WID_ROT_EXTENDED_DEPOT: + if (HandlePlacePushButton(this, widget, this->rti->cursor.depot, HT_RECT)) { + ShowRoadDepotPicker(this, widget == WID_ROT_EXTENDED_DEPOT); this->last_started_action = widget; } break; @@ -642,11 +682,19 @@ struct BuildRoadToolbarWindow : Window { break; case WID_ROT_DEPOT: + case WID_ROT_EXTENDED_DEPOT: { CloseWindowById(WC_SELECT_DEPOT, VEH_ROAD); + _place_road_dir = DiagDirToAxis(_road_depot_orientation); + _place_road_start_half_x = (_place_road_dir == AXIS_X) && (_tile_fract_coords.x >= 8); + _place_road_start_half_y = (_place_road_dir == AXIS_Y) && (_tile_fract_coords.y >= 8); + VpSetPlaceSizingLimit(_settings_game.depot.depot_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_BUILD_WAYPOINT: PlaceRoad_Waypoint(tile); @@ -681,7 +729,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_EXTENDED_DEPOT) && this->IsWidgetLowered(WID_ROT_EXTENDED_DEPOT)))) { + SetViewportHighlightDepot(INVALID_DEPOT, true); + } this->RaiseButtons(); this->SetWidgetDisabledState(WID_ROT_REMOVE, true); @@ -730,7 +782,14 @@ struct BuildRoadToolbarWindow : Window { _place_road_dir = AXIS_Y; _place_road_end_half = pt.y & 8; } + break; + case DDSP_BUILD_DEPOT: + if (_place_road_dir == AXIS_X) { + _place_road_end_half = pt.x & 8; + } else { + _place_road_end_half = pt.y & 8; + } break; default: @@ -817,11 +876,14 @@ struct BuildRoadToolbarWindow : Window { break; case DDSP_BUILD_DEPOT: { - bool adjacent = _ctrl_pressed; StringID error_string = this->rti->strings.err_depot; + bool adjacent = _ctrl_pressed; + bool extended = last_started_action == WID_ROT_EXTENDED_DEPOT; + bool half_start = _place_road_start_half_x || _place_road_start_half_y; + bool half_end = _place_road_end_half; auto proc = [=](DepotID join_to) -> bool { - return Command::Post(error_string, CcRoadDepot, start_tile, _cur_roadtype, _road_depot_orientation, adjacent, join_to, end_tile); + return Command::Post(error_string, CcRoadDepot, start_tile, _cur_roadtype, _road_depot_orientation, adjacent, extended, half_start, half_end, join_to, end_tile); }; ShowSelectDepotIfNeeded(TileArea(start_tile, end_tile), proc, VEH_ROAD); @@ -923,6 +985,27 @@ struct BuildRoadToolbarWindow : Window { }, TramToolbarGlobalHotkeys}; }; +/** + * Add the depot icons depending on availability of construction. + * @return Panel with road depot buttons. + */ +static std::unique_ptr MakeNWidgetRoadDepot() +{ + auto hor = std::make_unique(); + + if (HasBit(_settings_game.depot.road_depot_types, 0)) { + /* Add the widget for building standard road depots. */ + hor->Add(std::make_unique(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 extended road depots. */ + hor->Add(std::make_unique(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_EXTENDED_DEPOT, SPR_IMG_ROAD_DEPOT, STR_ROAD_TOOLBAR_TOOLTIP_BUILD_EXTENDED_ROAD_VEHICLE_DEPOT)); + } + + return hor; +} + static constexpr NWidgetPart _nested_build_road_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), @@ -938,8 +1021,7 @@ static constexpr 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_BUILD_WAYPOINT), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_ROAD_TO_WAYPOINT), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION), @@ -983,8 +1065,7 @@ static constexpr 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_BUILD_WAYPOINT), SetFill(0, 1), SetMinimalSize(22, 22), SetDataTip(SPR_IMG_WAYPOINT, STR_ROAD_TOOLBAR_TOOLTIP_CONVERT_TRAM_TO_WAYPOINT), NWidget(WWT_IMGBTN, COLOUR_DARK_GREEN, WID_ROT_BUS_STATION), @@ -1114,14 +1195,28 @@ Window *ShowBuildRoadScenToolbar(RoadType roadtype) } struct BuildRoadDepotWindow : public PickerWindowBase { - BuildRoadDepotWindow(WindowDesc &desc, Window *parent) : PickerWindowBase(desc, parent) + BuildRoadDepotWindow(WindowDesc &desc, Window *parent, bool extended_depot) : PickerWindowBase(desc, parent) { this->CreateNestedTree(); + /* Fix direction for extended depots. */ + if (extended_depot) { + switch (_road_depot_orientation) { + case DIAGDIR_NE: + _road_depot_orientation++; + break; + case DIAGDIR_NW: + _road_depot_orientation--; + break; + default: break; + } + } + this->LowerWidget(WID_BROD_DEPOT_NE + _road_depot_orientation); if (RoadTypeIsTram(_cur_roadtype)) { this->GetWidget(WID_BROD_CAPTION)->widget_data = STR_BUILD_DEPOT_TRAM_ORIENTATION_CAPTION; for (WidgetID i = WID_BROD_DEPOT_NE; i <= WID_BROD_DEPOT_NW; i++) { + if (!this->HasWidget(i)) continue; this->GetWidget(i)->tool_tip = STR_BUILD_DEPOT_TRAM_ORIENTATION_SELECT_TOOLTIP; } } @@ -1209,9 +1304,29 @@ static WindowDesc _build_road_depot_desc( _nested_build_road_depot_widgets ); -static void ShowRoadDepotPicker(Window *parent) +static const NWidgetPart _nested_build_extended_road_depot_widgets[] = { + NWidget(NWID_HORIZONTAL), + NWidget(WWT_CLOSEBOX, COLOUR_DARK_GREEN), + NWidget(WWT_CAPTION, COLOUR_DARK_GREEN, WID_BROD_CAPTION), SetDataTip(STR_BUILD_DEPOT_ROAD_ORIENTATION_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + EndContainer(), + NWidget(WWT_PANEL, COLOUR_DARK_GREEN), + NWidget(NWID_HORIZONTAL_LTR), SetPIP(0, WidgetDimensions::unscaled.hsep_normal, 0), SetPIPRatio(1, 0, 1), SetPadding(WidgetDimensions::unscaled.picker), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SW), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_BROD_DEPOT_SE), SetMinimalSize(66, 50), SetFill(0, 0), SetDataTip(0x0, STR_BUILD_DEPOT_ROAD_ORIENTATION_SELECT_TOOLTIP), + EndContainer(), + EndContainer(), +}; + +static WindowDesc _build_extended_road_depot_desc( + WDP_AUTO, nullptr, 0, 0, + WC_BUILD_DEPOT, WC_BUILD_TOOLBAR, + WDF_CONSTRUCTION, + _nested_build_extended_road_depot_widgets +); + +static void ShowRoadDepotPicker(Window *parent, bool extended_depot) { - new BuildRoadDepotWindow(_build_road_depot_desc, parent); + new BuildRoadDepotWindow(extended_depot ? _build_extended_road_depot_desc : _build_road_depot_desc, parent, extended_depot); } template diff --git a/src/road_map.cpp b/src/road_map.cpp index 66fe49010f..9fb6b03dac 100644 --- a/src/road_map.cpp +++ b/src/road_map.cpp @@ -38,9 +38,11 @@ RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge 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 06c000384c..ac184a25f2 100644 --- a/src/road_map.h +++ b/src/road_map.h @@ -118,16 +118,38 @@ debug_inline static bool IsRoadDepotTile(Tile t) return IsTileType(t, MP_ROAD) && IsRoadDepot(t); } +/** + * Return whether a road depot tile is an extended one. + * @param t Tile to query. + * @return True if extended road depot tile. + */ +static inline bool IsExtendedRoadDepot(Tile t) +{ + assert(IsTileType(t, MP_ROAD)); + assert(IsRoadDepot(t)); + return HasBit(t.m5(), 5); +} + +/** + * Return whether a tile is an extended road depot tile. + * @param t Tile to query. + * @return True if extended road depot tile. + */ +static inline bool IsExtendedRoadDepotTile(Tile t) +{ + return IsTileType(t, MP_ROAD) && IsRoadDepot(t) && IsExtendedRoadDepot(t); +} + /** * Get the present road bits for a specific road type. * @param t The tile to query. - * @param rt Road type. - * @pre IsNormalRoad(t) + * @param rtt Road tram type. + * @pre IsNormalRoad(t) || IsRoadDepotTile(t) * @return The present road bits for the road type. */ inline RoadBits GetRoadBits(Tile t, RoadTramType rtt) { - assert(IsNormalRoad(t)); + assert(IsNormalRoad(t) || IsRoadDepotTile(t)); if (rtt == RTT_TRAM) return (RoadBits)GB(t.m3(), 0, 4); return (RoadBits)GB(t.m5(), 0, 4); } @@ -152,7 +174,7 @@ inline RoadBits GetAllRoadBits(Tile tile) */ inline void SetRoadBits(Tile t, RoadBits r, RoadTramType rtt) { - assert(IsNormalRoad(t)); // XXX incomplete + assert(IsNormalRoad(t) || IsRoadDepotTile(t)); if (rtt == RTT_TRAM) { SB(t.m3(), 0, 4, r); } else { @@ -556,19 +578,28 @@ inline void TerminateRoadWorks(Tile t) SB(t.m7(), 0, 4, 0); } +/** + * Set the direction of the exit of a road depot. + * @param t The tile to query. + * @return Diagonal direction of the depot exit. + */ +static inline void SetRoadDepotDirection(Tile t, DiagDirection dir) +{ + assert(IsRoadDepot(t)); + SB(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. */ inline DiagDirection GetRoadDepotDirection(Tile t) { assert(IsRoadDepot(t)); - return (DiagDirection)GB(t.m5(), 0, 2); + return (DiagDirection)GB(t.m6(), 6, 2); } - RoadBits GetAnyRoadBits(Tile tile, RoadTramType rtt, bool straight_tunnel_bridge_entrance = false); /** @@ -680,7 +711,7 @@ inline void MakeRoadCrossing(Tile t, Owner road, Owner tram, Owner rail, Axis ro inline void SetRoadDepotExitDirection(Tile tile, DiagDirection dir) { assert(IsRoadDepotTile(tile)); - SB(tile.m5(), 0, 2, dir); + SB(tile.m6(), 6, 2, dir); } /** @@ -698,8 +729,9 @@ inline void MakeRoadDepot(Tile tile, Owner owner, DepotID depot_id, DiagDirectio tile.m2() = depot_id; tile.m3() = 0; tile.m4() = INVALID_ROADTYPE; - tile.m5() = ROAD_TILE_DEPOT << 6 | dir; - SB(tile.m6(), 2, 4, 0); + tile.m5() = ROAD_TILE_DEPOT << 6; + SB(tile.m6(), 0, 6, 0); + SB(tile.m6(), 6, 2, dir); tile.m7() = owner; tile.m8() = INVALID_ROADTYPE << 6; SetRoadType(tile, GetRoadTramType(rt), rt); diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 66e0798095..6afc3b3861 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2876,6 +2876,15 @@ bool AfterLoadGame() break; } } + + for (auto t : Map::Iterate()) { + if (!IsRoadDepotTile(t)) continue; + DiagDirection dir = (DiagDirection)GB(t.m5(), 0, 2); + SB(t.m5(), 0, 6, 0); + RoadBits rb = DiagDirToRoadBits(dir); + SetRoadBits(t, rb, HasRoadTypeRoad(t) ? RTT_ROAD : RTT_TRAM); + SB(t.m6(), 6, 2, dir); + } } /* 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 8e49af0eed..69d8cc515b 100644 --- a/src/saveload/company_sl.cpp +++ b/src/saveload/company_sl.cpp @@ -134,8 +134,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 9b29e2d4b9..73c6fa5da7 100644 --- a/src/script/api/script_road.cpp +++ b/src/script/api/script_road.cpp @@ -535,7 +535,7 @@ static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagD DiagDirection entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? DIAGDIR_SE : DIAGDIR_NW) : (::TileX(tile) < ::TileX(front) ? DIAGDIR_SW : DIAGDIR_NE); - return ScriptObject::Command::Do(tile, ScriptObject::GetRoadType(), entrance_dir, false, INVALID_DEPOT, tile); + return ScriptObject::Command::Do(tile, ScriptObject::GetRoadType(), entrance_dir, false, false, false, false, INVALID_DEPOT, tile); } /* 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 983065c108..0881cd41c9 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -1248,7 +1248,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/widgets/road_widget.h b/src/widgets/road_widget.h index 679545728a..2fbf3c6573 100644 --- a/src/widgets/road_widget.h +++ b/src/widgets/road_widget.h @@ -19,6 +19,7 @@ enum RoadToolbarWidgets : WidgetID { WID_ROT_AUTOROAD, ///< Autorail. WID_ROT_DEMOLISH, ///< Demolish. WID_ROT_DEPOT, ///< Build depot. + WID_ROT_EXTENDED_DEPOT, ///< Build extended depot. WID_ROT_BUILD_WAYPOINT, ///< Build waypoint. WID_ROT_BUS_STATION, ///< Build bus station. WID_ROT_TRUCK_STATION, ///< Build truck station.