/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file road.cpp Generic road related functions. */ #include "stdafx.h" #include "rail_map.h" #include "road_map.h" #include "water_map.h" #include "genworld.h" #include "company_func.h" #include "company_base.h" #include "engine_base.h" #include "date_func.h" #include "landscape.h" #include "road.h" #include "road_func.h" #include "roadveh.h" #include "safeguards.h" /** * Return if the tile is a valid tile for a crossing. * * @param tile the current tile * @param ax the axis of the road over the rail * @return true if it is a valid tile */ static bool IsPossibleCrossing(const TileIndex tile, Axis ax) { return (IsTileType(tile, MP_RAILWAY) && GetRailTileType(tile) == RAIL_TILE_NORMAL && GetTrackBits(tile) == (ax == AXIS_X ? TRACK_BIT_Y : TRACK_BIT_X) && GetFoundationSlope(tile) == SLOPE_FLAT); } /** * Clean up unnecessary RoadBits of a planned tile. * @param tile current tile * @param org_rb planned RoadBits * @return optimised RoadBits */ RoadBits CleanUpRoadBits(const TileIndex tile, RoadBits org_rb) { if (!IsValidTile(tile)) return ROAD_NONE; for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) { const TileIndex neighbor_tile = TileAddByDiagDir(tile, dir); /* Get the Roadbit pointing to the neighbor_tile */ const RoadBits target_rb = DiagDirToRoadBits(dir); /* If the roadbit is in the current plan */ if (org_rb & target_rb) { bool connective = false; const RoadBits mirrored_rb = MirrorRoadBits(target_rb); if (IsValidTile(neighbor_tile)) { switch (GetTileType(neighbor_tile)) { /* Always connective ones */ case MP_CLEAR: case MP_TREES: connective = true; break; /* The conditionally connective ones */ case MP_TUNNELBRIDGE: case MP_STATION: case MP_ROAD: if (IsNormalRoadTile(neighbor_tile)) { /* Always connective */ connective = true; } else { const RoadBits neighbor_rb = GetAnyRoadBits(neighbor_tile, RTT_ROAD) | GetAnyRoadBits(neighbor_tile, RTT_TRAM); /* Accept only connective tiles */ connective = (neighbor_rb & mirrored_rb) != ROAD_NONE; } break; case MP_RAILWAY: connective = IsPossibleCrossing(neighbor_tile, DiagDirToAxis(dir)); break; case MP_WATER: /* Check for real water tile */ connective = !IsWater(neighbor_tile); break; /* The definitely not connective ones */ default: break; } } /* If the neighbor tile is inconnective, remove the planned road connection to it */ if (!connective) org_rb ^= target_rb; } } return org_rb; } /** * Finds out, whether given company has a given RoadType available for construction. * @param company ID of company * @param roadtypet RoadType to test * @return true if company has the requested RoadType available */ bool HasRoadTypeAvail(const CompanyID company, RoadType roadtype) { if (company == OWNER_DEITY || company == OWNER_TOWN || _game_mode == GM_EDITOR || _generating_world) { return true; // TODO: should there be a proper check? } else { const Company *c = Company::GetIfValid(company); if (c == nullptr) return false; return HasBit(c->avail_roadtypes & ~_roadtypes_hidden_mask, roadtype); } } static RoadTypes GetMaskForRoadTramType(RoadTramType rtt) { return rtt == RTT_TRAM ? _roadtypes_type : ~_roadtypes_type; } /** * Test if any buildable RoadType is available for a company. * @param company the company in question * @return true if company has any RoadTypes available */ bool HasAnyRoadTypesAvail(CompanyID company, RoadTramType rtt) { return (Company::Get(company)->avail_roadtypes & ~_roadtypes_hidden_mask & GetMaskForRoadTramType(rtt)) != ROADTYPES_NONE; } /** * Validate functions for rail building. * @param roadtype road type to check. * @return true if the current company may build the road. */ bool ValParamRoadType(RoadType roadtype) { return roadtype != INVALID_ROADTYPE && HasRoadTypeAvail(_current_company, roadtype); } /** * Add the road types that are to be introduced at the given date. * @param rt Roadtype * @param current The currently available roadtypes. * @param date The date for the introduction comparisons. * @return The road types that should be available when date * introduced road types are taken into account as well. */ RoadTypes AddDateIntroducedRoadTypes(RoadTypes current, Date date) { RoadTypes rts = current; for (RoadType rt = ROADTYPE_BEGIN; rt != ROADTYPE_END; rt++) { const RoadTypeInfo *rti = GetRoadTypeInfo(rt); /* Unused road type. */ if (rti->label == 0) continue; /* Not date introduced. */ if (!IsInsideMM(rti->introduction_date, 0, MAX_DAY)) continue; /* Not yet introduced at this date. */ if (rti->introduction_date > date) continue; /* Have we introduced all required roadtypes? */ RoadTypes required = rti->introduction_required_roadtypes; if ((rts & required) != required) continue; rts |= rti->introduces_roadtypes; } /* When we added roadtypes we need to run this method again; the added * roadtypes might enable more rail types to become introduced. */ return rts == current ? rts : AddDateIntroducedRoadTypes(rts, date); } /** * Get the road types the given company can build. * @param company the company to get the road types for. * @param introduces If true, include road types introduced by other road types * @return the road types. */ RoadTypes GetCompanyRoadTypes(CompanyID company, bool introduces) { RoadTypes rts = ROADTYPES_NONE; for (const Engine *e : Engine::IterateType(VEH_ROAD)) { const EngineInfo *ei = &e->info; if (HasBit(ei->climates, _settings_game.game_creation.landscape) && (HasBit(e->company_avail, company) || _date >= e->intro_date + DAYS_IN_YEAR)) { const RoadVehicleInfo *rvi = &e->u.road; assert(rvi->roadtype < ROADTYPE_END); if (introduces) { rts |= GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes; } else { SetBit(rts, rvi->roadtype); } } } if (introduces) return AddDateIntroducedRoadTypes(rts, _date); return rts; } /** * Get list of road types, regardless of company availability. * @param introduces If true, include road types introduced by other road types * @return the road types. */ RoadTypes GetRoadTypes(bool introduces) { RoadTypes rts = ROADTYPES_NONE; for (const Engine *e : Engine::IterateType(VEH_ROAD)) { const EngineInfo *ei = &e->info; if (!HasBit(ei->climates, _settings_game.game_creation.landscape)) continue; const RoadVehicleInfo *rvi = &e->u.road; assert(rvi->roadtype < ROADTYPE_END); if (introduces) { rts |= GetRoadTypeInfo(rvi->roadtype)->introduces_roadtypes; } else { SetBit(rts, rvi->roadtype); } } if (introduces) return AddDateIntroducedRoadTypes(rts, MAX_DAY); return rts; } /** * Get the road type for a given label. * @param label the roadtype label. * @param allow_alternate_labels Search in the alternate label lists as well. * @return the roadtype. */ RoadType GetRoadTypeByLabel(RoadTypeLabel label, bool allow_alternate_labels) { /* Loop through each road type until the label is found */ for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) { const RoadTypeInfo *rti = GetRoadTypeInfo(r); if (rti->label == label) return r; } if (allow_alternate_labels) { /* Test if any road type defines the label as an alternate. */ for (RoadType r = ROADTYPE_BEGIN; r != ROADTYPE_END; r++) { const RoadTypeInfo *rti = GetRoadTypeInfo(r); if (std::find(rti->alternate_labels.begin(), rti->alternate_labels.end(), label) != rti->alternate_labels.end()) return r; } } /* No matching label was found, so it is invalid */ return INVALID_ROADTYPE; } /** * Returns the available RoadSubTypes for the provided RoadType * If the given company is valid then will be returned a list of the available sub types at the current date, while passing * a deity company will make all the sub types available * @param rt the RoadType to filter * @param c the company ID to check the roadtype against * @param any_date whether to return only currently introduced roadtypes or also future ones * @returns the existing RoadSubTypes */ RoadTypes ExistingRoadTypes(CompanyID c) { /* Check only players which can actually own vehicles, editor and gamescripts are considered deities */ if (c < OWNER_END) { const Company *company = Company::GetIfValid(c); if (company != nullptr) return company->avail_roadtypes; } RoadTypes known_roadtypes = ROADTYPES_NONE; /* Find used roadtypes */ for (Engine *e : Engine::IterateType(VEH_ROAD)) { /* Check if the roadtype can be used in the current climate */ if (!HasBit(e->info.climates, _settings_game.game_creation.landscape)) continue; /* Check whether available for all potential companies */ if (e->company_avail != (CompanyMask)-1) continue; known_roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes; } /* Get the date introduced roadtypes as well. */ known_roadtypes = AddDateIntroducedRoadTypes(known_roadtypes, MAX_DAY); return known_roadtypes; }