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