334 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * 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 <http://www.gnu.org/licenses/>.
 | |
|  */
 | |
| 
 | |
| /** @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;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Check whether we can build infrastructure for the given RoadType. This to disable building stations etc. when
 | |
|  * you are not allowed/able to have the RoadType yet.
 | |
|  * @param roadtype the roadtype to check this for
 | |
|  * @param company the company id to check this for
 | |
|  * @param any_date to check only existing vehicles or if it is possible to build them in the future
 | |
|  * @return true if there is any reason why you may build the infrastructure for the given roadtype
 | |
|  */
 | |
| bool CanBuildRoadTypeInfrastructure(RoadType roadtype, CompanyID company)
 | |
| {
 | |
| 	if (_game_mode != GM_EDITOR && !Company::IsValidID(company)) return false;
 | |
| 	if (!_settings_client.gui.disable_unsuitable_building) return true;
 | |
| 	if (!HasAnyRoadTypesAvail(company, GetRoadTramType(roadtype))) return false;
 | |
| 
 | |
| 	RoadTypes roadtypes = ExistingRoadTypes(company);
 | |
| 
 | |
| 	/* Check if the filtered roadtypes does have the roadtype we are checking for
 | |
| 	 * and if we can build new ones */
 | |
| 	if (_settings_game.vehicle.max_roadveh > 0 && HasBit(roadtypes, roadtype)) {
 | |
| 		/* Can we actually build the vehicle type? */
 | |
| 		for (const Engine *e : Engine::IterateType(VEH_ROAD)) {
 | |
| 			if (!HasBit(e->company_avail, company)) continue;
 | |
| 			if (HasPowerOnRoad(e->u.road.roadtype, roadtype) || HasPowerOnRoad(roadtype, e->u.road.roadtype)) return true;
 | |
| 		}
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	/* We should be able to build infrastructure when we have the actual vehicle type */
 | |
| 	for (const Vehicle *v : Vehicle::Iterate()) {
 | |
| 		if (v->type == VEH_ROAD && (company == OWNER_DEITY || v->owner == company) &&
 | |
| 			HasBit(roadtypes, RoadVehicle::From(v)->roadtype) && HasPowerOnRoad(RoadVehicle::From(v)->roadtype, roadtype)) return true;
 | |
| 	}
 | |
| 
 | |
| 	return false;
 | |
| }
 | 
