/** * Build a single piece of rail * @param tile tile to build on * @param flags operation to perform * @param p1 railtype of being built piece (normal, mono, maglev) * @param p2 rail track to build * @param text unused * @return the cost of this operation or an error */ CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text) { RailType railtype = Extract(p1); Track track = Extract(p2); CommandCost cost(EXPENSES_CONSTRUCTION); if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR; Slope tileh = GetTileSlope(tile); TrackBits trackbit = TrackToTrackBits(track); switch (GetTileType(tile)) { case MP_RAILWAY: { CommandCost ret = CheckTileOwnership(tile); if (ret.Failed()) return ret; if (!IsPlainRail(tile)) return CMD_ERROR; if (!IsCompatibleRail(GetRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION); ret = CheckTrackCombination(tile, trackbit, flags); if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track); if (ret.Failed()) return ret; ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile); if (ret.Failed()) return ret; cost.AddCost(ret); /* If the rail types don't match, try to convert only if engines of * the new rail type are not powered on the present rail type and engines of * the present rail type are powered on the new rail type. */ if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) { if (HasPowerOnRail(GetRailType(tile), railtype)) { ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL); if (ret.Failed()) return ret; cost.AddCost(ret); } else { return CMD_ERROR; } } if (flags & DC_EXEC) { SetRailGroundType(tile, RAIL_GROUND_BARREN); TrackBits bits = GetTrackBits(tile); SetTrackBits(tile, bits | trackbit); /* Subtract old infrastructure count. */ uint pieces = CountBits(bits); if (TracksOverlap(bits)) pieces *= pieces; Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces; /* Add new infrastructure count. */ pieces = CountBits(bits | trackbit); if (TracksOverlap(bits | trackbit)) pieces *= pieces; Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces; DirtyCompanyInfrastructureWindows(GetTileOwner(tile)); } break; } case MP_ROAD: { /* Level crossings may only be built on these slopes */ if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION); CommandCost ret = EnsureNoVehicleOnGround(tile); if (ret.Failed()) return ret; if (IsNormalRoad(tile)) { if (HasRoadWorks(tile)) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS); if (GetDisallowedRoadDirections(tile) != DRD_NONE) return_cmd_error(STR_ERROR_CROSSING_ON_ONEWAY_ROAD); if (RailNoLevelCrossings(railtype)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED); RoadTypes roadtypes = GetRoadTypes(tile); RoadBits road = GetRoadBits(tile, ROADTYPE_ROAD); RoadBits tram = GetRoadBits(tile, ROADTYPE_TRAM); if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) || (track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) { Owner road_owner = GetRoadOwner(tile, ROADTYPE_ROAD); Owner tram_owner = GetRoadOwner(tile, ROADTYPE_TRAM); /* Disallow breaking end-of-line of someone else * so trams can still reverse on this tile. */ if (Company::IsValidID(tram_owner) && HasExactlyOneBit(tram)) { CommandCost ret = CheckOwnership(tram_owner); if (ret.Failed()) return ret; } /* Crossings must always have a road... */ uint num_new_road_pieces = 2 - CountBits(road); if (road == ROAD_NONE) road_owner = _current_company; roadtypes |= ROADTYPES_ROAD; /* ...but tram is not required. */ uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0; cost.AddCost((num_new_road_pieces + num_new_tram_pieces) * _price[PR_BUILD_ROAD]); if (flags & DC_EXEC) { MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtypes, GetTownIndex(tile)); UpdateLevelCrossing(tile, false); Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR; DirtyCompanyInfrastructureWindows(_current_company); if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) { Company::Get(road_owner)->infrastructure.road[ROADTYPE_ROAD] += num_new_road_pieces; DirtyCompanyInfrastructureWindows(road_owner); } if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) { Company::Get(tram_owner)->infrastructure.road[ROADTYPE_TRAM] += num_new_tram_pieces; DirtyCompanyInfrastructureWindows(tram_owner); } } break; } } if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) { return_cmd_error(STR_ERROR_ALREADY_BUILT); } /* FALL THROUGH */ } default: { /* Will there be flat water on the lower halftile? */ bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh); CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile); if (ret.Failed()) return ret; cost.AddCost(ret); if (!water_ground) { ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); if (ret.Failed()) return ret; cost.AddCost(ret); } else { cost.AddCost(_price[PR_CLEAR_ROUGH]); } if (flags & DC_EXEC) { MakeRailNormal(tile, _current_company, trackbit, railtype); if (water_ground) SetRailGroundType(tile, RAIL_GROUND_WATER); Company::Get(_current_company)->infrastructure.rail[railtype]++; DirtyCompanyInfrastructureWindows(_current_company); } break; } } if (flags & DC_EXEC) { MarkTileDirtyByTile(tile); AddTrackToSignalBuffer(tile, track, _current_company); YapfNotifyTrackLayoutChange(tile, track); } cost.AddCost(RailBuildCost(railtype)); return cost; }