Files @ r24422:3fa67b2abcd2
Branch filter:

Location: cpp/openttd-patchpack/source/src/road.cpp

Patric Stout
Fix: change the working-dir searchpath when using '-c'

Basically, with '-c' you now create a sandbox. It will still use
your personal-dir and global-dir to find files you installed there,
but all new files are stored with a base folder identical to the
folder the configuration is in.

This is a bit of an old bug, that we many have tried to solve in
various of different ways. The code has grown sufficiently complex
that it is hard to see what consequences of actions are. This is
in my opinion the most harmless solution, while increasing the
usefulness of the '-c' flag.

In essence, the problem was that empty folders were always created
in the directory where the configuration was, but as that directory
wasn't added to any searchpath, files weren't stored there, unless
by accident it was a folder already on the searchpath. For example,
if you do './openttd -c local.cfg', it did work as expected. But
in the more generic variant, it did not.

With this patch, you can run './openttd -c /new/folder/local.cfg',
and it will create and prepare that folder to receive new files.

'content_download' is also stored in the directory the
configuration is in; this was already the case. Important to
note that there is only one search-path for 'content_download'.
In other words, when using '-c', it will not look in '~/.openttd'
inside the 'content_download' folder.
/*
 * 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;
}