Changeset - r15816:67664bff2b7d
[Not reviewed]
master
0 5 0
frosch - 14 years ago 2010-08-15 19:59:48
frosch@openttd.org
(svn r20505) -Feature [FS#3978]: Allow changing visual effect when changing railtype.
5 files changed with 33 insertions and 10 deletions:
0 comments (0 inline, 0 general)
src/lang/english.txt
Show inline comments
 
@@ -2421,96 +2421,97 @@ STR_SPRITE_ALIGNER_OFFSETS              
 
STR_SPRITE_ALIGNER_PICKER_BUTTON                                :{BLACK}Pick sprite
 
STR_SPRITE_ALIGNER_PICKER_TOOLTIP                               :{BLACK}Pick a sprite from anywhere on the screen
 

	
 
STR_SPRITE_ALIGNER_GOTO_CAPTION                                 :{WHITE}Go to sprite
 

	
 
# NewGRF (self) generated warnings/errors
 
STR_NEWGRF_ERROR_MSG_INFO                                       :{SILVER}{RAW_STRING}
 
STR_NEWGRF_ERROR_MSG_WARNING                                    :{RED}Warning: {SILVER}{RAW_STRING}
 
STR_NEWGRF_ERROR_MSG_ERROR                                      :{RED}Error: {SILVER}{RAW_STRING}
 
STR_NEWGRF_ERROR_MSG_FATAL                                      :{RED}Fatal: {SILVER}{RAW_STRING}
 
STR_NEWGRF_ERROR_FATAL_POPUP                                    :{WHITE}A fatal NewGRF error has occurred: {}{STRING5}
 
STR_NEWGRF_ERROR_VERSION_NUMBER                                 :{1:STRING1} will not work with the TTDPatch version reported by OpenTTD.
 
STR_NEWGRF_ERROR_DOS_OR_WINDOWS                                 :{1:STRING1} is for the {STRING1} version of TTD.
 
STR_NEWGRF_ERROR_UNSET_SWITCH                                   :{1:STRING1} is designed to be used with {STRING1}
 
STR_NEWGRF_ERROR_INVALID_PARAMETER                              :Invalid parameter for {1:STRING1}: parameter {STRING1} ({NUM})
 
STR_NEWGRF_ERROR_LOAD_BEFORE                                    :{1:STRING1} must be loaded before {STRING1}.
 
STR_NEWGRF_ERROR_LOAD_AFTER                                     :{1:STRING1} must be loaded after {STRING1}.
 
STR_NEWGRF_ERROR_OTTD_VERSION_NUMBER                            :{1:STRING1} requires OpenTTD version {STRING1} or better.
 
STR_NEWGRF_ERROR_AFTER_TRANSLATED_FILE                          :the GRF file it was designed to translate
 
STR_NEWGRF_ERROR_TOO_MANY_NEWGRFS_LOADED                        :Too many NewGRFs are loaded.
 
STR_NEWGRF_ERROR_STATIC_GRF_CAUSES_DESYNC                       :Loading {RAW_STRING} as static NewGRF with {RAW_STRING} could cause desyncs.
 
STR_NEWGRF_ERROR_UNEXPECTED_SPRITE                              :Unexpected sprite.
 
STR_NEWGRF_ERROR_UNKNOWN_PROPERTY                               :Unknown Action 0 property.
 
STR_NEWGRF_ERROR_INVALID_ID                                     :Attempt to use invalid ID.
 
STR_NEWGRF_ERROR_CORRUPT_SPRITE                                 :{YELLOW}{RAW_STRING} contains a corrupt sprite. All corrupt sprites will be shown as a red question mark (?).
 
STR_NEWGRF_ERROR_MULTIPLE_ACTION_8                              :Contains multiple Action 8 entries.
 
STR_NEWGRF_ERROR_READ_BOUNDS                                    :Read past end of pseudo-sprite.
 

	
 
# NewGRF related 'general' warnings
 
STR_NEWGRF_POPUP_CAUTION_CAPTION                                :{WHITE}Caution!
 
STR_NEWGRF_CONFIRMATION_TEXT                                    :{YELLOW}You are about to make changes to a running game. This can crash OpenTTD.{}Are you absolutely sure about this?
 

	
 
STR_NEWGRF_DUPLICATE_GRFID                                      :{WHITE}Can't add file: duplicate GRF ID
 
STR_NEWGRF_COMPATIBLE_LOADED                                    :{ORANGE}Matching file not found (compatible GRF loaded)
 

	
 
STR_NEWGRF_COMPATIBLE_LOAD_WARNING                              :{WHITE}Compatible GRF(s) loaded for missing files
 
STR_NEWGRF_DISABLED_WARNING                                     :{WHITE}Missing GRF file(s) have been disabled
 
STR_NEWGRF_UNPAUSE_WARNING_TITLE                                :{YELLOW}Missing GRF file(s)
 
STR_NEWGRF_UNPAUSE_WARNING                                      :{WHITE}Unpausing can crash OpenTTD. Do not file bug reports for subsequent crashes.{}Do you really want to unpause?
 

	
 
# NewGRF status
 
STR_NEWGRF_LIST_NONE                                            :None
 
STR_NEWGRF_LIST_ALL_FOUND                                       :All files present
 
STR_NEWGRF_LIST_COMPATIBLE                                      :{YELLOW}Found compatible files
 
STR_NEWGRF_LIST_MISSING                                         :{RED}Missing files
 

	
 
# NewGRF 'it's broken' warnings
 
STR_NEWGRF_BROKEN                                               :{WHITE}Behaviour of NewGRF '{0:RAW_STRING}' is likely to cause desyncs and/or crashes.
 
STR_NEWGRF_BROKEN_POWERED_WAGON                                 :{WHITE}Wagon '{1:ENGINE}' changed powered-wagon state when not inside a depot.
 
STR_NEWGRF_BROKEN_VEHICLE_LENGTH                                :{WHITE}It changes vehicle length for '{1:ENGINE}' when not inside a depot.
 
STR_BROKEN_VEHICLE_LENGTH                                       :{WHITE}Train '{VEHICLE}' belonging to '{COMPANY}' has invalid length. It is probably caused by problems with NewGRFs. Game may desync or crash.
 

	
 
STR_NEWGRF_BUGGY                                                :{WHITE}NewGRF '{0:RAW_STRING}' provides incorrect information.
 
STR_NEWGRF_BUGGY_ARTICULATED_CARGO                              :{WHITE}Cargo/refit information for '{1:ENGINE}' differs from purchase list after construction. This might cause autorenew/-replace to fail refitting correctly.
 
STR_NEWGRF_BUGGY_ENDLESS_PRODUCTION_CALLBACK                    :{WHITE}'{1:STRING}' caused an endless loop in the production callback.
 

	
 
# 'User removed essential NewGRFs'-placeholders for stuff without specs.
 
STR_NEWGRF_INVALID_CARGO                                        :<invalid cargo>
 
STR_NEWGRF_INVALID_CARGO_ABBREV                                 :??
 
STR_NEWGRF_INVALID_CARGO_QUANTITY                               :{COMMA} of <invalid cargo>
 
STR_NEWGRF_INVALID_ENGINE                                       :<invalid vehicle model>
 
STR_NEWGRF_INVALID_INDUSTRYTYPE                                 :<invalid industry>
 

	
 
# Sign list window
 
STR_SIGN_LIST_CAPTION                                           :{WHITE}Sign List - {COMMA} Sign{P "" s}
 

	
 
# Sign window
 
STR_EDIT_SIGN_CAPTION                                           :{WHITE}Edit sign text
 
STR_EDIT_SIGN_NEXT_SIGN_TOOLTIP                                 :{BLACK}Go to next sign
 
STR_EDIT_SIGN_PREVIOUS_SIGN_TOOLTIP                             :{BLACK}Go to previous sign
 

	
 
STR_EDIT_SIGN_SIGN_OSKTITLE                                     :{BLACK}Enter a name for the sign
 

	
 
# Town directory window
 
STR_TOWN_DIRECTORY_CAPTION                                      :{WHITE}Towns
 
STR_TOWN_DIRECTORY_NONE                                         :{ORANGE}- None -
 
STR_TOWN_DIRECTORY_TOWN                                         :{ORANGE}{TOWN}{BLACK} ({COMMA})
 
STR_TOWN_DIRECTORY_LIST_TOOLTIP                                 :{BLACK}Town names - click on name to centre view on town. Ctrl+Click opens a new viewport on town location
 
STR_TOWN_POPULATION                                             :{BLACK}World population: {COMMA}
 

	
 
# Town view window
 
STR_TOWN_VIEW_TOWN_CAPTION                                      :{WHITE}{TOWN}
 
STR_TOWN_VIEW_CITY_CAPTION                                      :{WHITE}{TOWN} (City)
 
STR_TOWN_VIEW_POPULATION_HOUSES                                 :{BLACK}Population: {ORANGE}{COMMA}{BLACK}  Houses: {ORANGE}{COMMA}
 
STR_TOWN_VIEW_PASSENGERS_LAST_MONTH_MAX                         :{BLACK}Passengers last month: {ORANGE}{COMMA}{BLACK}  max: {ORANGE}{COMMA}
 
STR_TOWN_VIEW_MAIL_LAST_MONTH_MAX                               :{BLACK}Mail last month: {ORANGE}{COMMA}{BLACK}  max: {ORANGE}{COMMA}
 
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH                              :{BLACK}Cargo needed for town growth:
 
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED                     :{ORANGE}{STRING}{BLACK} required
 
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_REQUIRED_WINTER              :{ORANGE}{STRING}{BLACK} required in winter
 
STR_TOWN_VIEW_CARGO_FOR_TOWNGROWTH_LAST_MONTH                   :{ORANGE}{CARGO}{BLACK} delivered last month
 
STR_TOWN_VIEW_NOISE_IN_TOWN                                     :{BLACK}Noise limit in town: {ORANGE}{COMMA}{BLACK}  max: {ORANGE}{COMMA}
 
STR_TOWN_VIEW_CENTER_TOOLTIP                                    :{BLACK}Centre the main view on town location. Ctrl+Click opens a new viewport on town location
 
STR_TOWN_VIEW_LOCAL_AUTHORITY_BUTTON                            :{BLACK}Local authority
 
STR_TOWN_VIEW_LOCAL_AUTHORITY_TOOLTIP                           :{BLACK}Show information on local authority
 
STR_TOWN_VIEW_RENAME_TOOLTIP                                    :{BLACK}Change town name
 

	
 
STR_TOWN_VIEW_EXPAND_BUTTON                                     :{BLACK}Expand
src/newgrf_config.h
Show inline comments
 
/* $Id$ */
 

	
 
/*
 
 * This file is part of OpenTTD.
 
 * 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.
 
 * 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.
 
 * 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/>.
 
 */
 

	
 
/** @file newgrf_config.h Functions to find and configure NewGRFs. */
 

	
 
#ifndef NEWGRF_CONFIG_H
 
#define NEWGRF_CONFIG_H
 

	
 
#include "strings_type.h"
 
#include "core/alloc_type.hpp"
 
#include "core/smallmap_type.hpp"
 

	
 
/** GRF config bit flags */
 
enum GCF_Flags {
 
	GCF_SYSTEM,     ///< GRF file is an openttd-internal system grf
 
	GCF_UNSAFE,     ///< GRF file is unsafe for static usage
 
	GCF_STATIC,     ///< GRF file is used statically (can be used in any MP game)
 
	GCF_COMPATIBLE, ///< GRF file does not exactly match the requested GRF (different MD5SUM), but grfid matches)
 
	GCF_COPY,       ///< The data is copied from a grf in _all_grfs
 
	GCF_INIT_ONLY,  ///< GRF file is processed up to GLS_INIT
 
	GCF_RESERVED,   ///< GRF file passed GLS_RESERVE stage
 

	
 
};
 

	
 
/** Status of GRF */
 
enum GRFStatus {
 
	GCS_UNKNOWN,      ///< The status of this grf file is unknown
 
	GCS_DISABLED,     ///< GRF file is disabled
 
	GCS_NOT_FOUND,    ///< GRF file was not found in the local cache
 
	GCS_INITIALISED,  ///< GRF file has been initialised
 
	GCS_ACTIVATED     ///< GRF file has been activated
 
};
 

	
 
/** Encountered GRF bugs */
 
enum GRFBugs {
 
	GBUG_VEH_LENGTH,  ///< Length of rail vehicle changes when not inside a depot
 
	GBUG_VEH_REFIT,   ///< Articulated vehicles carry different cargos resp. are differently refittable than specified in purchase list
 
	GBUG_VEH_POWERED_WAGON, ///< Powered wagon changed poweredness state when not inside a depot
 
};
 

	
 
/** Status of post-gameload GRF compatibility check */
 
enum GRFListCompatibility {
 
	GLC_ALL_GOOD,   ///< All GRF needed by game are present
 
	GLC_COMPATIBLE, ///< Compatible (eg. the same ID, but different chacksum) GRF found in at least one case
 
	GLC_NOT_FOUND   ///< At least one GRF couldn't be found (higher priority than GLC_COMPATIBLE)
 
};
 

	
 
/** Information that can/has to be stored about a GRF's palette. */
 
enum GRFPalette {
 
	GRFP_USE_BIT     = 0,   ///< The bit used for storing the palette to use.
 
	GRFP_GRF_OFFSET  = 2,   ///< The offset of the GRFP_GRF data.
 
	GRFP_GRF_SIZE    = 2,   ///< The size of the GRFP_GRF data.
 

	
 
	GRFP_USE_DOS     = 0x0, ///< The palette state is set to use the DOS palette.
 
	GRFP_USE_WINDOWS = 0x1, ///< The palette state is set to use the Windows palette.
 
	GRFP_USE_MASK    = 0x1, ///< Bitmask to get only the use palette use states.
 

	
 
	GRFP_GRF_UNSET   = 0x0 << GRFP_GRF_OFFSET,          ///< The NewGRF provided no information.
 
	GRFP_GRF_DOS     = 0x1 << GRFP_GRF_OFFSET,          ///< The NewGRF says the DOS palette can be used.
 
	GRFP_GRF_WINDOWS = 0x2 << GRFP_GRF_OFFSET,          ///< The NewGRF says the Windows palette can be used.
 
	GRFP_GRF_ANY     = GRFP_GRF_DOS | GRFP_GRF_WINDOWS, ///< The NewGRF says any palette can be used.
 
	GRFP_GRF_MASK    = GRFP_GRF_ANY,                    ///< Bitmask to get only the NewGRF supplied information.
 
};
 

	
 

	
 
/** Basic data to distinguish a GRF. Used in the server list window */
 
struct GRFIdentifier {
 
	uint32 grfid;     ///< GRF ID (defined by Action 0x08)
 
	uint8 md5sum[16]; ///< MD5 checksum of file to distinguish files with the same GRF ID (eg. newer version of GRF)
 

	
 
	/**
 
	 * Does the identification match the provided values?
 
	 * @param grfid  Expected grfid.
 
	 * @param md5sum Expected md5sum, may be \c NULL (in which case, do not check it).
 
	 * @return the object has the provided grfid and md5sum.
 
	 */
 
	FORCEINLINE bool HasGrfIdentifier(uint32 grfid, const uint8 *md5sum) const
 
	{
 
		if (this->grfid != grfid) return false;
 
		if (md5sum == NULL) return true;
 
		return memcmp(md5sum, this->md5sum, sizeof(this->md5sum)) == 0;
 
	}
 
};
 

	
 
/** Information about why GRF had problems during initialisation */
 
struct GRFError : ZeroedMemoryAllocator {
src/rail_cmd.cpp
Show inline comments
 
@@ -1335,97 +1335,96 @@ CommandCost CmdRemoveSingleSignal(TileIn
 
		}
 
		SetPresentSignals(tile, GetPresentSignals(tile) & ~SignalOnTrack(track));
 

	
 
		/* removed last signal from tile? */
 
		if (GetPresentSignals(tile) == 0) {
 
			SetSignalStates(tile, 0);
 
			SetHasSignals(tile, false);
 
			SetSignalVariant(tile, INVALID_TRACK, SIG_ELECTRIC); // remove any possible semaphores
 
		}
 

	
 
		AddTrackToSignalBuffer(tile, track, GetTileOwner(tile));
 
		YapfNotifyTrackLayoutChange(tile, track);
 
		if (v != NULL) TryPathReserve(v, false);
 

	
 
		MarkTileDirtyByTile(tile);
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_SIGNALS]);
 
}
 

	
 
