Changeset - r23431:60a46e3b7cd7
[Not reviewed]
master
0 13 0
Peter Nelson - 6 years ago 2019-02-14 21:07:15
peter1138@openttd.org
Feature: Non-rectangular sparse station catchment area.
13 files changed with 290 insertions and 152 deletions:
0 comments (0 inline, 0 general)
src/economy.cpp
Show inline comments
 
@@ -1049,11 +1049,6 @@ static uint DeliverGoodsToIndustry(const
 

	
 
		if (ind->index == source) continue;
 

	
 
		if (!_settings_game.station.serve_neutral_industries) {
 
			/* If this industry is only served by its neutral station, check it's us. */
 
			if (ind->neutral_station != NULL && ind->neutral_station != st) continue;
 
		}
 

	
 
		uint cargo_index;
 
		for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
 
			if (cargo_type == ind->accepts_cargo[cargo_index]) break;
src/industry.h
Show inline comments
 
@@ -63,6 +63,7 @@ struct Industry : IndustryPool::PoolItem
 
	byte was_cargo_delivered;           ///< flag that indicate this has been the closest industry chosen for cargo delivery by a station. see DeliverGoodsToIndustry
 

	
 
	PartOfSubsidyByte part_of_subsidy;  ///< NOSAVE: is this industry a source/destination of a subsidy?
 
	StationList stations_near;          ///< NOSAVE: List of nearby stations.
 

	
 
	OwnerByte founder;                  ///< Founder of the industry
 
	Date construction_date;             ///< Date of the construction of the industry
src/industry_cmd.cpp
Show inline comments
 
@@ -182,6 +182,10 @@ Industry::~Industry()
 

	
 
	DeleteSubsidyWith(ST_INDUSTRY, this->index);
 
	CargoPacket::InvalidateAllFrom(ST_INDUSTRY, this->index);
 

	
 
	for (Station *st : this->stations_near) {
 
		st->industries_near.erase(this);
 
	}
 
}
 

	
 
