Fix eabb9db: Drag building of road stops should not allow mixing z levels.

This commit is contained in:
Michael Lutz 2024-03-24 17:22:39 +01:00
parent 69acc132ca
commit 8fb7d74dfe
1 changed files with 98 additions and 98 deletions

View File

@ -952,7 +952,8 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_
/**
* Checks if a road stop can be built at the given tile.
* @param tile_area Area to check.
* @param cur_tile Tile to check.
* @param allowed_z Height allowed for the tile. If allowed_z is negative, it will be set to the height of this tile.
* @param flags Operation to perform.
* @param invalid_dirs Prohibited directions (set of DiagDirections).
* @param is_drive_through True if trying to build a drive-through station.
@ -962,108 +963,105 @@ static CommandCost CheckFlatLandRailStation(TileIndex tile_cur, TileIndex north_
* @param rt Road type to build.
* @return The cost in case of success, or an error code if it failed.
*/
static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt)
static CommandCost CheckFlatLandRoadStop(TileIndex cur_tile, int &allowed_z, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
int allowed_z = -1;
for (TileIndex cur_tile : tile_area) {
CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
if (ret.Failed()) return ret;
cost.AddCost(ret);
CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
if (ret.Failed()) return ret;
cost.AddCost(ret);
/* If station is set, then we have special handling to allow building on top of already existing stations.
* Station points to INVALID_STATION if we can build on any station.
* Or it points to a station if we're only allowed to build on exactly that station. */
if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
if (!IsRoadStop(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
} else {
if (is_truck_stop != IsTruckStop(cur_tile) ||
is_drive_through != IsDriveThroughStopTile(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
}
/* Drive-through station in the wrong direction. */
if (is_drive_through && IsDriveThroughStopTile(cur_tile) && DiagDirToAxis(GetRoadStopDir(cur_tile)) != axis){
return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
}
StationID st = GetStationIndex(cur_tile);
if (*station == INVALID_STATION) {
*station = st;
} else if (*station != st) {
return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
}
}
/* If station is set, then we have special handling to allow building on top of already existing stations.
* Station points to INVALID_STATION if we can build on any station.
* Or it points to a station if we're only allowed to build on exactly that station. */
if (station != nullptr && IsTileType(cur_tile, MP_STATION)) {
if (!IsRoadStop(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
} else {
bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
/* Road bits in the wrong direction. */
RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
/* Someone was pedantic and *NEEDED* three fracking different error messages. */
switch (CountBits(rb)) {
case 1:
return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
case 2:
if (rb == ROAD_X || rb == ROAD_Y) return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
return_cmd_error(STR_ERROR_DRIVE_THROUGH_CORNER);
default: // 3 or 4
return_cmd_error(STR_ERROR_DRIVE_THROUGH_JUNCTION);
}
if (is_truck_stop != IsTruckStop(cur_tile) ||
is_drive_through != IsDriveThroughStopTile(cur_tile)) {
return ClearTile_Station(cur_tile, DC_AUTO); // Get error message.
}
/* Drive-through station in the wrong direction. */
if (is_drive_through && IsDriveThroughStopTile(cur_tile) && DiagDirToAxis(GetRoadStopDir(cur_tile)) != axis){
return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
}
StationID st = GetStationIndex(cur_tile);
if (*station == INVALID_STATION) {
*station = st;
} else if (*station != st) {
return_cmd_error(STR_ERROR_ADJOINS_MORE_THAN_ONE_EXISTING);
}
}
} else {
bool build_over_road = is_drive_through && IsNormalRoadTile(cur_tile);
/* Road bits in the wrong direction. */
RoadBits rb = IsNormalRoadTile(cur_tile) ? GetAllRoadBits(cur_tile) : ROAD_NONE;
if (build_over_road && (rb & (axis == AXIS_X ? ROAD_Y : ROAD_X)) != 0) {
/* Someone was pedantic and *NEEDED* three fracking different error messages. */
switch (CountBits(rb)) {
case 1:
return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
if (build_over_road) {
/* There is a road, check if we can build road+tram stop over it. */
RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
if (road_rt != INVALID_ROADTYPE) {
Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
if (road_owner == OWNER_TOWN) {
if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
} else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
case 2:
if (rb == ROAD_X || rb == ROAD_Y) return_cmd_error(STR_ERROR_DRIVE_THROUGH_DIRECTION);
return_cmd_error(STR_ERROR_DRIVE_THROUGH_CORNER);
if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
default: // 3 or 4
return_cmd_error(STR_ERROR_DRIVE_THROUGH_JUNCTION);
}
}
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
if (build_over_road) {
/* There is a road, check if we can build road+tram stop over it. */
RoadType road_rt = GetRoadType(cur_tile, RTT_ROAD);
if (road_rt != INVALID_ROADTYPE) {
Owner road_owner = GetRoadOwner(cur_tile, RTT_ROAD);
if (road_owner == OWNER_TOWN) {
if (!_settings_game.construction.road_stop_on_town_road) return_cmd_error(STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD);
} else if (!_settings_game.construction.road_stop_on_competitor_road && road_owner != OWNER_NONE) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_ROAD));
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
if (RoadTypeIsRoad(rt) && !HasPowerOnRoad(rt, road_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
if (GetDisallowedRoadDirections(cur_tile) != DRD_NONE && road_owner != OWNER_TOWN) {
ret = CheckOwnership(road_owner);
if (ret.Failed()) return ret;
}
/* There is a tram, check if we can build road+tram stop over it. */
RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
if (tram_rt != INVALID_ROADTYPE) {
Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
if (Company::IsValidID(tram_owner) &&
(!_settings_game.construction.road_stop_on_competitor_road ||
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
ret = CheckOwnership(tram_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
if (RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
} else if (RoadTypeIsTram(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
} else {
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
if (ret.Failed()) return ret;
cost.AddCost(ret);
cost.AddCost(RoadBuildCost(road_rt) * (2 - num_pieces));
} else if (RoadTypeIsRoad(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
/* There is a tram, check if we can build road+tram stop over it. */
RoadType tram_rt = GetRoadType(cur_tile, RTT_TRAM);
if (tram_rt != INVALID_ROADTYPE) {
Owner tram_owner = GetRoadOwner(cur_tile, RTT_TRAM);
if (Company::IsValidID(tram_owner) &&
(!_settings_game.construction.road_stop_on_competitor_road ||
/* Disallow breaking end-of-line of someone else
* so trams can still reverse on this tile. */
HasExactlyOneBit(GetRoadBits(cur_tile, RTT_TRAM)))) {
ret = CheckOwnership(tram_owner);
if (ret.Failed()) return ret;
}
uint num_pieces = CountBits(GetRoadBits(cur_tile, RTT_TRAM));
if (RoadTypeIsTram(rt) && !HasPowerOnRoad(rt, tram_rt)) return_cmd_error(STR_ERROR_NO_SUITABLE_ROAD);
cost.AddCost(RoadBuildCost(tram_rt) * (2 - num_pieces));
} else if (RoadTypeIsTram(rt)) {
cost.AddCost(RoadBuildCost(rt) * 2);
}
} else {
ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, cur_tile);
if (ret.Failed()) return ret;
cost.AddCost(ret);
cost.AddCost(RoadBuildCost(rt) * 2);
}
}
@ -1885,17 +1883,19 @@ static CommandCost FindJoiningRoadStop(StationID existing_stop, StationID statio
*/
static CommandCost CalculateRoadStopCost(TileArea tile_area, DoCommandFlag flags, bool is_drive_through, bool is_truck_stop, Axis axis, DiagDirection ddir, StationID *est, RoadType rt, Money unit_cost)
{
CommandCost cost(EXPENSES_CONSTRUCTION);
uint invalid_dirs = 0;
if (is_drive_through) {
SetBit(invalid_dirs, AxisToDiagDir(axis));
SetBit(invalid_dirs, ReverseDiagDir(AxisToDiagDir(axis)));
} else {
SetBit(invalid_dirs, ddir);
}
/* Check every tile in the area. */
int allowed_z = -1;
CommandCost cost(EXPENSES_CONSTRUCTION);
for (TileIndex cur_tile : tile_area) {
uint invalid_dirs = 0;
if (is_drive_through) {
SetBit(invalid_dirs, AxisToDiagDir(axis));
SetBit(invalid_dirs, ReverseDiagDir(AxisToDiagDir(axis)));
} else {
SetBit(invalid_dirs, ddir);
}
CommandCost ret = CheckFlatLandRoadStop(TileArea(cur_tile, cur_tile), flags, invalid_dirs, is_drive_through, is_truck_stop, axis, est, rt);
CommandCost ret = CheckFlatLandRoadStop(cur_tile, allowed_z, flags, invalid_dirs, is_drive_through, is_truck_stop, axis, est, rt);
if (ret.Failed()) return ret;
bool is_preexisting_roadstop = IsTileType(cur_tile, MP_STATION) && IsRoadStop(cur_tile);