/**
 
 * Remove signals on a stretch of track.
 
 * Stub for the unified signal builder/remover
 
 * @param tile start tile of drag
 
 * @param flags operation to perform
 
 * @param p1  end tile of drag
 
 * @param p2 various bitstuffed elements
 
 * - p2 = (bit  0- 2) - track-orientation, valid values: 0-5 (Track enum)
 
 * - p2 = (bit  3)    - 1 = override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
 
 * - p2 = (bit  4)    - 0 = signals, 1 = semaphores
 
 * - p2 = (bit  5)    - 0 = build, 1 = remove signals
 
 * - p2 = (bit  6)    - 0 = selected stretch, 1 = auto fill
 
 * - p2 = (bit  7- 9) - default signal type
 
 * - p2 = (bit 24-31) - user defined signals_density
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 * @see CmdSignalTrackHelper
 
 */
 
CommandCost CmdRemoveSignalTrack(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	return CmdSignalTrackHelper(tile, flags, p1, SetBit(p2, 5), text); // bit 5 is remove bit
 
}
 

	
 
/** Update power of train under which is the railtype being converted */
 
static Vehicle *UpdateTrainPowerProc(Vehicle *v, void *data)
 
{
 
	if (v->type != VEH_TRAIN) return NULL;
 

	
 
	/* Similar checks as in Train::PowerChanged() */
 
	TrainList *affected_trains = static_cast<TrainList*>(data);
 
	affected_trains->Include(Train::From(v)->First());
 

	
 
	return NULL;
 
}
 

	
 
