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_road.cpp Implementation of ScriptRoad. */
9
10 #include "../../stdafx.h"
11 #include "script_map.hpp"
12 #include "script_station.hpp"
13 #include "script_cargo.hpp"
14 #include "../../station_base.h"
15 #include "../../script/squirrel_helper_type.hpp"
16
17 #include "../../safeguards.h"
18
GetRoadVehicleTypeForCargo(CargoID cargo_type)19 /* static */ ScriptRoad::RoadVehicleType ScriptRoad::GetRoadVehicleTypeForCargo(CargoID cargo_type)
20 {
21 return ScriptCargo::HasCargoClass(cargo_type, ScriptCargo::CC_PASSENGERS) ? ROADVEHTYPE_BUS : ROADVEHTYPE_TRUCK;
22 }
23
GetName(RoadType road_type)24 /* static */ char *ScriptRoad::GetName(RoadType road_type)
25 {
26 if (!IsRoadTypeAvailable(road_type)) return nullptr;
27
28 return GetString(GetRoadTypeInfo((::RoadType)road_type)->strings.name);
29 }
30
IsRoadTile(TileIndex tile)31 /* static */ bool ScriptRoad::IsRoadTile(TileIndex tile)
32 {
33 if (!::IsValidTile(tile)) return false;
34
35 return (::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) != ROAD_TILE_DEPOT) ||
36 IsDriveThroughRoadStationTile(tile);
37 }
38
IsRoadDepotTile(TileIndex tile)39 /* static */ bool ScriptRoad::IsRoadDepotTile(TileIndex tile)
40 {
41 if (!::IsValidTile(tile)) return false;
42 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
43
44 return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
45 HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
46 }
47
IsRoadStationTile(TileIndex tile)48 /* static */ bool ScriptRoad::IsRoadStationTile(TileIndex tile)
49 {
50 if (!::IsValidTile(tile)) return false;
51 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
52
53 return ::IsRoadStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
54 }
55
IsDriveThroughRoadStationTile(TileIndex tile)56 /* static */ bool ScriptRoad::IsDriveThroughRoadStationTile(TileIndex tile)
57 {
58 if (!::IsValidTile(tile)) return false;
59 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
60
61 return ::IsDriveThroughStopTile(tile) && HasBit(::GetPresentRoadTypes(tile), (::RoadType)GetCurrentRoadType());
62 }
63
IsRoadTypeAvailable(RoadType road_type)64 /* static */ bool ScriptRoad::IsRoadTypeAvailable(RoadType road_type)
65 {
66 return (::RoadType)road_type < ROADTYPE_END && ::HasRoadTypeAvail(ScriptObject::GetCompany(), (::RoadType)road_type);
67 }
68
GetCurrentRoadType()69 /* static */ ScriptRoad::RoadType ScriptRoad::GetCurrentRoadType()
70 {
71 return (RoadType)ScriptObject::GetRoadType();
72 }
73
SetCurrentRoadType(RoadType road_type)74 /* static */ void ScriptRoad::SetCurrentRoadType(RoadType road_type)
75 {
76 if (!IsRoadTypeAvailable(road_type)) return;
77
78 ScriptObject::SetRoadType((::RoadType)road_type);
79 }
80
RoadVehCanRunOnRoad(RoadType engine_road_type,RoadType road_road_type)81 /* static */ bool ScriptRoad::RoadVehCanRunOnRoad(RoadType engine_road_type, RoadType road_road_type)
82 {
83 return RoadVehHasPowerOnRoad(engine_road_type, road_road_type);
84 }
85
RoadVehHasPowerOnRoad(RoadType engine_road_type,RoadType road_road_type)86 /* static */ bool ScriptRoad::RoadVehHasPowerOnRoad(RoadType engine_road_type, RoadType road_road_type)
87 {
88 if (!IsRoadTypeAvailable(engine_road_type)) return false;
89 if (!IsRoadTypeAvailable(road_road_type)) return false;
90
91 return ::HasPowerOnRoad((::RoadType)engine_road_type, (::RoadType)road_road_type);
92 }
93
HasRoadType(TileIndex tile,RoadType road_type)94 /* static */ bool ScriptRoad::HasRoadType(TileIndex tile, RoadType road_type)
95 {
96 if (!ScriptMap::IsValidTile(tile)) return false;
97 if (!IsRoadTypeAvailable(road_type)) return false;
98 return ::GetAnyRoadBits(tile, ::GetRoadTramType((::RoadType)road_type), false) != ROAD_NONE;
99 }
100
AreRoadTilesConnected(TileIndex t1,TileIndex t2)101 /* static */ bool ScriptRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2)
102 {
103 if (!::IsValidTile(t1)) return false;
104 if (!::IsValidTile(t2)) return false;
105 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
106
107 /* Tiles not neighbouring */
108 if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false;
109
110 RoadTramType rtt = ::GetRoadTramType(ScriptObject::GetRoadType());
111 RoadBits r1 = ::GetAnyRoadBits(t1, rtt); // TODO
112 RoadBits r2 = ::GetAnyRoadBits(t2, rtt); // TODO
113
114 uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3);
115 uint dir_2 = 2 ^ dir_1;
116
117 DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE;
118
119 return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
120 }
121
ConvertRoadType(TileIndex start_tile,TileIndex end_tile,RoadType road_type)122 /* static */ bool ScriptRoad::ConvertRoadType(TileIndex start_tile, TileIndex end_tile, RoadType road_type)
123 {
124 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
125 EnforcePrecondition(false, ::IsValidTile(start_tile));
126 EnforcePrecondition(false, ::IsValidTile(end_tile));
127 EnforcePrecondition(false, IsRoadTypeAvailable(road_type));
128
129 return ScriptObject::DoCommand(start_tile, end_tile, (::RoadType)road_type, CMD_CONVERT_ROAD);
130 }
131
132 /* Helper functions for ScriptRoad::CanBuildConnectedRoadParts(). */
133
134 /**
135 * Check whether the given existing bits the start and end part can be build.
136 * As the function assumes the bits being build on a slope that does not
137 * allow level foundations all of the existing parts will always be in
138 * a straight line. This also needs to hold for the start and end parts,
139 * otherwise it is for sure not valid. Finally a check will be done to
140 * determine whether the existing road parts match the to-be-build parts.
141 * As they can only be placed in one direction, just checking the start
142 * part with the first existing part is enough.
143 * @param existing The existing road parts.
144 * @param start The part that should be build first.
145 * @param end The part that will be build second.
146 * @return True if and only if the road bits can be build.
147 */
CheckAutoExpandedRoadBits(const Array * existing,int32 start,int32 end)148 static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end)
149 {
150 return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end);
151 }
152
153 /**
154 * Lookup function for building road parts when building on slopes is disabled.
155 * @param slope The slope of the tile to examine.
156 * @param existing The existing road parts.
157 * @param start The part that should be build first.
158 * @param end The part that will be build second.
159 * @return 0 when the build parts do not connect, 1 when they do connect once
160 * they are build or 2 when building the first part automatically
161 * builds the second part.
162 */
LookupWithoutBuildOnSlopes(::Slope slope,const Array * existing,int32 start,int32 end)163 static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end)
164 {
165 switch (slope) {
166 /* Flat slopes can always be build. */
167 case SLOPE_FLAT:
168 return 1;
169
170 /* Only 4 of the slopes can be build upon. Testing the existing bits is
171 * necessary because these bits can be something else when the settings
172 * in the game have been changed.
173 */
174 case SLOPE_NE: case SLOPE_SW:
175 return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0;
176 case SLOPE_SE: case SLOPE_NW:
177 return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0;
178
179 /* Any other tile cannot be built on. */
180 default:
181 return 0;
182 }
183 }
184
185 /**
186 * Rotate a neighbour bit a single time clockwise.
187 * @param neighbour The neighbour.
188 * @return The rotate neighbour data.
189 */
RotateNeighbour(int32 neighbour)190 static int32 RotateNeighbour(int32 neighbour)
191 {
192 switch (neighbour) {
193 case -2: return -1;
194 case -1: return 2;
195 case 1: return -2;
196 case 2: return 1;
197 default: NOT_REACHED();
198 }
199 }
200
201 /**
202 * Convert a neighbour to a road bit representation for easy internal use.
203 * @param neighbour The neighbour.
204 * @return The bits representing the direction.
205 */
NeighbourToRoadBits(int32 neighbour)206 static RoadBits NeighbourToRoadBits(int32 neighbour)
207 {
208 switch (neighbour) {
209 case -2: return ROAD_NW;
210 case -1: return ROAD_NE;
211 case 2: return ROAD_SE;
212 case 1: return ROAD_SW;
213 default: NOT_REACHED();
214 }
215 }
216
217 /**
218 * Lookup function for building road parts when building on slopes is enabled.
219 * @param slope The slope of the tile to examine.
220 * @param existing The existing neighbours.
221 * @param start The part that should be build first.
222 * @param end The part that will be build second.
223 * @return 0 when the build parts do not connect, 1 when they do connect once
224 * they are build or 2 when building the first part automatically
225 * builds the second part.
226 */
LookupWithBuildOnSlopes(::Slope slope,Array * existing,int32 start,int32 end)227 static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end)
228 {
229 /* Steep slopes behave the same as slopes with one corner raised. */
230 if (IsSteepSlope(slope)) {
231 slope = SlopeWithOneCornerRaised(GetHighestSlopeCorner(slope));
232 }
233
234 /* The slope is not steep. Furthermore lots of slopes are generally the
235 * same but are only rotated. So to reduce the amount of lookup work that
236 * needs to be done the data is made uniform. This means rotating the
237 * existing parts and updating the slope. */
238 static const ::Slope base_slopes[] = {
239 SLOPE_FLAT, SLOPE_W, SLOPE_W, SLOPE_SW,
240 SLOPE_W, SLOPE_EW, SLOPE_SW, SLOPE_WSE,
241 SLOPE_W, SLOPE_SW, SLOPE_EW, SLOPE_WSE,
242 SLOPE_SW, SLOPE_WSE, SLOPE_WSE};
243 static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1};
244
245 if (slope >= (::Slope)lengthof(base_slopes)) {
246 /* This slope is an invalid slope, so ignore it. */
247 return -1;
248 }
249 byte base_rotate = base_rotates[slope];
250 slope = base_slopes[slope];
251
252 /* Some slopes don't need rotating, so return early when we know we do
253 * not need to rotate. */
254 switch (slope) {
255 case SLOPE_FLAT:
256 /* Flat slopes can always be build. */
257 return 1;
258
259 case SLOPE_EW:
260 case SLOPE_WSE:
261 /* A slope similar to a SLOPE_EW or SLOPE_WSE will always cause
262 * foundations which makes them accessible from all sides. */
263 return 1;
264
265 case SLOPE_W:
266 case SLOPE_SW:
267 /* A slope for which we need perform some calculations. */
268 break;
269
270 default:
271 /* An invalid slope. */
272 return -1;
273 }
274
275 /* Now perform the actual rotation. */
276 for (int j = 0; j < base_rotate; j++) {
277 for (size_t i = 0; i < existing->size; i++) {
278 existing->array[i] = RotateNeighbour(existing->array[i]);
279 }
280 start = RotateNeighbour(start);
281 end = RotateNeighbour(end);
282 }
283
284 /* Create roadbits out of the data for easier handling. */
285 RoadBits start_roadbits = NeighbourToRoadBits(start);
286 RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end);
287 RoadBits existing_roadbits = ROAD_NONE;
288 for (size_t i = 0; i < existing->size; i++) {
289 existing_roadbits |= NeighbourToRoadBits(existing->array[i]);
290 }
291
292 switch (slope) {
293 case SLOPE_W:
294 /* A slope similar to a SLOPE_W. */
295 switch (new_roadbits) {
296 case ROAD_N:
297 case ROAD_E:
298 case ROAD_S:
299 /* Cannot build anything with a turn from the low side. */
300 return 0;
301
302 case ROAD_X:
303 case ROAD_Y:
304 /* A 'sloped' tile is going to be build. */
305 if ((existing_roadbits | new_roadbits) != new_roadbits) {
306 /* There is already a foundation on the tile, or at least
307 * another slope that is not compatible with the new one. */
308 return 0;
309 }
310 /* If the start is in the low part, it is automatically
311 * building the second part too. */
312 return ((start_roadbits & ROAD_E) && !(existing_roadbits & ROAD_W)) ? 2 : 1;
313
314 default:
315 /* Roadbits causing a foundation are going to be build.
316 * When the existing roadbits are slopes (the lower bits
317 * are used), this cannot be done. */
318 if ((existing_roadbits | new_roadbits) == new_roadbits) return 1;
319 return (existing_roadbits & ROAD_E) ? 0 : 1;
320 }
321
322 case SLOPE_SW:
323 /* A slope similar to a SLOPE_SW. */
324 switch (new_roadbits) {
325 case ROAD_N:
326 case ROAD_E:
327 /* Cannot build anything with a turn from the low side. */
328 return 0;
329
330 case ROAD_X:
331 /* A 'sloped' tile is going to be build. */
332 if ((existing_roadbits | new_roadbits) != new_roadbits) {
333 /* There is already a foundation on the tile, or at least
334 * another slope that is not compatible with the new one. */
335 return 0;
336 }
337 /* If the start is in the low part, it is automatically
338 * building the second part too. */
339 return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1;
340
341 default:
342 /* Roadbits causing a foundation are going to be build.
343 * When the existing roadbits are slopes (the lower bits
344 * are used), this cannot be done. */
345 return (existing_roadbits & ROAD_NE) ? 0 : 1;
346 }
347
348 default:
349 NOT_REACHED();
350 }
351 }
352
353 /**
354 * Normalise all input data so we can easily handle it without needing
355 * to call the API lots of times or create large if-elseif-elseif-else
356 * constructs.
357 * In this case it means that a TileXY(0, -1) becomes -2 and TileXY(0, 1)
358 * becomes 2. TileXY(-1, 0) and TileXY(1, 0) stay respectively -1 and 1.
359 * Any other value means that it is an invalid tile offset.
360 * @param tile The tile to normalise.
361 * @return True if and only if the tile offset is valid.
362 */
NormaliseTileOffset(int32 * tile)363 static bool NormaliseTileOffset(int32 *tile)
364 {
365 if (*tile == 1 || *tile == -1) return true;
366 if (*tile == ::TileDiffXY(0, -1)) {
367 *tile = -2;
368 return true;
369 }
370 if (*tile == ::TileDiffXY(0, 1)) {
371 *tile = 2;
372 return true;
373 }
374 return false;
375 }
376
CanBuildConnectedRoadParts(ScriptTile::Slope slope_,Array * existing,TileIndex start_,TileIndex end_)377 /* static */ int32 ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::Slope slope_, Array *existing, TileIndex start_, TileIndex end_)
378 {
379 ::Slope slope = (::Slope)slope_;
380 int32 start = start_;
381 int32 end = end_;
382
383 /* The start tile and end tile cannot be the same tile either. */
384 if (start == end) return -1;
385
386 for (size_t i = 0; i < existing->size; i++) {
387 if (!NormaliseTileOffset(&existing->array[i])) return -1;
388 }
389
390 if (!NormaliseTileOffset(&start)) return -1;
391 if (!NormaliseTileOffset(&end)) return -1;
392
393 /* Without build on slopes the characteristics are vastly different, so use
394 * a different helper function (one that is much simpler). */
395 return _settings_game.construction.build_on_slopes ? LookupWithBuildOnSlopes(slope, existing, start, end) : LookupWithoutBuildOnSlopes(slope, existing, start, end);
396 }
397
CanBuildConnectedRoadPartsHere(TileIndex tile,TileIndex start,TileIndex end)398 /* static */ int32 ScriptRoad::CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end)
399 {
400 if (!::IsValidTile(tile) || !::IsValidTile(start) || !::IsValidTile(end)) return -1;
401 if (::DistanceManhattan(tile, start) != 1 || ::DistanceManhattan(tile, end) != 1) return -1;
402
403 /* ROAD_NW ROAD_SW ROAD_SE ROAD_NE */
404 const TileIndexDiff neighbours[] = {::TileDiffXY(0, -1), ::TileDiffXY(1, 0), ::TileDiffXY(0, 1), ::TileDiffXY(-1, 0)};
405 Array *existing = (Array*)alloca(sizeof(Array) + lengthof(neighbours) * sizeof(int32));
406 existing->size = 0;
407
408 ::RoadBits rb = ::ROAD_NONE;
409 if (::IsNormalRoadTile(tile)) {
410 rb = ::GetAllRoadBits(tile);
411 } else {
412 rb = ::GetAnyRoadBits(tile, RTT_ROAD) | ::GetAnyRoadBits(tile, RTT_TRAM);
413 }
414 for (uint i = 0; i < lengthof(neighbours); i++) {
415 if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i];
416 }
417
418 return ScriptRoad::CanBuildConnectedRoadParts(ScriptTile::GetSlope(tile), existing, start - tile, end - tile);
419 }
420
421 /**
422 * Check whether one can reach (possibly by building) a road piece the center
423 * of the neighbouring tile. This includes roads and (drive through) stations.
424 * @param rt The road type we want to know reachability for
425 * @param start_tile The tile to "enter" the neighbouring tile.
426 * @param neighbour The direction to the neighbouring tile to "enter".
427 * @return true if and only if the tile is reachable.
428 */
NeighbourHasReachableRoad(::RoadType rt,TileIndex start_tile,DiagDirection neighbour)429 static bool NeighbourHasReachableRoad(::RoadType rt, TileIndex start_tile, DiagDirection neighbour)
430 {
431 TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour);
432 if (!HasBit(::GetPresentRoadTypes(neighbour_tile), rt)) return false;
433
434 switch (::GetTileType(neighbour_tile)) {
435 case MP_ROAD:
436 return (::GetRoadTileType(neighbour_tile) != ROAD_TILE_DEPOT);
437
438 case MP_STATION:
439 if (::IsDriveThroughStopTile(neighbour_tile)) {
440 return (::DiagDirToAxis(neighbour) == ::DiagDirToAxis(::GetRoadStopDir(neighbour_tile)));
441 }
442 return false;
443
444 default:
445 return false;
446 }
447 }
448
GetNeighbourRoadCount(TileIndex tile)449 /* static */ int32 ScriptRoad::GetNeighbourRoadCount(TileIndex tile)
450 {
451 if (!::IsValidTile(tile)) return false;
452 if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
453
454 ::RoadType rt = (::RoadType)GetCurrentRoadType();
455 int32 neighbour = 0;
456
457 if (TileX(tile) > 0 && NeighbourHasReachableRoad(rt, tile, DIAGDIR_NE)) neighbour++;
458 if (NeighbourHasReachableRoad(rt, tile, DIAGDIR_SE)) neighbour++;
459 if (NeighbourHasReachableRoad(rt, tile, DIAGDIR_SW)) neighbour++;
460 if (TileY(tile) > 0 && NeighbourHasReachableRoad(rt, tile, DIAGDIR_NW)) neighbour++;
461
462 return neighbour;
463 }
464
GetRoadDepotFrontTile(TileIndex depot)465 /* static */ TileIndex ScriptRoad::GetRoadDepotFrontTile(TileIndex depot)
466 {
467 if (!IsRoadDepotTile(depot)) return INVALID_TILE;
468
469 return depot + ::TileOffsByDiagDir(::GetRoadDepotDirection(depot));
470 }
471
GetRoadStationFrontTile(TileIndex station)472 /* static */ TileIndex ScriptRoad::GetRoadStationFrontTile(TileIndex station)
473 {
474 if (!IsRoadStationTile(station)) return INVALID_TILE;
475
476 return station + ::TileOffsByDiagDir(::GetRoadStopDir(station));
477 }
478
GetDriveThroughBackTile(TileIndex station)479 /* static */ TileIndex ScriptRoad::GetDriveThroughBackTile(TileIndex station)
480 {
481 if (!IsDriveThroughRoadStationTile(station)) return INVALID_TILE;
482
483 return station + ::TileOffsByDiagDir(::ReverseDiagDir(::GetRoadStopDir(station)));
484 }
485
_BuildRoadInternal(TileIndex start,TileIndex end,bool one_way,bool full)486 /* static */ bool ScriptRoad::_BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full)
487 {
488 EnforcePrecondition(false, start != end);
489 EnforcePrecondition(false, ::IsValidTile(start));
490 EnforcePrecondition(false, ::IsValidTile(end));
491 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
492 EnforcePrecondition(false, !one_way || RoadTypeIsRoad(ScriptObject::GetRoadType()));
493 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
494
495 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (ScriptObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 10) | 1 << 11, CMD_BUILD_LONG_ROAD);
496 }
497
BuildRoad(TileIndex start,TileIndex end)498 /* static */ bool ScriptRoad::BuildRoad(TileIndex start, TileIndex end)
499 {
500 return _BuildRoadInternal(start, end, false, false);
501 }
502
BuildOneWayRoad(TileIndex start,TileIndex end)503 /* static */ bool ScriptRoad::BuildOneWayRoad(TileIndex start, TileIndex end)
504 {
505 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
506 return _BuildRoadInternal(start, end, true, false);
507 }
508
BuildRoadFull(TileIndex start,TileIndex end)509 /* static */ bool ScriptRoad::BuildRoadFull(TileIndex start, TileIndex end)
510 {
511 return _BuildRoadInternal(start, end, false, true);
512 }
513
BuildOneWayRoadFull(TileIndex start,TileIndex end)514 /* static */ bool ScriptRoad::BuildOneWayRoadFull(TileIndex start, TileIndex end)
515 {
516 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
517 return _BuildRoadInternal(start, end, true, true);
518 }
519
BuildRoadDepot(TileIndex tile,TileIndex front)520 /* static */ bool ScriptRoad::BuildRoadDepot(TileIndex tile, TileIndex front)
521 {
522 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
523 EnforcePrecondition(false, tile != front);
524 EnforcePrecondition(false, ::IsValidTile(tile));
525 EnforcePrecondition(false, ::IsValidTile(front));
526 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
527 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
528
529 uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
530
531 return ScriptObject::DoCommand(tile, entrance_dir | (ScriptObject::GetRoadType() << 2), 0, CMD_BUILD_ROAD_DEPOT);
532 }
533
_BuildRoadStationInternal(TileIndex tile,TileIndex front,RoadVehicleType road_veh_type,bool drive_through,StationID station_id)534 /* static */ bool ScriptRoad::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)
535 {
536 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
537 EnforcePrecondition(false, tile != front);
538 EnforcePrecondition(false, ::IsValidTile(tile));
539 EnforcePrecondition(false, ::IsValidTile(front));
540 EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
541 EnforcePrecondition(false, station_id == ScriptStation::STATION_NEW || station_id == ScriptStation::STATION_JOIN_ADJACENT || ScriptStation::IsValidStation(station_id));
542 EnforcePrecondition(false, road_veh_type == ROADVEHTYPE_BUS || road_veh_type == ROADVEHTYPE_TRUCK);
543 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
544
545 uint entrance_dir;
546 if (drive_through) {
547 entrance_dir = ::TileY(tile) != ::TileY(front);
548 } else {
549 entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
550 }
551
552 uint p2 = station_id == ScriptStation::STATION_JOIN_ADJACENT ? 0 : 4;
553 p2 |= drive_through ? 2 : 0;
554 p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0;
555 p2 |= ScriptObject::GetRoadType() << 5;
556 p2 |= entrance_dir << 3;
557 p2 |= (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
558 return ScriptObject::DoCommand(tile, 1 | 1 << 8, p2, CMD_BUILD_ROAD_STOP);
559 }
560
BuildRoadStation(TileIndex tile,TileIndex front,RoadVehicleType road_veh_type,StationID station_id)561 /* static */ bool ScriptRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
562 {
563 return _BuildRoadStationInternal(tile, front, road_veh_type, false, station_id);
564 }
565
BuildDriveThroughRoadStation(TileIndex tile,TileIndex front,RoadVehicleType road_veh_type,StationID station_id)566 /* static */ bool ScriptRoad::BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
567 {
568 return _BuildRoadStationInternal(tile, front, road_veh_type, true, station_id);
569 }
570
RemoveRoad(TileIndex start,TileIndex end)571 /* static */ bool ScriptRoad::RemoveRoad(TileIndex start, TileIndex end)
572 {
573 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
574 EnforcePrecondition(false, start != end);
575 EnforcePrecondition(false, ::IsValidTile(start));
576 EnforcePrecondition(false, ::IsValidTile(end));
577 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
578 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
579
580 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 1 : 2) | (ScriptObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
581 }
582
RemoveRoadFull(TileIndex start,TileIndex end)583 /* static */ bool ScriptRoad::RemoveRoadFull(TileIndex start, TileIndex end)
584 {
585 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
586 EnforcePrecondition(false, start != end);
587 EnforcePrecondition(false, ::IsValidTile(start));
588 EnforcePrecondition(false, ::IsValidTile(end));
589 EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
590 EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
591
592 return ScriptObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 2 : 1) | (ScriptObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
593 }
594
RemoveRoadDepot(TileIndex tile)595 /* static */ bool ScriptRoad::RemoveRoadDepot(TileIndex tile)
596 {
597 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
598 EnforcePrecondition(false, ::IsValidTile(tile));
599 EnforcePrecondition(false, IsTileType(tile, MP_ROAD))
600 EnforcePrecondition(false, GetRoadTileType(tile) == ROAD_TILE_DEPOT);
601
602 return ScriptObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
603 }
604
RemoveRoadStation(TileIndex tile)605 /* static */ bool ScriptRoad::RemoveRoadStation(TileIndex tile)
606 {
607 EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
608 EnforcePrecondition(false, ::IsValidTile(tile));
609 EnforcePrecondition(false, IsTileType(tile, MP_STATION));
610 EnforcePrecondition(false, IsRoadStop(tile));
611
612 return ScriptObject::DoCommand(tile, 1 | 1 << 8, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
613 }
614
GetBuildCost(RoadType roadtype,BuildType build_type)615 /* static */ Money ScriptRoad::GetBuildCost(RoadType roadtype, BuildType build_type)
616 {
617 if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return -1;
618
619 switch (build_type) {
620 case BT_ROAD: return ::RoadBuildCost((::RoadType)roadtype);
621 case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_ROAD, 1, nullptr);
622 case BT_BUS_STOP: return ::GetPrice(PR_BUILD_STATION_BUS, 1, nullptr);
623 case BT_TRUCK_STOP: return ::GetPrice(PR_BUILD_STATION_TRUCK, 1, nullptr);
624 default: return -1;
625 }
626 }
627
GetRoadTramType(RoadType roadtype)628 /* static */ ScriptRoad::RoadTramTypes ScriptRoad::GetRoadTramType(RoadType roadtype)
629 {
630 return (RoadTramTypes)(1 << ::GetRoadTramType((::RoadType)roadtype));
631 }
632
GetMaxSpeed(RoadType road_type)633 /* static */ int32 ScriptRoad::GetMaxSpeed(RoadType road_type)
634 {
635 if (!ScriptRoad::IsRoadTypeAvailable(road_type)) return 0;
636
637 return GetRoadTypeInfo((::RoadType)road_type)->max_speed;
638 }
639
GetMaintenanceCostFactor(RoadType roadtype)640 /* static */ uint16 ScriptRoad::GetMaintenanceCostFactor(RoadType roadtype)
641 {
642 if (!ScriptRoad::IsRoadTypeAvailable(roadtype)) return 0;
643
644 return GetRoadTypeInfo((::RoadType)roadtype)->maintenance_multiplier;
645 }
646