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_rail.cpp Implementation of ScriptRail. */
9 
10 #include "../../stdafx.h"
11 #include "script_rail.hpp"
12 #include "script_map.hpp"
13 #include "script_station.hpp"
14 #include "script_industrytype.hpp"
15 #include "script_cargo.hpp"
16 #include "../../debug.h"
17 #include "../../station_base.h"
18 #include "../../newgrf_generic.h"
19 #include "../../newgrf_station.h"
20 #include "../../strings_func.h"
21 
22 #include "../../safeguards.h"
23 
GetName(RailType rail_type)24 /* static */ char *ScriptRail::GetName(RailType rail_type)
25 {
26 	if (!IsRailTypeAvailable(rail_type)) return nullptr;
27 
28 	return GetString(GetRailTypeInfo((::RailType)rail_type)->strings.menu_text);
29 }
30 
IsRailTile(TileIndex tile)31 /* static */ bool ScriptRail::IsRailTile(TileIndex tile)
32 {
33 	if (!::IsValidTile(tile)) return false;
34 
35 	return (::IsTileType(tile, MP_RAILWAY) && !::IsRailDepot(tile)) ||
36 			(::HasStationTileRail(tile) && !::IsStationTileBlocked(tile)) || ::IsLevelCrossingTile(tile);
37 }
38 
IsLevelCrossingTile(TileIndex tile)39 /* static */ bool ScriptRail::IsLevelCrossingTile(TileIndex tile)
40 {
41 	if (!::IsValidTile(tile)) return false;
42 
43 	return ::IsLevelCrossingTile(tile);
44 }
45 
IsRailDepotTile(TileIndex tile)46 /* static */ bool ScriptRail::IsRailDepotTile(TileIndex tile)
47 {
48 	if (!::IsValidTile(tile)) return false;
49 
50 	return ::IsRailDepotTile(tile);
51 }
52 
IsRailStationTile(TileIndex tile)53 /* static */ bool ScriptRail::IsRailStationTile(TileIndex tile)
54 {
55 	if (!::IsValidTile(tile)) return false;
56 
57 	return ::IsRailStationTile(tile);
58 }
59 
IsRailWaypointTile(TileIndex tile)60 /* static */ bool ScriptRail::IsRailWaypointTile(TileIndex tile)
61 {
62 	if (!::IsValidTile(tile)) return false;
63 
64 	return ::IsRailWaypointTile(tile);
65 }
66 
IsRailTypeAvailable(RailType rail_type)67 /* static */ bool ScriptRail::IsRailTypeAvailable(RailType rail_type)
68 {
69 	if ((::RailType)rail_type >= RAILTYPE_END) return false;
70 
71 	return ScriptObject::GetCompany() == OWNER_DEITY || ::HasRailtypeAvail(ScriptObject::GetCompany(), (::RailType)rail_type);
72 }
73 
GetCurrentRailType()74 /* static */ ScriptRail::RailType ScriptRail::GetCurrentRailType()
75 {
76 	return (RailType)ScriptObject::GetRailType();
77 }
78 
SetCurrentRailType(RailType rail_type)79 /* static */ void ScriptRail::SetCurrentRailType(RailType rail_type)
80 {
81 	if (!IsRailTypeAvailable(rail_type)) return;
82 
83 	ScriptObject::SetRailType((::RailType)rail_type);
84 }
85 
TrainCanRunOnRail(ScriptRail::RailType engine_rail_type,ScriptRail::RailType track_rail_type)86 /* static */ bool ScriptRail::TrainCanRunOnRail(ScriptRail::RailType engine_rail_type, ScriptRail::RailType track_rail_type)
87 {
88 	if (!ScriptRail::IsRailTypeAvailable(engine_rail_type)) return false;
89 	if (!ScriptRail::IsRailTypeAvailable(track_rail_type)) return false;
90 
91 	return ::IsCompatibleRail((::RailType)engine_rail_type, (::RailType)track_rail_type);
92 }
93 
TrainHasPowerOnRail(ScriptRail::RailType engine_rail_type,ScriptRail::RailType track_rail_type)94 /* static */ bool ScriptRail::TrainHasPowerOnRail(ScriptRail::RailType engine_rail_type, ScriptRail::RailType track_rail_type)
95 {\
96 	if (!ScriptRail::IsRailTypeAvailable(engine_rail_type)) return false;
97 	if (!ScriptRail::IsRailTypeAvailable(track_rail_type)) return false;
98 
99 	return ::HasPowerOnRail((::RailType)engine_rail_type, (::RailType)track_rail_type);
100 }
101 
GetRailType(TileIndex tile)102 /* static */ ScriptRail::RailType ScriptRail::GetRailType(TileIndex tile)
103 {
104 	if (!ScriptTile::HasTransportType(tile, ScriptTile::TRANSPORT_RAIL)) return RAILTYPE_INVALID;
105 
106 	return (RailType)::GetRailType(tile);
107 }
108 
ConvertRailType(TileIndex start_tile,TileIndex end_tile,ScriptRail::RailType convert_to)109 /* static */ bool ScriptRail::ConvertRailType(TileIndex start_tile, TileIndex end_tile, ScriptRail::RailType convert_to)
110 {
111 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
112 	EnforcePrecondition(false, ::IsValidTile(start_tile));
113 	EnforcePrecondition(false, ::IsValidTile(end_tile));
114 	EnforcePrecondition(false, IsRailTypeAvailable(convert_to));
115 
116 	return ScriptObject::DoCommand(start_tile, end_tile, convert_to, CMD_CONVERT_RAIL);
117 }
118 
GetRailDepotFrontTile(TileIndex depot)119 /* static */ TileIndex ScriptRail::GetRailDepotFrontTile(TileIndex depot)
120 {
121 	if (!IsRailDepotTile(depot)) return INVALID_TILE;
122 
123 	return depot + ::TileOffsByDiagDir(::GetRailDepotDirection(depot));
124 }
125 
GetRailStationDirection(TileIndex tile)126 /* static */ ScriptRail::RailTrack ScriptRail::GetRailStationDirection(TileIndex tile)
127 {
128 	if (!IsRailStationTile(tile)) return RAILTRACK_INVALID;
129 
130 	return (RailTrack)::GetRailStationTrackBits(tile);
131 }
132 
BuildRailDepot(TileIndex tile,TileIndex front)133 /* static */ bool ScriptRail::BuildRailDepot(TileIndex tile, TileIndex front)
134 {
135 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
136 	EnforcePrecondition(false, tile != front);
137 	EnforcePrecondition(false, ::IsValidTile(tile));
138 	EnforcePrecondition(false, ::IsValidTile(front));
139 	EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
140 	EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
141 
142 	uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
143 
144 	return ScriptObject::DoCommand(tile, ScriptObject::GetRailType(), entrance_dir, CMD_BUILD_TRAIN_DEPOT);
145 }
146 
BuildRailStation(TileIndex tile,RailTrack direction,uint num_platforms,uint platform_length,StationID station_id)147 /* static */ bool ScriptRail::BuildRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, StationID station_id)
148 {
149 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
150 	EnforcePrecondition(false, ::IsValidTile(tile));
151 	EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW);
152 	EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF);
153 	EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF);
154 	EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
155 	EnforcePrecondition(false, station_id == ScriptStation::STATION_NEW || station_id == ScriptStation::STATION_JOIN_ADJACENT || ScriptStation::IsValidStation(station_id));
156 
157 	uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
158 	if (direction == RAILTRACK_NW_SE) p1 |= (1 << 6);
159 	if (station_id != ScriptStation::STATION_JOIN_ADJACENT) p1 |= (1 << 24);
160 	return ScriptObject::DoCommand(tile, p1, (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16, CMD_BUILD_RAIL_STATION);
161 }
162 
BuildNewGRFRailStation(TileIndex tile,RailTrack direction,uint num_platforms,uint platform_length,StationID station_id,CargoID cargo_id,IndustryType source_industry,IndustryType goal_industry,int distance,bool source_station)163 /* static */ bool ScriptRail::BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, StationID station_id, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station)
164 {
165 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
166 	EnforcePrecondition(false, ::IsValidTile(tile));
167 	EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW);
168 	EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF);
169 	EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF);
170 	EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
171 	EnforcePrecondition(false, station_id == ScriptStation::STATION_NEW || station_id == ScriptStation::STATION_JOIN_ADJACENT || ScriptStation::IsValidStation(station_id));
172 	EnforcePrecondition(false, ScriptCargo::IsValidCargo(cargo_id));
173 	EnforcePrecondition(false, source_industry == ScriptIndustryType::INDUSTRYTYPE_UNKNOWN || source_industry == ScriptIndustryType::INDUSTRYTYPE_TOWN || ScriptIndustryType::IsValidIndustryType(source_industry));
174 	EnforcePrecondition(false, goal_industry   == ScriptIndustryType::INDUSTRYTYPE_UNKNOWN || goal_industry   == ScriptIndustryType::INDUSTRYTYPE_TOWN || ScriptIndustryType::IsValidIndustryType(goal_industry));
175 
176 	uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
177 	if (direction == RAILTRACK_NW_SE) p1 |= 1 << 6;
178 	if (station_id != ScriptStation::STATION_JOIN_ADJACENT) p1 |= (1 << 24);
179 
180 	const GRFFile *file;
181 	uint16 res = GetAiPurchaseCallbackResult(
182 		GSF_STATIONS,
183 		cargo_id,
184 		0,
185 		source_industry,
186 		goal_industry,
187 		std::min(255, distance / 2),
188 		AICE_STATION_GET_STATION_ID,
189 		source_station ? 0 : 1,
190 		std::min(15u, num_platforms) << 4 | std::min(15u, platform_length),
191 		&file
192 	);
193 	uint32 p2 = (ScriptStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
194 	if (res != CALLBACK_FAILED) {
195 		int index = 0;
196 		const StationSpec *spec = StationClass::GetByGrf(file->grfid, res, &index);
197 		if (spec == nullptr) {
198 			Debug(grf, 1, "{} returned an invalid station ID for 'AI construction/purchase selection (18)' callback", file->filename);
199 		} else {
200 			/* We might have gotten an usable station spec. Try to build it, but if it fails we'll fall back to the original station. */
201 			if (ScriptObject::DoCommand(tile, p1, p2 | spec->cls_id | index << 8, CMD_BUILD_RAIL_STATION)) return true;
202 		}
203 	}
204 
205 	return ScriptObject::DoCommand(tile, p1, p2, CMD_BUILD_RAIL_STATION);
206 }
207 
BuildRailWaypoint(TileIndex tile)208 /* static */ bool ScriptRail::BuildRailWaypoint(TileIndex tile)
209 {
210 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
211 	EnforcePrecondition(false, ::IsValidTile(tile));
212 	EnforcePrecondition(false, IsRailTile(tile));
213 	EnforcePrecondition(false, GetRailTracks(tile) == RAILTRACK_NE_SW || GetRailTracks(tile) == RAILTRACK_NW_SE);
214 	EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
215 
216 	return ScriptObject::DoCommand(tile, GetCurrentRailType() | (GetRailTracks(tile) == RAILTRACK_NE_SW ? AXIS_X : AXIS_Y) << 6 | 1 << 8 | 1 << 16, STAT_CLASS_WAYP | INVALID_STATION << 16, CMD_BUILD_RAIL_WAYPOINT);
217 }
218 
RemoveRailWaypointTileRectangle(TileIndex tile,TileIndex tile2,bool keep_rail)219 /* static */ bool ScriptRail::RemoveRailWaypointTileRectangle(TileIndex tile, TileIndex tile2, bool keep_rail)
220 {
221 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
222 	EnforcePrecondition(false, ::IsValidTile(tile));
223 	EnforcePrecondition(false, ::IsValidTile(tile2));
224 
225 	return ScriptObject::DoCommand(tile, tile2, keep_rail ? 1 : 0, CMD_REMOVE_FROM_RAIL_WAYPOINT);
226 }
227 
RemoveRailStationTileRectangle(TileIndex tile,TileIndex tile2,bool keep_rail)228 /* static */ bool ScriptRail::RemoveRailStationTileRectangle(TileIndex tile, TileIndex tile2, bool keep_rail)
229 {
230 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
231 	EnforcePrecondition(false, ::IsValidTile(tile));
232 	EnforcePrecondition(false, ::IsValidTile(tile2));
233 
234 	return ScriptObject::DoCommand(tile, tile2, keep_rail ? 1 : 0, CMD_REMOVE_FROM_RAIL_STATION);
235 }
236 
GetRailTracks(TileIndex tile)237 /* static */ uint ScriptRail::GetRailTracks(TileIndex tile)
238 {
239 	if (!IsRailTile(tile)) return RAILTRACK_INVALID;
240 
241 	if (IsRailStationTile(tile) || IsRailWaypointTile(tile)) return ::TrackToTrackBits(::GetRailStationTrack(tile));
242 	if (IsLevelCrossingTile(tile)) return ::GetCrossingRailBits(tile);
243 	if (IsRailDepotTile(tile)) return ::TRACK_BIT_NONE;
244 	return ::GetTrackBits(tile);
245 }
246 
BuildRailTrack(TileIndex tile,RailTrack rail_track)247 /* static */ bool ScriptRail::BuildRailTrack(TileIndex tile, RailTrack rail_track)
248 {
249 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
250 	EnforcePrecondition(false, ::IsValidTile(tile));
251 	EnforcePrecondition(false, rail_track != 0);
252 	EnforcePrecondition(false, (rail_track & ~::TRACK_BIT_ALL) == 0);
253 	EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0);
254 	EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
255 
256 	return ScriptObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 6), CMD_BUILD_RAILROAD_TRACK);
257 }
258 
RemoveRailTrack(TileIndex tile,RailTrack rail_track)259 /* static */ bool ScriptRail::RemoveRailTrack(TileIndex tile, RailTrack rail_track)
260 {
261 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
262 	EnforcePrecondition(false, ::IsValidTile(tile));
263 	EnforcePrecondition(false, ::IsPlainRailTile(tile) || ::IsLevelCrossingTile(tile));
264 	EnforcePrecondition(false, GetRailTracks(tile) & rail_track);
265 	EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0);
266 
267 	return ScriptObject::DoCommand(tile, tile, FindFirstTrack((::TrackBits)rail_track) << 6, CMD_REMOVE_RAILROAD_TRACK);
268 }
269 
AreTilesConnected(TileIndex from,TileIndex tile,TileIndex to)270 /* static */ bool ScriptRail::AreTilesConnected(TileIndex from, TileIndex tile, TileIndex to)
271 {
272 	if (!IsRailTile(tile)) return false;
273 	if (from == to || ScriptMap::DistanceManhattan(from, tile) != 1 || ScriptMap::DistanceManhattan(tile, to) != 1) return false;
274 
275 	if (to < from) ::Swap(from, to);
276 
277 	if (tile - from == 1) {
278 		if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NE_SW) != 0;
279 		if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NE_SE) != 0;
280 	} else if (tile - from == ::MapSizeX()) {
281 		if (tile - to == 1) return (GetRailTracks(tile) & RAILTRACK_NW_NE) != 0;
282 		if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NW_SW) != 0;
283 		if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NW_SE) != 0;
284 	} else {
285 		return (GetRailTracks(tile) & RAILTRACK_SW_SE) != 0;
286 	}
287 
288 	NOT_REACHED();
289 }
290 
291 /**
292  * Prepare the second parameter for CmdBuildRailroadTrack and CmdRemoveRailroadTrack. The direction
293  * depends on all three tiles. Sometimes the third tile needs to be adjusted.
294  */
SimulateDrag(TileIndex from,TileIndex tile,TileIndex * to)295 static uint32 SimulateDrag(TileIndex from, TileIndex tile, TileIndex *to)
296 {
297 	int diag_offset = abs(abs((int)::TileX(*to) - (int)::TileX(tile)) - abs((int)::TileY(*to) - (int)::TileY(tile)));
298 	uint32 p2 = 0;
299 	if (::TileY(from) == ::TileY(*to)) {
300 		p2 |= (TRACK_X << 6);
301 		*to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
302 	} else if (::TileX(from) == ::TileX(*to)) {
303 		p2 |= (TRACK_Y << 6);
304 		*to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
305 	} else if (::TileY(from) < ::TileY(tile)) {
306 		if (::TileX(*to) < ::TileX(tile)) {
307 			p2 |= (TRACK_UPPER << 6);
308 		} else {
309 			p2 |= (TRACK_LEFT << 6);
310 		}
311 		if (diag_offset != 0) {
312 			*to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
313 		} else {
314 			*to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
315 		}
316 	} else if (::TileY(from) > ::TileY(tile)) {
317 		if (::TileX(*to) < ::TileX(tile)) {
318 			p2 |= (TRACK_RIGHT << 6);
319 		} else {
320 			p2 |= (TRACK_LOWER << 6);
321 		}
322 		if (diag_offset != 0) {
323 			*to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
324 		} else {
325 			*to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
326 		}
327 	} else if (::TileX(from) < ::TileX(tile)) {
328 		if (::TileY(*to) < ::TileY(tile)) {
329 			p2 |= (TRACK_UPPER << 6);
330 		} else {
331 			p2 |= (TRACK_RIGHT << 6);
332 		}
333 		if (diag_offset == 0) {
334 			*to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
335 		} else {
336 			*to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
337 		}
338 	} else if (::TileX(from) > ::TileX(tile)) {
339 		if (::TileY(*to) < ::TileY(tile)) {
340 			p2 |= (TRACK_LEFT << 6);
341 		} else {
342 			p2 |= (TRACK_LOWER << 6);
343 		}
344 		if (diag_offset == 0) {
345 			*to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
346 		} else {
347 			*to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
348 		}
349 	}
350 	return p2;
351 }
352 
BuildRail(TileIndex from,TileIndex tile,TileIndex to)353 /* static */ bool ScriptRail::BuildRail(TileIndex from, TileIndex tile, TileIndex to)
354 {
355 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
356 	EnforcePrecondition(false, ::IsValidTile(from));
357 	EnforcePrecondition(false, ::IsValidTile(tile));
358 	EnforcePrecondition(false, ::IsValidTile(to));
359 	EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1);
360 	EnforcePrecondition(false, ::DistanceManhattan(tile, to) >= 1);
361 	EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
362 	int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile)));
363 	EnforcePrecondition(false, diag_offset <= 1 ||
364 			(::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) ||
365 			(::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to)));
366 
367 	uint32 p2 = SimulateDrag(from, tile, &to) | 1 << 10 | ScriptRail::GetCurrentRailType();;
368 	return ScriptObject::DoCommand(tile, to, p2, CMD_BUILD_RAILROAD_TRACK);
369 }
370 
RemoveRail(TileIndex from,TileIndex tile,TileIndex to)371 /* static */ bool ScriptRail::RemoveRail(TileIndex from, TileIndex tile, TileIndex to)
372 {
373 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
374 	EnforcePrecondition(false, ::IsValidTile(from));
375 	EnforcePrecondition(false, ::IsValidTile(tile));
376 	EnforcePrecondition(false, ::IsValidTile(to));
377 	EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1);
378 	EnforcePrecondition(false, ::DistanceManhattan(tile, to) >= 1);
379 	int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile)));
380 	EnforcePrecondition(false, diag_offset <= 1 ||
381 			(::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) ||
382 			(::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to)));
383 
384 	uint32 p2 = SimulateDrag(from, tile, &to);
385 	return ScriptObject::DoCommand(tile, to, p2, CMD_REMOVE_RAILROAD_TRACK);
386 }
387 
388 /**
389  * Contains information about the trackdir that belongs to a track when entering
390  *   from a specific direction.
391  */
392 struct ScriptRailSignalData {
393 	Track track;        ///< The track that will be taken to travel.
394 	Trackdir trackdir;  ///< The Trackdir belonging to that track.
395 	uint signal_cycles; ///< How many times the signal should be cycled in order to build it in the correct direction.
396 };
397 
398 static const int NUM_TRACK_DIRECTIONS = 3; ///< The number of directions you can go when entering a tile.
399 
400 /**
401  * List information about the trackdir and number of needed cycles for building signals when
402  *   entering a track from a specific direction. The first index is the difference between the
403  *   TileIndex of the previous and current tile, where (-)MapSizeX is replaced with -2 / 2 and
404  *   2 it added.
405  */
406 static const ScriptRailSignalData _possible_trackdirs[5][NUM_TRACK_DIRECTIONS] = {
407 	{{TRACK_UPPER,   TRACKDIR_UPPER_E, 0}, {TRACK_Y,       TRACKDIR_Y_SE,    0}, {TRACK_LEFT,    TRACKDIR_LEFT_S,  1}},
408 	{{TRACK_RIGHT,   TRACKDIR_RIGHT_S, 1}, {TRACK_X,       TRACKDIR_X_SW,    1}, {TRACK_UPPER,   TRACKDIR_UPPER_W, 1}},
409 	{{INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}},
410 	{{TRACK_LOWER,   TRACKDIR_LOWER_E, 0}, {TRACK_X,       TRACKDIR_X_NE,    0}, {TRACK_LEFT,    TRACKDIR_LEFT_N,  0}},
411 	{{TRACK_RIGHT,   TRACKDIR_RIGHT_N, 0}, {TRACK_Y,       TRACKDIR_Y_NW,    1}, {TRACK_LOWER,   TRACKDIR_LOWER_W, 1}}
412 };
413 
GetSignalType(TileIndex tile,TileIndex front)414 /* static */ ScriptRail::SignalType ScriptRail::GetSignalType(TileIndex tile, TileIndex front)
415 {
416 	if (ScriptMap::DistanceManhattan(tile, front) != 1) return SIGNALTYPE_NONE;
417 	if (!::IsTileType(tile, MP_RAILWAY) || !::HasSignals(tile)) return SIGNALTYPE_NONE;
418 
419 	int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
420 
421 	for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
422 		const Track &track = _possible_trackdirs[data_index][i].track;
423 		if (!(::TrackToTrackBits(track) & GetRailTracks(tile))) continue;
424 		if (!HasSignalOnTrack(tile, track)) continue;
425 		if (!HasSignalOnTrackdir(tile, _possible_trackdirs[data_index][i].trackdir)) continue;
426 		SignalType st = (SignalType)::GetSignalType(tile, track);
427 		if (HasSignalOnTrackdir(tile, ::ReverseTrackdir(_possible_trackdirs[data_index][i].trackdir))) st = (SignalType)(st | SIGNALTYPE_TWOWAY);
428 		return st;
429 	}
430 
431 	return SIGNALTYPE_NONE;
432 }
433 
434 /**
435  * Check if signal_type is a valid SignalType.
436  */
IsValidSignalType(int signal_type)437 static bool IsValidSignalType(int signal_type)
438 {
439 	if (signal_type < ScriptRail::SIGNALTYPE_NORMAL || signal_type > ScriptRail::SIGNALTYPE_COMBO_TWOWAY) return false;
440 	if (signal_type > ScriptRail::SIGNALTYPE_PBS_ONEWAY && signal_type < ScriptRail::SIGNALTYPE_NORMAL_TWOWAY) return false;
441 	return true;
442 }
443 
BuildSignal(TileIndex tile,TileIndex front,SignalType signal)444 /* static */ bool ScriptRail::BuildSignal(TileIndex tile, TileIndex front, SignalType signal)
445 {
446 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
447 	EnforcePrecondition(false, ScriptMap::DistanceManhattan(tile, front) == 1)
448 	EnforcePrecondition(false, ::IsPlainRailTile(tile));
449 	EnforcePrecondition(false, ::IsValidSignalType(signal));
450 
451 	Track track = INVALID_TRACK;
452 	uint signal_cycles;
453 
454 	int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
455 	for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
456 		const Track &t = _possible_trackdirs[data_index][i].track;
457 		if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue;
458 		track = t;
459 		signal_cycles = _possible_trackdirs[data_index][i].signal_cycles;
460 		break;
461 	}
462 	EnforcePrecondition(false, track != INVALID_TRACK);
463 
464 	uint p1 = track;
465 	if (signal < SIGNALTYPE_TWOWAY) {
466 		if (signal != SIGNALTYPE_PBS && signal != SIGNALTYPE_PBS_ONEWAY) signal_cycles++;
467 		p1 |= (signal_cycles << 15);
468 	}
469 	p1 |= ((signal >= SIGNALTYPE_TWOWAY ? signal ^ SIGNALTYPE_TWOWAY : signal) << 5);
470 
471 	return ScriptObject::DoCommand(tile, p1, 0, CMD_BUILD_SIGNALS);
472 }
473 
RemoveSignal(TileIndex tile,TileIndex front)474 /* static */ bool ScriptRail::RemoveSignal(TileIndex tile, TileIndex front)
475 {
476 	EnforcePrecondition(false, ScriptObject::GetCompany() != OWNER_DEITY);
477 	EnforcePrecondition(false, ScriptMap::DistanceManhattan(tile, front) == 1)
478 	EnforcePrecondition(false, GetSignalType(tile, front) != SIGNALTYPE_NONE);
479 
480 	Track track = INVALID_TRACK;
481 	int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
482 	for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
483 		const Track &t = _possible_trackdirs[data_index][i].track;
484 		if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue;
485 		track = t;
486 		break;
487 	}
488 	EnforcePrecondition(false, track != INVALID_TRACK);
489 
490 	return ScriptObject::DoCommand(tile, track, 0, CMD_REMOVE_SIGNALS);
491 }
492 
GetBuildCost(RailType railtype,BuildType build_type)493 /* static */ Money ScriptRail::GetBuildCost(RailType railtype, BuildType build_type)
494 {
495 	if (!ScriptRail::IsRailTypeAvailable(railtype)) return -1;
496 
497 	switch (build_type) {
498 		case BT_TRACK:    return ::RailBuildCost((::RailType)railtype);
499 		case BT_SIGNAL:   return ::GetPrice(PR_BUILD_SIGNALS, 1, nullptr);
500 		case BT_DEPOT:    return ::GetPrice(PR_BUILD_DEPOT_TRAIN, 1, nullptr);
501 		case BT_STATION:  return ::GetPrice(PR_BUILD_STATION_RAIL, 1, nullptr) + ::GetPrice(PR_BUILD_STATION_RAIL_LENGTH, 1, nullptr);
502 		case BT_WAYPOINT: return ::GetPrice(PR_BUILD_WAYPOINT_RAIL, 1, nullptr);
503 		default: return -1;
504 	}
505 }
506 
GetMaxSpeed(RailType railtype)507 /* static */ int32 ScriptRail::GetMaxSpeed(RailType railtype)
508 {
509 	if (!ScriptRail::IsRailTypeAvailable(railtype)) return -1;
510 
511 	return ::GetRailTypeInfo((::RailType)railtype)->max_speed;
512 }
513 
GetMaintenanceCostFactor(RailType railtype)514 /* static */ uint16 ScriptRail::GetMaintenanceCostFactor(RailType railtype)
515 {
516 	if (!ScriptRail::IsRailTypeAvailable(railtype)) return 0;
517 
518 	return ::GetRailTypeInfo((::RailType)railtype)->maintenance_multiplier;
519 }
520