/**
 
 * Convert one rail type to the other. You can convert normal rail to
 
 * monorail/maglev easily or vice-versa.
 
 * @param tile end tile of rail conversion drag
 
 * @param flags operation to perform
 
 * @param p1 start tile of drag
 
 * @param p2 new railtype to convert to
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdConvertRail(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	RailType totype = Extract<RailType, 0, 4>(p2);
 

	
 
	if (!ValParamRailtype(totype)) return CMD_ERROR;
 
	if (p1 >= MapSize()) return CMD_ERROR;
 

	
 
	uint ex = TileX(tile);
 
	uint ey = TileY(tile);
 
	uint sx = TileX(p1);
 
	uint sy = TileY(p1);
 

	
 
	/* make sure sx,sy are smaller than ex,ey */
 
	if (ex < sx) Swap(ex, sx);
 
	if (ey < sy) Swap(ey, sy);
 

	
 
	TrainList affected_trains;
 

	
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 
	CommandCost error = CommandCost(STR_ERROR_NO_SUITABLE_RAILROAD_TRACK); // by default, there is no track to convert.
 
	for (uint x = sx; x <= ex; ++x) {
 
		for (uint y = sy; y <= ey; ++y) {
 
			TileIndex tile = TileXY(x, y);
 
			TileType tt = GetTileType(tile);
 

	
 
			/* Check if there is any track on tile */
 
			switch (tt) {
 
				case MP_RAILWAY:
 
					break;
 
				case MP_STATION:
 
					if (!HasStationRail(tile)) continue;
 
					break;
 
@@ -1538,98 +1537,97 @@ CommandCost CmdConvertRail(TileIndex til
 
							if (v != NULL && !HasPowerOnRail(v->railtype, totype)) {
 
								/* No power on new rail type, reroute. */
 
								FreeTrainTrackReservation(v);
 
								*vehicles_affected.Append() = v;
 
							}
 
						}
 
						SetRailType(tile, totype);
 
						SetRailType(endtile, totype);
 

	
 
						FindVehicleOnPos(tile, &affected_trains, &UpdateTrainPowerProc);
 
						FindVehicleOnPos(endtile, &affected_trains, &UpdateTrainPowerProc);
 

	
 
						YapfNotifyTrackLayoutChange(tile, track);
 
						YapfNotifyTrackLayoutChange(endtile, track);
 

	
 
						MarkTileDirtyByTile(tile);
 
						MarkTileDirtyByTile(endtile);
 

	
 
						if (IsBridge(tile)) {
 
							TileIndexDiff delta = TileOffsByDiagDir(GetTunnelBridgeDirection(tile));
 
							TileIndex t = tile + delta;
 
							for (; t != endtile; t += delta) MarkTileDirtyByTile(t); // TODO encapsulate this into a function
 
						}
 
					}
 

	
 
					cost.AddCost((GetTunnelBridgeLength(tile, endtile) + 2) * RailConvertCost(type, totype));
 
					break;
 
				}
 

	
 
				default: // MP_STATION, MP_ROAD
 
					if (flags & DC_EXEC) {
 
						Track track = ((tt == MP_STATION) ? GetRailStationTrack(tile) : GetCrossingRailTrack(tile));
 
						YapfNotifyTrackLayoutChange(tile, track);
 
					}
 

	
 
					cost.AddCost(RailConvertCost(type, totype));
 
					break;
 
			}
 

	
 
			for (uint i = 0; i < vehicles_affected.Length(); ++i) {
 
				TryPathReserve(vehicles_affected[i], true);
 
			}
 
		}
 
	}
 

	
 
	if (flags & DC_EXEC) {
 
		/* Railtype changed, update trains as when entering different track */
 
		for (Train **v = affected_trains.Begin(); v != affected_trains.End(); v++) {
 
			(*v)->PowerChanged();
 
			(*v)->UpdateAcceleration();
 
			(*v)->RailtypeChanged();
 
		}
 
	}
 

	
 
	return (cost.GetCost() == 0) ? error : cost;
 
}
 

	
 
