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/>.
8 /** @file script_tile.cpp Implementation of ScriptTile. */
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 #include "../../landscape_cmd.h"
21 #include "../../terraform_cmd.h"
22 #include "../../tree_cmd.h"
24 #include "../../safeguards.h"
26 /* static */ bool ScriptTile::IsBuildable(TileIndex tile
)
28 EnforceDeityOrCompanyModeValid(false);
29 if (!::IsValidTile(tile
)) return false;
31 switch (::GetTileType(tile
)) {
32 default: return false;
33 case MP_CLEAR
: return true;
34 case MP_TREES
: return true;
35 case MP_WATER
: return IsCoast(tile
);
37 /* Tram bits aren't considered buildable */
38 if (::GetRoadTypeTram(tile
) != INVALID_ROADTYPE
) return false;
39 /* Depots and crossings aren't considered buildable */
40 if (::GetRoadTileType(tile
) != ROAD_TILE_NORMAL
) return false;
41 if (!HasExactlyOneBit(::GetRoadBits(tile
, RTT_ROAD
))) return false;
42 if (::IsRoadOwner(tile
, RTT_ROAD
, OWNER_TOWN
)) return true;
43 if (::IsRoadOwner(tile
, RTT_ROAD
, ScriptObject::GetCompany())) return true;
48 /* static */ bool ScriptTile::IsBuildableRectangle(TileIndex tile
, SQInteger width
, SQInteger height
)
50 /* Check whether we can extract valid X and Y */
51 if (!::IsValidTile(tile
) || width
< 0 || height
< 0) return false;
53 uint tx
= ScriptMap::GetTileX(tile
);
54 uint ty
= ScriptMap::GetTileY(tile
);
56 for (uint x
= tx
; x
< width
+ tx
; x
++) {
57 for (uint y
= ty
; y
< height
+ ty
; y
++) {
58 if (!IsBuildable(ScriptMap::GetTileIndex(x
, y
))) return false;
65 /* static */ bool ScriptTile::IsSeaTile(TileIndex tile
)
67 if (!::IsValidTile(tile
)) return false;
69 return ::IsTileType(tile
, MP_WATER
) && ::IsSea(tile
);
72 /* static */ bool ScriptTile::IsRiverTile(TileIndex tile
)
74 if (!::IsValidTile(tile
)) return false;
76 return ::IsTileType(tile
, MP_WATER
) && ::IsRiver(tile
);
79 /* static */ bool ScriptTile::IsWaterTile(TileIndex tile
)
81 if (!::IsValidTile(tile
)) return false;
83 return ::IsTileType(tile
, MP_WATER
) && !::IsCoast(tile
);
86 /* static */ bool ScriptTile::IsCoastTile(TileIndex tile
)
88 if (!::IsValidTile(tile
)) return false;
90 return (::IsTileType(tile
, MP_WATER
) && ::IsCoast(tile
)) ||
91 (::IsTileType(tile
, MP_TREES
) && ::GetTreeGround(tile
) == TREE_GROUND_SHORE
);
94 /* static */ bool ScriptTile::IsStationTile(TileIndex tile
)
96 if (!::IsValidTile(tile
)) return false;
98 return ::IsTileType(tile
, MP_STATION
);
101 /* static */ bool ScriptTile::IsSteepSlope(Slope slope
)
103 if ((slope
& ~(SLOPE_ELEVATED
| SLOPE_STEEP
| SLOPE_HALFTILE_MASK
)) != 0) return false;
105 return ::IsSteepSlope((::Slope
)slope
);
108 /* static */ bool ScriptTile::IsHalftileSlope(Slope slope
)
110 if ((slope
& ~(SLOPE_ELEVATED
| SLOPE_STEEP
| SLOPE_HALFTILE_MASK
)) != 0) return false;
112 return ::IsHalftileSlope((::Slope
)slope
);
115 /* static */ bool ScriptTile::HasTreeOnTile(TileIndex tile
)
117 if (!::IsValidTile(tile
)) return false;
119 return ::IsTileType(tile
, MP_TREES
);
122 /* static */ bool ScriptTile::IsFarmTile(TileIndex tile
)
124 if (!::IsValidTile(tile
)) return false;
126 return (::IsTileType(tile
, MP_CLEAR
) && ::IsClearGround(tile
, CLEAR_FIELDS
));
129 /* static */ bool ScriptTile::IsRockTile(TileIndex tile
)
131 if (!::IsValidTile(tile
)) return false;
133 return (::IsTileType(tile
, MP_CLEAR
) && ::GetRawClearGround(tile
) == ::CLEAR_ROCKS
);
136 /* static */ bool ScriptTile::IsRoughTile(TileIndex tile
)
138 if (!::IsValidTile(tile
)) return false;
140 return (::IsTileType(tile
, MP_CLEAR
) && ::GetRawClearGround(tile
) == ::CLEAR_ROUGH
);
143 /* static */ bool ScriptTile::IsSnowTile(TileIndex tile
)
145 if (!::IsValidTile(tile
)) return false;
147 return (::IsTileType(tile
, MP_CLEAR
) && ::IsSnowTile(tile
));
150 /* static */ bool ScriptTile::IsDesertTile(TileIndex tile
)
152 if (!::IsValidTile(tile
)) return false;
154 return (::IsTileType(tile
, MP_CLEAR
) && ::IsClearGround(tile
, CLEAR_DESERT
));
157 /* static */ ScriptTile::TerrainType
ScriptTile::GetTerrainType(TileIndex tile
)
159 if (!::IsValidTile(tile
)) return TERRAIN_NORMAL
;
161 switch (::GetTerrainType(tile
)) {
163 case 0: return TERRAIN_NORMAL
;
164 case 1: return TERRAIN_DESERT
;
165 case 2: return TERRAIN_RAINFOREST
;
166 case 4: return TERRAIN_SNOW
;
170 /* static */ ScriptTile::Slope
ScriptTile::GetSlope(TileIndex tile
)
172 if (!::IsValidTile(tile
)) return SLOPE_INVALID
;
174 return (Slope
)::GetTileSlope(tile
);
177 /* static */ ScriptTile::Slope
ScriptTile::GetComplementSlope(Slope slope
)
179 if ((slope
& ~SLOPE_ELEVATED
) != 0) return SLOPE_INVALID
;
181 return (Slope
)::ComplementSlope((::Slope
)slope
);
184 /* static */ SQInteger
ScriptTile::GetMinHeight(TileIndex tile
)
186 if (!::IsValidTile(tile
)) return -1;
188 return ::GetTileZ(tile
);
191 /* static */ SQInteger
ScriptTile::GetMaxHeight(TileIndex tile
)
193 if (!::IsValidTile(tile
)) return -1;
195 return ::GetTileMaxZ(tile
);
198 /* static */ SQInteger
ScriptTile::GetCornerHeight(TileIndex tile
, Corner corner
)
200 if (!::IsValidTile(tile
) || !::IsValidCorner((::Corner
)corner
)) return -1;
202 auto [slope
, z
] = ::GetTileSlopeZ(tile
);
203 return (z
+ ::GetSlopeZInCorner(slope
, (::Corner
)corner
));
206 /* static */ ScriptCompany::CompanyID
ScriptTile::GetOwner(TileIndex tile
)
208 if (!::IsValidTile(tile
)) return ScriptCompany::COMPANY_INVALID
;
209 if (::IsTileType(tile
, MP_HOUSE
)) return ScriptCompany::COMPANY_INVALID
;
210 if (::IsTileType(tile
, MP_INDUSTRY
)) return ScriptCompany::COMPANY_INVALID
;
212 return ScriptCompany::ResolveCompanyID((ScriptCompany::CompanyID
)(uint8_t)::GetTileOwner(tile
));
215 /* static */ bool ScriptTile::HasTransportType(TileIndex tile
, TransportType transport_type
)
217 if (!::IsValidTile(tile
)) return false;
219 if (transport_type
== TRANSPORT_ROAD
) {
220 return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile
, (::TransportType
)transport_type
, 0)) != TRACKDIR_BIT_NONE
||
221 ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile
, (::TransportType
)transport_type
, 1)) != TRACKDIR_BIT_NONE
;
223 return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile
, (::TransportType
)transport_type
, 0)) != TRACKDIR_BIT_NONE
;
227 /* static */ SQInteger
ScriptTile::GetCargoAcceptance(TileIndex tile
, CargoID cargo_type
, SQInteger width
, SQInteger height
, SQInteger radius
)
229 if (!::IsValidTile(tile
) || width
<= 0 || height
<= 0 || radius
< 0 || !ScriptCargo::IsValidCargo(cargo_type
)) return -1;
231 CargoArray acceptance
= ::GetAcceptanceAroundTiles(tile
, width
, height
, _settings_game
.station
.modified_catchment
? radius
: (int)CA_UNMODIFIED
);
232 return acceptance
[cargo_type
];
235 /* static */ SQInteger
ScriptTile::GetCargoProduction(TileIndex tile
, CargoID cargo_type
, SQInteger width
, SQInteger height
, SQInteger radius
)
237 if (!::IsValidTile(tile
) || width
<= 0 || height
<= 0 || radius
< 0 || !ScriptCargo::IsValidCargo(cargo_type
)) return -1;
239 CargoArray produced
= ::GetProductionAroundTiles(tile
, width
, height
, _settings_game
.station
.modified_catchment
? radius
: (int)CA_UNMODIFIED
);
240 return produced
[cargo_type
];
243 /* static */ SQInteger
ScriptTile::GetDistanceManhattanToTile(TileIndex tile_from
, TileIndex tile_to
)
245 return ScriptMap::DistanceManhattan(tile_from
, tile_to
);
248 /* static */ SQInteger
ScriptTile::GetDistanceSquareToTile(TileIndex tile_from
, TileIndex tile_to
)
250 return ScriptMap::DistanceSquare(tile_from
, tile_to
);
253 /* static */ bool ScriptTile::RaiseTile(TileIndex tile
, Slope slope
)
255 EnforceCompanyModeValid(false);
256 EnforcePrecondition(false, tile
< ScriptMap::GetMapSize());
258 return ScriptObject::Command
<CMD_TERRAFORM_LAND
>::Do(tile
, (::Slope
)slope
, true);
261 /* static */ bool ScriptTile::LowerTile(TileIndex tile
, Slope slope
)
263 EnforceCompanyModeValid(false);
264 EnforcePrecondition(false, tile
< ScriptMap::GetMapSize());
266 return ScriptObject::Command
<CMD_TERRAFORM_LAND
>::Do(tile
, (::Slope
)slope
, false);
269 /* static */ bool ScriptTile::LevelTiles(TileIndex start_tile
, TileIndex end_tile
)
271 EnforceCompanyModeValid(false);
272 EnforcePrecondition(false, start_tile
< ScriptMap::GetMapSize());
273 EnforcePrecondition(false, end_tile
< ScriptMap::GetMapSize());
275 return ScriptObject::Command
<CMD_LEVEL_LAND
>::Do(end_tile
, start_tile
, false, LM_LEVEL
);
278 /* static */ bool ScriptTile::DemolishTile(TileIndex tile
)
280 EnforceDeityOrCompanyModeValid(false);
281 EnforcePrecondition(false, ::IsValidTile(tile
));
283 return ScriptObject::Command
<CMD_LANDSCAPE_CLEAR
>::Do(tile
);
286 /* static */ bool ScriptTile::PlantTree(TileIndex tile
)
288 EnforceCompanyModeValid(false);
289 EnforcePrecondition(false, ::IsValidTile(tile
));
291 return ScriptObject::Command
<CMD_PLANT_TREE
>::Do(tile
, tile
, TREE_INVALID
, false);
294 /* static */ bool ScriptTile::PlantTreeRectangle(TileIndex tile
, SQInteger width
, SQInteger height
)
296 EnforceCompanyModeValid(false);
297 EnforcePrecondition(false, ::IsValidTile(tile
));
298 EnforcePrecondition(false, width
>= 1 && width
<= 20);
299 EnforcePrecondition(false, height
>= 1 && height
<= 20);
300 TileIndex end_tile
= tile
+ ::TileDiffXY(width
- 1, height
- 1);
302 return ScriptObject::Command
<CMD_PLANT_TREE
>::Do(tile
, end_tile
, TREE_INVALID
, false);
305 /* static */ bool ScriptTile::IsWithinTownInfluence(TileIndex tile
, TownID town_id
)
307 return ScriptTown::IsWithinTownInfluence(town_id
, tile
);
310 /* static */ TownID
ScriptTile::GetTownAuthority(TileIndex tile
)
312 if (!::IsValidTile(tile
)) return INVALID_TOWN
;
314 Town
*town
= ::ClosestTownFromTile(tile
, _settings_game
.economy
.dist_local_authority
);
315 if (town
== nullptr) return INVALID_TOWN
;
320 /* static */ TownID
ScriptTile::GetClosestTown(TileIndex tile
)
322 if (!::IsValidTile(tile
)) return INVALID_TOWN
;
324 Town
*town
= ::ClosestTownFromTile(tile
, UINT_MAX
);
325 if (town
== nullptr) return INVALID_TOWN
;
330 /* static */ Money
ScriptTile::GetBuildCost(BuildType build_type
)
332 switch (build_type
) {
333 case BT_FOUNDATION
: return ::GetPrice(PR_BUILD_FOUNDATION
, 1, nullptr);
334 case BT_TERRAFORM
: return ::GetPrice(PR_TERRAFORM
, 1, nullptr);
335 case BT_BUILD_TREES
: return ::GetPrice(PR_BUILD_TREES
, 1, nullptr);
336 case BT_CLEAR_GRASS
: return ::GetPrice(PR_CLEAR_GRASS
, 1, nullptr);
337 case BT_CLEAR_ROUGH
: return ::GetPrice(PR_CLEAR_ROUGH
, 1, nullptr);
338 case BT_CLEAR_ROCKY
: return ::GetPrice(PR_CLEAR_ROCKS
, 1, nullptr);
339 case BT_CLEAR_FIELDS
: return ::GetPrice(PR_CLEAR_FIELDS
, 1, nullptr);
340 case BT_CLEAR_HOUSE
: return ::GetPrice(PR_CLEAR_HOUSE
, 1, nullptr);
341 case BT_CLEAR_WATER
: return ::GetPrice(PR_CLEAR_WATER
, 1, nullptr);