/**
 
@@ -191,7 +195,6 @@ Industry::~Industry()
 
void Industry::PostDestructor(size_t index)
 
{
 
	InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
 
	Station::RecomputeIndustriesNearForAll();
 
}
 

	
 

	
 
@@ -516,14 +519,6 @@ static bool TransportIndustryGoods(TileI
 
	const IndustrySpec *indspec = GetIndustrySpec(i->type);
 
	bool moved_cargo = false;
 

	
 
	StationFinder stations(i->location);
 
	StationList neutral;
 

	
 
	if (i->neutral_station != NULL && !_settings_game.station.serve_neutral_industries) {
 
		/* Industry has a neutral station. Use it and ignore any other nearby stations. */
 
		neutral.insert(i->neutral_station);
 
	}
 

	
 
	for (uint j = 0; j < lengthof(i->produced_cargo_waiting); j++) {
 
		uint cw = min(i->produced_cargo_waiting[j], 255);
 
		if (cw > indspec->minimal_cargo && i->produced_cargo[j] != CT_INVALID) {
 
@@ -534,7 +529,7 @@ static bool TransportIndustryGoods(TileI
 

	
 
			i->this_month_production[j] += cw;
 

	
 
			uint am = MoveGoodsToStation(i->produced_cargo[j], cw, ST_INDUSTRY, i->index, neutral.size() != 0 ? &neutral : stations.GetStations());
 
			uint am = MoveGoodsToStation(i->produced_cargo[j], cw, ST_INDUSTRY, i->index, &i->stations_near);
 
			i->this_month_transported[j] += am;
 

	
 
			moved_cargo |= (am != 0);
 
@@ -1651,6 +1646,37 @@ static void AdvertiseIndustryOpening(con
 
}
 

	
 
/**
 
 * Populate an industry's list of nearby stations, and if it accepts any cargo, also
 
 * add the industry to each station's nearby industry list.
 
 * @param ind Industry
 
 */
 
static void PopulateStationsNearby(Industry *ind)
 
{
 
	if (ind->neutral_station != NULL && !_settings_game.station.serve_neutral_industries) {
 
		/* Industry has a neutral station. Use it and ignore any other nearby stations. */
 
		ind->stations_near.insert(ind->neutral_station);
 
		ind->neutral_station->industries_near.clear();
 
		ind->neutral_station->industries_near.insert(ind);
 
		return;
 
	}
 

	
 
	/* Get our list of nearby stations. */
 
	FindStationsAroundTiles(ind->location, &ind->stations_near, false);
 

	
 
	/* Test if industry can accept cargo */
 
	uint cargo_index;
 
	for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
 
		if (ind->accepts_cargo[cargo_index] != CT_INVALID) break;
 
	}
 
	if (cargo_index >= lengthof(ind->accepts_cargo)) return;
 

	
 
	/* Cargo is accepted, add industry to nearby stations nearby industry list. */
 
	for (Station *st : ind->stations_near) {
 
		st->industries_near.insert(ind);
 
	}
 
}
 

	
 
/**
 
 * Put an industry on the map.
 
 * @param i       Just allocated poolitem, mostly empty.
 
 * @param tile    North tile of the industry.
 
@@ -1823,7 +1849,7 @@ static void DoCreateNewIndustry(Industry
 
	}
 
	InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
 

	
 
	Station::RecomputeIndustriesNearForAll();
 
	if (!_generating_world) PopulateStationsNearby(i);
 
}
 

	
 
/**
 
@@ -2428,11 +2454,7 @@ static void CanCargoServiceIndustry(Carg
 
 */
 
static int WhoCanServiceIndustry(Industry *ind)
 
{
 
	/* Find all stations within reach of the industry */
 
	StationList stations;
 
	FindStationsAroundTiles(ind->location, &stations);
 

	
 
	if (stations.size() == 0) return 0; // No stations found at all => nobody services
 
	if (ind->stations_near.size() == 0) return 0; // No stations found at all => nobody services
 

	
 
	const Vehicle *v;
 
	int result = 0;
 
@@ -2468,7 +2490,7 @@ static int WhoCanServiceIndustry(Industr
 
				/* Same cargo produced by industry is dropped here => not serviced by vehicle v */
 
				if ((o->GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
 

	
 
				if (stations.find(st) != stations.end()) {
 
				if (ind->stations_near.find(st) != ind->stations_near.end()) {
 
					if (v->owner == _local_company) return 2; // Company services industry
 
					result = 1; // Competitor services industry
 
				}
src/saveload/afterload.cpp
Show inline comments
 
@@ -285,7 +285,6 @@ static void InitializeWindowsAndCaches()
 

	
 
	GroupStatistics::UpdateAfterLoad();
 

	
 
	Station::RecomputeIndustriesNearForAll();
 
	RebuildSubsidisedSourceAndDestinationCache();
 

	
 
	/* Towns have a noise controlled number of airports system
 
@@ -3104,6 +3103,9 @@ bool AfterLoadGame()
 
		FOR_ALL_INDUSTRIES(ind) if (ind->neutral_station != NULL) ind->neutral_station->industry = ind;
 
	}
 

	
 
	/* Compute station catchment areas. This is needed here in case UpdateStationAcceptance is called below. */
 
	Station::RecomputeCatchmentForAll();
 

	
 
	/* Station acceptance is some kind of cache */
 
	if (IsSavegameVersionBefore(SLV_127)) {
 
		Station *st;
src/script/api/script_industry.cpp
Show inline comments
 
@@ -132,9 +132,7 @@
 
	if (!IsValidIndustry(industry_id)) return -1;
 

	
 
	Industry *ind = ::Industry::Get(industry_id);
 
	StationList stations;
 
	::FindStationsAroundTiles(ind->location, &stations);
 
	return (int32)stations.size();
 
	return (int32)ind->stations_near.size();
 
}
 

	
 
/* static */ int32 ScriptIndustry::GetDistanceManhattanToTile(IndustryID industry_id, TileIndex tile)
src/settings.cpp
Show inline comments
 
@@ -1302,7 +1302,7 @@ static bool ChangeMaxHeightLevel(int32 p
 

	
 
static bool StationCatchmentChanged(int32 p1)
 
{
 
	Station::RecomputeIndustriesNearForAll();
 
	Station::RecomputeCatchmentForAll();
 
	return true;
 
}
 

	
src/station.cpp
Show inline comments
 
@@ -23,6 +23,7 @@
 
#include "station_base.h"
 
#include "roadstop_base.h"
 
#include "industry.h"
 
#include "town.h"
 
#include "core/random_func.hpp"
 
#include "linkgraph/linkgraph.h"
 
#include "linkgraph/linkgraphschedule.h"
 
@@ -119,6 +120,9 @@ Station::~Station()
 
		}
 
	}
 

	
 
	/* Remove station from industries and towns that reference it. */
 
	this->RemoveFromAllNearbyLists();
 

	
 
	/* Clear the persistent storage. */
 
	delete this->airport.psa;
 

	
 
@@ -262,6 +266,39 @@ void Station::MarkTilesDirty(bool cargo_
 
}
 

	
 
/**
 
 * Get the catchment size of an individual station tile.
 
 * @param tile Station tile to get catchment size of.
 
 * @param st Associated station of station tile.
 
 * @pre IsTileType(tile, MP_STATION)
 
 * @return The catchment size of the station tile.
 
 */
 
static uint GetTileCatchmentRadius(TileIndex tile, const Station *st)
 
{
 
	assert(IsTileType(tile, MP_STATION));
 

	
 
	if (_settings_game.station.modified_catchment) {
 
		switch (GetStationType(tile)) {
 
			case STATION_RAIL:    return CA_TRAIN;
 
			case STATION_OILRIG:  return CA_UNMODIFIED;
 
			case STATION_AIRPORT: return st->airport.GetSpec()->catchment;
 
			case STATION_TRUCK:   return CA_TRUCK;
 
			case STATION_BUS:     return CA_BUS;
 
			case STATION_DOCK:    return CA_DOCK;
 

	
 
			default: NOT_REACHED();
 
			case STATION_BUOY:
 
			case STATION_WAYPOINT: return CA_NONE;
 
		}
 
	} else {
 
		switch (GetStationType(tile)) {
 
			default:               return CA_UNMODIFIED;
 
			case STATION_BUOY:
 
			case STATION_WAYPOINT: return CA_NONE;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Determines the catchment radius of the station
 
 * @return The radius
 
 */
 
@@ -305,85 +342,127 @@ Rect Station::GetCatchmentRect() const
 
	return ret;
 
}
 

	
 
/** Rect and pointer to IndustryVector */
 
struct RectAndIndustryVector {
 
	Rect rect;                     ///< The rectangle to search the industries in.
 
	IndustryList *industries_near; ///< The nearby industries.
 
};
 

	
 
/**
 
 * Callback function for Station::RecomputeIndustriesNear()
 
 * Tests whether tile is an industry and possibly adds
 
 * the industry to station's industries_near list.
 
 * @param ind_tile tile to check
 
 * @param user_data pointer to RectAndIndustryVector
 
 * @return always false, we want to search all tiles
 
 * Add nearby industry to station's industries_near list if it accepts cargo.
 
 * @param ind Industry
 
 * @param st Station
 
 */
 
static bool FindIndustryToDeliver(TileIndex ind_tile, void *user_data)
 
static void AddIndustryToDeliver(Industry *ind, Station *st)
 
{
 
	/* Only process industry tiles */
 
	if (!IsTileType(ind_tile, MP_INDUSTRY)) return false;
 

	
 
	RectAndIndustryVector *riv = (RectAndIndustryVector *)user_data;
 
	Industry *ind = Industry::GetByTile(ind_tile);
 

	
 
	/* Don't check further if this industry is already in the list */
 
	if (riv->industries_near->find(ind) != riv->industries_near->end()) return false;
 

	
 
	/* Only process tiles in the station acceptance rectangle */
 
	int x = TileX(ind_tile);
 
	int y = TileY(ind_tile);
 
	if (x < riv->rect.left || x > riv->rect.right || y < riv->rect.top || y > riv->rect.bottom) return false;
 
	if (st->industries_near.find(ind) != st->industries_near.end()) return;
 

	
 
	/* Include only industries that can accept cargo */
 
	uint cargo_index;
 
	for (cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
 
		if (ind->accepts_cargo[cargo_index] != CT_INVALID) break;
 
	}
 
	if (cargo_index >= lengthof(ind->accepts_cargo)) return false;
 
	if (cargo_index >= lengthof(ind->accepts_cargo)) return;
 

	
 
	st->industries_near.insert(ind);
 
}
 

	
 
	riv->industries_near->insert(ind);
 
/**
 
 * Remove this station from the nearby stations lists of all towns and industries.
 
 */
 
void Station::RemoveFromAllNearbyLists()
 
{
 
	Town *t;
 
	FOR_ALL_TOWNS(t) { t->stations_near.erase(this); }
 
	Industry *i;
 
	FOR_ALL_INDUSTRIES(i) { i->stations_near.erase(this); }
 
}
 

	
 
/**
 
 * Test if the given town ID is covered by our catchment area.
 
 * This is used when removing a house tile to determine if it was the last house tile
 
 * within our catchment.
 
 * @param t TownID to test.
 
 * @return true if at least one house tile of TownID is covered.
 
 */
 
bool Station::CatchmentCoversTown(TownID t) const
 
{
 
	BitmapTileIterator it(this->catchment_tiles);
 
	for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
 
		if (IsTileType(tile, MP_HOUSE) && GetTownIndex(tile) == t) return true;
 
	}
 
	return false;
 
}
 

	
 
/**
 
 * Recomputes Station::industries_near, list of industries possibly
 
 * accepting cargo in station's catchment radius
 
 * Recompute tiles covered in our catchment area.
 
 * This will additionally recompute nearby towns and industries.
 
 */
 
void Station::RecomputeIndustriesNear()
 
void Station::RecomputeCatchment()
 
{
 
	this->industries_near.clear();
 
	if (this->rect.IsEmpty()) return;
 
	this->RemoveFromAllNearbyLists();
 

	
 
	if (this->rect.IsEmpty()) {
 
		this->catchment_tiles.Reset();
 
		return;
 
	}
 
	this->catchment_tiles.Initialize(GetCatchmentRect());
 

	
 
	if (!_settings_game.station.serve_neutral_industries && this->industry != NULL) {
 
		/* Station is associated with an industry, so we only need to deliver to that industry. */
 
		TILE_AREA_LOOP(tile, this->industry->location) {
 
			if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == this->industry->index) {
 
				this->catchment_tiles.SetTile(tile);
 
			}
 
		}
 
		/* The industry's stations_near may have been computed before its neutral station was built so clear and re-add here. */
 
		for (Station *st : this->industry->stations_near) {
 
			st->industries_near.erase(this->industry);
 
		}
 
		this->industry->stations_near.clear();
 
		this->industry->stations_near.insert(this);
 
		this->industries_near.insert(this->industry);
 
		return;
 
	}
 

	
 
	RectAndIndustryVector riv = {
 
		this->GetCatchmentRect(),
 
		&this->industries_near
 
	};
 
	/* Loop finding all station tiles */
 
	TileArea ta(TileXY(this->rect.left, this->rect.top), TileXY(this->rect.right, this->rect.bottom));
 
	TILE_AREA_LOOP(tile, ta) {
 
		if (!IsTileType(tile, MP_STATION) || GetStationIndex(tile) != this->index) continue;
 

	
 
		uint r = GetTileCatchmentRadius(tile, this);
 
		if (r == CA_NONE) continue;
 

	
 
		/* This tile sub-loop doesn't need to test any tiles, they are simply added to the catchment set. */
 
		TileArea ta2(TileXY(max<int>(TileX(tile) - r, 0), max<int>(TileY(tile) - r, 0)), TileXY(min<int>(TileX(tile) + r, MapMaxX()), min<int>(TileY(tile) + r, MapMaxY())));
 
		TILE_AREA_LOOP(tile2, ta2) this->catchment_tiles.SetTile(tile2);
 
	}
 

	
 
	/* Compute maximum extent of acceptance rectangle wrt. station sign */
 
	TileIndex start_tile = this->xy;
 
	uint max_radius = max(
 
		max(DistanceManhattan(start_tile, TileXY(riv.rect.left,  riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.left,  riv.rect.bottom))),
 
		max(DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.top)), DistanceManhattan(start_tile, TileXY(riv.rect.right, riv.rect.bottom)))
 
	);
 
	/* Search catchment tiles for towns and industries */
 
	BitmapTileIterator it(this->catchment_tiles);
 
	for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
 
		if (IsTileType(tile, MP_HOUSE)) {
 
			Town *t = Town::GetByTile(tile);
 
			t->stations_near.insert(this);
 
		}
 
		if (IsTileType(tile, MP_INDUSTRY)) {
 
			Industry *i = Industry::GetByTile(tile);
 

	
 
	CircularTileSearch(&start_tile, 2 * max_radius + 1, &FindIndustryToDeliver, &riv);
 
			/* Ignore industry if it has a neutral station. It already can't be this station. */
 
			if (!_settings_game.station.serve_neutral_industries && i->neutral_station != NULL) continue;
 

	
 
			i->stations_near.insert(this);
 

	
 
			/* Add if we can deliver to this industry as well */
 
			AddIndustryToDeliver(i, this);
 
		}
 
	}
 
}
 

	
 
/**
 
 * Recomputes Station::industries_near for all stations
 
 * Recomputes catchment of all stations.
 
 * This will additionally recompute nearby stations for all towns and industries.
 
 */
 
/* static */ void Station::RecomputeIndustriesNearForAll()
 
/* static */ void Station::RecomputeCatchmentForAll()
 
{
 
	Station *st;
 
	FOR_ALL_STATIONS(st) st->RecomputeIndustriesNear();
 
	FOR_ALL_STATIONS(st) { st->RecomputeCatchment(); }
 
}
 

	
 
/************************************************************************/
src/station_base.h
Show inline comments
 
@@ -19,6 +19,7 @@
 
#include "industry_type.h"
 
#include "linkgraph/linkgraph_type.h"
 
#include "newgrf_storage.h"
 
#include "bitmap_type.h"
 
#include <map>
 
#include <set>
 

	
 
@@ -467,6 +468,8 @@ public:
 

	
 
	IndustryType indtype;   ///< Industry type to get the name from
 

	
 
	BitmapTileArea catchment_tiles; ///< NOSAVE: Set of individual tiles covered by catchment area
 

	
 
	StationHadVehicleOfTypeByte had_vehicle_of_type;
 

	
 
	byte time_since_load;
 
@@ -493,11 +496,18 @@ public:
 

	
 
	/* virtual */ uint GetPlatformLength(TileIndex tile, DiagDirection dir) const;
 
	/* virtual */ uint GetPlatformLength(TileIndex tile) const;
 
	void RecomputeIndustriesNear();
 
	static void RecomputeIndustriesNearForAll();
 
	void RecomputeCatchment();
 
	static void RecomputeCatchmentForAll();
 

	
 
	uint GetCatchmentRadius() const;
 
	Rect GetCatchmentRect() const;
 
	bool CatchmentCoversTown(TownID t) const;
 
	void RemoveFromAllNearbyLists();
 

	
 
	inline bool TileIsInCatchment(TileIndex tile) const
 
	{
 
		return this->catchment_tiles.HasTile(tile);
 
	}
 

	
 
	/* virtual */ inline bool TileBelongsToRailStation(TileIndex tile) const
 
	{
src/station_cmd.cpp
Show inline comments
 
@@ -527,7 +527,7 @@ CargoArray GetProductionAroundTiles(Tile
 
 * @param always_accepted bitmask of cargo accepted by houses and headquarters; can be NULL
 
 * @param ind Industry associated with neutral station (e.g. oil rig) or NULL
 
 */
 
CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, CargoTypes *always_accepted, const Industry *ind)
 
CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, CargoTypes *always_accepted)
 
{
 
	CargoArray acceptance;
 
	if (always_accepted != NULL) *always_accepted = 0;
 
@@ -551,14 +551,9 @@ CargoArray GetAcceptanceAroundTiles(Tile
 
		for (int xc = x1; xc != x2; xc++) {
 
			TileIndex tile = TileXY(xc, yc);
 

	
 
			if (!_settings_game.station.serve_neutral_industries) {
 
				if (ind != NULL) {
 
					if (!IsTileType(tile, MP_INDUSTRY)) continue;
 
					if (Industry::GetByTile(tile) != ind) continue;
 
				} else {
 
					if (IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile)->neutral_station != NULL) continue;
 
				}
 
			}
 
			/* Ignore industry if it has a neutral station. */
 
			if (!_settings_game.station.serve_neutral_industries && IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile)->neutral_station != NULL) continue;
 

	
 
			AddAcceptedCargo(tile, acceptance, always_accepted);
 
		}
 
	}
 
@@ -567,6 +562,24 @@ CargoArray GetAcceptanceAroundTiles(Tile
 
}
 

	
 
/**
 
 * Get the acceptance of cargoes around the station in.
 
 * @param st Station to get acceptance of.
 
 * @param always_accepted bitmask of cargo accepted by houses and headquarters; can be NULL
 
 */
 
static CargoArray GetAcceptanceAroundStation(const Station *st, CargoTypes *always_accepted)
 
{
 
	CargoArray acceptance;
 
	if (always_accepted != NULL) *always_accepted = 0;
 

	
 
	BitmapTileIterator it(st->catchment_tiles);
 
	for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
 
		AddAcceptedCargo(tile, acceptance, always_accepted);
 
	}
 

	
 
	return acceptance;
 
}
 

	
 
/**
 
 * Update the acceptance for a station.
 
 * @param st Station to update
 
 * @param show_msg controls whether to display a message that acceptance was changed.
 
@@ -579,14 +592,7 @@ void UpdateStationAcceptance(Station *st
 
	/* And retrieve the acceptance. */
 
	CargoArray acceptance;
 
	if (!st->rect.IsEmpty()) {
 
		acceptance = GetAcceptanceAroundTiles(
 
			TileXY(st->rect.left, st->rect.top),
 
			st->rect.right  - st->rect.left + 1,
 
			st->rect.bottom - st->rect.top  + 1,
 
			st->GetCatchmentRadius(),
 
			&st->always_accepted,
 
			_settings_game.station.serve_neutral_industries ? NULL : st->industry
 
		);
 
		acceptance = GetAcceptanceAroundStation(st, &st->always_accepted);
 
	}
 

	
 
	/* Adjust in case our station only accepts fewer kinds of goods */
 
@@ -736,7 +742,7 @@ static void DeleteStationIfEmpty(BaseSta
 
void Station::AfterStationTileSetChange(bool adding, StationType type)
 
{
 
	this->UpdateVirtCoord();
 
	this->RecomputeIndustriesNear();
 
	this->RecomputeCatchment();
 
	DirtyCompanyInfrastructureWindows(this->owner);
 
	if (adding) InvalidateWindowData(WC_STATION_LIST, this->owner, 0);
 

	
 
@@ -1643,7 +1649,7 @@ CommandCost CmdRemoveFromRailStation(Til
 

	
 
		if (st->train_station.tile == INVALID_TILE) SetWindowWidgetDirty(WC_STATION_VIEW, st->index, WID_SV_TRAINS);
 
		st->MarkTilesDirty(false);
 
		st->RecomputeIndustriesNear();
 
		st->RecomputeCatchment();
 
	}
 

	
 
	/* Now apply the rail cost to the number that we deleted */
 
@@ -1726,7 +1732,7 @@ static CommandCost RemoveRailStation(Til
 
	Station *st = Station::GetByTile(tile);
 
	CommandCost cost = RemoveRailStation(st, flags, _price[PR_CLEAR_STATION_RAIL]);
 

	
 
	if (flags & DC_EXEC) st->RecomputeIndustriesNear();
 
	if (flags & DC_EXEC) st->RecomputeCatchment();
 

	
 
	return cost;
 
}
 
@@ -3215,9 +3221,8 @@ void TriggerWatchedCargoCallbacks(Statio
 
	if (cargoes == 0) return;
 

	
 
	/* Loop over all houses in the catchment. */
 
	Rect r = st->GetCatchmentRect();
 
	TileArea ta(TileXY(r.left, r.top), TileXY(r.right, r.bottom));
 
	TILE_AREA_LOOP(tile, ta) {
 
	BitmapTileIterator it(st->catchment_tiles);
 
	for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
 
		if (IsTileType(tile, MP_HOUSE)) {
 
			WatchedCargoCallback(tile, cargoes);
 
		}
 
@@ -3623,7 +3628,7 @@ void IncreaseStats(Station *st, const Ve
 
			 * As usage is not such an important figure anyway we just
 
			 * ignore the additional cargo then.*/
 
			IncreaseStats(st, v->cargo_type, next_station_id, v->refit_cap,
 
				min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE);
 
					min(v->refit_cap, v->cargo.StoredCount()), EUM_INCREASE);
 
		}
 
	}
 
}
 
@@ -3789,57 +3794,63 @@ CommandCost CmdRenameStation(TileIndex t
 
	return CommandCost();
 
}
 

	
 
static void AddNearbyStationsByCatchment(TileIndex tile, StationList *stations, StationList &nearby)
 
{
 
	for (Station *st : nearby) {
 
		if (st->TileIsInCatchment(tile)) stations->insert(st);
 
	}
 
}
 

	
 
/**
 
 * Find all stations around a rectangular producer (industry, house, headquarter, ...)
 
 *
 
 * @param location The location/area of the producer
 
 * @param stations The list to store the stations in
 
 * @param use_nearby Use nearby station list of industry/town associated with location.tile
 
 */
 
void FindStationsAroundTiles(const TileArea &location, StationList *stations)
 
void FindStationsAroundTiles(const TileArea &location, StationList *stations, bool use_nearby)
 
{
 
	/* area to search = producer plus station catchment radius */
 
	uint max_rad = (_settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED);
 

	
 
	if (use_nearby) {
 
		/* Industries and towns maintain a list of nearby stations */
 
		if (IsTileType(location.tile, MP_INDUSTRY)) {
 
			/* Industry nearby stations are already filtered by catchment. */
 
			stations = &Industry::GetByTile(location.tile)->stations_near;
 
			return;
 
		} else if (IsTileType(location.tile, MP_HOUSE)) {
 
			/* Town nearby stations need to be filtered per tile. */
 
			assert(location.w == 1 && location.h == 1);
 
			AddNearbyStationsByCatchment(location.tile, stations, Town::GetByTile(location.tile)->stations_near);
 
			return;
 
		}
 
	}
 

	
 
	/* Not using, or don't have a nearby stations list, so we need to scan. */
 
	uint x = TileX(location.tile);
 
	uint y = TileY(location.tile);
 

	
 
	uint min_x = (x > max_rad) ? x - max_rad : 0;
 
	uint max_x = x + location.w + max_rad;
 
	uint min_y = (y > max_rad) ? y - max_rad : 0;
 
	uint max_y = y + location.h + max_rad;
 

	
 
	IndustryID ind = IsTileType(location.tile, MP_INDUSTRY) ? GetIndustryIndex(location.tile) : INVALID_INDUSTRY;
 

	
 
	if (min_x == 0 && _settings_game.construction.freeform_edges) min_x = 1;
 
	if (min_y == 0 && _settings_game.construction.freeform_edges) min_y = 1;
 
	if (max_x >= MapSizeX()) max_x = MapSizeX() - 1;
 
	if (max_y >= MapSizeY()) max_y = MapSizeY() - 1;
 

	
 
	for (uint cy = min_y; cy < max_y; cy++) {
 
		for (uint cx = min_x; cx < max_x; cx++) {
 
			TileIndex cur_tile = TileXY(cx, cy);
 
			if (!IsTileType(cur_tile, MP_STATION)) continue;
 

	
 
			Station *st = Station::GetByTile(cur_tile);
 
			/* st can be NULL in case of waypoints */
 
			if (st == NULL) continue;
 

	
 
			/* Check if neutral station is attached to us */
 
			if (!_settings_game.station.serve_neutral_industries && st->industry != NULL && st->industry->index != ind) continue;
 

	
 
			if (_settings_game.station.modified_catchment) {
 
				int rad = st->GetCatchmentRadius();
 
				int rad_x = cx - x;
 
				int rad_y = cy - y;
 

	
 
				if (rad_x < -rad || rad_x >= rad + location.w) continue;
 
				if (rad_y < -rad || rad_y >= rad + location.h) continue;
 
	std::set<StationID> seen_stations;
 

	
 
	/* Scan an area around the building covering the maximum possible station
 
	 * to find the possible nearby stations. */
 
	uint max_c = _settings_game.station.modified_catchment ? MAX_CATCHMENT : CA_UNMODIFIED;
 
	TileArea ta(TileXY(max<int>(0, x - max_c), max<int>(0, y - max_c)), TileXY(min<int>(MapMaxX(), x + location.w + max_c), min<int>(MapMaxY(), y + location.h + max_c)));
 
	TILE_AREA_LOOP(tile, ta) {
 
		if (IsTileType(tile, MP_STATION)) seen_stations.insert(GetStationIndex(tile));
 
	}
 

	
 
	for (StationID stationid : seen_stations) {
 
		Station *st = Station::GetIfValid(stationid);
 
		if (st == NULL) continue; /* Waypoint */
 

	
 
		/* Check if station is attached to an industry */
 
		if (!_settings_game.station.serve_neutral_industries && st->industry != NULL) continue;
 

	
 
		/* Test if the tile is within the station's catchment */
 
		TILE_AREA_LOOP(tile, location) {
 
			if (st->TileIsInCatchment(tile)) {
 
				stations->insert(st);
 
				break;
 
			}
 

	
 
			/* Insert the station in the set. This will fail if it has
 
			 * already been added.
 
			 */
 
			stations->insert(st);
 
		}
 
	}
 
}
 
@@ -3950,8 +3961,8 @@ void BuildOilRig(TileIndex tile)
 
	st->rect.BeforeAddTile(tile, StationRect::ADD_FORCE);
 

	
 
	st->UpdateVirtCoord();
 
	st->RecomputeCatchment();
 
	UpdateStationAcceptance(st, false);
 
	st->RecomputeIndustriesNear();
 
}
 

	
 
void DeleteOilRig(TileIndex tile)
 
@@ -3968,7 +3979,7 @@ void DeleteOilRig(TileIndex tile)
 
	st->rect.AfterRemoveTile(st, tile);
 

	
 
	st->UpdateVirtCoord();
 
	st->RecomputeIndustriesNear();
 
	st->RecomputeCatchment();
 
	if (!st->IsInUse()) delete st;
 
}
 

	
src/station_func.h
Show inline comments
 
@@ -23,13 +23,13 @@
 

	
 
void ModifyStationRatingAround(TileIndex tile, Owner owner, int amount, uint radius);
 

	
 
void FindStationsAroundTiles(const TileArea &location, StationList *stations);
 
void FindStationsAroundTiles(const TileArea &location, StationList *stations, bool use_nearby = true);
 

	
 
void ShowStationViewWindow(StationID station);
 
void UpdateAllStationVirtCoords();
 

	
 
CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad);
 
CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, CargoTypes *always_accepted = NULL, const Industry *ind = NULL);
 
CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, CargoTypes *always_accepted = NULL);
 

	
 