static CommandCost RemoveTrainDepot(TileIndex tile, DoCommandFlag flags)
 
{
 
	if (_current_company != OWNER_WATER) {
 
		CommandCost ret = CheckTileOwnership(tile);
 
		if (ret.Failed()) return ret;
 
	}
 

	
 
	CommandCost ret = EnsureNoVehicleOnGround(tile);
 
	if (ret.Failed()) return ret;
 

	
 
	if (flags & DC_EXEC) {
 
		/* read variables before the depot is removed */
 
		DiagDirection dir = GetRailDepotDirection(tile);
 
		Owner owner = GetTileOwner(tile);
 
		Train *v = NULL;
 

	
 
		if (HasDepotReservation(tile)) {
 
			v = GetTrainForReservation(tile, DiagDirToDiagTrack(dir));
 
			if (v != NULL) FreeTrainTrackReservation(v);
 
		}
 

	
 
		delete Depot::GetByTile(tile);
 
		DoClearSquare(tile);
 
		AddSideToSignalBuffer(tile, dir, owner);
 
		YapfNotifyTrackLayoutChange(tile, DiagDirToDiagTrack(dir));
 
		if (v != NULL) TryPathReserve(v, true);
 
	}
 

	
 
	return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_DEPOT_TRAIN]);
 
}
 

	
 
static CommandCost ClearTile_Track(TileIndex tile, DoCommandFlag flags)
 
