mirror of https://github.com/OpenTTD/OpenTTD.git
Feature: Non-rectangular sparse station catchment area.
This commit is contained in:
parent
3542ed53d4
commit
8b1b3fd0f9
|
@ -1049,11 +1049,6 @@ static uint DeliverGoodsToIndustry(const Station *st, CargoID cargo_type, uint n
|
|||
|
||||
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;
|
||||
|
|
|
@ -63,6 +63,7 @@ struct Industry : IndustryPool::PoolItem<&_industry_pool> {
|
|||
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
|
||||
|
|
|
@ -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(TileIndex tile)
|
|||
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(TileIndex tile)
|
|||
|
||||
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);
|
||||
|
@ -1650,6 +1645,37 @@ static void AdvertiseIndustryOpening(const Industry *ind)
|
|||
Game::NewEvent(new ScriptEventIndustryOpen(ind->index));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
@ -1823,7 +1849,7 @@ static void DoCreateNewIndustry(Industry *i, TileIndex tile, IndustryType type,
|
|||
}
|
||||
InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
|
||||
|
||||
Station::RecomputeIndustriesNearForAll();
|
||||
if (!_generating_world) PopulateStationsNearby(i);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2428,11 +2454,7 @@ static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accept
|
|||
*/
|
||||
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(Industry *ind)
|
|||
/* 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
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1302,7 +1302,7 @@ static bool ChangeMaxHeightLevel(int32 p1)
|
|||
|
||||
static bool StationCatchmentChanged(int32 p1)
|
||||
{
|
||||
Station::RecomputeIndustriesNearForAll();
|
||||
Station::RecomputeCatchmentForAll();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
169
src/station.cpp
169
src/station.cpp
|
@ -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;
|
||||
|
||||
|
@ -261,6 +265,39 @@ void Station::MarkTilesDirty(bool cargo_change) const
|
|||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
riv->industries_near->insert(ind);
|
||||
st->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;
|
||||
|
||||
/* 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)))
|
||||
);
|
||||
uint r = GetTileCatchmentRadius(tile, this);
|
||||
if (r == CA_NONE) continue;
|
||||
|
||||
CircularTileSearch(&start_tile, 2 * max_radius + 1, &FindIndustryToDeliver, &riv);
|
||||
/* 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);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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(); }
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -527,7 +527,7 @@ CargoArray GetProductionAroundTiles(TileIndex tile, int w, int h, int rad)
|
|||
* @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(TileIndex tile, int w, int h, int rad, Cargo
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
@ -566,6 +561,24 @@ CargoArray GetAcceptanceAroundTiles(TileIndex tile, int w, int h, int rad, Cargo
|
|||
return acceptance;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -579,14 +592,7 @@ void UpdateStationAcceptance(Station *st, bool show_msg)
|
|||
/* 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(BaseStation *st)
|
|||
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(TileIndex start, DoCommandFlag flags, uint3
|
|||
|
||||
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(TileIndex tile, DoCommandFlag flags)
|
|||
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(Station *st)
|
|||
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 Vehicle *front, StationID next_station_id)
|
|||
* 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 tile, DoCommandFlag flags, uint32 p1, uin
|
|||
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;
|
||||
std::set<StationID> seen_stations;
|
||||
|
||||
IndustryID ind = IsTileType(location.tile, MP_INDUSTRY) ? GetIndustryIndex(location.tile) : INVALID_INDUSTRY;
|
||||
/* 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));
|
||||
}
|
||||
|
||||
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 (StationID stationid : seen_stations) {
|
||||
Station *st = Station::GetIfValid(stationid);
|
||||
if (st == NULL) continue; /* Waypoint */
|
||||
|
||||
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;
|
||||
/* Check if station is attached to an industry */
|
||||
if (!_settings_game.station.serve_neutral_industries && st->industry != NULL) 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;
|
||||
/* 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -573,15 +573,11 @@ bool CheckSubsidised(CargoID cargo_type, CompanyID company, SourceType src_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;
|
||||
}
|
||||
|
|
|
@ -88,6 +88,7 @@ struct Town : TownPool::PoolItem<&_town_pool> {
|
|||
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
|
||||
|
||||
|
|
|
@ -446,6 +446,22 @@ uint32 GetWorldPopulation()
|
|||
return pop;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(TileIndex tile, DoCommandFlag flags)
|
|||
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, Town *town, byte counter, byte stage, Hou
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue