mirror of
https://github.com/JGRennison/OpenTTD-patches.git
synced 2024-11-11 13:10:45 +00:00
309 lines
10 KiB
C++
309 lines
10 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 "timer/timer_game_calendar.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) {
|
|
const RoadTypeInfo *rti = GetRoadTypeInfo(roadtype);
|
|
if (rti->label == 0) return false;
|
|
|
|
/* Not yet introduced at this date. */
|
|
if (IsInsideMM(rti->introduction_date, 0, MAX_DATE) && rti->introduction_date > TimerGameCalendar::date) return false;
|
|
|
|
/*
|
|
* Do not allow building hidden road types, except when a town may build it.
|
|
* The GS under deity mode, as well as anybody in the editor builds roads that are
|
|
* owned by towns. So if a town may build it, it should be buildable by them too.
|
|
*/
|
|
return (rti->flags & ROTFB_HIDDEN) == 0 || (rti->flags & ROTFB_TOWN_BUILD) != 0;
|
|
} 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 < ROADTYPE_END && 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, TimerGameCalendar::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_DATE)) 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) || TimerGameCalendar::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, TimerGameCalendar::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_DATE);
|
|
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 != MAX_UVALUE(CompanyMask)) continue;
|
|
|
|
known_roadtypes |= GetRoadTypeInfo(e->u.road.roadtype)->introduces_roadtypes;
|
|
}
|
|
|
|
/* Get the date introduced roadtypes as well. */
|
|
known_roadtypes = AddDateIntroducedRoadTypes(known_roadtypes, MAX_DATE);
|
|
|
|
return known_roadtypes;
|
|
}
|