File diff r26193:4bc7915a2156 → r26194:f7347205838e
src/station_cmd.cpp
Show inline comments
 
@@ -61,12 +61,14 @@
 
#include "rail_cmd.h"
 

	
 
#include "table/strings.h"
 

	
 
#include "safeguards.h"
 

	
 
#include "company_func.h"
 

	
 
/**
 
 * Static instance of FlowStat::SharesMap.
 
 * Note: This instance is created on task start.
 
 *       Lazy creation on first usage results in a data race between the CDist threads.
 
 */
 
/* static */ const FlowStat::SharesMap FlowStat::empty_sharesmap;
 
@@ -235,29 +237,15 @@ static bool FindNearIndustryName(TileInd
 
	/* In all cases if an industry that provides a name is found two of
 
	 * the standard names will be disabled. */
 
	sni->free_names &= ~(1 << M(STR_SV_STNAME_OILFIELD) | 1 << M(STR_SV_STNAME_MINES));
 
	return !sni->indtypes[indtype];
 
}
 

	
 
static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
 
/** Fetch the available station names. Patchpack moves this code from GenerateStationName. Should be easy to see what was moved if you do a diff :) */
 
static void FetchAvailableStationNames(uint32& free_names, bool* indtypes, Station* st, const Town* t)
 