{
 
	CommandCost cost(EXPENSES_CONSTRUCTION);
 

	
 
	if (flags & DC_AUTO) {
 
		if (!IsTileOwner(tile, _current_company)) {
 
			return_cmd_error(STR_ERROR_AREA_IS_OWNED_BY_ANOTHER);
 
		}
 

	
 
		if (IsPlainRail(tile)) {
 
			return_cmd_error(STR_ERROR_MUST_REMOVE_RAILROAD_TRACK);
src/train.h
Show inline comments
 
@@ -100,96 +100,98 @@ struct Train : public GroundVehicle<Trai
 

	
 
	/* Link between the two ends of a multiheaded engine */
 
	Train *other_multiheaded_part;
 

	
 
	uint16 crash_anim_pos;
 

	
 
	uint16 flags;
 
	TrackBitsByte track;
 
	TrainForceProceedingByte force_proceed;
 
	RailTypeByte railtype;
 
	RailTypes compatible_railtypes;
 

	
 
	/** Ticks waiting in front of a signal, ticks being stuck or a counter for forced proceeding through signals. */
 
	uint16 wait_counter;
 

	
 
	/** We don't want GCC to zero our struct! It already is zeroed and has an index! */
 
	Train() : GroundVehicle<Train, VEH_TRAIN>() {}
 
	/** We want to 'destruct' the right class. */
 
	virtual ~Train() { this->PreDestructor(); }
 

	
 
	friend struct GroundVehicle<Train, VEH_TRAIN>; // GroundVehicle needs to use the acceleration functions defined at Train.
 

	
 
	const char *GetTypeString() const { return "train"; }
 
	void MarkDirty();
 
	void UpdateDeltaXY(Direction direction);
 
	ExpensesType GetExpenseType(bool income) const { return income ? EXPENSES_TRAIN_INC : EXPENSES_TRAIN_RUN; }
 
	void PlayLeaveStationSound() const;
 
	bool IsPrimaryVehicle() const { return this->IsFrontEngine(); }
 
	SpriteID GetImage(Direction direction) const;
 
	int GetDisplaySpeed() const { return this->tcache.last_speed; }
 
	int GetDisplayMaxSpeed() const { return this->tcache.cached_max_speed; }
 
	Money GetRunningCost() const;
 
	int GetDisplayImageWidth(Point *offset = NULL) const;
 
	bool IsInDepot() const;
 
	bool IsStoppedInDepot() const;
 
	bool Tick();
 
	void OnNewDay();
 
	uint Crash(bool flooded = false);
 
	Trackdir GetVehicleTrackdir() const;
 
	TileIndex GetOrderStationLocation(StationID station);
 
	bool FindClosestDepot(TileIndex *location, DestinationID *destination, bool *reverse);
 

	
 
	void ReserveTrackUnderConsist() const;
 

	
 
	int GetCurveSpeedLimit() const;
 

	
 
	void ConsistChanged(bool same_length);
 

	
 
	void RailtypeChanged();
 

	
 
	int UpdateSpeed();
 

	
 
	void UpdateAcceleration();
 

	
 
	int GetCurrentMaxSpeed() const;
 

	
 
	/**
 
	 * enum to handle train subtypes
 
	 * Do not access it directly unless you have to. Use the access functions below
 
	 * This is an enum to tell what bit to access as it is a bitmask
 
	 */
 
	enum TrainSubtype {
 
		TS_FRONT             = 0, ///< Leading engine of a train
 
		TS_ARTICULATED_PART  = 1, ///< Articulated part of an engine
 
		TS_WAGON             = 2, ///< Wagon
 
		TS_ENGINE            = 3, ///< Engine, that can be front engine, but might be placed behind another engine
 
		TS_FREE_WAGON        = 4, ///< First in a wagon chain (in depot)
 
		TS_MULTIHEADED       = 5, ///< Engine is multiheaded
 
	};
 

	
 
	/**
 
	 * Set front engine state
 
	 */
 
	FORCEINLINE void SetFrontEngine() { SetBit(this->subtype, TS_FRONT); }
 

	
 
	/**
 
	 * Remove the front engine state
 
	 */
 
	FORCEINLINE void ClearFrontEngine() { ClrBit(this->subtype, TS_FRONT); }
 

	
 
	/**
 
	 * Set a vehicle to be an articulated part
 
	 */
 
	FORCEINLINE void SetArticulatedPart() { SetBit(this->subtype, TS_ARTICULATED_PART); }
 

	
 
	/**
 
	 * Clear a vehicle from being an articulated part
 
	 */
 
	FORCEINLINE void ClearArticulatedPart() { ClrBit(this->subtype, TS_ARTICULATED_PART); }
 

	
 
	/**
 
	 * Set a vehicle to be a wagon
 
	 */
 
	FORCEINLINE void SetWagon() { SetBit(this->subtype, TS_WAGON); }
 

	
 
	/**
 
	 * Clear wagon property
 
	 */
 
@@ -327,97 +329,97 @@ struct Train : public GroundVehicle<Trai
 
	FORCEINLINE Train *GetNextVehicle() const
 
	{
 
		const Train *v = this;
 
		while (v->HasArticulatedPart()) v = v->GetNextArticPart();
 

	
 
		/* v now contains the last artic part in the engine */
 
		return v->Next();
 
	}
 

	
 
	/**
 
	 * Get the previous real (non-articulated part) vehicle in the consist.
 
	 * @return Previous vehicle in the consist.
 
	 */
 
	FORCEINLINE Train *GetPrevVehicle() const
 
	{
 
		Train *v = this->Previous();
 
		while (v != NULL && v->IsArticulatedPart()) v = v->Previous();
 

	
 
		return v;
 
	}
 

	
 
	/**
 
	 * Get the next real (non-articulated part and non rear part of dualheaded engine) vehicle in the consist.
 
	 * @return Next vehicle in the consist.
 
	 */
 
	FORCEINLINE Train *GetNextUnit() const
 
	{
 
		Train *v = this->GetNextVehicle();
 
		if (v != NULL && v->IsRearDualheaded()) v = v->GetNextVehicle();
 

	
 
		return v;
 
	}
 

	
 
	/**
 
	 * Get the previous real (non-articulated part and non rear part of dualheaded engine) vehicle in the consist.
 
	 * @return Previous vehicle in the consist.
 
	 */
 
	FORCEINLINE Train *GetPrevUnit()
 
	{
 
		Train *v = this->GetPrevVehicle();
 
		if (v != NULL && v->IsRearDualheaded()) v = v->GetPrevVehicle();
 

	
 
		return v;
 
	}
 

	
 

	
 
protected: // These functions should not be called outside acceleration code.
 

	
 
	void UpdateVisualEffect();
 
	void UpdateVisualEffect(bool allow_power_change);
 

	
 
	/**
 
	 * Allows to know the power value that this vehicle will use.
 
	 * @return Power value from the engine in HP, or zero if the vehicle is not powered.
 
	 */
 
	FORCEINLINE uint16 GetPower() const
 
	{
 
		/* Power is not added for articulated parts */
 
		if (!this->IsArticulatedPart() && HasPowerOnRail(this->railtype, GetRailType(this->tile))) {
 
			uint16 power = GetVehicleProperty(this, PROP_TRAIN_POWER, RailVehInfo(this->engine_type)->power);
 
			/* Halve power for multiheaded parts */
 
			if (this->IsMultiheaded()) power /= 2;
 
			return power;
 
		}
 

	
 
		return 0;
 
	}
 

	
 
	/**
 
	 * Returns a value if this articulated part is powered.
 
	 * @return Power value from the articulated part in HP, or zero if it is not powered.
 
	 */
 
	FORCEINLINE uint16 GetPoweredPartPower(const Train *head) const
 
	{
 
		/* For powered wagons the engine defines the type of engine (i.e. railtype) */
 
		if (HasBit(this->flags, VRF_POWEREDWAGON) && HasPowerOnRail(head->railtype, GetRailType(this->tile))) {
 
			return RailVehInfo(this->tcache.first_engine)->pow_wag_power;
 
		}
 

	
 
		return 0;
 
	}
 

	
 
	/**
 
	 * Allows to know the weight value that this vehicle will use.
 
	 * @return Weight value from the engine in tonnes.
 
	 */
 
	FORCEINLINE uint16 GetWeight() const
 
	{
 
		uint16 weight = (CargoSpec::Get(this->cargo_type)->weight * this->cargo.Count() * FreightWagonMult(this->cargo_type)) / 16;
 

	
 
		/* Vehicle weight is not added for articulated parts. */
 
		if (!this->IsArticulatedPart()) {
 
			weight += GetVehicleProperty(this, PROP_TRAIN_WEIGHT, RailVehInfo(this->engine_type)->weight);
 
		}
 

	
 
		/* Powered wagons have extra weight added. */
 
		if (HasBit(this->flags, VRF_POWEREDWAGON)) {
 
			weight += RailVehInfo(this->tcache.first_engine)->pow_wag_weight;
src/train_cmd.cpp
Show inline comments
 
@@ -85,182 +85,204 @@ static inline DiagDirection TrainExitDir
 
 */
 
byte FreightWagonMult(CargoID cargo)
 
{
 
	if (!CargoSpec::Get(cargo)->is_freight) return 1;
 
	return _settings_game.vehicle.freight_trains;
 
}
 

	
 
/**
 
 * Logs a bug in GRF and shows a warning message if this
 
 * is for the first time this happened.
 
 * @param u first vehicle of chain
 
 */
 
static void RailVehicleLengthChanged(const Train *u)
 
{
 
	/* show a warning once for each engine in whole game and once for each GRF after each game load */
 
	const Engine *engine = Engine::Get(u->engine_type);
 
	uint32 grfid = engine->grf_prop.grffile->grfid;
 
	GRFConfig *grfconfig = GetGRFConfig(grfid);
 
	if (GamelogGRFBugReverse(grfid, engine->grf_prop.local_id) || !HasBit(grfconfig->grf_bugs, GBUG_VEH_LENGTH)) {
 
		ShowNewGrfVehicleError(u->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_VEHICLE_LENGTH, GBUG_VEH_LENGTH, true);
 
	}
 
}
 

	
 
/** Checks if lengths of all rail vehicles are valid. If not, shows an error message. */
 
void CheckTrainsLengths()
 
{
 
	const Train *v;
 

	
 
	FOR_ALL_TRAINS(v) {
 
		if (v->First() == v && !(v->vehstatus & VS_CRASHED)) {
 
			for (const Train *u = v, *w = v->Next(); w != NULL; u = w, w = w->Next()) {
 
				if (u->track != TRACK_BIT_DEPOT) {
 
					if ((w->track != TRACK_BIT_DEPOT &&
 
							max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->tcache.cached_veh_length) ||
 
							(w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) {
 
						SetDParam(0, v->index);
 
						SetDParam(1, v->owner);
 
						ShowErrorMessage(STR_BROKEN_VEHICLE_LENGTH, INVALID_STRING_ID, WL_CRITICAL);
 

	
 
						if (!_networking) DoCommandP(0, PM_PAUSED_ERROR, 1, CMD_PAUSE);
 
					}
 
				}
 
			}
 
		}
 
	}
 
}
 

	
 
/**
 
 * Update the cached visual effect.
 
 * Update visual effect, power and acceleration caches.
 
 * Called when a vehicle in the consist enters a different railtype.
 
 */
 
void Train::UpdateVisualEffect()
 
void Train::RailtypeChanged()
 
{
 
	for (Train *u = this; u != NULL; u = u->Next()) {
 
		/* The wagon-is-powered-state should not change, so the weight does not change. */
 
		u->UpdateVisualEffect(false);
 
	}
 
	this->PowerChanged();
 
	if (this->IsFrontEngine()) this->UpdateAcceleration();
 
}
 

	
 
/**
 
 * Update the cached visual effect.
 
 * @param allow_power_change true if the wagon-is-powered-state may change.
 
 */
 
void Train::UpdateVisualEffect(bool allow_power_change)
 
{
 
	byte powered_before = this->tcache.cached_vis_effect & 0x80;
 

	
 
	const Engine *e = Engine::Get(this->engine_type);
 
	if (e->u.rail.visual_effect != 0) {
 
		this->tcache.cached_vis_effect = e->u.rail.visual_effect;
 
	} else {
 
		if (this->IsWagon() || this->IsArticulatedPart()) {
 
			/* Wagons and articulated parts have no effect by default */
 
			this->tcache.cached_vis_effect = 0x40;
 
		} else if (e->u.rail.engclass == 0) {
 
			/* Steam is offset by -4 units */
 
			this->tcache.cached_vis_effect = 4;
 
		} else {
 
			/* Diesel fumes and sparks come from the centre */
 
			this->tcache.cached_vis_effect = 8;
 
		}
 
	}
 

	
 
	/* Check powered wagon / visual effect callback */
 
	if (HasBit(e->info.callback_mask, CBM_TRAIN_WAGON_POWER)) {
 
		uint16 callback = GetVehicleCallback(CBID_TRAIN_WAGON_POWER, 0, 0, this->engine_type, this);
 

	
 
		if (callback != CALLBACK_FAILED) this->tcache.cached_vis_effect = GB(callback, 0, 8);
 
	}
 

	
 
	if (!allow_power_change && powered_before != (this->tcache.cached_vis_effect & 0x80)) {
 
		this->tcache.cached_vis_effect ^= 0x80;
 
		ShowNewGrfVehicleError(this->engine_type, STR_NEWGRF_BROKEN, STR_NEWGRF_BROKEN_POWERED_WAGON, GBUG_VEH_POWERED_WAGON, false);
 
	}
 
}
 

	
 
/**
 
 * Recalculates the cached stuff of a train. Should be called each time a vehicle is added
 
 * to/removed from the chain, and when the game is loaded.
 
 * Note: this needs to be called too for 'wagon chains' (in the depot, without an engine)
 
 * @param same_length should length of vehicles stay the same?
 
 */
 
void Train::ConsistChanged(bool same_length)
 
{
 
	uint16 max_speed = UINT16_MAX;
 

	
 
	assert(this->IsFrontEngine() || this->IsFreeWagon());
 

	
 
	const RailVehicleInfo *rvi_v = RailVehInfo(this->engine_type);
 
	EngineID first_engine = this->IsFrontEngine() ? this->engine_type : INVALID_ENGINE;
 
	this->tcache.cached_total_length = 0;
 
	this->compatible_railtypes = RAILTYPES_NONE;
 

	
 
	bool train_can_tilt = true;
 

	
 
	for (Train *u = this; u != NULL; u = u->Next()) {
 
		const RailVehicleInfo *rvi_u = RailVehInfo(u->engine_type);
 

	
 
		/* Check the this->first cache. */
 
		assert(u->First() == this);
 

	
 
		/* update the 'first engine' */
 
		u->tcache.first_engine = this == u ? INVALID_ENGINE : first_engine;
 
		u->railtype = rvi_u->railtype;
 

	
 
		if (u->IsEngine()) first_engine = u->engine_type;
 

	
 
		/* Set user defined data to its default value */
 
		u->tcache.user_def_data = rvi_u->user_def_data;
 
		this->InvalidateNewGRFCache();
 
		u->InvalidateNewGRFCache();
 
	}
 

	
 
	for (Train *u = this; u != NULL; u = u->Next()) {
 
		/* Update user defined data (must be done before other properties) */
 
		u->tcache.user_def_data = GetVehicleProperty(u, PROP_TRAIN_USER_DATA, u->tcache.user_def_data);
 
		this->InvalidateNewGRFCache();
 
		u->InvalidateNewGRFCache();
 
	}
 

	
 
	for (Train *u = this; u != NULL; u = u->Next()) {
 
		const Engine *e_u = Engine::Get(u->engine_type);
 
		const RailVehicleInfo *rvi_u = &e_u->u.rail;
 

	
 
		if (!HasBit(e_u->info.misc_flags, EF_RAIL_TILTS)) train_can_tilt = false;
 

	
 
		/* Cache wagon override sprite group. NULL is returned if there is none */
 
		u->tcache.cached_override = GetWagonOverrideSpriteSet(u->engine_type, u->cargo_type, u->tcache.first_engine);
 

	
 
		/* Reset colour map */
 
		u->colourmap = PAL_NONE;
 

	
 
		/* Update powered-wagon-status and visual effect */
 
		u->UpdateVisualEffect();
 
		u->UpdateVisualEffect(true);
 

	
 
		if (rvi_v->pow_wag_power != 0 && rvi_u->railveh_type == RAILVEH_WAGON &&
 
				UsesWagonOverride(u) && !HasBit(u->tcache.cached_vis_effect, 7)) {
 
			/* wagon is powered */
 
			SetBit(u->flags, VRF_POWEREDWAGON); // cache 'powered' status
 
		} else {
 
			ClrBit(u->flags, VRF_POWEREDWAGON);
 
		}
 

	
 
		if (!u->IsArticulatedPart()) {
 
			/* Do not count powered wagons for the compatible railtypes, as wagons always
 
			   have railtype normal */
 
			if (rvi_u->power > 0) {
 
				this->compatible_railtypes |= GetRailTypeInfo(u->railtype)->powered_railtypes;
 
			}
 

	
 
			/* Some electric engines can be allowed to run on normal rail. It happens to all
 
			 * existing electric engines when elrails are disabled and then re-enabled */
 
			if (HasBit(u->flags, VRF_EL_ENGINE_ALLOWED_NORMAL_RAIL)) {
 
				u->railtype = RAILTYPE_RAIL;
 
				u->compatible_railtypes |= RAILTYPES_RAIL;
 
			}
 

	
 
			/* max speed is the minimum of the speed limits of all vehicles in the consist */
 
			if ((rvi_u->railveh_type != RAILVEH_WAGON || _settings_game.vehicle.wagon_speed_limits) && !UsesWagonOverride(u)) {
 
				uint16 speed = GetVehicleProperty(u, PROP_TRAIN_SPEED, rvi_u->max_speed);
 
				if (speed != 0) max_speed = min(speed, max_speed);
 
			}
 
		}
 

	
 
		u->cargo_cap = GetVehicleCapacity(u);
 

	
 
		/* check the vehicle length (callback) */
 
		uint16 veh_len = CALLBACK_FAILED;
 
		if (HasBit(e_u->info.callback_mask, CBM_VEHICLE_LENGTH)) {
 
			veh_len = GetVehicleCallback(CBID_VEHICLE_LENGTH, 0, 0, u->engine_type, u);
 
		}
 
		if (veh_len == CALLBACK_FAILED) veh_len = rvi_u->shorten_factor;
 
		veh_len = 8 - Clamp(veh_len, 0, 7);
 

	
 
		/* verify length hasn't changed */
 
		if (same_length && veh_len != u->tcache.cached_veh_length) RailVehicleLengthChanged(u);
 

	
 
		/* update vehicle length? */
 
		if (!same_length) u->tcache.cached_veh_length = veh_len;
 

	
 
		this->tcache.cached_total_length += u->tcache.cached_veh_length;
 
		this->InvalidateNewGRFCache();
 
@@ -1521,97 +1543,97 @@ static void SwapTrainFlags(uint16 *swap_
 
	}
 
}
 

	
 
static void ReverseTrainSwapVeh(Train *v, int l, int r)
 
{
 
	Train *a, *b;
 

	
 
	/* locate vehicles to swap */
 
	for (a = v; l != 0; l--) a = a->Next();
 
	for (b = v; r != 0; r--) b = b->Next();
 

	
 
	if (a != b) {
 
		/* swap the hidden bits */
 
		{
 
			uint16 tmp = (a->vehstatus & ~VS_HIDDEN) | (b->vehstatus & VS_HIDDEN);
 
			b->vehstatus = (b->vehstatus & ~VS_HIDDEN) | (a->vehstatus & VS_HIDDEN);
 
			a->vehstatus = tmp;
 
		}
 

	
 
		Swap(a->track, b->track);
 
		Swap(a->direction, b->direction);
 

	
 
		/* toggle direction */
 
		if (a->track != TRACK_BIT_DEPOT) a->direction = ReverseDir(a->direction);
 
		if (b->track != TRACK_BIT_DEPOT) b->direction = ReverseDir(b->direction);
 

	
 
		Swap(a->x_pos, b->x_pos);
 
		Swap(a->y_pos, b->y_pos);
 
		Swap(a->tile,  b->tile);
 
		Swap(a->z_pos, b->z_pos);
 

	
 
		SwapTrainFlags(&a->gv_flags, &b->gv_flags);
 

	
 
		/* update other vars */
 
		a->UpdateViewport(true, true);
 
		b->UpdateViewport(true, true);
 

	
 
		/* call the proper EnterTile function unless we are in a wormhole */
 
		if (a->track != TRACK_BIT_WORMHOLE) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos);
 
		if (b->track != TRACK_BIT_WORMHOLE) VehicleEnterTile(b, b->tile, b->x_pos, b->y_pos);
 
	} else {
 
		if (a->track != TRACK_BIT_DEPOT) a->direction = ReverseDir(a->direction);
 
		a->UpdateViewport(true, true);
 

	
 
		if (a->track != TRACK_BIT_WORMHOLE) VehicleEnterTile(a, a->tile, a->x_pos, a->y_pos);
 
	}
 

	
 
	/* Update power of the train in case tiles were different rail type. */
 
	v->PowerChanged();
 
	v->RailtypeChanged();
 
}
 

	
 

	
 
/**
 
 * Check if the vehicle is a train
 
 * @param v vehicle on tile
 
 * @return v if it is a train, NULL otherwise
 
 */
 
static Vehicle *TrainOnTileEnum(Vehicle *v, void *)
 
{
 
	return (v->type == VEH_TRAIN) ? v : NULL;
 
}
 

	
 

	
 
/**
 
 * Checks if a train is approaching a rail-road crossing
 
 * @param v vehicle on tile
 
 * @param data tile with crossing we are testing
 
 * @return v if it is approaching a crossing, NULL otherwise
 
 */
 
static Vehicle *TrainApproachingCrossingEnum(Vehicle *v, void *data)
 
{
 
	if (v->type != VEH_TRAIN || (v->vehstatus & VS_CRASHED)) return NULL;
 

	
 
	Train *t = Train::From(v);
 
	if (!t->IsFrontEngine()) return NULL;
 

	
 
	TileIndex tile = *(TileIndex *)data;
 

	
 
	if (TrainApproachingCrossingTile(t) != tile) return NULL;
 

	
 
	return t;
 
}
 

	
 

	
 
/**
 
 * Finds a vehicle approaching rail-road crossing
 
 * @param tile tile to test
 
 * @return true if a vehicle is approaching the crossing
 
 * @pre tile is a rail-road crossing
 
 */
 
static bool TrainApproachingCrossing(TileIndex tile)
 
{
 
	assert(IsLevelCrossingTile(tile));
 

	
 
	DiagDirection dir = AxisToDiagDir(GetCrossingRailAxis(tile));
 
	TileIndex tile_from = tile + TileOffsByDiagDir(dir);
 

	
 
@@ -3322,98 +3344,97 @@ static void TrainController(Train *v, Ve
 
						 *  2) some orientations of tunnel entries, where the vehicle is already inside the wormhole at 8/16 from the tile edge.
 
						 *     Is also the train just reversing, the wagon inside the tunnel is 'on' the tile of the opposite tunnel entry.
 
						 */
 
						static const TrackBits _connecting_track[DIAGDIR_END][DIAGDIR_END] = {
 
							{TRACK_BIT_X,     TRACK_BIT_LOWER, TRACK_BIT_NONE,  TRACK_BIT_LEFT },
 
							{TRACK_BIT_UPPER, TRACK_BIT_Y,     TRACK_BIT_LEFT,  TRACK_BIT_NONE },
 
							{TRACK_BIT_NONE,  TRACK_BIT_RIGHT, TRACK_BIT_X,     TRACK_BIT_UPPER},
 
							{TRACK_BIT_RIGHT, TRACK_BIT_NONE,  TRACK_BIT_LOWER, TRACK_BIT_Y    }
 
						};
 
						DiagDirection exitdir = DiagdirBetweenTiles(gp.new_tile, prev->tile);
 
						assert(IsValidDiagDirection(exitdir));
 
						chosen_track = _connecting_track[enterdir][exitdir];
 
					}
 
					chosen_track &= bits;
 
				}
 

	
 
				/* Make sure chosen track is a valid track */
 
				assert(
 
						chosen_track == TRACK_BIT_X     || chosen_track == TRACK_BIT_Y ||
 
						chosen_track == TRACK_BIT_UPPER || chosen_track == TRACK_BIT_LOWER ||
 
						chosen_track == TRACK_BIT_LEFT  || chosen_track == TRACK_BIT_RIGHT);
 

	
 
				/* Update XY to reflect the entrance to the new tile, and select the direction to use */
 
				const byte *b = _initial_tile_subcoord[FIND_FIRST_BIT(chosen_track)][enterdir];
 
				gp.x = (gp.x & ~0xF) | b[0];
 
				gp.y = (gp.y & ~0xF) | b[1];
 
				Direction chosen_dir = (Direction)b[2];
 

	
 
				/* Call the landscape function and tell it that the vehicle entered the tile */
 
				uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
 
				if (HasBit(r, VETS_CANNOT_ENTER)) {
 
					goto invalid_rail;
 
				}
 

	
 
				if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
 
					Track track = FindFirstTrack(chosen_track);
 
					Trackdir tdir = TrackDirectionToTrackdir(track, chosen_dir);
 
					if (v->IsFrontEngine() && HasPbsSignalOnTrackdir(gp.new_tile, tdir)) {
 
						SetSignalStateByTrackdir(gp.new_tile, tdir, SIGNAL_STATE_RED);
 
						MarkTileDirtyByTile(gp.new_tile);
 
					}
 

	
 
					/* Clear any track reservation when the last vehicle leaves the tile */
 
					if (v->Next() == NULL) ClearPathReservation(v, v->tile, v->GetVehicleTrackdir());
 

	
 
					v->tile = gp.new_tile;
 

	
 
					if (GetTileRailType(gp.new_tile) != GetTileRailType(gp.old_tile)) {
 
						v->First()->PowerChanged();
 
						v->First()->UpdateAcceleration();
 
						v->First()->RailtypeChanged();
 
					}
 

	
 
					v->track = chosen_track;
 
					assert(v->track);
 
				}
 

	
 
				/* We need to update signal status, but after the vehicle position hash
 
				 * has been updated by UpdateInclination() */
 
				update_signals_crossing = true;
 

	
 
				if (chosen_dir != v->direction) {
 
					if (prev == NULL && _settings_game.vehicle.train_acceleration_model == AM_ORIGINAL) {
 
						const RailtypeSlowdownParams *rsp = &_railtype_slowdown[v->railtype];
 
						DirDiff diff = DirDifference(v->direction, chosen_dir);
 
						v->cur_speed -= (diff == DIRDIFF_45RIGHT || diff == DIRDIFF_45LEFT ? rsp->small_turn : rsp->large_turn) * v->cur_speed >> 8;
 
					}
 
					direction_changed = true;
 
					v->direction = chosen_dir;
 
				}
 

	
 
				if (v->IsFrontEngine()) {
 
					v->wait_counter = 0;
 

	
 
					/* If we are approaching a crossing that is reserved, play the sound now. */
 
					TileIndex crossing = TrainApproachingCrossingTile(v);
 
					if (crossing != INVALID_TILE && HasCrossingReservation(crossing)) SndPlayTileFx(SND_0E_LEVEL_CROSSING, crossing);
 

	
 
					/* Always try to extend the reservation when entering a tile. */
 
					CheckNextTrainTile(v);
 
				}
 

	
 
				if (HasBit(r, VETS_ENTERED_STATION)) {
 
					/* The new position is the location where we want to stop */
 
					TrainEnterStation(v, r >> VETS_STATION_ID_OFFSET);
 
				}
 
			}
 
		} else {
 
			/* In a tunnel or on a bridge
 
			 * - for tunnels, only the part when the vehicle is not visible (part of enter/exit tile too)
 
			 * - for bridges, only the middle part - without the bridge heads */
 
			if (!(v->vehstatus & VS_HIDDEN)) {
 
				v->cur_speed =
 
					min(v->cur_speed, GetBridgeSpec(GetBridgeType(v->tile))->speed);
 
			}
 

	
 
			if (IsTileType(gp.new_tile, MP_TUNNELBRIDGE) && HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
 
				/* Perform look-ahead on tunnel exit. */
 
				if (v->IsFrontEngine()) {
0 comments (0 inline, 0 general)