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 rail_cmd.cpp Handling of rail tiles. */
9 
10 #include "stdafx.h"
11 #include "cmd_helper.h"
12 #include "viewport_func.h"
13 #include "command_func.h"
14 #include "depot_base.h"
15 #include "pathfinder/yapf/yapf_cache.h"
16 #include "newgrf_debug.h"
17 #include "newgrf_railtype.h"
18 #include "train.h"
19 #include "autoslope.h"
20 #include "water.h"
21 #include "tunnelbridge_map.h"
22 #include "vehicle_func.h"
23 #include "sound_func.h"
24 #include "tunnelbridge.h"
25 #include "elrail_func.h"
26 #include "town.h"
27 #include "pbs.h"
28 #include "company_base.h"
29 #include "core/backup_type.hpp"
30 #include "date_func.h"
31 #include "strings_func.h"
32 #include "company_gui.h"
33 #include "object_map.h"
34 
35 #include "table/strings.h"
36 #include "table/railtypes.h"
37 #include "table/track_land.h"
38 
39 #include "safeguards.h"
40 
41 /** Helper type for lists/vectors of trains */
42 typedef std::vector<Train *> TrainList;
43 
44 RailtypeInfo _railtypes[RAILTYPE_END];
45 std::vector<RailType> _sorted_railtypes;
46 RailTypes _railtypes_hidden_mask;
47 
48 /** Enum holding the signal offset in the sprite sheet according to the side it is representing. */
49 enum SignalOffsets {
50 	SIGNAL_TO_SOUTHWEST,
51 	SIGNAL_TO_NORTHEAST,
52 	SIGNAL_TO_SOUTHEAST,
53 	SIGNAL_TO_NORTHWEST,
54 	SIGNAL_TO_EAST,
55 	SIGNAL_TO_WEST,
56 	SIGNAL_TO_SOUTH,
57 	SIGNAL_TO_NORTH,
58 };
59 
60 /**
61  * Reset all rail type information to its default values.
62  */
ResetRailTypes()63 void ResetRailTypes()
64 {
65 	static_assert(lengthof(_original_railtypes) <= lengthof(_railtypes));
66 
67 	uint i = 0;
68 	for (; i < lengthof(_original_railtypes); i++) _railtypes[i] = _original_railtypes[i];
69 
70 	static const RailtypeInfo empty_railtype = {
71 		{0,0,0,0,0,0,0,0,0,0,0,0},
72 		{0,0,0,0,0,0,0,0,{}},
73 		{0,0,0,0,0,0,0,0},
74 		{0,0,0,0,0,0},
75 		0, RAILTYPES_NONE, RAILTYPES_NONE, 0, 0, 0, RTFB_NONE, 0, 0, 0, 0, 0,
76 		RailTypeLabelList(), 0, 0, RAILTYPES_NONE, RAILTYPES_NONE, 0,
77 		{}, {} };
78 	for (; i < lengthof(_railtypes);          i++) _railtypes[i] = empty_railtype;
79 
80 	_railtypes_hidden_mask = RAILTYPES_NONE;
81 }
82 
ResolveRailTypeGUISprites(RailtypeInfo * rti)83 void ResolveRailTypeGUISprites(RailtypeInfo *rti)
84 {
85 	SpriteID cursors_base = GetCustomRailSprite(rti, INVALID_TILE, RTSG_CURSORS);
86 	if (cursors_base != 0) {
87 		rti->gui_sprites.build_ns_rail = cursors_base +  0;
88 		rti->gui_sprites.build_x_rail  = cursors_base +  1;
89 		rti->gui_sprites.build_ew_rail = cursors_base +  2;
90 		rti->gui_sprites.build_y_rail  = cursors_base +  3;
91 		rti->gui_sprites.auto_rail     = cursors_base +  4;
92 		rti->gui_sprites.build_depot   = cursors_base +  5;
93 		rti->gui_sprites.build_tunnel  = cursors_base +  6;
94 		rti->gui_sprites.convert_rail  = cursors_base +  7;
95 		rti->cursor.rail_ns   = cursors_base +  8;
96 		rti->cursor.rail_swne = cursors_base +  9;
97 		rti->cursor.rail_ew   = cursors_base + 10;
98 		rti->cursor.rail_nwse = cursors_base + 11;
99 		rti->cursor.autorail  = cursors_base + 12;
100 		rti->cursor.depot     = cursors_base + 13;
101 		rti->cursor.tunnel    = cursors_base + 14;
102 		rti->cursor.convert   = cursors_base + 15;
103 	}
104 
105 	/* Array of default GUI signal sprite numbers. */
106 	const SpriteID _signal_lookup[2][SIGTYPE_END] = {
107 		{SPR_IMG_SIGNAL_ELECTRIC_NORM,  SPR_IMG_SIGNAL_ELECTRIC_ENTRY, SPR_IMG_SIGNAL_ELECTRIC_EXIT,
108 		 SPR_IMG_SIGNAL_ELECTRIC_COMBO, SPR_IMG_SIGNAL_ELECTRIC_PBS,   SPR_IMG_SIGNAL_ELECTRIC_PBS_OWAY},
109 
110 		{SPR_IMG_SIGNAL_SEMAPHORE_NORM,  SPR_IMG_SIGNAL_SEMAPHORE_ENTRY, SPR_IMG_SIGNAL_SEMAPHORE_EXIT,
111 		 SPR_IMG_SIGNAL_SEMAPHORE_COMBO, SPR_IMG_SIGNAL_SEMAPHORE_PBS,   SPR_IMG_SIGNAL_SEMAPHORE_PBS_OWAY},
112 	};
113 
114 	for (SignalType type = SIGTYPE_NORMAL; type < SIGTYPE_END; type = (SignalType)(type + 1)) {
115 		for (SignalVariant var = SIG_ELECTRIC; var <= SIG_SEMAPHORE; var = (SignalVariant)(var + 1)) {
116 			SpriteID red   = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_RED, true);
117 			SpriteID green = GetCustomSignalSprite(rti, INVALID_TILE, type, var, SIGNAL_STATE_GREEN, true);
118 			rti->gui_sprites.signals[type][var][0] = (red != 0)   ? red + SIGNAL_TO_SOUTH   : _signal_lookup[var][type];
119 			rti->gui_sprites.signals[type][var][1] = (green != 0) ? green + SIGNAL_TO_SOUTH : _signal_lookup[var][type] + 1;
120 		}
121 	}
122 }
123 
124 /**
125  * Compare railtypes based on their sorting order.
126  * @param first  The railtype to compare to.
127  * @param second The railtype to compare.
128  * @return True iff the first should be sorted before the second.
129  */
CompareRailTypes(const RailType & first,const RailType & second)130 static bool CompareRailTypes(const RailType &first, const RailType &second)
131 {
132 	return GetRailTypeInfo(first)->sorting_order < GetRailTypeInfo(second)->sorting_order;
133 }
134 
135 /**
136  * Resolve sprites of custom rail types
137  */
InitRailTypes()138 void InitRailTypes()
139 {
140 	for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
141 		RailtypeInfo *rti = &_railtypes[rt];
142 		ResolveRailTypeGUISprites(rti);
143 		if (HasBit(rti->flags, RTF_HIDDEN)) SetBit(_railtypes_hidden_mask, rt);
144 	}
145 
146 	_sorted_railtypes.clear();
147 	for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
148 		if (_railtypes[rt].label != 0 && !HasBit(_railtypes_hidden_mask, rt)) {
149 			_sorted_railtypes.push_back(rt);
150 		}
151 	}
152 	std::sort(_sorted_railtypes.begin(), _sorted_railtypes.end(), CompareRailTypes);
153 }
154 
155 /**
156  * Allocate a new rail type label
157  */
AllocateRailType(RailTypeLabel label)158 RailType AllocateRailType(RailTypeLabel label)
159 {
160 	for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
161 		RailtypeInfo *rti = &_railtypes[rt];
162 
163 		if (rti->label == 0) {
164 			/* Set up new rail type */
165 			*rti = _original_railtypes[RAILTYPE_RAIL];
166 			rti->label = label;
167 			rti->alternate_labels.clear();
168 
169 			/* Make us compatible with ourself. */
170 			rti->powered_railtypes    = (RailTypes)(1LL << rt);
171 			rti->compatible_railtypes = (RailTypes)(1LL << rt);
172 
173 			/* We also introduce ourself. */
174 			rti->introduces_railtypes = (RailTypes)(1LL << rt);
175 
176 			/* Default sort order; order of allocation, but with some
177 			 * offsets so it's easier for NewGRF to pick a spot without
178 			 * changing the order of other (original) rail types.
179 			 * The << is so you can place other railtypes in between the
180 			 * other railtypes, the 7 is to be able to place something
181 			 * before the first (default) rail type. */
182 			rti->sorting_order = rt << 4 | 7;
183 			return rt;
184 		}
185 	}
186 
187 	return INVALID_RAILTYPE;
188 }
189 
190 static const byte _track_sloped_sprites[14] = {
191 	14, 15, 22, 13,
192 	 0, 21, 17, 12,
193 	23,  0, 18, 20,
194 	19, 16
195 };
196 
197 
198 /*         4
199  *     ---------
200  *    |\       /|
201  *    | \    1/ |
202  *    |  \   /  |
203  *    |   \ /   |
204  *  16|    \    |32
205  *    |   / \2  |
206  *    |  /   \  |
207  *    | /     \ |
208  *    |/       \|
209  *     ---------
210  *         8
211  */
212 
213 
214 
215 /* MAP2 byte:    abcd???? => Signal On? Same coding as map3lo
216  * MAP3LO byte:  abcd???? => Signal Exists?
217  *               a and b are for diagonals, upper and left,
218  *               one for each direction. (ie a == NE->SW, b ==
219  *               SW->NE, or v.v., I don't know. b and c are
220  *               similar for lower and right.
221  * MAP2 byte:    ????abcd => Type of ground.
222  * MAP3LO byte:  ????abcd => Type of rail.
223  * MAP5:         00abcdef => rail
224  *               01abcdef => rail w/ signals
225  *               10uuuuuu => unused
226  *               11uuuudd => rail depot
227  */
228 
229 /**
230  * Tests if a vehicle interacts with the specified track.
231  * All track bits interact except parallel #TRACK_BIT_HORZ or #TRACK_BIT_VERT.
232  *
233  * @param tile The tile.
234  * @param track The track.
235  * @return Succeeded command (no train found), or a failed command (a train was found).
236  */
EnsureNoTrainOnTrack(TileIndex tile,Track track)237 static CommandCost EnsureNoTrainOnTrack(TileIndex tile, Track track)
238 {
239 	TrackBits rail_bits = TrackToTrackBits(track);
240 	return EnsureNoTrainOnTrackBits(tile, rail_bits);
241 }
242 
243 /**
244  * Check that the new track bits may be built.
245  * @param tile %Tile to build on.
246  * @param to_build New track bits.
247  * @param flags    Flags of the operation.
248  * @return Succeeded or failed command.
249  */
CheckTrackCombination(TileIndex tile,TrackBits to_build,uint flags)250 static CommandCost CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags)
251 {
252 	if (!IsPlainRail(tile)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
253 
254 	/* So, we have a tile with tracks on it (and possibly signals). Let's see
255 	 * what tracks first */
256 	TrackBits current = GetTrackBits(tile); // The current track layout.
257 	TrackBits future = current | to_build;  // The track layout we want to build.
258 
259 	/* Are we really building something new? */
260 	if (current == future) {
261 		/* Nothing new is being built */
262 		return_cmd_error(STR_ERROR_ALREADY_BUILT);
263 	}
264 
265 	/* Normally, we may overlap and any combination is valid */
266 	return CommandCost();
267 }
268 
269 
270 /** Valid TrackBits on a specific (non-steep)-slope without foundation */
271 static const TrackBits _valid_tracks_without_foundation[15] = {
272 	TRACK_BIT_ALL,
273 	TRACK_BIT_RIGHT,
274 	TRACK_BIT_UPPER,
275 	TRACK_BIT_X,
276 
277 	TRACK_BIT_LEFT,
278 	TRACK_BIT_NONE,
279 	TRACK_BIT_Y,
280 	TRACK_BIT_LOWER,
281 
282 	TRACK_BIT_LOWER,
283 	TRACK_BIT_Y,
284 	TRACK_BIT_NONE,
285 	TRACK_BIT_LEFT,
286 
287 	TRACK_BIT_X,
288 	TRACK_BIT_UPPER,
289 	TRACK_BIT_RIGHT,
290 };
291 
292 /** Valid TrackBits on a specific (non-steep)-slope with leveled foundation */
293 static const TrackBits _valid_tracks_on_leveled_foundation[15] = {
294 	TRACK_BIT_NONE,
295 	TRACK_BIT_LEFT,
296 	TRACK_BIT_LOWER,
297 	TRACK_BIT_Y | TRACK_BIT_LOWER | TRACK_BIT_LEFT,
298 
299 	TRACK_BIT_RIGHT,
300 	TRACK_BIT_ALL,
301 	TRACK_BIT_X | TRACK_BIT_LOWER | TRACK_BIT_RIGHT,
302 	TRACK_BIT_ALL,
303 
304 	TRACK_BIT_UPPER,
305 	TRACK_BIT_X | TRACK_BIT_UPPER | TRACK_BIT_LEFT,
306 	TRACK_BIT_ALL,
307 	TRACK_BIT_ALL,
308 
309 	TRACK_BIT_Y | TRACK_BIT_UPPER | TRACK_BIT_RIGHT,
310 	TRACK_BIT_ALL,
311 	TRACK_BIT_ALL
312 };
313 
314 /**
315  * Checks if a track combination is valid on a specific slope and returns the needed foundation.
316  *
317  * @param tileh Tile slope.
318  * @param bits  Trackbits.
319  * @return Needed foundation or FOUNDATION_INVALID if track/slope combination is not allowed.
320  */
GetRailFoundation(Slope tileh,TrackBits bits)321 Foundation GetRailFoundation(Slope tileh, TrackBits bits)
322 {
323 	if (bits == TRACK_BIT_NONE) return FOUNDATION_NONE;
324 
325 	if (IsSteepSlope(tileh)) {
326 		/* Test for inclined foundations */
327 		if (bits == TRACK_BIT_X) return FOUNDATION_INCLINED_X;
328 		if (bits == TRACK_BIT_Y) return FOUNDATION_INCLINED_Y;
329 
330 		/* Get higher track */
331 		Corner highest_corner = GetHighestSlopeCorner(tileh);
332 		TrackBits higher_track = CornerToTrackBits(highest_corner);
333 
334 		/* Only higher track? */
335 		if (bits == higher_track) return HalftileFoundation(highest_corner);
336 
337 		/* Overlap with higher track? */
338 		if (TracksOverlap(bits | higher_track)) return FOUNDATION_INVALID;
339 
340 		/* either lower track or both higher and lower track */
341 		return ((bits & higher_track) != 0 ? FOUNDATION_STEEP_BOTH : FOUNDATION_STEEP_LOWER);
342 	} else {
343 		if ((~_valid_tracks_without_foundation[tileh] & bits) == 0) return FOUNDATION_NONE;
344 
345 		bool valid_on_leveled = ((~_valid_tracks_on_leveled_foundation[tileh] & bits) == 0);
346 
347 		Corner track_corner;
348 		switch (bits) {
349 			case TRACK_BIT_LEFT:  track_corner = CORNER_W; break;
350 			case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
351 			case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
352 			case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
353 
354 			case TRACK_BIT_HORZ:
355 				if (tileh == SLOPE_N) return HalftileFoundation(CORNER_N);
356 				if (tileh == SLOPE_S) return HalftileFoundation(CORNER_S);
357 				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
358 
359 			case TRACK_BIT_VERT:
360 				if (tileh == SLOPE_W) return HalftileFoundation(CORNER_W);
361 				if (tileh == SLOPE_E) return HalftileFoundation(CORNER_E);
362 				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
363 
364 			case TRACK_BIT_X:
365 				if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_X;
366 				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
367 
368 			case TRACK_BIT_Y:
369 				if (IsSlopeWithOneCornerRaised(tileh)) return FOUNDATION_INCLINED_Y;
370 				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
371 
372 			default:
373 				return (valid_on_leveled ? FOUNDATION_LEVELED : FOUNDATION_INVALID);
374 		}
375 		/* Single diagonal track */
376 
377 		/* Track must be at least valid on leveled foundation */
378 		if (!valid_on_leveled) return FOUNDATION_INVALID;
379 
380 		/* If slope has three raised corners, build leveled foundation */
381 		if (IsSlopeWithThreeCornersRaised(tileh)) return FOUNDATION_LEVELED;
382 
383 		/* If neighboured corners of track_corner are lowered, build halftile foundation */
384 		if ((tileh & SlopeWithThreeCornersRaised(OppositeCorner(track_corner))) == SlopeWithOneCornerRaised(track_corner)) return HalftileFoundation(track_corner);
385 
386 		/* else special anti-zig-zag foundation */
387 		return SpecialRailFoundation(track_corner);
388 	}
389 }
390 
391 
392 /**
393  * Tests if a track can be build on a tile.
394  *
395  * @param tileh Tile slope.
396  * @param rail_bits Tracks to build.
397  * @param existing Tracks already built.
398  * @param tile Tile (used for water test)
399  * @return Error message or cost for foundation building.
400  */
CheckRailSlope(Slope tileh,TrackBits rail_bits,TrackBits existing,TileIndex tile)401 static CommandCost CheckRailSlope(Slope tileh, TrackBits rail_bits, TrackBits existing, TileIndex tile)
402 {
403 	/* don't allow building on the lower side of a coast */
404 	if (GetFloodingBehaviour(tile) != FLOOD_NONE) {
405 		if (!IsSteepSlope(tileh) && ((~_valid_tracks_on_leveled_foundation[tileh] & (rail_bits | existing)) != 0)) return_cmd_error(STR_ERROR_CAN_T_BUILD_ON_WATER);
406 	}
407 
408 	Foundation f_new = GetRailFoundation(tileh, rail_bits | existing);
409 
410 	/* check track/slope combination */
411 	if ((f_new == FOUNDATION_INVALID) ||
412 			((f_new != FOUNDATION_NONE) && (!_settings_game.construction.build_on_slopes))) {
413 		return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
414 	}
415 
416 	Foundation f_old = GetRailFoundation(tileh, existing);
417 	return CommandCost(EXPENSES_CONSTRUCTION, f_new != f_old ? _price[PR_BUILD_FOUNDATION] : (Money)0);
418 }
419 
420 /* Validate functions for rail building */
ValParamTrackOrientation(Track track)421 static inline bool ValParamTrackOrientation(Track track)
422 {
423 	return IsValidTrack(track);
424 }
425 
426 /**
427  * Build a single piece of rail
428  * @param tile tile  to build on
429  * @param flags operation to perform
430  * @param p1 railtype of being built piece (normal, mono, maglev)
431  * @param p2 various bitstuffed elements
432  *           - (bit  0- 2) - track-orientation, valid values: 0-5 (@see Track)
433  *           - (bit  3)    - 0 = error on signal in the way, 1 = auto remove signals when in the way
434  * @param text unused
435  * @return the cost of this operation or an error
436  */
CmdBuildSingleRail(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)437 CommandCost CmdBuildSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
438 {
439 	RailType railtype = Extract<RailType, 0, 6>(p1);
440 	Track track = Extract<Track, 0, 3>(p2);
441 	bool auto_remove_signals = HasBit(p2, 3);
442 	CommandCost cost(EXPENSES_CONSTRUCTION);
443 
444 	if (!ValParamRailtype(railtype) || !ValParamTrackOrientation(track)) return CMD_ERROR;
445 
446 	Slope tileh = GetTileSlope(tile);
447 	TrackBits trackbit = TrackToTrackBits(track);
448 
449 	switch (GetTileType(tile)) {
450 		case MP_RAILWAY: {
451 			CommandCost ret = CheckTileOwnership(tile);
452 			if (ret.Failed()) return ret;
453 
454 			if (!IsPlainRail(tile)) return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); // just get appropriate error message
455 
456 			if (!IsCompatibleRail(GetRailType(tile), railtype)) return_cmd_error(STR_ERROR_IMPOSSIBLE_TRACK_COMBINATION);
457 
458 			ret = CheckTrackCombination(tile, trackbit, flags);
459 			if (ret.Succeeded()) ret = EnsureNoTrainOnTrack(tile, track);
460 			if (ret.Failed()) return ret;
461 
462 			ret = CheckRailSlope(tileh, trackbit, GetTrackBits(tile), tile);
463 			if (ret.Failed()) return ret;
464 			cost.AddCost(ret);
465 
466 			if (HasSignals(tile) && TracksOverlap(GetTrackBits(tile) | TrackToTrackBits(track))) {
467 				/* If adding the new track causes any overlap, all signals must be removed first */
468 				if (!auto_remove_signals) return_cmd_error(STR_ERROR_MUST_REMOVE_SIGNALS_FIRST);
469 
470 				for (Track track_it = TRACK_BEGIN; track_it < TRACK_END; track_it++) {
471 					if (HasTrack(tile, track_it) && HasSignalOnTrack(tile, track_it)) {
472 						CommandCost ret_remove_signals = DoCommand(tile, track_it, 0, flags, CMD_REMOVE_SIGNALS);
473 						if (ret_remove_signals.Failed()) return ret_remove_signals;
474 						cost.AddCost(ret_remove_signals);
475 					}
476 				}
477 			}
478 
479 			/* If the rail types don't match, try to convert only if engines of
480 			 * the new rail type are not powered on the present rail type and engines of
481 			 * the present rail type are powered on the new rail type. */
482 			if (GetRailType(tile) != railtype && !HasPowerOnRail(railtype, GetRailType(tile))) {
483 				if (HasPowerOnRail(GetRailType(tile), railtype)) {
484 					ret = DoCommand(tile, tile, railtype, flags, CMD_CONVERT_RAIL);
485 					if (ret.Failed()) return ret;
486 					cost.AddCost(ret);
487 				} else {
488 					return CMD_ERROR;
489 				}
490 			}
491 
492 			if (flags & DC_EXEC) {
493 				SetRailGroundType(tile, RAIL_GROUND_BARREN);
494 				TrackBits bits = GetTrackBits(tile);
495 				SetTrackBits(tile, bits | trackbit);
496 				/* Subtract old infrastructure count. */
497 				uint pieces = CountBits(bits);
498 				if (TracksOverlap(bits)) pieces *= pieces;
499 				Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] -= pieces;
500 				/* Add new infrastructure count. */
501 				pieces = CountBits(bits | trackbit);
502 				if (TracksOverlap(bits | trackbit)) pieces *= pieces;
503 				Company::Get(GetTileOwner(tile))->infrastructure.rail[GetRailType(tile)] += pieces;
504 				DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
505 			}
506 			break;
507 		}
508 
509 		case MP_ROAD: {
510 			/* Level crossings may only be built on these slopes */
511 			if (!HasBit(VALID_LEVEL_CROSSING_SLOPES, tileh)) return_cmd_error(STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION);
512 
513 			CommandCost ret = EnsureNoVehicleOnGround(tile);
514 			if (ret.Failed()) return ret;
515 
516 			if (IsNormalRoad(tile)) {
517 				if (HasRoadWorks(tile)) return_cmd_error(STR_ERROR_ROAD_WORKS_IN_PROGRESS);
518 
519 				if (GetDisallowedRoadDirections(tile) != DRD_NONE) return_cmd_error(STR_ERROR_CROSSING_ON_ONEWAY_ROAD);
520 
521 				if (RailNoLevelCrossings(railtype)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_RAIL);
522 
523 				RoadType roadtype_road = GetRoadTypeRoad(tile);
524 				RoadType roadtype_tram = GetRoadTypeTram(tile);
525 
526 				if (roadtype_road != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_road)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD);
527 				if (roadtype_tram != INVALID_ROADTYPE && RoadNoLevelCrossing(roadtype_tram)) return_cmd_error(STR_ERROR_CROSSING_DISALLOWED_ROAD);
528 
529 				RoadBits road = GetRoadBits(tile, RTT_ROAD);
530 				RoadBits tram = GetRoadBits(tile, RTT_TRAM);
531 				if ((track == TRACK_X && ((road | tram) & ROAD_X) == 0) ||
532 						(track == TRACK_Y && ((road | tram) & ROAD_Y) == 0)) {
533 					Owner road_owner = GetRoadOwner(tile, RTT_ROAD);
534 					Owner tram_owner = GetRoadOwner(tile, RTT_TRAM);
535 					/* Disallow breaking end-of-line of someone else
536 					 * so trams can still reverse on this tile. */
537 					if (Company::IsValidID(tram_owner) && HasExactlyOneBit(tram)) {
538 						CommandCost ret = CheckOwnership(tram_owner);
539 						if (ret.Failed()) return ret;
540 					}
541 
542 					uint num_new_road_pieces = (road != ROAD_NONE) ? 2 - CountBits(road) : 0;
543 					if (num_new_road_pieces > 0) {
544 						cost.AddCost(num_new_road_pieces * RoadBuildCost(roadtype_road));
545 					}
546 
547 					uint num_new_tram_pieces = (tram != ROAD_NONE) ? 2 - CountBits(tram) : 0;
548 					if (num_new_tram_pieces > 0) {
549 						cost.AddCost(num_new_tram_pieces * RoadBuildCost(roadtype_tram));
550 					}
551 
552 					if (flags & DC_EXEC) {
553 						MakeRoadCrossing(tile, road_owner, tram_owner, _current_company, (track == TRACK_X ? AXIS_Y : AXIS_X), railtype, roadtype_road, roadtype_tram, GetTownIndex(tile));
554 						UpdateLevelCrossing(tile, false);
555 						Company::Get(_current_company)->infrastructure.rail[railtype] += LEVELCROSSING_TRACKBIT_FACTOR;
556 						DirtyCompanyInfrastructureWindows(_current_company);
557 						if (num_new_road_pieces > 0 && Company::IsValidID(road_owner)) {
558 							Company::Get(road_owner)->infrastructure.road[roadtype_road] += num_new_road_pieces;
559 							DirtyCompanyInfrastructureWindows(road_owner);
560 						}
561 						if (num_new_tram_pieces > 0 && Company::IsValidID(tram_owner)) {
562 							Company::Get(tram_owner)->infrastructure.road[roadtype_tram] += num_new_tram_pieces;
563 							DirtyCompanyInfrastructureWindows(tram_owner);
564 						}
565 					}
566 					break;
567 				}
568 			}
569 
570 			if (IsLevelCrossing(tile) && GetCrossingRailBits(tile) == trackbit) {
571 				return_cmd_error(STR_ERROR_ALREADY_BUILT);
572 			}
573 			FALLTHROUGH;
574 		}
575 
576 		default: {
577 			/* Will there be flat water on the lower halftile? */
578 			bool water_ground = IsTileType(tile, MP_WATER) && IsSlopeWithOneCornerRaised(tileh);
579 
580 			CommandCost ret = CheckRailSlope(tileh, trackbit, TRACK_BIT_NONE, tile);
581 			if (ret.Failed()) return ret;
582 			cost.AddCost(ret);
583 
584 			ret = DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
585 			if (ret.Failed()) return ret;
586 			cost.AddCost(ret);
587 
588 			if (water_ground) {
589 				cost.AddCost(-_price[PR_CLEAR_WATER]);
590 				cost.AddCost(_price[PR_CLEAR_ROUGH]);
591 			}
592 
593 			if (flags & DC_EXEC) {
594 				MakeRailNormal(tile, _current_company, trackbit, railtype);
595 				if (water_ground) {
596 					SetRailGroundType(tile, RAIL_GROUND_WATER);
597 					if (IsPossibleDockingTile(tile)) CheckForDockingTile(tile);
598 				}
599 				Company::Get(_current_company)->infrastructure.rail[railtype]++;
600 				DirtyCompanyInfrastructureWindows(_current_company);
601 			}
602 			break;
603 		}
604 	}
605 
606 	if (flags & DC_EXEC) {
607 		MarkTileDirtyByTile(tile);
608 		AddTrackToSignalBuffer(tile, track, _current_company);
609 		YapfNotifyTrackLayoutChange(tile, track);
610 	}
611 
612 	cost.AddCost(RailBuildCost(railtype));
613 	return cost;
614 }
615 
616 /**
617  * Remove a single piece of track
618  * @param tile tile to remove track from
619  * @param flags operation to perform
620  * @param p1 unused
621  * @param p2 rail orientation
622  * @param text unused
623  * @return the cost of this operation or an error
624  */
CmdRemoveSingleRail(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)625 CommandCost CmdRemoveSingleRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
626 {
627 	Track track = Extract<Track, 0, 3>(p2);
628 	CommandCost cost(EXPENSES_CONSTRUCTION);
629 	bool crossing = false;
630 
631 	if (!ValParamTrackOrientation(track)) return CMD_ERROR;
632 	TrackBits trackbit = TrackToTrackBits(track);
633 
634 	/* Need to read tile owner now because it may change when the rail is removed
635 	 * Also, in case of floods, _current_company != owner
636 	 * There may be invalid tiletype even in exec run (when removing long track),
637 	 * so do not call GetTileOwner(tile) in any case here */
638 	Owner owner = INVALID_OWNER;
639 
640 	Train *v = nullptr;
641 
642 	switch (GetTileType(tile)) {
643 		case MP_ROAD: {
644 			if (!IsLevelCrossing(tile) || GetCrossingRailBits(tile) != trackbit) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
645 
646 			if (_current_company != OWNER_WATER) {
647 				CommandCost ret = CheckTileOwnership(tile);
648 				if (ret.Failed()) return ret;
649 			}
650 
651 			if (!(flags & DC_BANKRUPT)) {
652 				CommandCost ret = EnsureNoVehicleOnGround(tile);
653 				if (ret.Failed()) return ret;
654 			}
655 
656 			cost.AddCost(RailClearCost(GetRailType(tile)));
657 
658 			if (flags & DC_EXEC) {
659 				if (HasReservedTracks(tile, trackbit)) {
660 					v = GetTrainForReservation(tile, track);
661 					if (v != nullptr) FreeTrainTrackReservation(v);
662 				}
663 
664 				owner = GetTileOwner(tile);
665 				Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= LEVELCROSSING_TRACKBIT_FACTOR;
666 				DirtyCompanyInfrastructureWindows(owner);
667 				MakeRoadNormal(tile, GetCrossingRoadBits(tile), GetRoadTypeRoad(tile), GetRoadTypeTram(tile), GetTownIndex(tile), GetRoadOwner(tile, RTT_ROAD), GetRoadOwner(tile, RTT_TRAM));
668 				DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile);
669 			}
670 			break;
671 		}
672 
673 		case MP_RAILWAY: {
674 			TrackBits present;
675 			/* There are no rails present at depots. */
676 			if (!IsPlainRail(tile)) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
677 
678 			if (_current_company != OWNER_WATER) {
679 				CommandCost ret = CheckTileOwnership(tile);
680 				if (ret.Failed()) return ret;
681 			}
682 
683 			CommandCost ret = EnsureNoTrainOnTrack(tile, track);
684 			if (ret.Failed()) return ret;
685 
686 			present = GetTrackBits(tile);
687 			if ((present & trackbit) == 0) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
688 			if (present == (TRACK_BIT_X | TRACK_BIT_Y)) crossing = true;
689 
690 			cost.AddCost(RailClearCost(GetRailType(tile)));
691 
692 			/* Charge extra to remove signals on the track, if they are there */
693 			if (HasSignalOnTrack(tile, track)) {
694 				cost.AddCost(DoCommand(tile, track, 0, flags, CMD_REMOVE_SIGNALS));
695 			}
696 
697 			if (flags & DC_EXEC) {
698 				if (HasReservedTracks(tile, trackbit)) {
699 					v = GetTrainForReservation(tile, track);
700 					if (v != nullptr) FreeTrainTrackReservation(v);
701 				}
702 
703 				owner = GetTileOwner(tile);
704 
705 				/* Subtract old infrastructure count. */
706 				uint pieces = CountBits(present);
707 				if (TracksOverlap(present)) pieces *= pieces;
708 				Company::Get(owner)->infrastructure.rail[GetRailType(tile)] -= pieces;
709 				/* Add new infrastructure count. */
710 				present ^= trackbit;
711 				pieces = CountBits(present);
712 				if (TracksOverlap(present)) pieces *= pieces;
713 				Company::Get(owner)->infrastructure.rail[GetRailType(tile)] += pieces;
714 				DirtyCompanyInfrastructureWindows(owner);
715 
716 				if (present == 0) {
717 					Slope tileh = GetTileSlope(tile);
718 					/* If there is flat water on the lower halftile, convert the tile to shore so the water remains */
719 					if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh)) {
720 						bool docking = IsDockingTile(tile);
721 						MakeShore(tile);
722 						SetDockingTile(tile, docking);
723 					} else {
724 						DoClearSquare(tile);
725 					}
726 					DeleteNewGRFInspectWindow(GSF_RAILTYPES, tile);
727 				} else {
728 					SetTrackBits(tile, present);
729 					SetTrackReservation(tile, GetRailReservationTrackBits(tile) & present);
730 				}
731 			}
732 			break;
733 		}
734 
735 		default: return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
736 	}
737 
738 	if (flags & DC_EXEC) {
739 		/* if we got that far, 'owner' variable is set correctly */
740 		assert(Company::IsValidID(owner));
741 
742 		MarkTileDirtyByTile(tile);
743 		if (crossing) {
744 			/* crossing is set when only TRACK_BIT_X and TRACK_BIT_Y are set. As we
745 			 * are removing one of these pieces, we'll need to update signals for
746 			 * both directions explicitly, as after the track is removed it won't
747 			 * 'connect' with the other piece. */
748 			AddTrackToSignalBuffer(tile, TRACK_X, owner);
749 			AddTrackToSignalBuffer(tile, TRACK_Y, owner);
750 			YapfNotifyTrackLayoutChange(tile, TRACK_X);
751 			YapfNotifyTrackLayoutChange(tile, TRACK_Y);
752 		} else {
753 			AddTrackToSignalBuffer(tile, track, owner);
754 			YapfNotifyTrackLayoutChange(tile, track);
755 		}
756 
757 		if (v != nullptr) TryPathReserve(v, true);
758 	}
759 
760 	return cost;
761 }
762 
763 
764 /**
765  * Called from water_cmd if a non-flat rail-tile gets flooded and should be converted to shore.
766  * The function floods the lower halftile, if the tile has a halftile foundation.
767  *
768  * @param t The tile to flood.
769  * @return true if something was flooded.
770  */
FloodHalftile(TileIndex t)771 bool FloodHalftile(TileIndex t)
772 {
773 	assert(IsPlainRailTile(t));
774 
775 	bool flooded = false;
776 	if (GetRailGroundType(t) == RAIL_GROUND_WATER) return flooded;
777 
778 	Slope tileh = GetTileSlope(t);
779 	TrackBits rail_bits = GetTrackBits(t);
780 
781 	if (IsSlopeWithOneCornerRaised(tileh)) {
782 		TrackBits lower_track = CornerToTrackBits(OppositeCorner(GetHighestSlopeCorner(tileh)));
783 
784 		TrackBits to_remove = lower_track & rail_bits;
785 		if (to_remove != 0) {
786 			Backup<CompanyID> cur_company(_current_company, OWNER_WATER, FILE_LINE);
787 			flooded = DoCommand(t, 0, FIND_FIRST_BIT(to_remove), DC_EXEC, CMD_REMOVE_SINGLE_RAIL).Succeeded();
788 			cur_company.Restore();
789 			if (!flooded) return flooded; // not yet floodable
790 			rail_bits = rail_bits & ~to_remove;
791 			if (rail_bits == 0) {
792 				MakeShore(t);
793 				MarkTileDirtyByTile(t);
794 				return flooded;
795 			}
796 		}
797 
798 		if (IsNonContinuousFoundation(GetRailFoundation(tileh, rail_bits))) {
799 			flooded = true;
800 			SetRailGroundType(t, RAIL_GROUND_WATER);
801 			MarkTileDirtyByTile(t);
802 		}
803 	} else {
804 		/* Make shore on steep slopes and 'three-corners-raised'-slopes. */
805 		if (ApplyFoundationToSlope(GetRailFoundation(tileh, rail_bits), &tileh) == 0) {
806 			if (IsSteepSlope(tileh) || IsSlopeWithThreeCornersRaised(tileh)) {
807 				flooded = true;
808 				SetRailGroundType(t, RAIL_GROUND_WATER);
809 				MarkTileDirtyByTile(t);
810 			}
811 		}
812 	}
813 	return flooded;
814 }
815 
816 static const TileIndexDiffC _trackdelta[] = {
817 	{ -1,  0 }, {  0,  1 }, { -1,  0 }, {  0,  1 }, {  1,  0 }, {  0,  1 },
818 	{  0,  0 },
819 	{  0,  0 },
820 	{  1,  0 }, {  0, -1 }, {  0, -1 }, {  1,  0 }, {  0, -1 }, { -1,  0 },
821 	{  0,  0 },
822 	{  0,  0 }
823 };
824 
825 
ValidateAutoDrag(Trackdir * trackdir,TileIndex start,TileIndex end)826 static CommandCost ValidateAutoDrag(Trackdir *trackdir, TileIndex start, TileIndex end)
827 {
828 	int x = TileX(start);
829 	int y = TileY(start);
830 	int ex = TileX(end);
831 	int ey = TileY(end);
832 
833 	if (!ValParamTrackOrientation(TrackdirToTrack(*trackdir))) return CMD_ERROR;
834 
835 	/* calculate delta x,y from start to end tile */
836 	int dx = ex - x;
837 	int dy = ey - y;
838 
839 	/* calculate delta x,y for the first direction */
840 	int trdx = _trackdelta[*trackdir].x;
841 	int trdy = _trackdelta[*trackdir].y;
842 
843 	if (!IsDiagonalTrackdir(*trackdir)) {
844 		trdx += _trackdelta[*trackdir ^ 1].x;
845 		trdy += _trackdelta[*trackdir ^ 1].y;
846 	}
847 
848 	/* validate the direction */
849 	while ((trdx <= 0 && dx > 0) ||
850 			(trdx >= 0 && dx < 0) ||
851 			(trdy <= 0 && dy > 0) ||
852 			(trdy >= 0 && dy < 0)) {
853 		if (!HasBit(*trackdir, 3)) { // first direction is invalid, try the other
854 			SetBit(*trackdir, 3); // reverse the direction
855 			trdx = -trdx;
856 			trdy = -trdy;
857 		} else { // other direction is invalid too, invalid drag
858 			return CMD_ERROR;
859 		}
860 	}
861 
862 	/* (for diagonal tracks, this is already made sure of by above test), but:
863 	 * for non-diagonal tracks, check if the start and end tile are on 1 line */
864 	if (!IsDiagonalTrackdir(*trackdir)) {
865 		trdx = _trackdelta[*trackdir].x;
866 		trdy = _trackdelta[*trackdir].y;
867 		if (abs(dx) != abs(dy) && abs(dx) + abs(trdy) != abs(dy) + abs(trdx)) return CMD_ERROR;
868 	}
869 
870 	return CommandCost();
871 }
872 
873 /**
874  * Build or remove a stretch of railroad tracks.
875  * @param tile start tile of drag
876  * @param flags operation to perform
877  * @param p1 end tile of drag
878  * @param p2 various bitstuffed elements
879  * - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev), only used for building
880  * - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
881  * - p2 = (bit 9)   - 0 = build, 1 = remove tracks
882  * - p2 = (bit 10)  - 0 = build up to an obstacle, 1 = fail if an obstacle is found (used for AIs).
883  * - p2 = (bit 11)  - 0 = error on signal in the way, 1 = auto remove signals when in the way
884  * @param text unused
885  * @return the cost of this operation or an error
886  */
CmdRailTrackHelper(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)887 static CommandCost CmdRailTrackHelper(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
888 {
889 	CommandCost total_cost(EXPENSES_CONSTRUCTION);
890 	Track track = Extract<Track, 6, 3>(p2);
891 	bool remove = HasBit(p2, 9);
892 	bool auto_remove_signals = HasBit(p2, 11);
893 	RailType railtype = Extract<RailType, 0, 6>(p2);
894 
895 	if ((!remove && !ValParamRailtype(railtype)) || !ValParamTrackOrientation(track)) return CMD_ERROR;
896 	if (p1 >= MapSize()) return CMD_ERROR;
897 	TileIndex end_tile = p1;
898 	Trackdir trackdir = TrackToTrackdir(track);
899 
900 	CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
901 	if (ret.Failed()) return ret;
902 
903 	bool had_success = false;
904 	CommandCost last_error = CMD_ERROR;
905 	for (;;) {
906 		CommandCost ret = DoCommand(tile, remove ? 0 : railtype, TrackdirToTrack(trackdir) | (auto_remove_signals << 3), flags, remove ? CMD_REMOVE_SINGLE_RAIL : CMD_BUILD_SINGLE_RAIL);
907 
908 		if (ret.Failed()) {
909 			last_error = ret;
910 			if (last_error.GetErrorMessage() != STR_ERROR_ALREADY_BUILT && !remove) {
911 				if (HasBit(p2, 10)) return last_error;
912 				break;
913 			}
914 
915 			/* Ownership errors are more important. */
916 			if (last_error.GetErrorMessage() == STR_ERROR_OWNED_BY && remove) break;
917 		} else {
918 			had_success = true;
919 			total_cost.AddCost(ret);
920 		}
921 
922 		if (tile == end_tile) break;
923 
924 		tile += ToTileIndexDiff(_trackdelta[trackdir]);
925 
926 		/* toggle railbit for the non-diagonal tracks */
927 		if (!IsDiagonalTrackdir(trackdir)) ToggleBit(trackdir, 0);
928 	}
929 
930 	if (had_success) return total_cost;
931 	return last_error;
932 }
933 
934 /**
935  * Build rail on a stretch of track.
936  * Stub for the unified rail builder/remover
937  * @param tile start tile of drag
938  * @param flags operation to perform
939  * @param p1 end tile of drag
940  * @param p2 various bitstuffed elements
941  * - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev)
942  * - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
943  * - p2 = (bit 9)   - 0 = build, 1 = remove tracks
944  * @param text unused
945  * @return the cost of this operation or an error
946  * @see CmdRailTrackHelper
947  */
CmdBuildRailroadTrack(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)948 CommandCost CmdBuildRailroadTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
949 {
950 	return CmdRailTrackHelper(tile, flags, p1, ClrBit(p2, 9), text);
951 }
952 
953 /**
954  * Build rail on a stretch of track.
955  * Stub for the unified rail builder/remover
956  * @param tile start tile of drag
957  * @param flags operation to perform
958  * @param p1 end tile of drag
959  * @param p2 various bitstuffed elements
960  * - p2 = (bit 0-5) - railroad type normal/maglev (0 = normal, 1 = mono, 2 = maglev), only used for building
961  * - p2 = (bit 6-8) - track-orientation, valid values: 0-5 (Track enum)
962  * - p2 = (bit 9)   - 0 = build, 1 = remove tracks
963  * @param text unused
964  * @return the cost of this operation or an error
965  * @see CmdRailTrackHelper
966  */
CmdRemoveRailroadTrack(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)967 CommandCost CmdRemoveRailroadTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
968 {
969 	return CmdRailTrackHelper(tile, flags, p1, SetBit(p2, 9), text);
970 }
971 
972 /**
973  * Build a train depot
974  * @param tile position of the train depot
975  * @param flags operation to perform
976  * @param p1 rail type
977  * @param p2 bit 0..1 entrance direction (DiagDirection)
978  * @param text unused
979  * @return the cost of this operation or an error
980  *
981  * @todo When checking for the tile slope,
982  * distinguish between "Flat land required" and "land sloped in wrong direction"
983  */
CmdBuildTrainDepot(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)984 CommandCost CmdBuildTrainDepot(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
985 {
986 	/* check railtype and valid direction for depot (0 through 3), 4 in total */
987 	RailType railtype = Extract<RailType, 0, 6>(p1);
988 	if (!ValParamRailtype(railtype)) return CMD_ERROR;
989 
990 	Slope tileh = GetTileSlope(tile);
991 
992 	DiagDirection dir = Extract<DiagDirection, 0, 2>(p2);
993 
994 	CommandCost cost(EXPENSES_CONSTRUCTION);
995 
996 	/* Prohibit construction if
997 	 * The tile is non-flat AND
998 	 * 1) build-on-slopes is disabled
999 	 * 2) the tile is steep i.e. spans two height levels
1000 	 * 3) the exit points in the wrong direction
1001 	 */
1002 
1003 	if (tileh != SLOPE_FLAT) {
1004 		if (!_settings_game.construction.build_on_slopes || !CanBuildDepotByTileh(dir, tileh)) {
1005 			return_cmd_error(STR_ERROR_FLAT_LAND_REQUIRED);
1006 		}
1007 		cost.AddCost(_price[PR_BUILD_FOUNDATION]);
1008 	}
1009 
1010 	cost.AddCost(DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR));
1011 	if (cost.Failed()) return cost;
1012 
1013 	if (IsBridgeAbove(tile)) return_cmd_error(STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST);
1014 
1015 	if (!Depot::CanAllocateItem()) return CMD_ERROR;
1016 
1017 	if (flags & DC_EXEC) {
1018 		Depot *d = new Depot(tile);
1019 		d->build_date = _date;
1020 
1021 		MakeRailDepot(tile, _current_company, d->index, dir, railtype);
1022 		MarkTileDirtyByTile(tile);
1023 		MakeDefaultName(d);
1024 
1025 		Company::Get(_current_company)->infrastructure.rail[railtype]++;
1026 		DirtyCompanyInfrastructureWindows(_current_company);
1027 
1028 		AddSideToSignalBuffer(tile, INVALID_DIAGDIR, _current_company);
1029 		YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
1030 	}
1031 
1032 	cost.AddCost(_price[PR_BUILD_DEPOT_TRAIN]);
1033 	cost.AddCost(RailBuildCost(railtype));
1034 	return cost;
1035 }
1036 
1037 /**
1038  * Build signals, alternate between double/single, signal/semaphore,
1039  * pre/exit/combo-signals, and what-else not. If the rail piece does not
1040  * have any signals, bit 4 (cycle signal-type) is ignored
1041  * @param tile tile where to build the signals
1042  * @param flags operation to perform
1043  * @param p1 various bitstuffed elements
1044  * - p1 = (bit 0-2) - track-orientation, valid values: 0-5 (Track enum)
1045  * - p1 = (bit 3)   - 1 = override signal/semaphore, or pre/exit/combo signal or (for bit 7) toggle variant (CTRL-toggle)
1046  * - p1 = (bit 4)   - 0 = signals, 1 = semaphores
1047  * - p1 = (bit 5-7) - type of the signal, for valid values see enum SignalType in rail_map.h
1048  * - p1 = (bit 8)   - convert the present signal type and variant
1049  * - p1 = (bit 9-11)- start cycle from this signal type
1050  * - p1 = (bit 12-14)-wrap around after this signal type
1051  * - p1 = (bit 15-16)-cycle the signal direction this many times
1052  * - p1 = (bit 17)  - 1 = don't modify an existing signal but don't fail either, 0 = always set new signal type
1053  * @param p2 used for CmdBuildManySignals() to copy direction of first signal
1054  * @param text unused
1055  * @return the cost of this operation or an error
1056  * @todo p2 should be replaced by two bits for "along" and "against" the track.
1057  */
CmdBuildSingleSignal(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)1058 CommandCost CmdBuildSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
1059 {
1060 	Track track = Extract<Track, 0, 3>(p1);
1061 	bool ctrl_pressed = HasBit(p1, 3); // was the CTRL button pressed
1062 	SignalVariant sigvar = (ctrl_pressed ^ HasBit(p1, 4)) ? SIG_SEMAPHORE : SIG_ELECTRIC; // the signal variant of the new signal
1063 	SignalType sigtype = Extract<SignalType, 5, 3>(p1); // the signal type of the new signal
1064 	bool convert_signal = HasBit(p1, 8); // convert button pressed
1065 	SignalType cycle_start = Extract<SignalType, 9, 3>(p1);
1066 	SignalType cycle_stop = Extract<SignalType, 12, 3>(p1);
1067 	uint num_dir_cycle = GB(p1, 15, 2);
1068 
1069 	if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
1070 	if (cycle_start > cycle_stop || cycle_stop > SIGTYPE_LAST) return CMD_ERROR;
1071 
1072 	/* You can only build signals on plain rail tiles, and the selected track must exist */
1073 	if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) ||
1074 			!HasTrack(tile, track)) {
1075 		return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1076 	}
1077 	/* Protect against invalid signal copying */
1078 	if (p2 != 0 && (p2 & SignalOnTrack(track)) == 0) return CMD_ERROR;
1079 
1080 	CommandCost ret = CheckTileOwnership(tile);
1081 	if (ret.Failed()) return ret;
1082 
1083 	/* See if this is a valid track combination for signals (no overlap) */
1084 	if (TracksOverlap(GetTrackBits(tile))) return_cmd_error(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK);
1085 
1086 	/* In case we don't want to change an existing signal, return without error. */
1087 	if (HasBit(p1, 17) && HasSignalOnTrack(tile, track)) return CommandCost();
1088 
1089 	/* you can not convert a signal if no signal is on track */
1090 	if (convert_signal && !HasSignalOnTrack(tile, track)) return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
1091 
1092 	CommandCost cost;
1093 	if (!HasSignalOnTrack(tile, track)) {
1094 		/* build new signals */
1095 		cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS]);
1096 	} else {
1097 		if (p2 != 0 && sigvar != GetSignalVariant(tile, track)) {
1098 			/* convert signals <-> semaphores */
1099 			cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
1100 
1101 		} else if (convert_signal) {
1102 			/* convert button pressed */
1103 			if (ctrl_pressed || GetSignalVariant(tile, track) != sigvar) {
1104 				/* convert electric <-> semaphore */
1105 				cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_SIGNALS] + _price[PR_CLEAR_SIGNALS]);
1106 			} else {
1107 				/* it is free to change signal type: normal-pre-exit-combo */
1108 				cost = CommandCost();
1109 			}
1110 
1111 		} else {
1112 			/* it is free to change orientation/pre-exit-combo signals */
1113 			cost = CommandCost();
1114 		}
1115 	}
1116 
1117 	if (flags & DC_EXEC) {
1118 		Train *v = nullptr;
1119 		/* The new/changed signal could block our path. As this can lead to
1120 		 * stale reservations, we clear the path reservation here and try
1121 		 * to redo it later on. */
1122 		if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1123 			v = GetTrainForReservation(tile, track);
1124 			if (v != nullptr) FreeTrainTrackReservation(v);
1125 		}
1126 
1127 		if (!HasSignals(tile)) {
1128 			/* there are no signals at all on this tile yet */
1129 			SetHasSignals(tile, true);
1130 			SetSignalStates(tile, 0xF); // all signals are on
1131 			SetPresentSignals(tile, 0); // no signals built by default
1132 			SetSignalType(tile, track, sigtype);
1133 			SetSignalVariant(tile, track, sigvar);
1134 		}
1135 
1136 		/* Subtract old signal infrastructure count. */
1137 		Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1138 
1139 		if (p2 == 0) {
1140 			if (!HasSignalOnTrack(tile, track)) {
1141 				/* build new signals */
1142 				SetPresentSignals(tile, GetPresentSignals(tile) | (IsPbsSignal(sigtype) ? KillFirstBit(SignalOnTrack(track)) : SignalOnTrack(track)));
1143 				SetSignalType(tile, track, sigtype);
1144 				SetSignalVariant(tile, track, sigvar);
1145 				while (num_dir_cycle-- > 0) CycleSignalSide(tile, track);
1146 			} else {
1147 				if (convert_signal) {
1148 					/* convert signal button pressed */
1149 					if (ctrl_pressed) {
1150 						/* toggle the present signal variant: SIG_ELECTRIC <-> SIG_SEMAPHORE */
1151 						SetSignalVariant(tile, track, (GetSignalVariant(tile, track) == SIG_ELECTRIC) ? SIG_SEMAPHORE : SIG_ELECTRIC);
1152 						/* Query current signal type so the check for PBS signals below works. */
1153 						sigtype = GetSignalType(tile, track);
1154 					} else {
1155 						/* convert the present signal to the chosen type and variant */
1156 						SetSignalType(tile, track, sigtype);
1157 						SetSignalVariant(tile, track, sigvar);
1158 						if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1159 							SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track)));
1160 						}
1161 					}
1162 
1163 				} else if (ctrl_pressed) {
1164 					/* cycle between cycle_start and cycle_end */
1165 					sigtype = (SignalType)(GetSignalType(tile, track) + 1);
1166 
1167 					if (sigtype < cycle_start || sigtype > cycle_stop) sigtype = cycle_start;
1168 
1169 					SetSignalType(tile, track, sigtype);
1170 					if (IsPbsSignal(sigtype) && (GetPresentSignals(tile) & SignalOnTrack(track)) == SignalOnTrack(track)) {
1171 						SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | KillFirstBit(SignalOnTrack(track)));
1172 					}
1173 				} else {
1174 					/* cycle the signal side: both -> left -> right -> both -> ... */
1175 					CycleSignalSide(tile, track);
1176 					/* Query current signal type so the check for PBS signals below works. */
1177 					sigtype = GetSignalType(tile, track);
1178 				}
1179 			}
1180 		} else {
1181 			/* If CmdBuildManySignals is called with copying signals, just copy the
1182 			 * direction of the first signal given as parameter by CmdBuildManySignals */
1183 			SetPresentSignals(tile, (GetPresentSignals(tile) & ~SignalOnTrack(track)) | (p2 & SignalOnTrack(track)));
1184 			SetSignalVariant(tile, track, sigvar);
1185 			SetSignalType(tile, track, sigtype);
1186 		}
1187 
1188 		/* Add new signal infrastructure count. */
1189 		Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1190 		DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
1191 
1192 		if (IsPbsSignal(sigtype)) {
1193 			/* PBS signals should show red unless they are on reserved tiles without a train. */
1194 			uint mask = GetPresentSignals(tile) & SignalOnTrack(track);
1195 			SetSignalStates(tile, (GetSignalStates(tile) & ~mask) | ((HasBit(GetRailReservationTrackBits(tile), track) && EnsureNoVehicleOnGround(tile).Succeeded() ? UINT_MAX : 0) & mask));
1196 		}
1197 		MarkTileDirtyByTile(tile);
1198 		AddTrackToSignalBuffer(tile, track, _current_company);
1199 		YapfNotifyTrackLayoutChange(tile, track);
1200 		if (v != nullptr && v->track != TRACK_BIT_DEPOT) {
1201 			/* Extend the train's path if it's not stopped or loading, or not at a safe position. */
1202 			if (!(((v->vehstatus & VS_STOPPED) && v->cur_speed == 0) || v->current_order.IsType(OT_LOADING)) ||
1203 					!IsSafeWaitingPosition(v, v->tile, v->GetVehicleTrackdir(), true, _settings_game.pf.forbid_90_deg)) {
1204 				TryPathReserve(v, true);
1205 			}
1206 		}
1207 	}
1208 
1209 	return cost;
1210 }
1211 
CheckSignalAutoFill(TileIndex & tile,Trackdir & trackdir,int & signal_ctr,bool remove)1212 static bool CheckSignalAutoFill(TileIndex &tile, Trackdir &trackdir, int &signal_ctr, bool remove)
1213 {
1214 	tile = AddTileIndexDiffCWrap(tile, _trackdelta[trackdir]);
1215 	if (tile == INVALID_TILE) return false;
1216 
1217 	/* Check for track bits on the new tile */
1218 	TrackdirBits trackdirbits = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0));
1219 
1220 	if (TracksOverlap(TrackdirBitsToTrackBits(trackdirbits))) return false;
1221 	trackdirbits &= TrackdirReachesTrackdirs(trackdir);
1222 
1223 	/* No track bits, must stop */
1224 	if (trackdirbits == TRACKDIR_BIT_NONE) return false;
1225 
1226 	/* Get the first track dir */
1227 	trackdir = RemoveFirstTrackdir(&trackdirbits);
1228 
1229 	/* Any left? It's a junction so we stop */
1230 	if (trackdirbits != TRACKDIR_BIT_NONE) return false;
1231 
1232 	switch (GetTileType(tile)) {
1233 		case MP_RAILWAY:
1234 			if (IsRailDepot(tile)) return false;
1235 			if (!remove && HasSignalOnTrack(tile, TrackdirToTrack(trackdir))) return false;
1236 			signal_ctr++;
1237 			if (IsDiagonalTrackdir(trackdir)) {
1238 				signal_ctr++;
1239 				/* Ensure signal_ctr even so X and Y pieces get signals */
1240 				ClrBit(signal_ctr, 0);
1241 			}
1242 			return true;
1243 
1244 		case MP_ROAD:
1245 			if (!IsLevelCrossing(tile)) return false;
1246 			signal_ctr += 2;
1247 			return true;
1248 
1249 		case MP_TUNNELBRIDGE: {
1250 			TileIndex orig_tile = tile; // backup old value
1251 
1252 			if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) return false;
1253 			if (GetTunnelBridgeDirection(tile) != TrackdirToExitdir(trackdir)) return false;
1254 
1255 			/* Skip to end of tunnel or bridge
1256 			 * note that tile is a parameter by reference, so it must be updated */
1257 			tile = GetOtherTunnelBridgeEnd(tile);
1258 
1259 			signal_ctr += (GetTunnelBridgeLength(orig_tile, tile) + 2) * 2;
1260 			return true;
1261 		}
1262 
1263 		default: return false;
1264 	}
1265 }
1266 
1267 /**
1268  * Build many signals by dragging; AutoSignals
1269  * @param tile start tile of drag
1270  * @param flags operation to perform
1271  * @param p1  end tile of drag
1272  * @param p2 various bitstuffed elements
1273  * - p2 = (bit  0- 2) - track-orientation, valid values: 0-5 (Track enum)
1274  * - p2 = (bit  3)    - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
1275  * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
1276  * - p2 = (bit  5)    - 0 = build, 1 = remove signals
1277  * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
1278  * - p2 = (bit  7- 9) - default signal type
1279  * - p2 = (bit 10)    - 0 = keep fixed distance, 1 = minimise gaps between signals
1280  * - p2 = (bit 24-31) - user defined signals_density
1281  * @param text unused
1282  * @return the cost of this operation or an error
1283  */
CmdSignalTrackHelper(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)1284 static CommandCost CmdSignalTrackHelper(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
1285 {
1286 	CommandCost total_cost(EXPENSES_CONSTRUCTION);
1287 	TileIndex start_tile = tile;
1288 
1289 	Track track = Extract<Track, 0, 3>(p2);
1290 	bool mode = HasBit(p2, 3);
1291 	bool semaphores = HasBit(p2, 4);
1292 	bool remove = HasBit(p2, 5);
1293 	bool autofill = HasBit(p2, 6);
1294 	bool minimise_gaps = HasBit(p2, 10);
1295 	byte signal_density = GB(p2, 24, 8);
1296 
1297 	if (p1 >= MapSize() || !ValParamTrackOrientation(track)) return CMD_ERROR;
1298 	TileIndex end_tile = p1;
1299 	if (signal_density == 0 || signal_density > 20) return CMD_ERROR;
1300 
1301 	if (!IsPlainRailTile(tile)) return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1302 
1303 	/* for vertical/horizontal tracks, double the given signals density
1304 	 * since the original amount will be too dense (shorter tracks) */
1305 	signal_density *= 2;
1306 
1307 	Trackdir trackdir = TrackToTrackdir(track);
1308 	CommandCost ret = ValidateAutoDrag(&trackdir, tile, end_tile);
1309 	if (ret.Failed()) return ret;
1310 
1311 	track = TrackdirToTrack(trackdir); // trackdir might have changed, keep track in sync
1312 	Trackdir start_trackdir = trackdir;
1313 
1314 	/* Must start on a valid track to be able to avoid loops */
1315 	if (!HasTrack(tile, track)) return CMD_ERROR;
1316 
1317 	SignalType sigtype = (SignalType)GB(p2, 7, 3);
1318 	if (sigtype > SIGTYPE_LAST) return CMD_ERROR;
1319 
1320 	byte signals;
1321 	/* copy the signal-style of the first rail-piece if existing */
1322 	if (HasSignalOnTrack(tile, track)) {
1323 		signals = GetPresentSignals(tile) & SignalOnTrack(track);
1324 		assert(signals != 0);
1325 
1326 		/* copy signal/semaphores style (independent of CTRL) */
1327 		semaphores = GetSignalVariant(tile, track) != SIG_ELECTRIC;
1328 
1329 		sigtype = GetSignalType(tile, track);
1330 		/* Don't but copy entry or exit-signal type */
1331 		if (sigtype == SIGTYPE_ENTRY || sigtype == SIGTYPE_EXIT) sigtype = SIGTYPE_NORMAL;
1332 	} else { // no signals exist, drag a two-way signal stretch
1333 		signals = IsPbsSignal(sigtype) ? SignalAlongTrackdir(trackdir) : SignalOnTrack(track);
1334 	}
1335 
1336 	byte signal_dir = 0;
1337 	if (signals & SignalAlongTrackdir(trackdir))   SetBit(signal_dir, 0);
1338 	if (signals & SignalAgainstTrackdir(trackdir)) SetBit(signal_dir, 1);
1339 
1340 	/* signal_ctr         - amount of tiles already processed
1341 	 * last_used_ctr      - amount of tiles before previously placed signal
1342 	 * signals_density    - setting to put signal on every Nth tile (double space on |, -- tracks)
1343 	 * last_suitable_ctr  - amount of tiles before last possible signal place
1344 	 * last_suitable_tile - last tile where it is possible to place a signal
1345 	 * last_suitable_trackdir - trackdir of the last tile
1346 	 **********
1347 	 * trackdir   - trackdir to build with autorail
1348 	 * semaphores - semaphores or signals
1349 	 * signals    - is there a signal/semaphore on the first tile, copy its style (two-way/single-way)
1350 	 *              and convert all others to semaphore/signal
1351 	 * remove     - 1 remove signals, 0 build signals */
1352 	int signal_ctr = 0;
1353 	int last_used_ctr = INT_MIN; // initially INT_MIN to force building/removing at the first tile
1354 	int last_suitable_ctr = 0;
1355 	TileIndex last_suitable_tile = INVALID_TILE;
1356 	Trackdir last_suitable_trackdir = INVALID_TRACKDIR;
1357 	CommandCost last_error = CMD_ERROR;
1358 	bool had_success = false;
1359 	for (;;) {
1360 		/* only build/remove signals with the specified density */
1361 		if (remove || minimise_gaps || signal_ctr % signal_density == 0) {
1362 			uint32 param1 = GB(TrackdirToTrack(trackdir), 0, 3);
1363 			SB(param1, 3, 1, mode);
1364 			SB(param1, 4, 1, semaphores);
1365 			SB(param1, 5, 3, sigtype);
1366 			if (!remove && signal_ctr == 0) SetBit(param1, 17);
1367 
1368 			/* Pick the correct orientation for the track direction */
1369 			signals = 0;
1370 			if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(trackdir);
1371 			if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(trackdir);
1372 
1373 			/* Test tiles in between for suitability as well if minimising gaps. */
1374 			bool test_only = !remove && minimise_gaps && signal_ctr < (last_used_ctr + signal_density);
1375 			CommandCost ret = DoCommand(tile, param1, signals, test_only ? flags & ~DC_EXEC : flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
1376 
1377 			if (ret.Succeeded()) {
1378 				/* Remember last track piece where we can place a signal. */
1379 				last_suitable_ctr = signal_ctr;
1380 				last_suitable_tile = tile;
1381 				last_suitable_trackdir = trackdir;
1382 			} else if (!test_only && last_suitable_tile != INVALID_TILE) {
1383 				/* If a signal can't be placed, place it at the last possible position. */
1384 				SB(param1, 0, 3, TrackdirToTrack(last_suitable_trackdir));
1385 				ClrBit(param1, 17);
1386 
1387 				/* Pick the correct orientation for the track direction. */
1388 				signals = 0;
1389 				if (HasBit(signal_dir, 0)) signals |= SignalAlongTrackdir(last_suitable_trackdir);
1390 				if (HasBit(signal_dir, 1)) signals |= SignalAgainstTrackdir(last_suitable_trackdir);
1391 
1392 				ret = DoCommand(last_suitable_tile, param1, signals, flags, remove ? CMD_REMOVE_SIGNALS : CMD_BUILD_SIGNALS);
1393 			}
1394 
1395 			/* Collect cost. */
1396 			if (!test_only) {
1397 				/* Be user-friendly and try placing signals as much as possible */
1398 				if (ret.Succeeded()) {
1399 					had_success = true;
1400 					total_cost.AddCost(ret);
1401 					last_used_ctr = last_suitable_ctr;
1402 					last_suitable_tile = INVALID_TILE;
1403 				} else {
1404 					/* The "No railway" error is the least important one. */
1405 					if (ret.GetErrorMessage() != STR_ERROR_THERE_IS_NO_RAILROAD_TRACK ||
1406 							last_error.GetErrorMessage() == INVALID_STRING_ID) {
1407 						last_error = ret;
1408 					}
1409 				}
1410 			}
1411 		}
1412 
1413 		if (autofill) {
1414 			if (!CheckSignalAutoFill(tile, trackdir, signal_ctr, remove)) break;
1415 
1416 			/* Prevent possible loops */
1417 			if (tile == start_tile && trackdir == start_trackdir) break;
1418 		} else {
1419 			if (tile == end_tile) break;
1420 
1421 			tile += ToTileIndexDiff(_trackdelta[trackdir]);
1422 			signal_ctr++;
1423 
1424 			/* toggle railbit for the non-diagonal tracks (|, -- tracks) */
1425 			if (IsDiagonalTrackdir(trackdir)) {
1426 				signal_ctr++;
1427 			} else {
1428 				ToggleBit(trackdir, 0);
1429 			}
1430 		}
1431 	}
1432 
1433 	return had_success ? total_cost : last_error;
1434 }
1435 
1436 /**
1437  * Build signals on a stretch of track.
1438  * Stub for the unified signal builder/remover
1439  * @param tile start tile of drag
1440  * @param flags operation to perform
1441  * @param p1  end tile of drag
1442  * @param p2 various bitstuffed elements
1443  * - p2 = (bit  0- 2) - track-orientation, valid values: 0-5 (Track enum)
1444  * - p2 = (bit  3)    - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
1445  * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
1446  * - p2 = (bit  5)    - 0 = build, 1 = remove signals
1447  * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
1448  * - p2 = (bit  7- 9) - default signal type
1449  * - p2 = (bit 24-31) - user defined signals_density
1450  * @param text unused
1451  * @return the cost of this operation or an error
1452  * @see CmdSignalTrackHelper
1453  */
CmdBuildSignalTrack(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)1454 CommandCost CmdBuildSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
1455 {
1456 	return CmdSignalTrackHelper(tile, flags, p1, p2, text);
1457 }
1458 
1459 /**
1460  * Remove signals
1461  * @param tile coordinates where signal is being deleted from
1462  * @param flags operation to perform
1463  * @param p1 various bitstuffed elements, only track information is used
1464  *           - (bit  0- 2) - track-orientation, valid values: 0-5 (Track enum)
1465  *           - (bit  3)    - override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
1466  *           - (bit  4)    - 0 = signals, 1 = semaphores
1467  * @param p2 unused
1468  * @param text unused
1469  * @return the cost of this operation or an error
1470  */
CmdRemoveSingleSignal(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)1471 CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
1472 {
1473 	Track track = Extract<Track, 0, 3>(p1);
1474 
1475 	if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
1476 		return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
1477 	}
1478 	if (!HasSignalOnTrack(tile, track)) {
1479 		return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
1480 	}
1481 
1482 	/* Only water can remove signals from anyone */
1483 	if (_current_company != OWNER_WATER) {
1484 		CommandCost ret = CheckTileOwnership(tile);
1485 		if (ret.Failed()) return ret;
1486 	}
1487 
1488 	/* Do it? */
1489 	if (flags & DC_EXEC) {
1490 		Train *v = nullptr;
1491 		if (HasReservedTracks(tile, TrackToTrackBits(track))) {
1492 			v = GetTrainForReservation(tile, track);
1493 		} else if (IsPbsSignal(GetSignalType(tile, track))) {
1494 			/* PBS signal, might be the end of a path reservation. */
1495 			Trackdir td = TrackToTrackdir(track);
1496 			for (int i = 0; v == nullptr && i < 2; i++, td = ReverseTrackdir(td)) {
1497 				/* Only test the active signal side. */
1498 				if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
1499 				TileIndex next = TileAddByDiagDir(tile, TrackdirToExitdir(td));
1500 				TrackBits tracks = TrackdirBitsToTrackBits(TrackdirReachesTrackdirs(td));
1501 				if (HasReservedTracks(next, tracks)) {
1502 					v = GetTrainForReservation(next, TrackBitsToTrack(GetReservedTrackbits(next) & tracks));
1503 				}
1504 			}
1505 		}
1506 		Company::Get(GetTileOwner(tile))->infrastructure.signal -= CountBits(GetPresentSignals(tile));
1507 		SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
1508 		Company::Get(GetTileOwner(tile))->infrastructure.signal += CountBits(GetPresentSignals(tile));
1509 		DirtyCompanyInfrastructureWindows(GetTileOwner(tile));
1510 
1511 		/* removed last signal from tile? */
1512 		if (GetPresentSignals(tile) == 0) {
1513 			SetSignalStates(tile, 0);
1514 			SetHasSignals(tile, false);
1515 			SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
1516 		}
1517 
1518 		AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
1519 		YapfNotifyTrackLayoutChange(tile, track);
1520 		if (v != nullptr) TryPathReserve(v, false);
1521 
1522 		MarkTileDirtyByTile(tile);
1523 	}
1524 
1525 	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]);
1526 }
1527 
1528 /**
1529  * Remove signals on a stretch of track.
1530  * Stub for the unified signal builder/remover
1531  * @param tile start tile of drag
1532  * @param flags operation to perform
1533  * @param p1  end tile of drag
1534  * @param p2 various bitstuffed elements
1535  * - p2 = (bit  0- 2) - track-orientation, valid values: 0-5 (Track enum)
1536  * - p2 = (bit  3)    - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
1537  * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
1538  * - p2 = (bit  5)    - 0 = build, 1 = remove signals
1539  * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
1540  * - p2 = (bit  7- 9) - default signal type
1541  * - p2 = (bit 24-31) - user defined signals_density
1542  * @param text unused
1543  * @return the cost of this operation or an error
1544  * @see CmdSignalTrackHelper
1545  */
CmdRemoveSignalTrack(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)1546 CommandCost CmdRemoveSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
1547 {
1548 	return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5), text); // bit 5 is remove bit
1549 }
1550 
1551 /** Update power of train under which is the railtype being converted */
UpdateTrainPowerProc(Vehicle * v,void * data)1552 static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data)
1553 {
1554 	if (v->type != VEH_TRAIN) return nullptr;
1555 
1556 	TrainList *affected_trains = static_cast<TrainList*>(data);
1557 	include(*affected_trains, Train::From(v)->First());
1558 
1559 	return nullptr;
1560 }
1561 
1562 /**
1563  * Convert one rail type to the other. You can convert normal rail to
1564  * monorail/maglev easily or vice-versa.
1565  * @param tile end tile of rail conversion drag
1566  * @param flags operation to perform
1567  * @param p1 start tile of drag
1568  * @param p2 various bitstuffed elements:
1569  * - p2 = (bit  0- 5) new railtype to convert to.
1570  * - p2 = (bit  6)    build diagonally or not.
1571  * @param text unused
1572  * @return the cost of this operation or an error
1573  */
CmdConvertRail(TileIndex tile,DoCommandFlag flags,uint32 p1,uint32 p2,const std::string & text)1574 CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const std::string &text)
1575 {
1576 	RailType totype = Extract<RailType, 0, 6>(p2);
1577 	TileIndex area_start = p1;
1578 	TileIndex area_end = tile;
1579 	bool diagonal = HasBit(p2, 6);
1580 
1581 	if (!ValParamRailtype(totype)) return CMD_ERROR;
1582 	if (area_start >= MapSize()) return CMD_ERROR;
1583 
1584 	TrainList affected_trains;
1585 
1586 	CommandCost cost(EXPENSES_CONSTRUCTION);
1587 	CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
1588 	bool found_convertible_track = false; // whether we actually did convert some track (see bug #7633)
1589 
1590 	TileIterator *iter = diagonal ? (TileIterator *)new DiagonalTileIterator(area_start, area_end) : new OrthogonalTileIterator(area_start, area_end);
1591 	for (; (tile = *iter) != INVALID_TILE; ++(*iter)) {
1592 		TileType tt = GetTileType(tile);
1593 
1594 		/* Check if there is any track on tile */
1595 		switch (tt) {
1596 			case MP_RAILWAY:
1597 				break;
1598 			case MP_STATION:
1599 				if (!HasStationRail(tile)) continue;
1600 				break;
1601 			case MP_ROAD:
1602 				if (!IsLevelCrossing(tile)) continue;
1603 				if (RailNoLevelCrossings(totype)) {
1604 					error.MakeError(STR_ERROR_CROSSING_DISALLOWED_RAIL);
1605 					continue;
1606 				}
1607 				break;
1608 			case MP_TUNNELBRIDGE:
1609 				if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
1610 				break;
1611 			default: continue;
1612 		}
1613 
1614 		/* Original railtype we are converting from */
1615 		RailType type = GetRailType(tile);
1616 
1617 		/* Converting to the same type or converting 'hidden' elrail -> rail */
1618 		if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
1619 
1620 		/* Trying to convert other's rail */
1621 		CommandCost ret = CheckTileOwnership(tile);
1622 		if (ret.Failed()) {
1623 			error = ret;
1624 			continue;
1625 		}
1626 
1627 		std::vector<Train *> vehicles_affected;
1628 
1629 		/* Vehicle on the tile when not converting Rail <-> ElRail
1630 		 * Tunnels and bridges have special check later */
1631 		if (tt != MP_TUNNELBRIDGE) {
1632 			if (!IsCompatibleRail(type, totype)) {
1633 				CommandCost ret = IsPlainRailTile(tile) ? EnsureNoTrainOnTrackBits(tile, GetTrackBits(tile)) : EnsureNoVehicleOnGround(tile);
1634 				if (ret.Failed()) {
1635 					error = ret;
1636 					continue;
1637 				}
1638 			}
1639 			if (flags & DC_EXEC) { // we can safely convert, too
1640 				TrackBits reserved = GetReservedTrackbits(tile);
1641 				Track     track;
1642 				while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
1643 					Train *v = GetTrainForReservation(tile, track);
1644 					if (v != nullptr && !HasPowerOnRail(v->railtype, totype)) {
1645 						/* No power on new rail type, reroute. */
1646 						FreeTrainTrackReservation(v);
1647 						vehicles_affected.push_back(v);
1648 					}
1649 				}
1650 
1651 				/* Update the company infrastructure counters. */
1652 				if (!IsRailStationTile(tile) || !IsStationTileBlocked(tile)) {
1653 					Company *c = Company::Get(GetTileOwner(tile));
1654 					uint num_pieces = IsLevelCrossingTile(tile) ? LEVELCROSSING_TRACKBIT_FACTOR : 1;
1655 					if (IsPlainRailTile(tile)) {
1656 						TrackBits bits = GetTrackBits(tile);
1657 						num_pieces = CountBits(bits);
1658 						if (TracksOverlap(bits)) num_pieces *= num_pieces;
1659 					}
1660 					c->infrastructure.rail[type] -= num_pieces;
1661 					c->infrastructure.rail[totype] += num_pieces;
1662 					DirtyCompanyInfrastructureWindows(c->index);
1663 				}
1664 
1665 				SetRailType(tile, totype);
1666 				MarkTileDirtyByTile(tile);
1667 				/* update power of train on this tile */
1668 				FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc);
1669 			}
1670 		}
1671 
1672 		switch (tt) {
1673 			case MP_RAILWAY:
1674 				switch (GetRailTileType(tile)) {
1675 					case RAIL_TILE_DEPOT:
1676 						if (flags & DC_EXEC) {
1677 							/* notify YAPF about the track layout change */
1678 							YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
1679 
1680 							/* Update build vehicle window related to this depot */
1681 							InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
1682 							InvalidateWindowData(WC_BUILD_VEHICLE, tile);
1683 						}
1684 						found_convertible_track = true;
1685 						cost.AddCost(RailConvertCost(type, totype));
1686 						break;
1687 
1688 					default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
1689 						if (flags & DC_EXEC) {
1690 							/* notify YAPF about the track layout change */
1691 							TrackBits tracks = GetTrackBits(tile);
1692 							while (tracks != TRACK_BIT_NONE) {
1693 								YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
1694 							}
1695 						}
1696 						found_convertible_track = true;
1697 						cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
1698 						break;
1699 				}
1700 				break;
1701 
1702 			case MP_TUNNELBRIDGE: {
1703 				TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
1704 
1705 				/* If both ends of tunnel/bridge are in the range, do not try to convert twice -
1706 				 * it would cause assert because of different test and exec runs */
1707 				if (endtile < tile) {
1708 					if (diagonal) {
1709 						if (DiagonalTileArea(area_start, area_end).Contains(endtile)) continue;
1710 					} else {
1711 						if (OrthogonalTileArea(area_start, area_end).Contains(endtile)) continue;
1712 					}
1713 				}
1714 
1715 				/* When not converting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
1716 				if (!IsCompatibleRail(GetRailType(tile), totype)) {
1717 					CommandCost ret = TunnelBridgeIsFree(tile, endtile);
1718 					if (ret.Failed()) {
1719 						error = ret;
1720 						continue;
1721 					}
1722 				}
1723 
1724 				if (flags & DC_EXEC) {
1725 					Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile));
1726 					if (HasTunnelBridgeReservation(tile)) {
1727 						Train *v = GetTrainForReservation(tile, track);
1728 						if (v != nullptr && !HasPowerOnRail(v->railtype, totype)) {
1729 							/* No power on new rail type, reroute. */
1730 							FreeTrainTrackReservation(v);
1731 							vehicles_affected.push_back(v);
1732 						}
1733 					}
1734 
1735 					/* Update the company infrastructure counters. */
1736 					uint num_pieces = (GetTunnelBridgeLength(tile, endtile) + 2) * TUNNELBRIDGE_TRACKBIT_FACTOR;
1737 					Company *c = Company::Get(GetTileOwner(tile));
1738 					c->infrastructure.rail[GetRailType(tile)] -= num_pieces;
1739 					c->infrastructure.rail[totype] += num_pieces;
1740 					DirtyCompanyInfrastructureWindows(c->index);
1741 
1742 					SetRailType(tile, totype);
1743 					SetRailType(endtile, totype);
1744 
1745 					FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc);
1746 					FindVehicleOnPos(endtile, &affected_trains, &UpdateTrainPowerProc);
1747 
1748 					YapfNotifyTrackLayoutChange(tile, track);
1749 					YapfNotifyTrackLayoutChange(endtile, track);
1750 
1751 					if (IsBridge(tile)) {
1752 						MarkBridgeDirty(tile);
1753 					} else {
1754 						MarkTileDirtyByTile(tile);
1755 						MarkTileDirtyByTile(endtile);
1756 					}
1757 				}
1758 
1759 				found_convertible_track = true;
1760 				cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
1761 				break;
1762 			}
1763 
1764 			default: // MP_STATION, MP_ROAD
1765 				if (flags & DC_EXEC) {
1766 					Track track = ((tt == MP_STATION) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
1767 					YapfNotifyTrackLayoutChange(tile, track);
1768 				}
1769 
1770 				found_convertible_track = true;
1771 				cost.AddCost(RailConvertCost(type, totype));
1772 				break;
1773 		}
1774 
1775 		for (uint i = 0; i < vehicles_affected.size(); ++i) {
1776 			TryPathReserve(vehicles_affected[i], true);
1777 		}
1778 	}
1779 
1780 	if (flags & DC_EXEC) {
1781 		/* Railtype changed, update trains as when entering different track */
1782 		for (Train *v : affected_trains) {
1783 			v->ConsistChanged(CCF_TRACK);
1784 		}
1785 	}
1786 
1787 	delete iter;
1788 	return found_convertible_track ? cost : error;
1789 }
1790 
RemoveTrainDepot(TileIndex tile,DoCommandFlag flags)1791 static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
1792 {
1793 	if (_current_company != OWNER_WATER) {
1794 		CommandCost ret = CheckTileOwnership(tile);
1795 		if (ret.Failed()) return ret;
1796 	}
1797 
1798 	CommandCost ret = EnsureNoVehicleOnGround(tile);
1799 	if (ret.Failed()) return ret;
1800 
1801 	if (flags & DC_EXEC) {
1802 		/* read variables before the depot is removed */
1803 		DiagDirection dir = GetRailDepotDirection(tile);
1804 		Owner owner = GetTileOwner(tile);
1805 		Train *v = nullptr;
1806 
1807 		if (HasDepotReservation(tile)) {
1808 			v = GetTrainForReservation(tile, DiagDirToDiagTrack(dir));
1809 			if (v != nullptr) FreeTrainTrackReservation(v);
1810 		}
1811 
1812 		Company::Get(owner)->infrastructure.rail[GetRailType(tile)]--;
1813 		DirtyCompanyInfrastructureWindows(owner);
1814 
1815 		delete Depot::GetByTile(tile);
1816 		DoClearSquare(tile);
1817 		AddSideToSignalBuffer(tile, dir, owner);
1818 		YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
1819 		if (v != nullptr) TryPathReserve(v, true);
1820 	}
1821 
1822 	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]);
1823 }
1824 
ClearTile_Track(TileIndex tile,DoCommandFlag flags)1825 static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
1826 {
1827 	CommandCost cost(EXPENSES_CONSTRUCTION);
1828 
1829 	if (flags & DC_AUTO) {
1830 		if (!IsTileOwner(tile, _current_company)) {
1831 			return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
1832 		}
1833 
1834 		if (IsPlainRail(tile)) {
1835 			return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
1836 		} else {
1837 			return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
1838 		}
1839 	}
1840 
1841 	switch (GetRailTileType(tile)) {
1842 		case RAIL_TILE_SIGNALS:
1843 		case RAIL_TILE_NORMAL: {
1844 			Slope tileh = GetTileSlope(tile);
1845 			/* Is there flat water on the lower halftile that gets cleared expensively? */
1846 			bool water_ground = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh));
1847 
1848 			TrackBits tracks = GetTrackBits(tile);
1849 			while (tracks != TRACK_BIT_NONE) {
1850 				Track track = RemoveFirstTrack(&tracks);
1851 				CommandCost ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
1852 				if (ret.Failed()) return ret;
1853 				cost.AddCost(ret);
1854 			}
1855 
1856 			/* When bankrupting, don't make water dirty, there could be a ship on lower halftile.
1857 			 * Same holds for non-companies clearing the tile, e.g. disasters. */
1858 			if (water_ground && !(flags & DC_BANKRUPT) && Company::IsValidID(_current_company)) {
1859 				CommandCost ret = EnsureNoVehicleOnGround(tile);
1860 				if (ret.Failed()) return ret;
1861 
1862 				/* The track was removed, and left a coast tile. Now also clear the water. */
1863 				if (flags & DC_EXEC) {
1864 					bool remove = IsDockingTile(tile);
1865 					DoClearSquare(tile);
1866 					if (remove) RemoveDockingTile(tile);
1867 				}
1868 				cost.AddCost(_price[PR_CLEAR_WATER]);
1869 			}
1870 
1871 			return cost;
1872 		}
1873 
1874 		case RAIL_TILE_DEPOT:
1875 			return RemoveTrainDepot(tile, flags);
1876 
1877 		default:
1878 			return CMD_ERROR;
1879 	}
1880 }
1881 
1882 /**
1883  * Get surface height in point (x,y)
1884  * On tiles with halftile foundations move (x,y) to a safe point wrt. track
1885  */
GetSaveSlopeZ(uint x,uint y,Track track)1886 static uint GetSaveSlopeZ(uint x, uint y, Track track)
1887 {
1888 	switch (track) {
1889 		case TRACK_UPPER: x &= ~0xF; y &= ~0xF; break;
1890 		case TRACK_LOWER: x |=  0xF; y |=  0xF; break;
1891 		case TRACK_LEFT:  x |=  0xF; y &= ~0xF; break;
1892 		case TRACK_RIGHT: x &= ~0xF; y |=  0xF; break;
1893 		default: break;
1894 	}
1895 	return GetSlopePixelZ(x, y);
1896 }
1897 
DrawSingleSignal(TileIndex tile,const RailtypeInfo * rti,Track track,SignalState condition,SignalOffsets image,uint pos)1898 static void DrawSingleSignal(TileIndex tile, const RailtypeInfo *rti, Track track, SignalState condition, SignalOffsets image, uint pos)
1899 {
1900 	bool side;
1901 	switch (_settings_game.construction.train_signal_side) {
1902 		case 0:  side = false;                                 break; // left
1903 		case 2:  side = true;                                  break; // right
1904 		default: side = _settings_game.vehicle.road_side != 0; break; // driving side
1905 	}
1906 	static const Point SignalPositions[2][12] = {
1907 		{ // Signals on the left side
1908 		/*  LEFT      LEFT      RIGHT     RIGHT     UPPER     UPPER */
1909 			{ 8,  5}, {14,  1}, { 1, 14}, { 9, 11}, { 1,  0}, { 3, 10},
1910 		/*  LOWER     LOWER     X         X         Y         Y     */
1911 			{11,  4}, {14, 14}, {11,  3}, { 4, 13}, { 3,  4}, {11, 13}
1912 		}, { // Signals on the right side
1913 		/*  LEFT      LEFT      RIGHT     RIGHT     UPPER     UPPER */
1914 			{14,  1}, {12, 10}, { 4,  6}, { 1, 14}, {10,  4}, { 0,  1},
1915 		/*  LOWER     LOWER     X         X         Y         Y     */
1916 			{14, 14}, { 5, 12}, {11, 13}, { 4,  3}, {13,  4}, { 3, 11}
1917 		}
1918 	};
1919 
1920 	uint x = TileX(tile) * TILE_SIZE + SignalPositions[side][pos].x;
1921 	uint y = TileY(tile) * TILE_SIZE + SignalPositions[side][pos].y;
1922 
1923 	SignalType type       = GetSignalType(tile, track);
1924 	SignalVariant variant = GetSignalVariant(tile, track);
1925 
1926 	SpriteID sprite = GetCustomSignalSprite(rti, tile, type, variant, condition);
1927 	if (sprite != 0) {
1928 		sprite += image;
1929 	} else {
1930 		/* Normal electric signals are stored in a different sprite block than all other signals. */
1931 		sprite = (type == SIGTYPE_NORMAL && variant == SIG_ELECTRIC) ? SPR_ORIGINAL_SIGNALS_BASE : SPR_SIGNALS_BASE - 16;
1932 		sprite += type * 16 + variant * 64 + image * 2 + condition + (type > SIGTYPE_LAST_NOPBS ? 64 : 0);
1933 	}
1934 
1935 	AddSortableSpriteToDraw(sprite, PAL_NONE, x, y, 1, 1, BB_HEIGHT_UNDER_BRIDGE, GetSaveSlopeZ(x, y, track));
1936 }
1937 
1938 static uint32 _drawtile_track_palette;
1939 
1940 
1941 
1942 /** Offsets for drawing fences */
1943 struct FenceOffset {
1944 	Corner height_ref;  //!< Corner to use height offset from.
1945 	int x_offs;         //!< Bounding box X offset.
1946 	int y_offs;         //!< Bounding box Y offset.
1947 	int x_size;         //!< Bounding box X size.
1948 	int y_size;         //!< Bounding box Y size.
1949 };
1950 
1951 /** Offsets for drawing fences */
1952 static FenceOffset _fence_offsets[] = {
1953 	{ CORNER_INVALID,  0,  1, 16,  1 }, // RFO_FLAT_X_NW
1954 	{ CORNER_INVALID,  1,  0,  1, 16 }, // RFO_FLAT_Y_NE
1955 	{ CORNER_W,        8,  8,  1,  1 }, // RFO_FLAT_LEFT
1956 	{ CORNER_N,        8,  8,  1,  1 }, // RFO_FLAT_UPPER
1957 	{ CORNER_INVALID,  0,  1, 16,  1 }, // RFO_SLOPE_SW_NW
1958 	{ CORNER_INVALID,  1,  0,  1, 16 }, // RFO_SLOPE_SE_NE
1959 	{ CORNER_INVALID,  0,  1, 16,  1 }, // RFO_SLOPE_NE_NW
1960 	{ CORNER_INVALID,  1,  0,  1, 16 }, // RFO_SLOPE_NW_NE
1961 	{ CORNER_INVALID,  0, 15, 16,  1 }, // RFO_FLAT_X_SE
1962 	{ CORNER_INVALID, 15,  0,  1, 16 }, // RFO_FLAT_Y_SW
1963 	{ CORNER_E,        8,  8,  1,  1 }, // RFO_FLAT_RIGHT
1964 	{ CORNER_S,        8,  8,  1,  1 }, // RFO_FLAT_LOWER
1965 	{ CORNER_INVALID,  0, 15, 16,  1 }, // RFO_SLOPE_SW_SE
1966 	{ CORNER_INVALID, 15,  0,  1, 16 }, // RFO_SLOPE_SE_SW
1967 	{ CORNER_INVALID,  0, 15, 16,  1 }, // RFO_SLOPE_NE_SE
1968 	{ CORNER_INVALID, 15,  0,  1, 16 }, // RFO_SLOPE_NW_SW
1969 };
1970 
1971 /**
1972  * Draw a track fence.
1973  * @param ti Tile drawing information.
1974  * @param base_image First fence sprite.
1975  * @param num_sprites Number of fence sprites.
1976  * @param rfo Fence to draw.
1977  */
DrawTrackFence(const TileInfo * ti,SpriteID base_image,uint num_sprites,RailFenceOffset rfo)1978 static void DrawTrackFence(const TileInfo *ti, SpriteID base_image, uint num_sprites, RailFenceOffset rfo)
1979 {
1980 	int z = ti->z;
1981 	if (_fence_offsets[rfo].height_ref != CORNER_INVALID) {
1982 		z += GetSlopePixelZInCorner(RemoveHalftileSlope(ti->tileh), _fence_offsets[rfo].height_ref);
1983 	}
1984 	AddSortableSpriteToDraw(base_image + (rfo % num_sprites), _drawtile_track_palette,
1985 		ti->x + _fence_offsets[rfo].x_offs,
1986 		ti->y + _fence_offsets[rfo].y_offs,
1987 		_fence_offsets[rfo].x_size,
1988 		_fence_offsets[rfo].y_size,
1989 		4, z);
1990 }
1991 
1992 /**
1993  * Draw fence at NW border matching the tile slope.
1994  */
DrawTrackFence_NW(const TileInfo * ti,SpriteID base_image,uint num_sprites)1995 static void DrawTrackFence_NW(const TileInfo *ti, SpriteID base_image, uint num_sprites)
1996 {
1997 	RailFenceOffset rfo = RFO_FLAT_X_NW;
1998 	if (ti->tileh & SLOPE_NW) rfo = (ti->tileh & SLOPE_W) ? RFO_SLOPE_SW_NW : RFO_SLOPE_NE_NW;
1999 	DrawTrackFence(ti, base_image, num_sprites, rfo);
2000 }
2001 
2002 /**
2003  * Draw fence at SE border matching the tile slope.
2004  */
DrawTrackFence_SE(const TileInfo * ti,SpriteID base_image,uint num_sprites)2005 static void DrawTrackFence_SE(const TileInfo *ti, SpriteID base_image, uint num_sprites)
2006 {
2007 	RailFenceOffset rfo = RFO_FLAT_X_SE;
2008 	if (ti->tileh & SLOPE_SE) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SW_SE : RFO_SLOPE_NE_SE;
2009 	DrawTrackFence(ti, base_image, num_sprites, rfo);
2010 }
2011 
2012 /**
2013  * Draw fence at NE border matching the tile slope.
2014  */
DrawTrackFence_NE(const TileInfo * ti,SpriteID base_image,uint num_sprites)2015 static void DrawTrackFence_NE(const TileInfo *ti, SpriteID base_image, uint num_sprites)
2016 {
2017 	RailFenceOffset rfo = RFO_FLAT_Y_NE;
2018 	if (ti->tileh & SLOPE_NE) rfo = (ti->tileh & SLOPE_E) ? RFO_SLOPE_SE_NE : RFO_SLOPE_NW_NE;
2019 	DrawTrackFence(ti, base_image, num_sprites, rfo);
2020 }
2021 
2022 /**
2023  * Draw fence at SW border matching the tile slope.
2024  */
DrawTrackFence_SW(const TileInfo * ti,SpriteID base_image,uint num_sprites)2025 static void DrawTrackFence_SW(const TileInfo *ti, SpriteID base_image, uint num_sprites)
2026 {
2027 	RailFenceOffset rfo = RFO_FLAT_Y_SW;
2028 	if (ti->tileh & SLOPE_SW) rfo = (ti->tileh & SLOPE_S) ? RFO_SLOPE_SE_SW : RFO_SLOPE_NW_SW;
2029 	DrawTrackFence(ti, base_image, num_sprites, rfo);
2030 }
2031 
2032 /**
2033  * Draw track fences.
2034  * @param ti Tile drawing information.
2035  * @param rti Rail type information.
2036  */
DrawTrackDetails(const TileInfo * ti,const RailtypeInfo * rti)2037 static void DrawTrackDetails(const TileInfo *ti, const RailtypeInfo *rti)
2038 {
2039 	/* Base sprite for track fences.
2040 	 * Note: Halftile slopes only have fences on the upper part. */
2041 	uint num_sprites = 0;
2042 	SpriteID base_image = GetCustomRailSprite(rti, ti->tile, RTSG_FENCES, IsHalftileSlope(ti->tileh) ? TCX_UPPER_HALFTILE : TCX_NORMAL, &num_sprites);
2043 	if (base_image == 0) {
2044 		base_image = SPR_TRACK_FENCE_FLAT_X;
2045 		num_sprites = 8;
2046 	}
2047 
2048 	assert(num_sprites > 0);
2049 
2050 	switch (GetRailGroundType(ti->tile)) {
2051 		case RAIL_GROUND_FENCE_NW:     DrawTrackFence_NW(ti, base_image, num_sprites); break;
2052 		case RAIL_GROUND_FENCE_SE:     DrawTrackFence_SE(ti, base_image, num_sprites); break;
2053 		case RAIL_GROUND_FENCE_SENW:   DrawTrackFence_NW(ti, base_image, num_sprites);
2054 		                               DrawTrackFence_SE(ti, base_image, num_sprites); break;
2055 		case RAIL_GROUND_FENCE_NE:     DrawTrackFence_NE(ti, base_image, num_sprites); break;
2056 		case RAIL_GROUND_FENCE_SW:     DrawTrackFence_SW(ti, base_image, num_sprites); break;
2057 		case RAIL_GROUND_FENCE_NESW:   DrawTrackFence_NE(ti, base_image, num_sprites);
2058 		                               DrawTrackFence_SW(ti, base_image, num_sprites); break;
2059 		case RAIL_GROUND_FENCE_VERT1:  DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LEFT);  break;
2060 		case RAIL_GROUND_FENCE_VERT2:  DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_RIGHT); break;
2061 		case RAIL_GROUND_FENCE_HORIZ1: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_UPPER); break;
2062 		case RAIL_GROUND_FENCE_HORIZ2: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LOWER); break;
2063 		case RAIL_GROUND_WATER: {
2064 			Corner track_corner;
2065 			if (IsHalftileSlope(ti->tileh)) {
2066 				/* Steep slope or one-corner-raised slope with halftile foundation */
2067 				track_corner = GetHalftileSlopeCorner(ti->tileh);
2068 			} else {
2069 				/* Three-corner-raised slope */
2070 				track_corner = OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti->tileh)));
2071 			}
2072 			switch (track_corner) {
2073 				case CORNER_W: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LEFT);  break;
2074 				case CORNER_S: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_LOWER); break;
2075 				case CORNER_E: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_RIGHT); break;
2076 				case CORNER_N: DrawTrackFence(ti, base_image, num_sprites, RFO_FLAT_UPPER); break;
2077 				default: NOT_REACHED();
2078 			}
2079 			break;
2080 		}
2081 		default: break;
2082 	}
2083 }
2084 
2085 /* SubSprite for drawing the track halftile of 'three-corners-raised'-sloped rail sprites. */
2086 static const int INF = 1000; // big number compared to tilesprite size
2087 static const SubSprite _halftile_sub_sprite[4] = {
2088 	{ -INF    , -INF  , 32 - 33, INF     }, // CORNER_W, clip 33 pixels from right
2089 	{ -INF    ,  0 + 7, INF    , INF     }, // CORNER_S, clip 7 pixels from top
2090 	{ -31 + 33, -INF  , INF    , INF     }, // CORNER_E, clip 33 pixels from left
2091 	{ -INF    , -INF  , INF    , 30 - 23 }  // CORNER_N, clip 23 pixels from bottom
2092 };
2093 
DrawTrackSprite(SpriteID sprite,PaletteID pal,const TileInfo * ti,Slope s)2094 static inline void DrawTrackSprite(SpriteID sprite, PaletteID pal, const TileInfo *ti, Slope s)
2095 {
2096 	DrawGroundSprite(sprite, pal, nullptr, 0, (ti->tileh & s) ? -8 : 0);
2097 }
2098 
DrawTrackBitsOverlay(TileInfo * ti,TrackBits track,const RailtypeInfo * rti)2099 static void DrawTrackBitsOverlay(TileInfo *ti, TrackBits track, const RailtypeInfo *rti)
2100 {
2101 	RailGroundType rgt = GetRailGroundType(ti->tile);
2102 	Foundation f = GetRailFoundation(ti->tileh, track);
2103 	Corner halftile_corner = CORNER_INVALID;
2104 
2105 	if (IsNonContinuousFoundation(f)) {
2106 		/* Save halftile corner */
2107 		halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
2108 		/* Draw lower part first */
2109 		track &= ~CornerToTrackBits(halftile_corner);
2110 		f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
2111 	}
2112 
2113 	DrawFoundation(ti, f);
2114 	/* DrawFoundation modifies ti */
2115 
2116 	/* Draw ground */
2117 	if (rgt == RAIL_GROUND_WATER) {
2118 		if (track != TRACK_BIT_NONE || IsSteepSlope(ti->tileh)) {
2119 			/* three-corner-raised slope or steep slope with track on upper part */
2120 			DrawShoreTile(ti->tileh);
2121 		} else {
2122 			/* single-corner-raised slope with track on upper part */
2123 			DrawGroundSprite(SPR_FLAT_WATER_TILE, PAL_NONE);
2124 		}
2125 	} else {
2126 		SpriteID image;
2127 
2128 		switch (rgt) {
2129 			case RAIL_GROUND_BARREN:     image = SPR_FLAT_BARE_LAND;  break;
2130 			case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2131 			default:                     image = SPR_FLAT_GRASS_TILE; break;
2132 		}
2133 
2134 		image += SlopeToSpriteOffset(ti->tileh);
2135 
2136 		DrawGroundSprite(image, PAL_NONE);
2137 	}
2138 
2139 	bool no_combine = ti->tileh == SLOPE_FLAT && HasBit(rti->flags, RTF_NO_SPRITE_COMBINE);
2140 	SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2141 	SpriteID ground = GetCustomRailSprite(rti, ti->tile, no_combine ? RTSG_GROUND_COMPLETE : RTSG_GROUND);
2142 	TrackBits pbs = _settings_client.gui.show_track_reservation ? GetRailReservationTrackBits(ti->tile) : TRACK_BIT_NONE;
2143 
2144 	if (track == TRACK_BIT_NONE) {
2145 		/* Half-tile foundation, no track here? */
2146 	} else if (no_combine) {
2147 		/* Use trackbits as direct index from ground sprite, subtract 1
2148 		 * because there is no sprite for no bits. */
2149 		DrawGroundSprite(ground + track - 1, PAL_NONE);
2150 
2151 		/* Draw reserved track bits */
2152 		if (pbs & TRACK_BIT_X)     DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2153 		if (pbs & TRACK_BIT_Y)     DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2154 		if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2155 		if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2156 		if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2157 		if (pbs & TRACK_BIT_LEFT)  DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2158 	} else if (ti->tileh == SLOPE_NW && track == TRACK_BIT_Y) {
2159 		DrawGroundSprite(ground + RTO_SLOPE_NW, PAL_NONE);
2160 		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + RTO_SLOPE_NW, PALETTE_CRASH);
2161 	} else if (ti->tileh == SLOPE_NE && track == TRACK_BIT_X) {
2162 		DrawGroundSprite(ground + RTO_SLOPE_NE, PAL_NONE);
2163 		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + RTO_SLOPE_NE, PALETTE_CRASH);
2164 	} else if (ti->tileh == SLOPE_SE && track == TRACK_BIT_Y) {
2165 		DrawGroundSprite(ground + RTO_SLOPE_SE, PAL_NONE);
2166 		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + RTO_SLOPE_SE, PALETTE_CRASH);
2167 	} else if (ti->tileh == SLOPE_SW && track == TRACK_BIT_X) {
2168 		DrawGroundSprite(ground + RTO_SLOPE_SW, PAL_NONE);
2169 		if (pbs != TRACK_BIT_NONE) DrawGroundSprite(overlay + RTO_SLOPE_SW, PALETTE_CRASH);
2170 	} else {
2171 		switch (track) {
2172 			/* Draw single ground sprite when not overlapping. No track overlay
2173 			 * is necessary for these sprites. */
2174 			case TRACK_BIT_X:     DrawGroundSprite(ground + RTO_X, PAL_NONE); break;
2175 			case TRACK_BIT_Y:     DrawGroundSprite(ground + RTO_Y, PAL_NONE); break;
2176 			case TRACK_BIT_UPPER: DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N); break;
2177 			case TRACK_BIT_LOWER: DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2178 			case TRACK_BIT_RIGHT: DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E); break;
2179 			case TRACK_BIT_LEFT:  DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2180 			case TRACK_BIT_CROSS: DrawGroundSprite(ground + RTO_CROSSING_XY, PAL_NONE); break;
2181 			case TRACK_BIT_HORZ:  DrawTrackSprite(ground + RTO_N, PAL_NONE, ti, SLOPE_N);
2182 			                      DrawTrackSprite(ground + RTO_S, PAL_NONE, ti, SLOPE_S); break;
2183 			case TRACK_BIT_VERT:  DrawTrackSprite(ground + RTO_E, PAL_NONE, ti, SLOPE_E);
2184 			                      DrawTrackSprite(ground + RTO_W, PAL_NONE, ti, SLOPE_W); break;
2185 
2186 			default:
2187 				/* We're drawing a junction tile */
2188 				if ((track & TRACK_BIT_3WAY_NE) == 0) {
2189 					DrawGroundSprite(ground + RTO_JUNCTION_SW, PAL_NONE);
2190 				} else if ((track & TRACK_BIT_3WAY_SW) == 0) {
2191 					DrawGroundSprite(ground + RTO_JUNCTION_NE, PAL_NONE);
2192 				} else if ((track & TRACK_BIT_3WAY_NW) == 0) {
2193 					DrawGroundSprite(ground + RTO_JUNCTION_SE, PAL_NONE);
2194 				} else if ((track & TRACK_BIT_3WAY_SE) == 0) {
2195 					DrawGroundSprite(ground + RTO_JUNCTION_NW, PAL_NONE);
2196 				} else {
2197 					DrawGroundSprite(ground + RTO_JUNCTION_NSEW, PAL_NONE);
2198 				}
2199 
2200 				/* Mask out PBS bits as we shall draw them afterwards anyway. */
2201 				track &= ~pbs;
2202 
2203 				/* Draw regular track bits */
2204 				if (track & TRACK_BIT_X)     DrawGroundSprite(overlay + RTO_X, PAL_NONE);
2205 				if (track & TRACK_BIT_Y)     DrawGroundSprite(overlay + RTO_Y, PAL_NONE);
2206 				if (track & TRACK_BIT_UPPER) DrawGroundSprite(overlay + RTO_N, PAL_NONE);
2207 				if (track & TRACK_BIT_LOWER) DrawGroundSprite(overlay + RTO_S, PAL_NONE);
2208 				if (track & TRACK_BIT_RIGHT) DrawGroundSprite(overlay + RTO_E, PAL_NONE);
2209 				if (track & TRACK_BIT_LEFT)  DrawGroundSprite(overlay + RTO_W, PAL_NONE);
2210 		}
2211 
2212 		/* Draw reserved track bits */
2213 		if (pbs & TRACK_BIT_X)     DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2214 		if (pbs & TRACK_BIT_Y)     DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2215 		if (pbs & TRACK_BIT_UPPER) DrawTrackSprite(overlay + RTO_N, PALETTE_CRASH, ti, SLOPE_N);
2216 		if (pbs & TRACK_BIT_LOWER) DrawTrackSprite(overlay + RTO_S, PALETTE_CRASH, ti, SLOPE_S);
2217 		if (pbs & TRACK_BIT_RIGHT) DrawTrackSprite(overlay + RTO_E, PALETTE_CRASH, ti, SLOPE_E);
2218 		if (pbs & TRACK_BIT_LEFT)  DrawTrackSprite(overlay + RTO_W, PALETTE_CRASH, ti, SLOPE_W);
2219 	}
2220 
2221 	if (IsValidCorner(halftile_corner)) {
2222 		DrawFoundation(ti, HalftileFoundation(halftile_corner));
2223 		overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY, TCX_UPPER_HALFTILE);
2224 		ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND, TCX_UPPER_HALFTILE);
2225 
2226 		/* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2227 		Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2228 
2229 		SpriteID image;
2230 		switch (rgt) {
2231 			case RAIL_GROUND_BARREN:     image = SPR_FLAT_BARE_LAND;  break;
2232 			case RAIL_GROUND_ICE_DESERT:
2233 			case RAIL_GROUND_HALF_SNOW:  image = SPR_FLAT_SNOW_DESERT_TILE; break;
2234 			default:                     image = SPR_FLAT_GRASS_TILE; break;
2235 		}
2236 
2237 		image += SlopeToSpriteOffset(fake_slope);
2238 
2239 		DrawGroundSprite(image, PAL_NONE, &(_halftile_sub_sprite[halftile_corner]));
2240 
2241 		track = CornerToTrackBits(halftile_corner);
2242 
2243 		int offset;
2244 		switch (track) {
2245 			default: NOT_REACHED();
2246 			case TRACK_BIT_UPPER: offset = RTO_N; break;
2247 			case TRACK_BIT_LOWER: offset = RTO_S; break;
2248 			case TRACK_BIT_RIGHT: offset = RTO_E; break;
2249 			case TRACK_BIT_LEFT:  offset = RTO_W; break;
2250 		}
2251 
2252 		DrawTrackSprite(ground + offset, PAL_NONE, ti, fake_slope);
2253 		if (_settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, track)) {
2254 			DrawTrackSprite(overlay + offset, PALETTE_CRASH, ti, fake_slope);
2255 		}
2256 	}
2257 }
2258 
2259 /**
2260  * Draw ground sprite and track bits
2261  * @param ti TileInfo
2262  * @param track TrackBits to draw
2263  */
DrawTrackBits(TileInfo * ti,TrackBits track)2264 static void DrawTrackBits(TileInfo *ti, TrackBits track)
2265 {
2266 	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2267 
2268 	if (rti->UsesOverlay()) {
2269 		DrawTrackBitsOverlay(ti, track, rti);
2270 		return;
2271 	}
2272 
2273 	RailGroundType rgt = GetRailGroundType(ti->tile);
2274 	Foundation f = GetRailFoundation(ti->tileh, track);
2275 	Corner halftile_corner = CORNER_INVALID;
2276 
2277 	if (IsNonContinuousFoundation(f)) {
2278 		/* Save halftile corner */
2279 		halftile_corner = (f == FOUNDATION_STEEP_BOTH ? GetHighestSlopeCorner(ti->tileh) : GetHalftileFoundationCorner(f));
2280 		/* Draw lower part first */
2281 		track &= ~CornerToTrackBits(halftile_corner);
2282 		f = (f == FOUNDATION_STEEP_BOTH ? FOUNDATION_STEEP_LOWER : FOUNDATION_NONE);
2283 	}
2284 
2285 	DrawFoundation(ti, f);
2286 	/* DrawFoundation modifies ti */
2287 
2288 	SpriteID image;
2289 	PaletteID pal = PAL_NONE;
2290 	const SubSprite *sub = nullptr;
2291 	bool junction = false;
2292 
2293 	/* Select the sprite to use. */
2294 	if (track == 0) {
2295 		/* Clear ground (only track on halftile foundation) */
2296 		if (rgt == RAIL_GROUND_WATER) {
2297 			if (IsSteepSlope(ti->tileh)) {
2298 				DrawShoreTile(ti->tileh);
2299 				image = 0;
2300 			} else {
2301 				image = SPR_FLAT_WATER_TILE;
2302 			}
2303 		} else {
2304 			switch (rgt) {
2305 				case RAIL_GROUND_BARREN:     image = SPR_FLAT_BARE_LAND;  break;
2306 				case RAIL_GROUND_ICE_DESERT: image = SPR_FLAT_SNOW_DESERT_TILE; break;
2307 				default:                     image = SPR_FLAT_GRASS_TILE; break;
2308 			}
2309 			image += SlopeToSpriteOffset(ti->tileh);
2310 		}
2311 	} else {
2312 		if (ti->tileh != SLOPE_FLAT) {
2313 			/* track on non-flat ground */
2314 			image = _track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.track_y;
2315 		} else {
2316 			/* track on flat ground */
2317 			switch (track) {
2318 				/* single track, select combined track + ground sprite*/
2319 				case TRACK_BIT_Y:     image = rti->base_sprites.track_y;     break;
2320 				case TRACK_BIT_X:     image = rti->base_sprites.track_y + 1; break;
2321 				case TRACK_BIT_UPPER: image = rti->base_sprites.track_y + 2; break;
2322 				case TRACK_BIT_LOWER: image = rti->base_sprites.track_y + 3; break;
2323 				case TRACK_BIT_RIGHT: image = rti->base_sprites.track_y + 4; break;
2324 				case TRACK_BIT_LEFT:  image = rti->base_sprites.track_y + 5; break;
2325 				case TRACK_BIT_CROSS: image = rti->base_sprites.track_y + 6; break;
2326 
2327 				/* double diagonal track, select combined track + ground sprite*/
2328 				case TRACK_BIT_HORZ:  image = rti->base_sprites.track_ns;     break;
2329 				case TRACK_BIT_VERT:  image = rti->base_sprites.track_ns + 1; break;
2330 
2331 				/* junction, select only ground sprite, handle track sprite later */
2332 				default:
2333 					junction = true;
2334 					if ((track & TRACK_BIT_3WAY_NE) == 0) { image = rti->base_sprites.ground;     break; }
2335 					if ((track & TRACK_BIT_3WAY_SW) == 0) { image = rti->base_sprites.ground + 1; break; }
2336 					if ((track & TRACK_BIT_3WAY_NW) == 0) { image = rti->base_sprites.ground + 2; break; }
2337 					if ((track & TRACK_BIT_3WAY_SE) == 0) { image = rti->base_sprites.ground + 3; break; }
2338 					image = rti->base_sprites.ground + 4;
2339 					break;
2340 			}
2341 		}
2342 
2343 		switch (rgt) {
2344 			case RAIL_GROUND_BARREN:     pal = PALETTE_TO_BARE_LAND; break;
2345 			case RAIL_GROUND_ICE_DESERT: image += rti->snow_offset;  break;
2346 			case RAIL_GROUND_WATER: {
2347 				/* three-corner-raised slope */
2348 				DrawShoreTile(ti->tileh);
2349 				Corner track_corner = OppositeCorner(GetHighestSlopeCorner(ComplementSlope(ti->tileh)));
2350 				sub = &(_halftile_sub_sprite[track_corner]);
2351 				break;
2352 			}
2353 			default: break;
2354 		}
2355 	}
2356 
2357 	if (image != 0) DrawGroundSprite(image, pal, sub);
2358 
2359 	/* Draw track pieces individually for junction tiles */
2360 	if (junction) {
2361 		if (track & TRACK_BIT_X)     DrawGroundSprite(rti->base_sprites.single_x, PAL_NONE);
2362 		if (track & TRACK_BIT_Y)     DrawGroundSprite(rti->base_sprites.single_y, PAL_NONE);
2363 		if (track & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PAL_NONE);
2364 		if (track & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PAL_NONE);
2365 		if (track & TRACK_BIT_LEFT)  DrawGroundSprite(rti->base_sprites.single_w, PAL_NONE);
2366 		if (track & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PAL_NONE);
2367 	}
2368 
2369 	/* PBS debugging, draw reserved tracks darker */
2370 	if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation) {
2371 		/* Get reservation, but mask track on halftile slope */
2372 		TrackBits pbs = GetRailReservationTrackBits(ti->tile) & track;
2373 		if (pbs & TRACK_BIT_X) {
2374 			if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2375 				DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);
2376 			} else {
2377 				DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2378 			}
2379 		}
2380 		if (pbs & TRACK_BIT_Y) {
2381 			if (ti->tileh == SLOPE_FLAT || ti->tileh == SLOPE_ELEVATED) {
2382 				DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH);
2383 			} else {
2384 				DrawGroundSprite(_track_sloped_sprites[ti->tileh - 1] + rti->base_sprites.single_sloped - 20, PALETTE_CRASH);
2385 			}
2386 		}
2387 		if (pbs & TRACK_BIT_UPPER) DrawGroundSprite(rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_N ? -(int)TILE_HEIGHT : 0);
2388 		if (pbs & TRACK_BIT_LOWER) DrawGroundSprite(rti->base_sprites.single_s, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_S ? -(int)TILE_HEIGHT : 0);
2389 		if (pbs & TRACK_BIT_LEFT)  DrawGroundSprite(rti->base_sprites.single_w, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_W ? -(int)TILE_HEIGHT : 0);
2390 		if (pbs & TRACK_BIT_RIGHT) DrawGroundSprite(rti->base_sprites.single_e, PALETTE_CRASH, nullptr, 0, ti->tileh & SLOPE_E ? -(int)TILE_HEIGHT : 0);
2391 	}
2392 
2393 	if (IsValidCorner(halftile_corner)) {
2394 		DrawFoundation(ti, HalftileFoundation(halftile_corner));
2395 
2396 		/* Draw higher halftile-overlay: Use the sloped sprites with three corners raised. They probably best fit the lightning. */
2397 		Slope fake_slope = SlopeWithThreeCornersRaised(OppositeCorner(halftile_corner));
2398 		image = _track_sloped_sprites[fake_slope - 1] + rti->base_sprites.track_y;
2399 		pal = PAL_NONE;
2400 		switch (rgt) {
2401 			case RAIL_GROUND_BARREN:     pal = PALETTE_TO_BARE_LAND; break;
2402 			case RAIL_GROUND_ICE_DESERT:
2403 			case RAIL_GROUND_HALF_SNOW:  image += rti->snow_offset;  break; // higher part has snow in this case too
2404 			default: break;
2405 		}
2406 		DrawGroundSprite(image, pal, &(_halftile_sub_sprite[halftile_corner]));
2407 
2408 		if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasReservedTracks(ti->tile, CornerToTrackBits(halftile_corner))) {
2409 			static const byte _corner_to_track_sprite[] = {3, 1, 2, 0};
2410 			DrawGroundSprite(_corner_to_track_sprite[halftile_corner] + rti->base_sprites.single_n, PALETTE_CRASH, nullptr, 0, -(int)TILE_HEIGHT);
2411 		}
2412 	}
2413 }
2414 
DrawSignals(TileIndex tile,TrackBits rails,const RailtypeInfo * rti)2415 static void DrawSignals(TileIndex tile, TrackBits rails, const RailtypeInfo *rti)
2416 {
2417 #define MAYBE_DRAW_SIGNAL(x, y, z, t) if (IsSignalPresent(tile, x)) DrawSingleSignal(tile, rti, t, GetSingleSignalState(tile, x), y, z)
2418 
2419 	if (!(rails & TRACK_BIT_Y)) {
2420 		if (!(rails & TRACK_BIT_X)) {
2421 			if (rails & TRACK_BIT_LEFT) {
2422 				MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTH, 0, TRACK_LEFT);
2423 				MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTH, 1, TRACK_LEFT);
2424 			}
2425 			if (rails & TRACK_BIT_RIGHT) {
2426 				MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_NORTH, 2, TRACK_RIGHT);
2427 				MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_SOUTH, 3, TRACK_RIGHT);
2428 			}
2429 			if (rails & TRACK_BIT_UPPER) {
2430 				MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_WEST, 4, TRACK_UPPER);
2431 				MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_EAST, 5, TRACK_UPPER);
2432 			}
2433 			if (rails & TRACK_BIT_LOWER) {
2434 				MAYBE_DRAW_SIGNAL(1, SIGNAL_TO_WEST, 6, TRACK_LOWER);
2435 				MAYBE_DRAW_SIGNAL(0, SIGNAL_TO_EAST, 7, TRACK_LOWER);
2436 			}
2437 		} else {
2438 			MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHWEST, 8, TRACK_X);
2439 			MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHEAST, 9, TRACK_X);
2440 		}
2441 	} else {
2442 		MAYBE_DRAW_SIGNAL(3, SIGNAL_TO_SOUTHEAST, 10, TRACK_Y);
2443 		MAYBE_DRAW_SIGNAL(2, SIGNAL_TO_NORTHWEST, 11, TRACK_Y);
2444 	}
2445 }
2446 
DrawTile_Track(TileInfo * ti)2447 static void DrawTile_Track(TileInfo *ti)
2448 {
2449 	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(ti->tile));
2450 
2451 	_drawtile_track_palette = COMPANY_SPRITE_COLOUR(GetTileOwner(ti->tile));
2452 
2453 	if (IsPlainRail(ti->tile)) {
2454 		TrackBits rails = GetTrackBits(ti->tile);
2455 
2456 		DrawTrackBits(ti, rails);
2457 
2458 		if (HasBit(_display_opt, DO_FULL_DETAIL)) DrawTrackDetails(ti, rti);
2459 
2460 		if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
2461 
2462 		if (HasSignals(ti->tile)) DrawSignals(ti->tile, rails, rti);
2463 	} else {
2464 		/* draw depot */
2465 		const DrawTileSprites *dts;
2466 		PaletteID pal = PAL_NONE;
2467 		SpriteID relocation;
2468 
2469 		if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
2470 
2471 		if (IsInvisibilitySet(TO_BUILDINGS)) {
2472 			/* Draw rail instead of depot */
2473 			dts = &_depot_invisible_gfx_table[GetRailDepotDirection(ti->tile)];
2474 		} else {
2475 			dts = &_depot_gfx_table[GetRailDepotDirection(ti->tile)];
2476 		}
2477 
2478 		SpriteID image;
2479 		if (rti->UsesOverlay()) {
2480 			image = SPR_FLAT_GRASS_TILE;
2481 		} else {
2482 			image = dts->ground.sprite;
2483 			if (image != SPR_FLAT_GRASS_TILE) image += rti->GetRailtypeSpriteOffset();
2484 		}
2485 
2486 		/* Adjust ground tile for desert and snow. */
2487 		if (IsSnowRailGround(ti->tile)) {
2488 			if (image != SPR_FLAT_GRASS_TILE) {
2489 				image += rti->snow_offset; // tile with tracks
2490 			} else {
2491 				image = SPR_FLAT_SNOW_DESERT_TILE; // flat ground
2492 			}
2493 		}
2494 
2495 		DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, _drawtile_track_palette));
2496 
2497 		if (rti->UsesOverlay()) {
2498 			SpriteID ground = GetCustomRailSprite(rti, ti->tile, RTSG_GROUND);
2499 
2500 			switch (GetRailDepotDirection(ti->tile)) {
2501 				case DIAGDIR_NE:
2502 					if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2503 					FALLTHROUGH;
2504 				case DIAGDIR_SW:
2505 					DrawGroundSprite(ground + RTO_X, PAL_NONE);
2506 					break;
2507 				case DIAGDIR_NW:
2508 					if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2509 					FALLTHROUGH;
2510 				case DIAGDIR_SE:
2511 					DrawGroundSprite(ground + RTO_Y, PAL_NONE);
2512 					break;
2513 				default:
2514 					break;
2515 			}
2516 
2517 			if (_settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2518 				SpriteID overlay = GetCustomRailSprite(rti, ti->tile, RTSG_OVERLAY);
2519 
2520 				switch (GetRailDepotDirection(ti->tile)) {
2521 					case DIAGDIR_NE:
2522 						if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2523 						FALLTHROUGH;
2524 					case DIAGDIR_SW:
2525 						DrawGroundSprite(overlay + RTO_X, PALETTE_CRASH);
2526 						break;
2527 					case DIAGDIR_NW:
2528 						if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2529 						FALLTHROUGH;
2530 					case DIAGDIR_SE:
2531 						DrawGroundSprite(overlay + RTO_Y, PALETTE_CRASH);
2532 						break;
2533 					default:
2534 						break;
2535 				}
2536 			}
2537 		} else {
2538 			/* PBS debugging, draw reserved tracks darker */
2539 			if (_game_mode != GM_MENU && _settings_client.gui.show_track_reservation && HasDepotReservation(ti->tile)) {
2540 				switch (GetRailDepotDirection(ti->tile)) {
2541 					case DIAGDIR_NE:
2542 						if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2543 						FALLTHROUGH;
2544 					case DIAGDIR_SW:
2545 						DrawGroundSprite(rti->base_sprites.single_x, PALETTE_CRASH);
2546 						break;
2547 					case DIAGDIR_NW:
2548 						if (!IsInvisibilitySet(TO_BUILDINGS)) break;
2549 						FALLTHROUGH;
2550 					case DIAGDIR_SE:
2551 						DrawGroundSprite(rti->base_sprites.single_y, PALETTE_CRASH);
2552 						break;
2553 					default:
2554 						break;
2555 				}
2556 			}
2557 		}
2558 		int depot_sprite = GetCustomRailSprite(rti, ti->tile, RTSG_DEPOT);
2559 		relocation = depot_sprite != 0 ? depot_sprite - SPR_RAIL_DEPOT_SE_1 : rti->GetRailtypeSpriteOffset();
2560 
2561 		if (HasRailCatenaryDrawn(GetRailType(ti->tile))) DrawRailCatenary(ti);
2562 
2563 		DrawRailTileSeq(ti, dts, TO_BUILDINGS, relocation, 0, _drawtile_track_palette);
2564 	}
2565 	DrawBridgeMiddle(ti);
2566 }
2567 
DrawTrainDepotSprite(int x,int y,int dir,RailType railtype)2568 void DrawTrainDepotSprite(int x, int y, int dir, RailType railtype)
2569 {
2570 	const DrawTileSprites *dts = &_depot_gfx_table[dir];
2571 	const RailtypeInfo *rti = GetRailTypeInfo(railtype);
2572 	SpriteID image = rti->UsesOverlay() ? SPR_FLAT_GRASS_TILE : dts->ground.sprite;
2573 	uint32 offset = rti->GetRailtypeSpriteOffset();
2574 
2575 	if (image != SPR_FLAT_GRASS_TILE) image += offset;
2576 	PaletteID palette = COMPANY_SPRITE_COLOUR(_local_company);
2577 
2578 	DrawSprite(image, PAL_NONE, x, y);
2579 
2580 	if (rti->UsesOverlay()) {
2581 		SpriteID ground = GetCustomRailSprite(rti, INVALID_TILE, RTSG_GROUND);
2582 
2583 		switch (dir) {
2584 			case DIAGDIR_SW: DrawSprite(ground + RTO_X, PAL_NONE, x, y); break;
2585 			case DIAGDIR_SE: DrawSprite(ground + RTO_Y, PAL_NONE, x, y); break;
2586 			default: break;
2587 		}
2588 	}
2589 	int depot_sprite = GetCustomRailSprite(rti, INVALID_TILE, RTSG_DEPOT);
2590 	if (depot_sprite != 0) offset = depot_sprite - SPR_RAIL_DEPOT_SE_1;
2591 
2592 	DrawRailTileSeqInGUI(x, y, dts, offset, 0, palette);
2593 }
2594 
GetSlopePixelZ_Track(TileIndex tile,uint x,uint y)2595 static int GetSlopePixelZ_Track(TileIndex tile, uint x, uint y)
2596 {
2597 	if (IsPlainRail(tile)) {
2598 		int z;
2599 		Slope tileh = GetTilePixelSlope(tile, &z);
2600 		if (tileh == SLOPE_FLAT) return z;
2601 
2602 		z += ApplyPixelFoundationToSlope(GetRailFoundation(tileh, GetTrackBits(tile)), &tileh);
2603 		return z + GetPartialPixelZ(x & 0xF, y & 0xF, tileh);
2604 	} else {
2605 		return GetTileMaxPixelZ(tile);
2606 	}
2607 }
2608 
GetFoundation_Track(TileIndex tile,Slope tileh)2609 static Foundation GetFoundation_Track(TileIndex tile, Slope tileh)
2610 {
2611 	return IsPlainRail(tile) ? GetRailFoundation(tileh, GetTrackBits(tile)) : FlatteningFoundation(tileh);
2612 }
2613 
TileLoop_Track(TileIndex tile)2614 static void TileLoop_Track(TileIndex tile)
2615 {
2616 	RailGroundType old_ground = GetRailGroundType(tile);
2617 	RailGroundType new_ground;
2618 
2619 	if (old_ground == RAIL_GROUND_WATER) {
2620 		TileLoop_Water(tile);
2621 		return;
2622 	}
2623 
2624 	switch (_settings_game.game_creation.landscape) {
2625 		case LT_ARCTIC: {
2626 			int z;
2627 			Slope slope = GetTileSlope(tile, &z);
2628 			bool half = false;
2629 
2630 			/* for non-flat track, use lower part of track
2631 			 * in other cases, use the highest part with track */
2632 			if (IsPlainRail(tile)) {
2633 				TrackBits track = GetTrackBits(tile);
2634 				Foundation f = GetRailFoundation(slope, track);
2635 
2636 				switch (f) {
2637 					case FOUNDATION_NONE:
2638 						/* no foundation - is the track on the upper side of three corners raised tile? */
2639 						if (IsSlopeWithThreeCornersRaised(slope)) z++;
2640 						break;
2641 
2642 					case FOUNDATION_INCLINED_X:
2643 					case FOUNDATION_INCLINED_Y:
2644 						/* sloped track - is it on a steep slope? */
2645 						if (IsSteepSlope(slope)) z++;
2646 						break;
2647 
2648 					case FOUNDATION_STEEP_LOWER:
2649 						/* only lower part of steep slope */
2650 						z++;
2651 						break;
2652 
2653 					default:
2654 						/* if it is a steep slope, then there is a track on higher part */
2655 						if (IsSteepSlope(slope)) z++;
2656 						z++;
2657 						break;
2658 				}
2659 
2660 				half = IsInsideMM(f, FOUNDATION_STEEP_BOTH, FOUNDATION_HALFTILE_N + 1);
2661 			} else {
2662 				/* is the depot on a non-flat tile? */
2663 				if (slope != SLOPE_FLAT) z++;
2664 			}
2665 
2666 			/* 'z' is now the lowest part of the highest track bit -
2667 			 * for sloped track, it is 'z' of lower part
2668 			 * for two track bits, it is 'z' of higher track bit
2669 			 * For non-continuous foundations (and STEEP_BOTH), 'half' is set */
2670 			if (z > GetSnowLine()) {
2671 				if (half && z - GetSnowLine() == 1) {
2672 					/* track on non-continuous foundation, lower part is not under snow */
2673 					new_ground = RAIL_GROUND_HALF_SNOW;
2674 				} else {
2675 					new_ground = RAIL_GROUND_ICE_DESERT;
2676 				}
2677 				goto set_ground;
2678 			}
2679 			break;
2680 			}
2681 
2682 		case LT_TROPIC:
2683 			if (GetTropicZone(tile) == TROPICZONE_DESERT) {
2684 				new_ground = RAIL_GROUND_ICE_DESERT;
2685 				goto set_ground;
2686 			}
2687 			break;
2688 	}
2689 
2690 	new_ground = RAIL_GROUND_GRASS;
2691 
2692 	if (IsPlainRail(tile) && old_ground != RAIL_GROUND_BARREN) { // wait until bottom is green
2693 		/* determine direction of fence */
2694 		TrackBits rail = GetTrackBits(tile);
2695 
2696 		Owner owner = GetTileOwner(tile);
2697 		byte fences = 0;
2698 
2699 		for (DiagDirection d = DIAGDIR_BEGIN; d < DIAGDIR_END; d++) {
2700 			static const TrackBits dir_to_trackbits[DIAGDIR_END] = {TRACK_BIT_3WAY_NE, TRACK_BIT_3WAY_SE, TRACK_BIT_3WAY_SW, TRACK_BIT_3WAY_NW};
2701 
2702 			/* Track bit on this edge => no fence. */
2703 			if ((rail & dir_to_trackbits[d]) != TRACK_BIT_NONE) continue;
2704 
2705 			TileIndex tile2 = tile + TileOffsByDiagDir(d);
2706 
2707 			/* Show fences if it's a house, industry, object, road, tunnelbridge or not owned by us. */
2708 			if (!IsValidTile(tile2) || IsTileType(tile2, MP_HOUSE) || IsTileType(tile2, MP_INDUSTRY) ||
2709 					IsTileType(tile2, MP_ROAD) || (IsTileType(tile2, MP_OBJECT) && !IsObjectType(tile2, OBJECT_OWNED_LAND)) || IsTileType(tile2, MP_TUNNELBRIDGE) || !IsTileOwner(tile2, owner)) {
2710 				fences |= 1 << d;
2711 			}
2712 		}
2713 
2714 		switch (fences) {
2715 			case 0: break;
2716 			case (1 << DIAGDIR_NE): new_ground = RAIL_GROUND_FENCE_NE; break;
2717 			case (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_SE; break;
2718 			case (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_SW; break;
2719 			case (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_NW; break;
2720 			case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_NESW; break;
2721 			case (1 << DIAGDIR_SE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_SENW; break;
2722 			case (1 << DIAGDIR_NE) | (1 << DIAGDIR_SE): new_ground = RAIL_GROUND_FENCE_VERT1; break;
2723 			case (1 << DIAGDIR_NE) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_HORIZ2; break;
2724 			case (1 << DIAGDIR_SE) | (1 << DIAGDIR_SW): new_ground = RAIL_GROUND_FENCE_HORIZ1; break;
2725 			case (1 << DIAGDIR_SW) | (1 << DIAGDIR_NW): new_ground = RAIL_GROUND_FENCE_VERT2; break;
2726 			default: NOT_REACHED();
2727 		}
2728 	}
2729 
2730 set_ground:
2731 	if (old_ground != new_ground) {
2732 		SetRailGroundType(tile, new_ground);
2733 		MarkTileDirtyByTile(tile);
2734 	}
2735 }
2736 
2737 
GetTileTrackStatus_Track(TileIndex tile,TransportType mode,uint sub_mode,DiagDirection side)2738 static TrackStatus GetTileTrackStatus_Track(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
2739 {
2740 	/* Case of half tile slope with water. */
2741 	if (mode == TRANSPORT_WATER && IsPlainRail(tile) && GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(GetTileSlope(tile))) {
2742 		TrackBits tb = GetTrackBits(tile);
2743 		switch (tb) {
2744 			default: NOT_REACHED();
2745 			case TRACK_BIT_UPPER: tb = TRACK_BIT_LOWER; break;
2746 			case TRACK_BIT_LOWER: tb = TRACK_BIT_UPPER; break;
2747 			case TRACK_BIT_LEFT:  tb = TRACK_BIT_RIGHT; break;
2748 			case TRACK_BIT_RIGHT: tb = TRACK_BIT_LEFT;  break;
2749 		}
2750 		return CombineTrackStatus(TrackBitsToTrackdirBits(tb), TRACKDIR_BIT_NONE);
2751 	}
2752 
2753 	if (mode != TRANSPORT_RAIL) return 0;
2754 
2755 	TrackBits trackbits = TRACK_BIT_NONE;
2756 	TrackdirBits red_signals = TRACKDIR_BIT_NONE;
2757 
2758 	switch (GetRailTileType(tile)) {
2759 		default: NOT_REACHED();
2760 		case RAIL_TILE_NORMAL:
2761 			trackbits = GetTrackBits(tile);
2762 			break;
2763 
2764 		case RAIL_TILE_SIGNALS: {
2765 			trackbits = GetTrackBits(tile);
2766 			byte a = GetPresentSignals(tile);
2767 			uint b = GetSignalStates(tile);
2768 
2769 			b &= a;
2770 
2771 			/* When signals are not present (in neither direction),
2772 			 * we pretend them to be green. Otherwise, it depends on
2773 			 * the signal type. For signals that are only active from
2774 			 * one side, we set the missing signals explicitly to
2775 			 * `green'. Otherwise, they implicitly become `red'. */
2776 			if (!IsOnewaySignal(tile, TRACK_UPPER) || (a & SignalOnTrack(TRACK_UPPER)) == 0) b |= ~a & SignalOnTrack(TRACK_UPPER);
2777 			if (!IsOnewaySignal(tile, TRACK_LOWER) || (a & SignalOnTrack(TRACK_LOWER)) == 0) b |= ~a & SignalOnTrack(TRACK_LOWER);
2778 
2779 			if ((b & 0x8) == 0) red_signals |= (TRACKDIR_BIT_LEFT_N | TRACKDIR_BIT_X_NE | TRACKDIR_BIT_Y_SE | TRACKDIR_BIT_UPPER_E);
2780 			if ((b & 0x4) == 0) red_signals |= (TRACKDIR_BIT_LEFT_S | TRACKDIR_BIT_X_SW | TRACKDIR_BIT_Y_NW | TRACKDIR_BIT_UPPER_W);
2781 			if ((b & 0x2) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_N | TRACKDIR_BIT_LOWER_E);
2782 			if ((b & 0x1) == 0) red_signals |= (TRACKDIR_BIT_RIGHT_S | TRACKDIR_BIT_LOWER_W);
2783 
2784 			break;
2785 		}
2786 
2787 		case RAIL_TILE_DEPOT: {
2788 			DiagDirection dir = GetRailDepotDirection(tile);
2789 
2790 			if (side != INVALID_DIAGDIR && side != dir) break;
2791 
2792 			trackbits = DiagDirToDiagTrackBits(dir);
2793 			break;
2794 		}
2795 	}
2796 
2797 	return CombineTrackStatus(TrackBitsToTrackdirBits(trackbits), red_signals);
2798 }
2799 
ClickTile_Track(TileIndex tile)2800 static bool ClickTile_Track(TileIndex tile)
2801 {
2802 	if (!IsRailDepot(tile)) return false;
2803 
2804 	ShowDepotWindow(tile, VEH_TRAIN);
2805 	return true;
2806 }
2807 
GetTileDesc_Track(TileIndex tile,TileDesc * td)2808 static void GetTileDesc_Track(TileIndex tile, TileDesc *td)
2809 {
2810 	const RailtypeInfo *rti = GetRailTypeInfo(GetRailType(tile));
2811 	td->rail_speed = rti->max_speed;
2812 	td->railtype = rti->strings.name;
2813 	td->owner[0] = GetTileOwner(tile);
2814 	switch (GetRailTileType(tile)) {
2815 		case RAIL_TILE_NORMAL:
2816 			td->str = STR_LAI_RAIL_DESCRIPTION_TRACK;
2817 			break;
2818 
2819 		case RAIL_TILE_SIGNALS: {
2820 			static const StringID signal_type[6][6] = {
2821 				{
2822 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_SIGNALS,
2823 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2824 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2825 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2826 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2827 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS
2828 				},
2829 				{
2830 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PRESIGNALS,
2831 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRESIGNALS,
2832 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2833 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2834 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2835 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS
2836 				},
2837 				{
2838 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_EXITSIGNALS,
2839 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_EXITSIGNALS,
2840 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXITSIGNALS,
2841 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2842 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2843 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS
2844 				},
2845 				{
2846 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_COMBOSIGNALS,
2847 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_COMBOSIGNALS,
2848 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_COMBOSIGNALS,
2849 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBOSIGNALS,
2850 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2851 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS
2852 				},
2853 				{
2854 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_PBSSIGNALS,
2855 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_PBSSIGNALS,
2856 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_PBSSIGNALS,
2857 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_PBSSIGNALS,
2858 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBSSIGNALS,
2859 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS
2860 				},
2861 				{
2862 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NORMAL_NOENTRYSIGNALS,
2863 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PRE_NOENTRYSIGNALS,
2864 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_EXIT_NOENTRYSIGNALS,
2865 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_COMBO_NOENTRYSIGNALS,
2866 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_PBS_NOENTRYSIGNALS,
2867 					STR_LAI_RAIL_DESCRIPTION_TRACK_WITH_NOENTRYSIGNALS
2868 				}
2869 			};
2870 
2871 			SignalType primary_signal;
2872 			SignalType secondary_signal;
2873 			if (HasSignalOnTrack(tile, TRACK_UPPER)) {
2874 				primary_signal = GetSignalType(tile, TRACK_UPPER);
2875 				secondary_signal = HasSignalOnTrack(tile, TRACK_LOWER) ? GetSignalType(tile, TRACK_LOWER) : primary_signal;
2876 			} else {
2877 				secondary_signal = primary_signal = GetSignalType(tile, TRACK_LOWER);
2878 			}
2879 
2880 			td->str = signal_type[secondary_signal][primary_signal];
2881 			break;
2882 		}
2883 
2884 		case RAIL_TILE_DEPOT:
2885 			td->str = STR_LAI_RAIL_DESCRIPTION_TRAIN_DEPOT;
2886 			if (_settings_game.vehicle.train_acceleration_model != AM_ORIGINAL) {
2887 				if (td->rail_speed > 0) {
2888 					td->rail_speed = std::min<uint16>(td->rail_speed, 61);
2889 				} else {
2890 					td->rail_speed = 61;
2891 				}
2892 			}
2893 			td->build_date = Depot::GetByTile(tile)->build_date;
2894 			break;
2895 
2896 		default:
2897 			NOT_REACHED();
2898 	}
2899 }
2900 
ChangeTileOwner_Track(TileIndex tile,Owner old_owner,Owner new_owner)2901 static void ChangeTileOwner_Track(TileIndex tile, Owner old_owner, Owner new_owner)
2902 {
2903 	if (!IsTileOwner(tile, old_owner)) return;
2904 
2905 	if (new_owner != INVALID_OWNER) {
2906 		/* Update company infrastructure counts. No need to dirty windows here, we'll redraw the whole screen anyway. */
2907 		uint num_pieces = 1;
2908 		if (IsPlainRail(tile)) {
2909 			TrackBits bits = GetTrackBits(tile);
2910 			num_pieces = CountBits(bits);
2911 			if (TracksOverlap(bits)) num_pieces *= num_pieces;
2912 		}
2913 		RailType rt = GetRailType(tile);
2914 		Company::Get(old_owner)->infrastructure.rail[rt] -= num_pieces;
2915 		Company::Get(new_owner)->infrastructure.rail[rt] += num_pieces;
2916 
2917 		if (HasSignals(tile)) {
2918 			uint num_sigs = CountBits(GetPresentSignals(tile));
2919 			Company::Get(old_owner)->infrastructure.signal -= num_sigs;
2920 			Company::Get(new_owner)->infrastructure.signal += num_sigs;
2921 		}
2922 
2923 		SetTileOwner(tile, new_owner);
2924 	} else {
2925 		DoCommand(tile, 0, 0, DC_EXEC | DC_BANKRUPT, CMD_LANDSCAPE_CLEAR);
2926 	}
2927 }
2928 
2929 static const byte _fractcoords_behind[4] = { 0x8F, 0x8, 0x80, 0xF8 };
2930 static const byte _fractcoords_enter[4] = { 0x8A, 0x48, 0x84, 0xA8 };
2931 static const int8 _deltacoord_leaveoffset[8] = {
2932 	-1,  0,  1,  0, /* x */
2933 	 0,  1,  0, -1  /* y */
2934 };
2935 
2936 
2937 /**
2938  * Compute number of ticks when next wagon will leave a depot.
2939  * Negative means next wagon should have left depot n ticks before.
2940  * @param v vehicle outside (leaving) the depot
2941  * @return number of ticks when the next wagon will leave
2942  */
TicksToLeaveDepot(const Train * v)2943 int TicksToLeaveDepot(const Train *v)
2944 {
2945 	DiagDirection dir = GetRailDepotDirection(v->tile);
2946 	int length = v->CalcNextVehicleOffset();
2947 
2948 	switch (dir) {
2949 		case DIAGDIR_NE: return  ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
2950 		case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4)   + (length + 1)));
2951 		case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1)));
2952 		case DIAGDIR_NW: return  ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4)   - (length + 1)));
2953 		default: NOT_REACHED();
2954 	}
2955 }
2956 
2957 /**
2958  * Tile callback routine when vehicle enters tile
2959  * @see vehicle_enter_tile_proc
2960  */
VehicleEnter_Track(Vehicle * u,TileIndex tile,int x,int y)2961 static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int x, int y)
2962 {
2963 	/* This routine applies only to trains in depot tiles. */
2964 	if (u->type != VEH_TRAIN || !IsRailDepotTile(tile)) return VETSB_CONTINUE;
2965 
2966 	/* Depot direction. */
2967 	DiagDirection dir = GetRailDepotDirection(tile);
2968 
2969 	byte fract_coord = (x & 0xF) + ((y & 0xF) << 4);
2970 
2971 	/* Make sure a train is not entering the tile from behind. */
2972 	if (_fractcoords_behind[dir] == fract_coord) return VETSB_CANNOT_ENTER;
2973 
2974 	Train *v = Train::From(u);
2975 
2976 	/* Leaving depot? */
2977 	if (v->direction == DiagDirToDir(dir)) {
2978 		/* Calculate the point where the following wagon should be activated. */
2979 		int length = v->CalcNextVehicleOffset();
2980 
2981 		byte fract_coord_leave =
2982 			((_fractcoords_enter[dir] & 0x0F) + // x
2983 				(length + 1) * _deltacoord_leaveoffset[dir]) +
2984 			(((_fractcoords_enter[dir] >> 4) +  // y
2985 				((length + 1) * _deltacoord_leaveoffset[dir + 4])) << 4);
2986 
2987 		if (fract_coord_leave == fract_coord) {
2988 			/* Leave the depot. */
2989 			if ((v = v->Next()) != nullptr) {
2990 				v->vehstatus &= ~VS_HIDDEN;
2991 				v->track = (DiagDirToAxis(dir) == AXIS_X ? TRACK_BIT_X : TRACK_BIT_Y);
2992 			}
2993 		}
2994 	} else if (_fractcoords_enter[dir] == fract_coord) {
2995 		/* Entering depot. */
2996 		assert(DiagDirToDir(ReverseDiagDir(dir)) == v->direction);
2997 		v->track = TRACK_BIT_DEPOT,
2998 		v->vehstatus |= VS_HIDDEN;
2999 		v->direction = ReverseDir(v->direction);
3000 		if (v->Next() == nullptr) VehicleEnterDepot(v->First());
3001 		v->tile = tile;
3002 
3003 		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
3004 		return VETSB_ENTERED_WORMHOLE;
3005 	}
3006 
3007 	return VETSB_CONTINUE;
3008 }
3009 
3010 /**
3011  * Tests if autoslope is allowed.
3012  *
3013  * @param tile The tile.
3014  * @param flags Terraform command flags.
3015  * @param z_old Old TileZ.
3016  * @param tileh_old Old TileSlope.
3017  * @param z_new New TileZ.
3018  * @param tileh_new New TileSlope.
3019  * @param rail_bits Trackbits.
3020  */
TestAutoslopeOnRailTile(TileIndex tile,uint flags,int z_old,Slope tileh_old,int z_new,Slope tileh_new,TrackBits rail_bits)3021 static CommandCost TestAutoslopeOnRailTile(TileIndex tile, uint flags, int z_old, Slope tileh_old, int z_new, Slope tileh_new, TrackBits rail_bits)
3022 {
3023 	if (!_settings_game.construction.build_on_slopes || !AutoslopeEnabled()) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3024 
3025 	/* Is the slope-rail_bits combination valid in general? I.e. is it safe to call GetRailFoundation() ? */
3026 	if (CheckRailSlope(tileh_new, rail_bits, TRACK_BIT_NONE, tile).Failed()) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3027 
3028 	/* Get the slopes on top of the foundations */
3029 	z_old += ApplyFoundationToSlope(GetRailFoundation(tileh_old, rail_bits), &tileh_old);
3030 	z_new += ApplyFoundationToSlope(GetRailFoundation(tileh_new, rail_bits), &tileh_new);
3031 
3032 	Corner track_corner;
3033 	switch (rail_bits) {
3034 		case TRACK_BIT_LEFT:  track_corner = CORNER_W; break;
3035 		case TRACK_BIT_LOWER: track_corner = CORNER_S; break;
3036 		case TRACK_BIT_RIGHT: track_corner = CORNER_E; break;
3037 		case TRACK_BIT_UPPER: track_corner = CORNER_N; break;
3038 
3039 		/* Surface slope must not be changed */
3040 		default:
3041 			if (z_old != z_new || tileh_old != tileh_new) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3042 			return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
3043 	}
3044 
3045 	/* The height of the track_corner must not be changed. The rest ensures GetRailFoundation() already. */
3046 	z_old += GetSlopeZInCorner(RemoveHalftileSlope(tileh_old), track_corner);
3047 	z_new += GetSlopeZInCorner(RemoveHalftileSlope(tileh_new), track_corner);
3048 	if (z_old != z_new) return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
3049 
3050 	CommandCost cost = CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
3051 	/* Make the ground dirty, if surface slope has changed */
3052 	if (tileh_old != tileh_new) {
3053 		/* If there is flat water on the lower halftile add the cost for clearing it */
3054 		if (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old)) cost.AddCost(_price[PR_CLEAR_WATER]);
3055 		if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN);
3056 	}
3057 	return  cost;
3058 }
3059 
3060 /**
3061  * Test-procedure for HasVehicleOnPos to check for a ship.
3062  */
EnsureNoShipProc(Vehicle * v,void * data)3063 static Vehicle *EnsureNoShipProc(Vehicle *v, void *data)
3064 {
3065 	return v->type == VEH_SHIP ? v : nullptr;
3066 }
3067 
TerraformTile_Track(TileIndex tile,DoCommandFlag flags,int z_new,Slope tileh_new)3068 static CommandCost TerraformTile_Track(TileIndex tile, DoCommandFlag flags, int z_new, Slope tileh_new)
3069 {
3070 	int z_old;
3071 	Slope tileh_old = GetTileSlope(tile, &z_old);
3072 	if (IsPlainRail(tile)) {
3073 		TrackBits rail_bits = GetTrackBits(tile);
3074 		/* Is there flat water on the lower halftile that must be cleared expensively? */
3075 		bool was_water = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh_old));
3076 
3077 		/* Allow clearing the water only if there is no ship */
3078 		if (was_water && HasVehicleOnPos(tile, nullptr, &EnsureNoShipProc)) return_cmd_error(STR_ERROR_SHIP_IN_THE_WAY);
3079 
3080 		/* First test autoslope. However if it succeeds we still have to test the rest, because non-autoslope terraforming is cheaper. */
3081 		CommandCost autoslope_result = TestAutoslopeOnRailTile(tile, flags, z_old, tileh_old, z_new, tileh_new, rail_bits);
3082 
3083 		/* When there is only a single horizontal/vertical track, one corner can be terraformed. */
3084 		Corner allowed_corner;
3085 		switch (rail_bits) {
3086 			case TRACK_BIT_RIGHT: allowed_corner = CORNER_W; break;
3087 			case TRACK_BIT_UPPER: allowed_corner = CORNER_S; break;
3088 			case TRACK_BIT_LEFT:  allowed_corner = CORNER_E; break;
3089 			case TRACK_BIT_LOWER: allowed_corner = CORNER_N; break;
3090 			default: return autoslope_result;
3091 		}
3092 
3093 		Foundation f_old = GetRailFoundation(tileh_old, rail_bits);
3094 
3095 		/* Do not allow terraforming if allowed_corner is part of anti-zig-zag foundations */
3096 		if (tileh_old != SLOPE_NS && tileh_old != SLOPE_EW && IsSpecialRailFoundation(f_old)) return autoslope_result;
3097 
3098 		/* Everything is valid, which only changes allowed_corner */
3099 		for (Corner corner = (Corner)0; corner < CORNER_END; corner = (Corner)(corner + 1)) {
3100 			if (allowed_corner == corner) continue;
3101 			if (z_old + GetSlopeZInCorner(tileh_old, corner) != z_new + GetSlopeZInCorner(tileh_new, corner)) return autoslope_result;
3102 		}
3103 
3104 		/* Make the ground dirty */
3105 		if ((flags & DC_EXEC) != 0) SetRailGroundType(tile, RAIL_GROUND_BARREN);
3106 
3107 		/* allow terraforming */
3108 		return CommandCost(EXPENSES_CONSTRUCTION, was_water ? _price[PR_CLEAR_WATER] : (Money)0);
3109 	} else if (_settings_game.construction.build_on_slopes && AutoslopeEnabled() &&
3110 			AutoslopeCheckForEntranceEdge(tile, z_new, tileh_new, GetRailDepotDirection(tile))) {
3111 		return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
3112 	}
3113 	return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
3114 }
3115 
3116 
3117 extern const TileTypeProcs _tile_type_rail_procs = {
3118 	DrawTile_Track,           // draw_tile_proc
3119 	GetSlopePixelZ_Track,     // get_slope_z_proc
3120 	ClearTile_Track,          // clear_tile_proc
3121 	nullptr,                     // add_accepted_cargo_proc
3122 	GetTileDesc_Track,        // get_tile_desc_proc
3123 	GetTileTrackStatus_Track, // get_tile_track_status_proc
3124 	ClickTile_Track,          // click_tile_proc
3125 	nullptr,                     // animate_tile_proc
3126 	TileLoop_Track,           // tile_loop_proc
3127 	ChangeTileOwner_Track,    // change_tile_owner_proc
3128 	nullptr,                     // add_produced_cargo_proc
3129 	VehicleEnter_Track,       // vehicle_enter_tile_proc
3130 	GetFoundation_Track,      // get_foundation_proc
3131 	TerraformTile_Track,      // terraform_tile_proc
3132 };
3133