{
 
	static const uint32 _gen_station_name_bits[] = {
 
		0,                                       // STATIONNAMING_RAIL
 
		0,                                       // STATIONNAMING_ROAD
 
		1U << M(STR_SV_STNAME_AIRPORT),          // STATIONNAMING_AIRPORT
 
		1U << M(STR_SV_STNAME_OILFIELD),         // STATIONNAMING_OILRIG
 
		1U << M(STR_SV_STNAME_DOCKS),            // STATIONNAMING_DOCK
 
		1U << M(STR_SV_STNAME_HELIPORT),         // STATIONNAMING_HELIPORT
 
	};
 

	
 
	const Town *t = st->town;
 
	uint32 free_names = UINT32_MAX;
 

	
 
	bool indtypes[NUM_INDUSTRYTYPES];
 
	memset(indtypes, 0, sizeof(indtypes));
 

	
 
	for (const Station *s : Station::Iterate()) {
 
		if (s != st && s->town == t) {
 
			if (s->indtype != IT_INVALID) {
 
				indtypes[s->indtype] = true;
 
				StringID name = GetIndustrySpec(s->indtype)->station_name;
 
				if (name != STR_UNDEFINED) {
 
@@ -275,21 +263,71 @@ static StringID GenerateStationName(Stat
 
					str = M(STR_SV_STNAME_WOODS);
 
				}
 
				ClrBit(free_names, str);
 
			}
 
		}
 
	}
 
}
 

	
 
/** Callback proc for CircularTileSearch */
 
static bool FindNearPatchpackIndustryName(TileIndex tile, void *user_data)
 
{
 
	/* Is this an industry tile? If not, skip it */
 
	if (!IsTileType(tile, MP_INDUSTRY)) return false;
 

	
 
	uint32 free_names = UINT32_MAX;
 
	bool indtypes[NUM_INDUSTRYTYPES];
 
	memset(indtypes, 0, sizeof(indtypes));
 
	FetchAvailableStationNames(free_names, indtypes, (Station*)user_data, Industry::GetByTile(tile)->town);
 

	
 
	IndustryType indtype = GetIndustryType(tile);
 

	
 
	return !indtypes[indtype];
 
}
 

	
 
/** Generates station names */
 
static StringID GenerateStationName(Station *st, TileIndex tile, StationNaming name_class)
 
{
 
	if (_settings_game.construction.name_stations_based_on_industries == 2)
 
	{
 
		/** I love having this as an in-game patch setting. DEM SETTINGS */
 
		TileIndex indtile = tile;
 
		if (CircularTileSearch(&indtile, 7, FindNearPatchpackIndustryName, (void*)st)) {
 
			IndustryType indtype = GetIndustryType(indtile);
 
			const IndustrySpec *indsp = GetIndustrySpec(indtype);
 
			st->indtype = indtype;
 
			st->town = Industry::GetByTile(indtile)->town;
 
			return STR_SV_STNAME_FALLBACK;
 
		}
 
	}
 

	
 
	static const uint32 _gen_station_name_bits[] = {
 
		0,                                       // STATIONNAMING_RAIL
 
		0,                                       // STATIONNAMING_ROAD
 
		1U << M(STR_SV_STNAME_AIRPORT),          // STATIONNAMING_AIRPORT
 
		1U << M(STR_SV_STNAME_OILFIELD),         // STATIONNAMING_OILRIG
 
		1U << M(STR_SV_STNAME_DOCKS),            // STATIONNAMING_DOCK
 
		1U << M(STR_SV_STNAME_HELIPORT),         // STATIONNAMING_HELIPORT
 
	};
 

	
 
	const Town *t = st->town;
 

	
 
	/* Fetch a list of available station names so we know which ones have already been used and are unavailable (including industry newGRF names) */
 
	uint32 free_names = UINT32_MAX;
 
	bool indtypes[NUM_INDUSTRYTYPES];
 
	memset(indtypes, 0, sizeof(indtypes));
 
	FetchAvailableStationNames(free_names, indtypes, st, t);
 

	
 
	TileIndex indtile = tile;
 
	StationNameInformation sni = { free_names, indtypes };
 
	if (CircularTileSearch(&indtile, 7, FindNearIndustryName, &sni)) {
 
		/* An industry has been found nearby */
 
		IndustryType indtype = GetIndustryType(indtile);
 
		const IndustrySpec *indsp = GetIndustrySpec(indtype);
 
		/* STR_NULL means it only disables oil rig/mines */
 
		if (indsp->station_name != STR_NULL) {
 
		if (indsp->station_name != STR_NULL && (_settings_game.construction.name_stations_based_on_industries == 1)) {
 
			st->indtype = indtype;
 
			return STR_SV_STNAME_FALLBACK;
 
		}
 
	}
 

	
 
	/* Oil rigs/mines name could be marked not free by looking for a near by industry. */
 
@@ -840,13 +878,13 @@ CommandCost CheckBuildableTile(TileIndex
 
 * @param tile_iter Airport tile iterator.
 
 * @param flags Operation to perform.
 
 * @return The cost in case of success, or an error code if it failed.
 
 */
 
static CommandCost CheckFlatLandAirport(AirportTileTableIterator tile_iter, DoCommandFlag flags)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_AIRCRAFT_CON);
 
	int allowed_z = -1;
 

	
 
	for (; tile_iter != INVALID_TILE; ++tile_iter) {
 
		CommandCost ret = CheckBuildableTile(tile_iter, 0, allowed_z, true);
 
		if (ret.Failed()) return ret;
 
		cost.AddCost(ret);
 
@@ -872,13 +910,13 @@ static CommandCost CheckFlatLandAirport(
 
 * @param plat_len Platform length.
 
 * @param numtracks Number of platforms.
 
 * @return The cost in case of success, or an error code if it failed.
 
 */
 
static CommandCost CheckFlatLandRailStation(TileArea tile_area, DoCommandFlag flags, Axis axis, StationID *station, RailType rt, std::vector<Train *> &affected_vehicles, StationClassID spec_class, byte spec_index, byte plat_len, byte numtracks)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_TRAIN_CON);
 
	int allowed_z = -1;
 
	uint invalid_dirs = 5 << axis;
 

	
 
	const StationSpec *statspec = StationClass::Get(spec_class)->GetSpec(spec_index);
 
	bool slope_cb = statspec != nullptr && HasBit(statspec->callback_mask, CBM_STATION_SLOPE_CHECK);
 

	
 
@@ -958,13 +996,13 @@ static CommandCost CheckFlatLandRailStat
 
 * @param station StationID to be queried and returned if available.
 
 * @param rt Road type to build.
 
 * @return The cost in case of success, or an error code if it failed.
 
 */
 
static CommandCost CheckFlatLandRoadStop(TileArea tile_area, DoCommandFlag flags, uint invalid_dirs, bool is_drive_through, bool is_truck_stop, Axis axis, StationID *station, RoadType rt)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_ROAD_CON);
 
	int allowed_z = -1;
 

	
 
	for (TileIndex cur_tile : tile_area) {
 
		CommandCost ret = CheckBuildableTile(cur_tile, invalid_dirs, allowed_z, !is_drive_through);
 
		if (ret.Failed()) return ret;
 
		cost.AddCost(ret);
 
@@ -1549,13 +1587,13 @@ static void MakeShipStationAreaSmaller(S
 
 */
 
template <class T>
 
CommandCost RemoveFromRailBaseStation(TileArea ta, std::vector<T *> &affected_stations, DoCommandFlag flags, Money removal_cost, bool keep_rail)
 
{
 
	/* Count of the number of tiles removed */
 
	int quantity = 0;
 
	CommandCost total_cost(EXPENSES_CONSTRUCTION);
 
	CommandCost total_cost(EXPENSES_T_TRAIN_CON);
 
	/* Accumulator for the errors seen during clearing. If no errors happen,
 
	 * and the quantity is 0 there is no station. Otherwise it will be one
 
	 * of the other error that got accumulated. */
 
	CommandCost error;
 

	
 
	/* Do the action for every tile into the area */
 
@@ -1718,13 +1756,13 @@ CommandCost RemoveRailStation(T *st, DoC
 

	
 
	/* determine width and height of platforms */
 
	TileArea ta = st->train_station;
 

	
 
	assert(ta.w != 0 && ta.h != 0);
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_TRAIN_CON);
 
	/* clear all areas of the station */
 
	for (TileIndex tile : ta) {
 
		/* only remove tiles that are actually train station tiles */
 
		if (st->TileBelongsToRailStation(tile)) {
 
			std::vector<T*> affected_stations; // dummy
 
			CommandCost ret = RemoveFromRailBaseStation(TileArea(tile, 1, 1), affected_stations, flags, removal_cost, false);
 
@@ -1850,13 +1888,13 @@ CommandCost CmdBuildRoadStop(DoCommandFl
 
	CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
 
	if (ret.Failed()) return ret;
 

	
 
	bool is_truck_stop = stop_type != ROADSTOP_BUS;
 

	
 
	/* Total road stop cost. */
 
	CommandCost cost(EXPENSES_CONSTRUCTION, roadstop_area.w * roadstop_area.h * _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
 
	CommandCost cost(EXPENSES_T_ROAD_CON, roadstop_area.w * roadstop_area.h * _price[is_truck_stop ? PR_BUILD_STATION_TRUCK : PR_BUILD_STATION_BUS]);
 
	StationID est = INVALID_STATION;
 
	ret = CheckFlatLandRoadStop(roadstop_area, flags, is_drive_through ? 5 << axis : 1 << ddir, is_drive_through, is_truck_stop, axis, &est, rt);
 
	if (ret.Failed()) return ret;
 
	cost.AddCost(ret);
 

	
 
	Station *st = nullptr;
 
@@ -2042,13 +2080,13 @@ static CommandCost RemoveRoadStop(TileIn
 
		} else {
 
			st->bus_station.Clear();
 
			for (const RoadStop *rs = st->bus_stops; rs != nullptr; rs = rs->next) st->bus_station.Add(rs->xy);
 
		}
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]);
 
	return CommandCost(EXPENSES_T_ROAD_CON, _price[is_truck ? PR_CLEAR_STATION_TRUCK : PR_CLEAR_STATION_BUS]);
 
}
 

	
 
/**
 
 * Remove bus or truck stops.
 
 * @param flags Operation to perform.
 
 * @param tile Northernmost tile of the removal area.
 
@@ -2067,13 +2105,13 @@ CommandCost CmdRemoveRoadStop(DoCommandF
 
	if (!IsValidTile(tile) || TileAddWrap(tile, width - 1, height - 1) == INVALID_TILE) return CMD_ERROR;
 
	/* Bankrupting company is not supposed to remove roads, there may be road vehicles. */
 
	if (remove_road && (flags & DC_BANKRUPT)) return CMD_ERROR;
 

	
 
	TileArea roadstop_area(tile, width, height);
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_ROAD_CON);
 
	CommandCost last_error(STR_ERROR_THERE_IS_NO_STATION);
 
	bool had_success = false;
 

	
 
	for (TileIndex cur_tile : roadstop_area) {
 
		/* Make sure the specified tile is a road stop of the correct type */
 
		if (!IsTileType(cur_tile, MP_STATION) || !IsRoadStop(cur_tile) || GetRoadStopType(cur_tile) != stop_type) continue;
 
@@ -2347,13 +2385,13 @@ static CommandCost RemoveAirport(TileInd
 
		CommandCost ret = CheckOwnership(st->owner);
 
		if (ret.Failed()) return ret;
 
	}
 

	
 
	tile = st->airport.tile;
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost cost(EXPENSES_T_AIRCRAFT_CON);
 

	
 
	for (const Aircraft *a : Aircraft::Iterate()) {
 
		if (!a->IsNormalAircraft()) continue;
 
		if (a->targetairport == st->index && a->state != FLYING) {
 
			return_cmd_error(STR_ERROR_AIRCRAFT_IN_THE_WAY);
 
		}
 
@@ -2493,13 +2531,13 @@ CommandCost CmdBuildDock(DoCommandFlag f
 

	
 
	CommandCost ret = CheckIfAuthorityAllowsNewStation(tile, flags);
 
	if (ret.Failed()) return ret;
 

	
 
	if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_STATION_DOCK]);
 
	CommandCost cost(EXPENSES_T_SHIP_CON, _price[PR_BUILD_STATION_DOCK]);
 
	ret = Command<CMD_LANDSCAPE_CLEAR>::Do(flags, tile);
 
	if (ret.Failed()) return ret;
 
	cost.AddCost(ret);
 

	
 
	TileIndex tile_cur = tile + TileOffsByDiagDir(direction);
 

	
 
@@ -2693,13 +2731,13 @@ static CommandCost RemoveDock(TileIndex 
 
			if (s->current_order.IsType(OT_GOTO_STATION) && !(st->facilities & FACIL_DOCK)) {
 
				s->SetDestTile(s->GetOrderStationLocation(st->index));
 
			}
 
		}
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_STATION_DOCK]);
 
	return CommandCost(EXPENSES_T_SHIP_CON, _price[PR_CLEAR_STATION_DOCK]);
 
}
 

	
 
#include "table/station_land.h"
 

	
 
const DrawTileSprites *GetStationTileLayout(StationType st, byte gfx)
 
{
 
@@ -2991,13 +3029,12 @@ draw_default_foundation:
 
				DrawGroundSprite(GetRailStationAxis(ti->tile) == AXIS_X ? rti->base_sprites.single_x : rti->base_sprites.single_y, PALETTE_CRASH);
 
			}
 
		}
 
	}
 

	
 
	if (HasStationRail(ti->tile) && HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
 

	
 
	if (IsRoadStop(ti->tile)) {
 
		RoadType road_rt = GetRoadTypeRoad(ti->tile);
 
		RoadType tram_rt = GetRoadTypeTram(ti->tile);
 
		const RoadTypeInfo* road_rti = road_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(road_rt);
 
		const RoadTypeInfo* tram_rti = tram_rt == INVALID_ROADTYPE ? nullptr : GetRoadTypeInfo(tram_rt);
 

	
 
@@ -3562,12 +3599,70 @@ static void UpdateStationRating(Station 
 
					ge->max_waiting_cargo = waiting_avg;
 
				}
 
			}
 
		}
 
	}
 

	
 
	// station dilapidation
 
	{
 
		if (_settings_game.economy.dilapidation_max_amount > 0) {
 
			bool dilapidation_calc = false;
 
			uint16 dilapidation_pop = 0;
 
			uint pop_count = st->goods[CT_PASSENGERS].cargo.AvailableCount();
 
			if (Company::IsValidID(st->owner)) {
 
				if ((st->facilities & FACIL_TRAIN) != 0) {
 
					dilapidation_calc = true;
 
					if (_settings_game.economy.dilapidation_pop_rail > dilapidation_pop) {
 
						dilapidation_pop = _settings_game.economy.dilapidation_pop_rail;
 
					}
 
				}
 
				if (((st->facilities & FACIL_BUS_STOP) != 0) || ((st->facilities & FACIL_TRUCK_STOP) != 0)) {
 
					dilapidation_calc = true;
 
					if (_settings_game.economy.dilapidation_pop_road > dilapidation_pop) {
 
						dilapidation_pop = _settings_game.economy.dilapidation_pop_road;
 
					}
 
				}
 
				if ((st->facilities & FACIL_DOCK) != 0) {
 
					dilapidation_calc = true;
 
					if (_settings_game.economy.dilapidation_pop_water > dilapidation_pop) {
 
						dilapidation_pop = _settings_game.economy.dilapidation_pop_water;
 
					}
 
				}
 
				if ((st->facilities & FACIL_AIRPORT) != 0) {
 
					dilapidation_calc = true;
 
					if (_settings_game.economy.dilapidation_pop_air > dilapidation_pop) {
 
						dilapidation_pop = _settings_game.economy.dilapidation_pop_air;
 
					}
 
				}
 
			}
 
			if (dilapidation_calc && pop_count > (uint)dilapidation_pop) {
 
				// increase the dilapidation, preventing overflow
 
				uint16 top_boundary = 65535 - _settings_game.economy.dilapidation_increase;
 
				if (st->dilapidation >= top_boundary) {
 
					st->dilapidation = 65535;
 
				}
 
				else {
 
					st->dilapidation += _settings_game.economy.dilapidation_increase;
 
				}
 
			}
 
			else
 
			{
 
				// decrease the dilapidation, preventing underflow
 
				if (st->dilapidation <= _settings_game.economy.dilapidation_decrease) {
 
					st->dilapidation = 0;
 
				}
 
				else {
 
					st->dilapidation -= _settings_game.economy.dilapidation_decrease;
 
				}
 
			}
 
		}
 
		if (st->dilapidation > _settings_game.economy.dilapidation_max_amount) {
 
			st->dilapidation = _settings_game.economy.dilapidation_max_amount;
 
		}
 
	}
 

	
 
	StationID index = st->index;
 
	if (waiting_changed) {
 
		SetWindowDirty(WC_STATION_VIEW, index); // update whole window
 
	} else {
 
		SetWindowWidgetDirty(WC_STATION_VIEW, index, WID_SV_ACCEPT_RATING_LIST); // update only ratings list
 
	}
 
@@ -3800,21 +3895,56 @@ void OnTick_Station()
 
			TriggerStationAnimation(st, st->xy, SAT_250_TICKS);
 
			if (Station::IsExpected(st)) AirportAnimationTrigger(Station::From(st), AAT_STATION_250_TICKS);
 
		}
 
	}
 
}
 

	
 
