diff --git a/src/command.cpp b/src/command.cpp index b09d43318a..e65b8a4b28 100644 --- a/src/command.cpp +++ b/src/command.cpp @@ -549,7 +549,6 @@ bool DoCommandP(TileIndex tile, uint32 p1, uint32 p2, CommandCallback *callback, * estimate the cost of cloning a vehicle. */ notest = (cmd & 0xFF) == CMD_CLEAR_AREA || - (cmd & 0xFF) == CMD_CONVERT_RAIL || (cmd & 0xFF) == CMD_LEVEL_LAND || (cmd & 0xFF) == CMD_REMOVE_ROAD || (cmd & 0xFF) == CMD_REMOVE_LONG_ROAD || diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 454e4b30e5..15146f5e5d 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -1143,8 +1143,7 @@ CommandCost CmdRemoveSignalTrack(TileIndex tile, uint32 flags, uint32 p1, uint32 return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5)); // bit 5 is remove bit } -typedef CommandCost DoConvertRailProc(TileIndex tile, RailType totype, bool exec); - +/** Update power of train under which is the railtype being converted */ void *UpdateTrainPowerProc(Vehicle *v, void *data) { /* Similiar checks as in TrainPowerChanged() */ @@ -1157,46 +1156,6 @@ void *UpdateTrainPowerProc(Vehicle *v, void *data) return NULL; } -/** - * Switches the rail type. - * Railtypes are stored on a per-tile basis, not on a per-track basis, so - * all the tracks in the given tile will be converted. - * @param tile The tile on which the railtype is to be convert. - * @param totype The railtype we want to convert to - * @param exec Switches between test and execute mode - * @return The cost and state of the operation - * @retval CMD_ERROR An error occured during the operation. - */ -static CommandCost DoConvertRail(TileIndex tile, RailType totype, bool exec) -{ - /* change type. */ - if (exec) { - SetRailType(tile, totype); - MarkTileDirtyByTile(tile); - - /* notify YAPF about the track layout change */ - TrackBits tracks = GetTrackBits(tile); - while (tracks != TRACK_BIT_NONE) { - YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); - } - - if (IsTileDepotType(tile, TRANSPORT_RAIL)) { - /* Update build vehicle window related to this depot */ - InvalidateWindowData(WC_VEHICLE_DEPOT, tile); - InvalidateWindowData(WC_BUILD_VEHICLE, tile); - } - - /* update power of train engines on this tile */ - VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); - } - - return CommandCost(RailConvertCost(GetRailType(tile), totype) * CountBits(GetTrackBits(tile))); -} - -extern CommandCost DoConvertStationRail(TileIndex tile, RailType totype, bool exec); -extern CommandCost DoConvertStreetRail(TileIndex tile, RailType totype, bool exec); -extern CommandCost DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec); - /** Convert one rail type to the other. You can convert normal rail to * monorail/maglev easily or vice-versa. * @param tile end tile of rail conversion drag @@ -1206,72 +1165,137 @@ extern CommandCost DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bo */ CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { - CommandCost ret, cost; - Money money; - int ex; - int ey; - int sx, sy, x, y; - - SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); + CommandCost cost; if (!ValParamRailtype(p2)) return CMD_ERROR; if (p1 >= MapSize()) return CMD_ERROR; + RailType totype = (RailType)p2; + + uint ex = TileX(tile); + uint ey = TileY(tile); + uint sx = TileX(p1); + uint sy = TileY(p1); + /* make sure sx,sy are smaller than ex,ey */ - ex = TileX(tile); - ey = TileY(tile); - sx = TileX(p1); - sy = TileY(p1); if (ex < sx) Swap(ex, sx); if (ey < sy) Swap(ey, sy); - money = GetAvailableMoneyForCommand(); + SET_EXPENSES_TYPE(EXPENSES_CONSTRUCTION); - for (x = sx; x <= ex; ++x) { - for (y = sy; y <= ey; ++y) { + _error_message = STR_1005_NO_SUITABLE_RAILROAD_TRACK; // by default, there is no track to convert + + for (uint x = sx; x <= ex; ++x) { + for (uint y = sy; y <= ey; ++y) { TileIndex tile = TileXY(x, y); - DoConvertRailProc *proc; - RailType totype = (RailType)p2; + TileType tt = GetTileType(tile); - switch (GetTileType(tile)) { - case MP_RAILWAY: proc = DoConvertRail; break; - case MP_STATION: proc = DoConvertStationRail; break; - case MP_ROAD: proc = DoConvertStreetRail; break; - case MP_TUNNELBRIDGE: proc = DoConvertTunnelBridgeRail; break; + /* Check if there is any track on tile */ + switch (tt) { + case MP_RAILWAY: + break; + case MP_STATION: + if (!IsRailwayStation(tile)) continue; + break; + case MP_ROAD: + if (!IsLevelCrossing(tile)) continue; + break; + case MP_TUNNELBRIDGE: + if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue; + break; default: continue; } - /* It is possible that 'type' is invalid when there is no rail on the tile, - * but this situation will be detected in proc() - */ + /* Original railtype we are converting from */ RailType type = GetRailType(tile); - /* Not own tile or track is already converted */ - if ((!CheckTileOwnership(tile) || type == totype) || - /* 'hidden' elrails can't be downgraded to normal rail when elrails are disabled */ - (_patches.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC) || - /* Vehicle on a tile while not converting Rail <-> ElRail */ - (!IsCompatibleRail(type, totype) && !EnsureNoVehicleOnGround(tile))) { - ret = CMD_ERROR; - continue; - } + /* Converting to the same type or converting 'hidden' elrail -> rail */ + if (type == totype || (_patches.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue; - ret = proc(tile, totype, false); - if (CmdFailed(ret)) continue; + /* Trying to convert other's rail */ + if (!CheckTileOwnership(tile)) continue; - if (flags & DC_EXEC) { - money -= ret.GetCost(); - if (money < 0) { - _additional_cash_required = ret.GetCost(); - return cost; + /* Vehicle on the tile when not converting Rail <-> ElRail + * Tunnels and bridges have special check later */ + if (tt != MP_TUNNELBRIDGE) { + if (!IsCompatibleRail(type, totype) && !EnsureNoVehicleOnGround(tile)) continue; + if (flags & DC_EXEC) { // we can safely convert, too + SetRailType(tile, totype); + MarkTileDirtyByTile(tile); + /* update power of train engines on this tile */ + VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); } - proc(tile, totype, true); } - cost.AddCost(ret); + + switch (tt) { + case MP_RAILWAY: + if (flags & DC_EXEC) { + /* notify YAPF about the track layout change */ + TrackBits tracks = GetTrackBits(tile); + while (tracks != TRACK_BIT_NONE) { + YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks)); + } + + if (IsTileDepotType(tile, TRANSPORT_RAIL)) { + /* Update build vehicle window related to this depot */ + InvalidateWindowData(WC_VEHICLE_DEPOT, tile); + InvalidateWindowData(WC_BUILD_VEHICLE, tile); + } + } + + cost.AddCost(CommandCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)))); + break; + + case MP_TUNNELBRIDGE: { + TileIndex endtile = IsTunnel(tile) ? GetOtherTunnelEnd(tile) : GetOtherBridgeEnd(tile); + + /* If both ends of tunnel/bridge are in the range, do not try to convert twice - + * it would cause assert because of different test and exec runs */ + if (endtile < tile && TileX(endtile) >= sx && TileX(endtile) <= ex && + TileY(endtile) >= sy && TileY(endtile) <= ey) continue; + + /* When not coverting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */ + if (!IsCompatibleRail(GetRailType(tile), totype) && + GetVehicleTunnelBridge(tile, endtile) != NULL) continue; + + if (flags & DC_EXEC) { + SetRailType(tile, totype); + SetRailType(endtile, totype); + + VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); + VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc); + + Track track = AxisToTrack(DiagDirToAxis(GetTunnelBridgeDirection(tile))); + + YapfNotifyTrackLayoutChange(tile, track); + YapfNotifyTrackLayoutChange(endtile, track); + + MarkTileDirtyByTile(tile); + MarkTileDirtyByTile(endtile); + + if (IsBridge(tile)) { + TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile)); + TileIndex t = tile + delta; + for (; t != endtile; t += delta) MarkTileDirtyByTile(t); // TODO encapsulate this into a function + } + } + + cost.AddCost((DistanceManhattan(tile, endtile) + 1) * RailConvertCost(type, totype)); + } break; + + default: // MP_STATION, MP_ROAD + if (flags & DC_EXEC) { + Track track = (tt == MP_STATION) ? GetRailStationTrack(tile) : AxisToTrack(OtherAxis(GetCrossingRoadAxis(tile))); + YapfNotifyTrackLayoutChange(tile, track); + } + + cost.AddCost(RailConvertCost(type, totype)); + break; + } } } - return (cost.GetCost() == 0) ? ret : cost; + return (cost.GetCost() == 0) ? CMD_ERROR : cost; } static CommandCost RemoveTrainDepot(TileIndex tile, uint32 flags) diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp index 58ad422086..f6a2a86945 100644 --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -593,30 +593,6 @@ do_clear:; return cost; } -/** - * Switches the rail type on a level crossing. - * @param tile The tile on which the railtype is to be convert. - * @param totype The railtype we want to convert to - * @param exec Switches between test and execute mode - * @return The cost and state of the operation - * @retval CMD_ERROR An error occured during the operation. - */ -CommandCost DoConvertStreetRail(TileIndex tile, RailType totype, bool exec) -{ - /* not a railroad crossing? */ - if (!IsLevelCrossing(tile)) return CMD_ERROR; - - if (exec) { - SetRailType(tile, totype); - MarkTileDirtyByTile(tile); - YapfNotifyTrackLayoutChange(tile, FindFirstTrack(GetCrossingRailBits(tile))); - VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); - } - - return CommandCost(RailConvertCost(GetRailType(tile), totype)); -} - - /** Build a long piece of road. * @param end_tile end tile of drag * @param flags operation to perform diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp index 0e189bd051..dc077e27f6 100644 --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1289,29 +1289,6 @@ static CommandCost RemoveRailroadStation(Station *st, TileIndex tile, uint32 fla return cost; } -/** - * Switches the rail type at a railway station tile. - * @param tile The tile on which the railtype is to be convert. - * @param totype The railtype we want to convert to - * @param exec Switches between test and execute mode - * @return The cost and state of the operation - * @retval CMD_ERROR An error occured during the operation. - */ -CommandCost DoConvertStationRail(TileIndex tile, RailType totype, bool exec) -{ - /* Tile is not a railroad station? */ - if (!IsRailwayStation(tile)) return CMD_ERROR; - - if (exec) { - SetRailType(tile, totype); - MarkTileDirtyByTile(tile); - YapfNotifyTrackLayoutChange(tile, GetRailStationTrack(tile)); - VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); - } - - return CommandCost(RailConvertCost(GetRailType(tile), totype)); -} - /** * @param truck_station Determines whether a stop is RoadStop::BUS or RoadStop::TRUCK * @param st The Station to do the whole procedure for diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 68be57389c..95fc6f980e 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -690,54 +690,6 @@ static CommandCost ClearTile_TunnelBridge(TileIndex tile, byte flags) return CMD_ERROR; } -/** - * Switches the rail type for a tunnel or a bridgehead. As the railtype - * on the bridge are determined by the one of the bridgehead, this - * functions converts the railtype on the entire bridge. - * @param tile The tile on which the railtype is to be convert. - * @param totype The railtype we want to convert to - * @param exec Switches between test and execute mode - * @return The cost and state of the operation - * @retval CMD_ERROR An error occured during the operation. - */ -CommandCost DoConvertTunnelBridgeRail(TileIndex tile, RailType totype, bool exec) -{ - if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return CMD_ERROR; - - TileIndex endtile = IsTunnel(tile) ? GetOtherTunnelEnd(tile) : GetOtherBridgeEnd(tile); - - /* If not coverting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */ - if (!IsCompatibleRail(GetRailType(tile), totype) && - GetVehicleTunnelBridge(tile, endtile) != NULL) { - return CMD_ERROR; - } - - if (exec) { - SetRailType(tile, totype); - SetRailType(endtile, totype); - - Track track = AxisToTrack(DiagDirToAxis(GetTunnelBridgeDirection(tile))); - - YapfNotifyTrackLayoutChange(tile, track); - YapfNotifyTrackLayoutChange(endtile, track); - - VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); - VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc); - - MarkTileDirtyByTile(tile); - MarkTileDirtyByTile(endtile); - - if (IsBridge(tile)) { - TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile)); - TileIndex t = tile + delta; - for (; t != endtile; t += delta) MarkTileDirtyByTile(t); // TODO encapsulate this into a function - } - } - - return CommandCost((DistanceManhattan(tile, endtile) + 1) * RailConvertCost(GetRailType(tile), totype)); -} - - /** * Draws the pillars under high bridges. *