void UpdateStationAcceptance(Station *st, bool show_msg);
 

	
src/subsidy.cpp
Show inline comments
 
@@ -573,15 +573,11 @@ bool CheckSubsidised(CargoID cargo_type,
 
			if (s->cargo_type != cargo_type || s->src_type != src_type || s->src != src) continue;
 
			if (s->IsAwarded() && s->awarded != company) continue;
 

	
 
			Rect rect = st->GetCatchmentRect();
 

	
 
			for (int y = rect.top; y <= rect.bottom; y++) {
 
				for (int x = rect.left; x <= rect.right; x++) {
 
					TileIndex tile = TileXY(x, y);
 
					if (!IsTileType(tile, MP_HOUSE)) continue;
 
					const Town *t = Town::GetByTile(tile);
 
					if (t->cache.part_of_subsidy & POS_DST) towns_near.Include(t);
 
				}
 
			BitmapTileIterator it(st->catchment_tiles);
 
			for (TileIndex tile = it; tile != INVALID_TILE; tile = ++it) {
 
				if (!IsTileType(tile, MP_HOUSE)) continue;
 
				const Town *t = Town::GetByTile(tile);
 
				if (t->cache.part_of_subsidy & POS_DST) towns_near.Include(t);
 
			}
 
			break;
 
		}
src/town.h
Show inline comments
 
@@ -88,6 +88,7 @@ struct Town : TownPool::PoolItem<&_town_
 
	CargoTypes cargo_produced;       ///< Bitmap of all cargoes produced by houses in this town.
 
	AcceptanceMatrix cargo_accepted; ///< Bitmap of cargoes accepted by houses for each 4*4 map square of the town.
 
	CargoTypes cargo_accepted_total; ///< NOSAVE: Bitmap of all cargoes accepted by houses in this town.
 
	StationList stations_near;       ///< NOSAVE: List of nearby stations.
 

	
 
	uint16 time_until_rebuild;     ///< time until we rebuild a house
 

	
src/town_cmd.cpp
Show inline comments
 
@@ -447,6 +447,22 @@ uint32 GetWorldPopulation()
 
}
 

	
 
