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_tile.cpp Implementation of ScriptTile. */
9
10 #include "../../stdafx.h"
11 #include "script_tile.hpp"
12 #include "script_map.hpp"
13 #include "script_town.hpp"
14 #include "../../station_func.h"
15 #include "../../water_map.h"
16 #include "../../clear_map.h"
17 #include "../../tree_map.h"
18 #include "../../town.h"
19 #include "../../landscape.h"
20
21 #include "../../safeguards.h"
22
IsBuildable(TileIndex tile)23 /* static */ bool ScriptTile::IsBuildable(TileIndex tile)
24 {
25 if (!::IsValidTile(tile)) return false;
26
27 switch (::GetTileType(tile)) {
28 default: return false;
29 case MP_CLEAR: return true;
30 case MP_TREES: return true;
31 case MP_WATER: return IsCoast(tile);
32 case MP_ROAD:
33 /* Tram bits aren't considered buildable */
34 if (::GetRoadTypeTram(tile) != INVALID_ROADTYPE) return false;
35 /* Depots and crossings aren't considered buildable */
36 if (::GetRoadTileType(tile) != ROAD_TILE_NORMAL) return false;
37 if (!HasExactlyOneBit(::GetRoadBits(tile, RTT_ROAD))) return false;
38 if (::IsRoadOwner(tile, RTT_ROAD, OWNER_TOWN)) return true;
39 if (::IsRoadOwner(tile, RTT_ROAD, ScriptObject::GetCompany())) return true;
40 return false;
41 }
42 }
43
IsBuildableRectangle(TileIndex tile,uint width,uint height)44 /* static */ bool ScriptTile::IsBuildableRectangle(TileIndex tile, uint width, uint height)
45 {
46 /* Check whether we can extract valid X and Y */
47 if (!::IsValidTile(tile)) return false;
48
49 uint tx = ScriptMap::GetTileX(tile);
50 uint ty = ScriptMap::GetTileY(tile);
51
52 for (uint x = tx; x < width + tx; x++) {
53 for (uint y = ty; y < height + ty; y++) {
54 if (!IsBuildable(ScriptMap::GetTileIndex(x, y))) return false;
55 }
56 }
57
58 return true;
59 }
60
IsSeaTile(TileIndex tile)61 /* static */ bool ScriptTile::IsSeaTile(TileIndex tile)
62 {
63 if (!::IsValidTile(tile)) return false;
64
65 return ::IsTileType(tile, MP_WATER) && ::IsSea(tile);
66 }
67
IsRiverTile(TileIndex tile)68 /* static */ bool ScriptTile::IsRiverTile(TileIndex tile)
69 {
70 if (!::IsValidTile(tile)) return false;
71
72 return ::IsTileType(tile, MP_WATER) && ::IsRiver(tile);
73 }
74
IsWaterTile(TileIndex tile)75 /* static */ bool ScriptTile::IsWaterTile(TileIndex tile)
76 {
77 if (!::IsValidTile(tile)) return false;
78
79 return ::IsTileType(tile, MP_WATER) && !::IsCoast(tile);
80 }
81
IsCoastTile(TileIndex tile)82 /* static */ bool ScriptTile::IsCoastTile(TileIndex tile)
83 {
84 if (!::IsValidTile(tile)) return false;
85
86 return (::IsTileType(tile, MP_WATER) && ::IsCoast(tile)) ||
87 (::IsTileType(tile, MP_TREES) && ::GetTreeGround(tile) == TREE_GROUND_SHORE);
88 }
89
IsStationTile(TileIndex tile)90 /* static */ bool ScriptTile::IsStationTile(TileIndex tile)
91 {
92 if (!::IsValidTile(tile)) return false;
93
94 return ::IsTileType(tile, MP_STATION);
95 }
96
IsSteepSlope(Slope slope)97 /* static */ bool ScriptTile::IsSteepSlope(Slope slope)
98 {
99 if ((slope & ~(SLOPE_ELEVATED | SLOPE_STEEP | SLOPE_HALFTILE_MASK)) != 0) return false;
100
101 return ::IsSteepSlope((::Slope)slope);
102 }
103
IsHalftileSlope(Slope slope)104 /* static */ bool ScriptTile::IsHalftileSlope(Slope slope)
105 {
106 if ((slope & ~(SLOPE_ELEVATED | SLOPE_STEEP | SLOPE_HALFTILE_MASK)) != 0) return false;
107
108 return ::IsHalftileSlope((::Slope)slope);
109 }
110
HasTreeOnTile(TileIndex tile)111 /* static */ bool ScriptTile::HasTreeOnTile(TileIndex tile)
112 {
113 if (!::IsValidTile(tile)) return false;
114
115 return ::IsTileType(tile, MP_TREES);
116 }
117
IsFarmTile(TileIndex tile)118 /* static */ bool ScriptTile::IsFarmTile(TileIndex tile)
119 {
120 if (!::IsValidTile(tile)) return false;
121
122 return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_FIELDS));
123 }
124
IsRockTile(TileIndex tile)125 /* static */ bool ScriptTile::IsRockTile(TileIndex tile)
126 {
127 if (!::IsValidTile(tile)) return false;
128
129 return (::IsTileType(tile, MP_CLEAR) && ::GetRawClearGround(tile) == ::CLEAR_ROCKS);
130 }
131
IsRoughTile(TileIndex tile)132 /* static */ bool ScriptTile::IsRoughTile(TileIndex tile)
133 {
134 if (!::IsValidTile(tile)) return false;
135
136 return (::IsTileType(tile, MP_CLEAR) && ::GetRawClearGround(tile) == ::CLEAR_ROUGH);
137 }
138
IsSnowTile(TileIndex tile)139 /* static */ bool ScriptTile::IsSnowTile(TileIndex tile)
140 {
141 if (!::IsValidTile(tile)) return false;
142
143 return (::IsTileType(tile, MP_CLEAR) && ::IsSnowTile(tile));
144 }
145
IsDesertTile(TileIndex tile)146 /* static */ bool ScriptTile::IsDesertTile(TileIndex tile)
147 {
148 if (!::IsValidTile(tile)) return false;
149
150 return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_DESERT));
151 }
152
GetTerrainType(TileIndex tile)153 /* static */ ScriptTile::TerrainType ScriptTile::GetTerrainType(TileIndex tile)
154 {
155 if (!::IsValidTile(tile)) return TERRAIN_NORMAL;
156
157 switch (::GetTerrainType(tile)) {
158 default:
159 case 0: return TERRAIN_NORMAL;
160 case 1: return TERRAIN_DESERT;
161 case 2: return TERRAIN_RAINFOREST;
162 case 4: return TERRAIN_SNOW;
163 }
164 }
165
GetSlope(TileIndex tile)166 /* static */ ScriptTile::Slope ScriptTile::GetSlope(TileIndex tile)
167 {
168 if (!::IsValidTile(tile)) return SLOPE_INVALID;
169
170 return (Slope)::GetTileSlope(tile);
171 }
172
GetComplementSlope(Slope slope)173 /* static */ ScriptTile::Slope ScriptTile::GetComplementSlope(Slope slope)
174 {
175 if ((slope & ~SLOPE_ELEVATED) != 0) return SLOPE_INVALID;
176
177 return (Slope)::ComplementSlope((::Slope)slope);
178 }
179
GetMinHeight(TileIndex tile)180 /* static */ int32 ScriptTile::GetMinHeight(TileIndex tile)
181 {
182 if (!::IsValidTile(tile)) return -1;
183
184 return ::GetTileZ(tile);
185 }
186
GetMaxHeight(TileIndex tile)187 /* static */ int32 ScriptTile::GetMaxHeight(TileIndex tile)
188 {
189 if (!::IsValidTile(tile)) return -1;
190
191 return ::GetTileMaxZ(tile);
192 }
193
GetCornerHeight(TileIndex tile,Corner corner)194 /* static */ int32 ScriptTile::GetCornerHeight(TileIndex tile, Corner corner)
195 {
196 if (!::IsValidTile(tile) || !::IsValidCorner((::Corner)corner)) return -1;
197
198 int z;
199 ::Slope slope = ::GetTileSlope(tile, &z);
200 return (z + ::GetSlopeZInCorner(slope, (::Corner)corner));
201 }
202
GetOwner(TileIndex tile)203 /* static */ ScriptCompany::CompanyID ScriptTile::GetOwner(TileIndex tile)
204 {
205 if (!::IsValidTile(tile)) return ScriptCompany::COMPANY_INVALID;
206 if (::IsTileType(tile, MP_HOUSE)) return ScriptCompany::COMPANY_INVALID;
207 if (::IsTileType(tile, MP_INDUSTRY)) return ScriptCompany::COMPANY_INVALID;
208
209 return ScriptCompany::ResolveCompanyID((ScriptCompany::CompanyID)(byte)::GetTileOwner(tile));
210 }
211
HasTransportType(TileIndex tile,TransportType transport_type)212 /* static */ bool ScriptTile::HasTransportType(TileIndex tile, TransportType transport_type)
213 {
214 if (!::IsValidTile(tile)) return false;
215
216 if (transport_type == TRANSPORT_ROAD) {
217 return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 0)) != TRACKDIR_BIT_NONE ||
218 ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 1)) != TRACKDIR_BIT_NONE;
219 } else {
220 return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, 0)) != TRACKDIR_BIT_NONE;
221 }
222 }
223
GetCargoAcceptance(TileIndex tile,CargoID cargo_type,int width,int height,int radius)224 /* static */ int32 ScriptTile::GetCargoAcceptance(TileIndex tile, CargoID cargo_type, int width, int height, int radius)
225 {
226 if (!::IsValidTile(tile) || width <= 0 || height <= 0 || radius < 0 || !ScriptCargo::IsValidCargo(cargo_type)) return -1;
227
228 CargoArray acceptance = ::GetAcceptanceAroundTiles(tile, width, height, _settings_game.station.modified_catchment ? radius : (int)CA_UNMODIFIED);
229 return acceptance[cargo_type];
230 }
231
GetCargoProduction(TileIndex tile,CargoID cargo_type,int width,int height,int radius)232 /* static */ int32 ScriptTile::GetCargoProduction(TileIndex tile, CargoID cargo_type, int width, int height, int radius)
233 {
234 if (!::IsValidTile(tile) || width <= 0 || height <= 0 || radius < 0 || !ScriptCargo::IsValidCargo(cargo_type)) return -1;
235
236 CargoArray produced = ::GetProductionAroundTiles(tile, width, height, _settings_game.station.modified_catchment ? radius : (int)CA_UNMODIFIED);
237 return produced[cargo_type];
238 }
239
GetDistanceManhattanToTile(TileIndex tile_from,TileIndex tile_to)240 /* static */ int32 ScriptTile::GetDistanceManhattanToTile(TileIndex tile_from, TileIndex tile_to)
241 {
242 return ScriptMap::DistanceManhattan(tile_from, tile_to);
243 }
244
GetDistanceSquareToTile(TileIndex tile_from,TileIndex tile_to)245 /* static */ int32 ScriptTile::GetDistanceSquareToTile(TileIndex tile_from, TileIndex tile_to)
246 {
247 return ScriptMap::DistanceSquare(tile_from, tile_to);
248 }
249
RaiseTile(TileIndex tile,int32 slope)250 /* static */ bool ScriptTile::RaiseTile(TileIndex tile, int32 slope)
251 {
252 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
253 EnforcePrecondition(false, tile < ::MapSize());
254
255 return ScriptObject::DoCommand(tile, slope, 1, CMD_TERRAFORM_LAND);
256 }
257
LowerTile(TileIndex tile,int32 slope)258 /* static */ bool ScriptTile::LowerTile(TileIndex tile, int32 slope)
259 {
260 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
261 EnforcePrecondition(false, tile < ::MapSize());
262
263 return ScriptObject::DoCommand(tile, slope, 0, CMD_TERRAFORM_LAND);
264 }
265
LevelTiles(TileIndex start_tile,TileIndex end_tile)266 /* static */ bool ScriptTile::LevelTiles(TileIndex start_tile, TileIndex end_tile)
267 {
268 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
269 EnforcePrecondition(false, start_tile < ::MapSize());
270 EnforcePrecondition(false, end_tile < ::MapSize());
271
272 return ScriptObject::DoCommand(end_tile, start_tile, LM_LEVEL << 1, CMD_LEVEL_LAND);
273 }
274
DemolishTile(TileIndex tile)275 /* static */ bool ScriptTile::DemolishTile(TileIndex tile)
276 {
277 EnforcePrecondition(false, ::IsValidTile(tile));
278
279 return ScriptObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
280 }
281
PlantTree(TileIndex tile)282 /* static */ bool ScriptTile::PlantTree(TileIndex tile)
283 {
284 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
285 EnforcePrecondition(false, ::IsValidTile(tile));
286
287 return ScriptObject::DoCommand(tile, TREE_INVALID, tile, CMD_PLANT_TREE);
288 }
289
PlantTreeRectangle(TileIndex tile,uint width,uint height)290 /* static */ bool ScriptTile::PlantTreeRectangle(TileIndex tile, uint width, uint height)
291 {
292 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
293 EnforcePrecondition(false, ::IsValidTile(tile));
294 EnforcePrecondition(false, width >= 1 && width <= 20);
295 EnforcePrecondition(false, height >= 1 && height <= 20);
296 TileIndex end_tile = tile + ::TileDiffXY(width - 1, height - 1);
297
298 return ScriptObject::DoCommand(tile, TREE_INVALID, end_tile, CMD_PLANT_TREE);
299 }
300
IsWithinTownInfluence(TileIndex tile,TownID town_id)301 /* static */ bool ScriptTile::IsWithinTownInfluence(TileIndex tile, TownID town_id)
302 {
303 return ScriptTown::IsWithinTownInfluence(town_id, tile);
304 }
305
GetTownAuthority(TileIndex tile)306 /* static */ TownID ScriptTile::GetTownAuthority(TileIndex tile)
307 {
308 if (!::IsValidTile(tile)) return INVALID_TOWN;
309
310 Town *town = ::ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority);
311 if (town == nullptr) return INVALID_TOWN;
312
313 return town->index;
314 }
315
GetClosestTown(TileIndex tile)316 /* static */ TownID ScriptTile::GetClosestTown(TileIndex tile)
317 {
318 if (!::IsValidTile(tile)) return INVALID_TOWN;
319
320 Town *town = ::ClosestTownFromTile(tile, UINT_MAX);
321 if (town == nullptr) return INVALID_TOWN;
322
323 return town->index;
324 }
325
GetBuildCost(BuildType build_type)326 /* static */ Money ScriptTile::GetBuildCost(BuildType build_type)
327 {
328 switch (build_type) {
329 case BT_FOUNDATION: return ::GetPrice(PR_BUILD_FOUNDATION, 1, nullptr);
330 case BT_TERRAFORM: return ::GetPrice(PR_TERRAFORM, 1, nullptr);
331 case BT_BUILD_TREES: return ::GetPrice(PR_BUILD_TREES, 1, nullptr);
332 case BT_CLEAR_GRASS: return ::GetPrice(PR_CLEAR_GRASS, 1, nullptr);
333 case BT_CLEAR_ROUGH: return ::GetPrice(PR_CLEAR_ROUGH, 1, nullptr);
334 case BT_CLEAR_ROCKY: return ::GetPrice(PR_CLEAR_ROCKS, 1, nullptr);
335 case BT_CLEAR_FIELDS: return ::GetPrice(PR_CLEAR_FIELDS, 1, nullptr);
336 case BT_CLEAR_HOUSE: return ::GetPrice(PR_CLEAR_HOUSE, 1, nullptr);
337 case BT_CLEAR_WATER: return ::GetPrice(PR_CLEAR_WATER, 1, nullptr);
338 default: return -1;
339 }
340 }
341