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