/** Charges player for dilapidation at station */
 
void ChargePlayerForDilapidation(Station *st)
 
{
 
	if (st->dilapidation > 0 && Company::IsValidID(st->owner)) {
 
		uint32 dilapidation_fine = 0;
 
		if ((st->facilities & FACIL_TRAIN) != 0) {
 
			if (_settings_game.economy.dilapidation_fine_rail > dilapidation_fine) {
 
				dilapidation_fine = _settings_game.economy.dilapidation_fine_rail;
 
			}
 
		}
 
		if (((st->facilities & FACIL_BUS_STOP) != 0) || ((st->facilities & FACIL_TRUCK_STOP) != 0)) {
 
			if (_settings_game.economy.dilapidation_fine_road > dilapidation_fine) {
 
				dilapidation_fine = _settings_game.economy.dilapidation_fine_road;
 
			}
 
		}
 
		if ((st->facilities & FACIL_DOCK) != 0) {
 
			if (_settings_game.economy.dilapidation_fine_water > dilapidation_fine) {
 
				dilapidation_fine = _settings_game.economy.dilapidation_fine_water;
 
			}
 
		}
 
		if ((st->facilities & FACIL_AIRPORT) != 0) {
 
			if (_settings_game.economy.dilapidation_fine_air > dilapidation_fine) {
 
				dilapidation_fine = _settings_game.economy.dilapidation_fine_air;
 
			}
 
		}
 
		Money actual_fine = dilapidation_fine;
 
		actual_fine *= st->dilapidation;
 
		actual_fine *= (_economy.inflation_prices >> 16);
 
		if (actual_fine > 0) {
 
			SubtractMoneyFromCompanyFract(st->owner, CommandCost(EXPENSES_T_DILAPIDATION, actual_fine << 8));
 
		}
 
	}
 
}
 

	
 
/** Monthly loop for stations. */
 
void StationMonthlyLoop()
 
{
 
	for (Station *st : Station::Iterate()) {
 
		for (CargoID i = 0; i < NUM_CARGO; i++) {
 
			GoodsEntry *ge = &st->goods[i];
 
			SB(ge->status, GoodsEntry::GES_LAST_MONTH, 1, GB(ge->status, GoodsEntry::GES_CURRENT_MONTH, 1));
 
			ClrBit(ge->status, GoodsEntry::GES_CURRENT_MONTH);
 
		}
 
		ChargePlayerForDilapidation(st);
 
	}
 
}
 

	
 

	
 
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius)
 
{
 
@@ -4299,26 +4429,26 @@ static CommandCost TerraformTile_Station
 
			switch (GetStationType(tile)) {
 
				case STATION_WAYPOINT:
 
				case STATION_RAIL: {
 
					DiagDirection direction = AxisToDiagDir(GetRailStationAxis(tile));
 
					if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
 
					if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
 
					return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
					return CommandCost(EXPENSES_T_TRAIN_CON, _price[PR_BUILD_FOUNDATION]);
 
				}
 

	
 
				case STATION_AIRPORT:
 
					return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
					return CommandCost(EXPENSES_T_AIRCRAFT_CON, _price[PR_BUILD_FOUNDATION]);
 

	
 
				case STATION_TRUCK:
 
				case STATION_BUS: {
 
					DiagDirection direction = GetRoadStopDir(tile);
 
					if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, direction)) break;
 
					if (IsDriveThroughStopTile(tile)) {
 
						if (!AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, ReverseDiagDir(direction))) break;
 
					}
 
					return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
 
					return CommandCost(EXPENSES_T_ROAD_CON, _price[PR_BUILD_FOUNDATION]);
 
				}
 

	
 
				default: break;
 
			}
 
		}
 
	}