/**
 
 * Remove stations from nearby station list if a town is no longer in the catchment area of each.
 
 * @param t Town to work on
 
 */
 
static void RemoveNearbyStations(Town *t)
 
{
 
	for (StationList::iterator it = t->stations_near.begin(); it != t->stations_near.end(); /* incremented inside loop */) {
 
		const Station *st = *it;
 
		if (!st->CatchmentCoversTown(t->index)) {
 
			it = t->stations_near.erase(it);
 
		} else {
 
			++it;
 
		}
 
	}
 
}
 

	
 
/**
 
 * Helper function for house completion stages progression
 
 * @param tile TileIndex of the house (or parts of it) to "grow"
 
 */
 
@@ -599,7 +615,11 @@ static void TileLoop_Town(TileIndex tile
 
		ClearTownHouse(t, tile);
 

	
 
		/* Rebuild with another house? */
 
		if (GB(r, 24, 8) >= 12) BuildTownHouse(t, tile);
 
		if (GB(r, 24, 8) < 12 || !BuildTownHouse(t, tile))
 
		{
 
			/* House wasn't replaced, so remove it */
 
			if (!_generating_world) RemoveNearbyStations(t);
 
		}
 
	}
 

	
 
	cur_company.Restore();
 
@@ -628,6 +648,7 @@ static CommandCost ClearTile_Town(TileIn
 
	ChangeTownRating(t, -rating, RATING_HOUSE_MINIMUM, flags);
 
	if (flags & DC_EXEC) {
 
		ClearTownHouse(t, tile);
 
		RemoveNearbyStations(t);
 
	}
 

	
 
	return cost;
 
@@ -2151,6 +2172,8 @@ static void MakeTownHouse(TileIndex t, T
 
	if (size & BUILDING_2_TILES_Y)   ClearMakeHouseTile(t + TileDiffXY(0, 1), town, counter, stage, ++type, random_bits);
 
	if (size & BUILDING_2_TILES_X)   ClearMakeHouseTile(t + TileDiffXY(1, 0), town, counter, stage, ++type, random_bits);
 
	if (size & BUILDING_HAS_4_TILES) ClearMakeHouseTile(t + TileDiffXY(1, 1), town, counter, stage, ++type, random_bits);
 

	
 
	if (!_generating_world) FindStationsAroundTiles(TileArea(t, (size & BUILDING_2_TILES_X) ? 2 : 1, (size & BUILDING_2_TILES_Y) ? 2 : 1), &town->stations_near, false);
 
}
 

	
 

	
0 comments (0 inline, 0 general)