1 /*
2  * This file is part of OpenTTD.
3  * 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.
4  * 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.
5  * 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/>.
6  */
7 
8 /** @file script_tilelist.cpp Implementation of ScriptTileList and friends. */
9 
10 #include "../../stdafx.h"
11 #include "script_tilelist.hpp"
12 #include "script_industry.hpp"
13 #include "../../industry.h"
14 #include "../../station_base.h"
15 
16 #include "../../safeguards.h"
17 
AddRectangle(TileIndex t1,TileIndex t2)18 void ScriptTileList::AddRectangle(TileIndex t1, TileIndex t2)
19 {
20 	if (!::IsValidTile(t1)) return;
21 	if (!::IsValidTile(t2)) return;
22 
23 	TileArea ta(t1, t2);
24 	for (TileIndex t : ta) this->AddItem(t);
25 }
26 
AddTile(TileIndex tile)27 void ScriptTileList::AddTile(TileIndex tile)
28 {
29 	if (!::IsValidTile(tile)) return;
30 
31 	this->AddItem(tile);
32 }
33 
RemoveRectangle(TileIndex t1,TileIndex t2)34 void ScriptTileList::RemoveRectangle(TileIndex t1, TileIndex t2)
35 {
36 	if (!::IsValidTile(t1)) return;
37 	if (!::IsValidTile(t2)) return;
38 
39 	TileArea ta(t1, t2);
40 	for (TileIndex t : ta) this->RemoveItem(t);
41 }
42 
RemoveTile(TileIndex tile)43 void ScriptTileList::RemoveTile(TileIndex tile)
44 {
45 	if (!::IsValidTile(tile)) return;
46 
47 	this->RemoveItem(tile);
48 }
49 
50 /**
51  * Helper to get list of tiles that will cover an industry's production or acceptance.
52  * @param i Industry in question
53  * @param radius Catchment radius to test
54  * @param bta BitmapTileArea to fill
55  */
FillIndustryCatchment(const Industry * i,int radius,BitmapTileArea & bta)56 static void FillIndustryCatchment(const Industry *i, int radius, BitmapTileArea &bta)
57 {
58 	for (TileIndex cur_tile : i->location) {
59 		if (!::IsTileType(cur_tile, MP_INDUSTRY) || ::GetIndustryIndex(cur_tile) != i->index) continue;
60 
61 		int tx = TileX(cur_tile);
62 		int ty = TileY(cur_tile);
63 		for (int y = -radius; y <= radius; y++) {
64 			if (ty + y < 0 || ty + y > (int)MapMaxY()) continue;
65 			for (int x = -radius; x <= radius; x++) {
66 				if (tx + x < 0 || tx + x > (int)MapMaxX()) continue;
67 				TileIndex tile = TileXY(tx + x, ty + y);
68 				if (!IsValidTile(tile)) continue;
69 				if (::IsTileType(tile, MP_INDUSTRY) && ::GetIndustryIndex(tile) == i->index) continue;
70 				bta.SetTile(tile);
71 			}
72 		}
73 	}
74 }
75 
ScriptTileList_IndustryAccepting(IndustryID industry_id,int radius)76 ScriptTileList_IndustryAccepting::ScriptTileList_IndustryAccepting(IndustryID industry_id, int radius)
77 {
78 	if (!ScriptIndustry::IsValidIndustry(industry_id) || radius <= 0) return;
79 
80 	const Industry *i = ::Industry::Get(industry_id);
81 
82 	/* Check if this industry is only served by its neutral station */
83 	if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) return;
84 
85 	/* Check if this industry accepts anything */
86 	{
87 		bool cargo_accepts = false;
88 		for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
89 			if (i->accepts_cargo[j] != CT_INVALID) cargo_accepts = true;
90 		}
91 		if (!cargo_accepts) return;
92 	}
93 
94 	if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
95 
96 	BitmapTileArea bta(TileArea(i->location).Expand(radius));
97 	FillIndustryCatchment(i, radius, bta);
98 
99 	BitmapTileIterator it(bta);
100 	for (TileIndex cur_tile = it; cur_tile != INVALID_TILE; cur_tile = ++it) {
101 		/* Only add the tile if it accepts the cargo (sometimes just 1 tile of an
102 		 *  industry triggers the acceptance). */
103 		CargoArray acceptance = ::GetAcceptanceAroundTiles(cur_tile, 1, 1, radius);
104 		{
105 			bool cargo_accepts = false;
106 			for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
107 				if (i->accepts_cargo[j] != CT_INVALID && acceptance[i->accepts_cargo[j]] != 0) cargo_accepts = true;
108 			}
109 			if (!cargo_accepts) continue;
110 		}
111 
112 		this->AddTile(cur_tile);
113 	}
114 }
115 
ScriptTileList_IndustryProducing(IndustryID industry_id,int radius)116 ScriptTileList_IndustryProducing::ScriptTileList_IndustryProducing(IndustryID industry_id, int radius)
117 {
118 	if (!ScriptIndustry::IsValidIndustry(industry_id) || radius <= 0) return;
119 
120 	const Industry *i = ::Industry::Get(industry_id);
121 
122 	/* Check if this industry is only served by its neutral station */
123 	if (i->neutral_station != nullptr && !_settings_game.station.serve_neutral_industries) return;
124 
125 	/* Check if this industry produces anything */
126 	bool cargo_produces = false;
127 	for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
128 		if (i->produced_cargo[j] != CT_INVALID) cargo_produces = true;
129 	}
130 	if (!cargo_produces) return;
131 
132 	if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
133 
134 	BitmapTileArea bta(TileArea(i->location).Expand(radius));
135 	FillIndustryCatchment(i, radius, bta);
136 
137 	BitmapTileIterator it(bta);
138 	for (TileIndex cur_tile = it; cur_tile != INVALID_TILE; cur_tile = ++it) {
139 		this->AddTile(cur_tile);
140 	}
141 }
142 
ScriptTileList_StationType(StationID station_id,ScriptStation::StationType station_type)143 ScriptTileList_StationType::ScriptTileList_StationType(StationID station_id, ScriptStation::StationType station_type)
144 {
145 	if (!ScriptStation::IsValidStation(station_id)) return;
146 
147 	const StationRect *rect = &::Station::Get(station_id)->rect;
148 
149 	uint station_type_value = 0;
150 	/* Convert ScriptStation::StationType to ::StationType, but do it in a
151 	 *  bitmask, so we can scan for multiple entries at the same time. */
152 	if ((station_type & ScriptStation::STATION_TRAIN) != 0)      station_type_value |= (1 << ::STATION_RAIL);
153 	if ((station_type & ScriptStation::STATION_TRUCK_STOP) != 0) station_type_value |= (1 << ::STATION_TRUCK);
154 	if ((station_type & ScriptStation::STATION_BUS_STOP) != 0)   station_type_value |= (1 << ::STATION_BUS);
155 	if ((station_type & ScriptStation::STATION_AIRPORT) != 0)    station_type_value |= (1 << ::STATION_AIRPORT) | (1 << ::STATION_OILRIG);
156 	if ((station_type & ScriptStation::STATION_DOCK) != 0)       station_type_value |= (1 << ::STATION_DOCK)    | (1 << ::STATION_OILRIG);
157 
158 	TileArea ta(::TileXY(rect->left, rect->top), rect->right - rect->left + 1, rect->bottom - rect->top + 1);
159 	for (TileIndex cur_tile : ta) {
160 		if (!::IsTileType(cur_tile, MP_STATION)) continue;
161 		if (::GetStationIndex(cur_tile) != station_id) continue;
162 		if (!HasBit(station_type_value, ::GetStationType(cur_tile))) continue;
163 		this->AddTile(cur_tile);
164 	}
165 }
166