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
 
@@ -2373,192 +2373,193 @@ STR_NEWGRF_SETTINGS_FILE_TOOLTIP        
 
STR_NEWGRF_SETTINGS_SET_PARAMETERS                              :{BLACK}Set parameters
 
STR_NEWGRF_SETTINGS_TOGGLE_PALETTE                              :{BLACK}Toggle palette
 
STR_NEWGRF_SETTINGS_TOGGLE_PALETTE_TOOLTIP                      :{BLACK}Toggle the palette of the selected NewGRF.{}Do this when the graphics from this NewGRF look pink in-game
 
STR_NEWGRF_SETTINGS_APPLY_CHANGES                               :{BLACK}Apply changes
 

	
 
STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_BUTTON                 :{BLACK}Find missing content online
 
STR_NEWGRF_SETTINGS_FIND_MISSING_CONTENT_TOOLTIP                :{BLACK}Check whether the missing content can be found online
 

	
 
STR_NEWGRF_SETTINGS_FILENAME                                    :{BLACK}Filename: {SILVER}{RAW_STRING}
 
STR_NEWGRF_SETTINGS_GRF_ID                                      :{BLACK}GRF ID: {SILVER}{RAW_STRING}
 
STR_NEWGRF_SETTINGS_VERSION                                     :{BLACK}Version: {SILVER}{NUM}
 
STR_NEWGRF_SETTINGS_MD5SUM                                      :{BLACK}MD5sum: {SILVER}{RAW_STRING}
 
STR_NEWGRF_SETTINGS_PALETTE                                     :{BLACK}Palette: {SILVER}{RAW_STRING}
 
STR_NEWGRF_SETTINGS_PARAMETER                                   :{BLACK}Parameters: {SILVER}{STRING1}
 

	
 
STR_NEWGRF_SETTINGS_NO_INFO                                     :{BLACK}No information available
 
STR_NEWGRF_SETTINGS_NOT_FOUND                                   :{RED}Matching file not found
 
STR_NEWGRF_SETTINGS_DISABLED                                    :{RED}Disabled
 

	
 
STR_NEWGRF_SETTINGS_PARAMETER_QUERY                             :{BLACK}Enter NewGRF parameters
 

	
 
# NewGRF parameters window
 
STR_NEWGRF_PARAMETERS_CAPTION                                   :{WHITE}Change NewGRF parameters
 
STR_NEWGRF_PARAMETERS_CLOSE                                     :{BLACK}Close
 
STR_NEWGRF_PARAMETERS_DEFAULT_NAME                              :Parameter {NUM}
 
STR_NEWGRF_PARAMETERS_SETTING                                   :{STRING1}: {ORANGE}{STRING1}
 

	
 
# NewGRF inspect window
 
STR_NEWGRF_INSPECT_CAPTION                                      :{WHITE}Inspect - {STRING5}
 
STR_NEWGRF_INSPECT_PARENT_BUTTON                                :{BLACK}Parent
 
STR_NEWGRF_INSPECT_PARENT_TOOLTIP                               :{BLACK}Inspect the object of the parent scope
 

	
 
STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT                            :{STRING1} at {HEX}
 
STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE                  :Rail type
 

	
 
STR_NEWGRF_INSPECT_QUERY_CAPTION                                :{WHITE}NewGRF variable 60+x parameter (hexadecimal)
 

	
 
# Sprite aligner window
 
STR_SPRITE_ALIGNER_CAPTION                                      :{WHITE}Aligning sprite {COMMA} ({RAW_STRING})
 
STR_SPRITE_ALIGNER_NEXT_BUTTON                                  :{BLACK}Next sprite
 
STR_SPRITE_ALIGNER_NEXT_TOOLTIP                                 :{BLACK}Proceed to the next normal sprite, skipping any pseudo/recolour/font sprites and wrapping around at the end
 
STR_SPRITE_ALIGNER_GOTO_BUTTON                                  :{BLACK}Go to sprite
 
STR_SPRITE_ALIGNER_GOTO_TOOLTIP                                 :{BLACK}Go to the given sprite. If the sprite is not a normal sprite, proceed to the next normal sprite
 
STR_SPRITE_ALIGNER_PREVIOUS_BUTTON                              :{BLACK}Previous sprite
 
STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP                             :{BLACK}Proceed to the previous normal sprite, skipping any pseudo/recolour/font sprites and wrapping around at the begin
 
STR_SPRITE_ALIGNER_SPRITE_TOOLTIP                               :{BLACK}Representation of the currently selected sprite. The alignment is ignored when drawing this sprite
 
STR_SPRITE_ALIGNER_MOVE_TOOLTIP                                 :{BLACK}Move the sprite around, changing the X and Y offsets
 
STR_SPRITE_ALIGNER_OFFSETS                                      :{BLACK}X offset: {NUM}, Y offset: {NUM}
 
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
 
STR_TOWN_VIEW_EXPAND_TOOLTIP                                    :{BLACK}Increase size of town
 
STR_TOWN_VIEW_DELETE_BUTTON                                     :{BLACK}Delete
 
STR_TOWN_VIEW_DELETE_TOOLTIP                                    :{BLACK}Delete this town completely
 

	
 
STR_TOWN_VIEW_RENAME_TOWN_BUTTON                                :Rename Town
 

	
 
# Town local authority window
 
STR_LOCAL_AUTHORITY_CAPTION                                     :{WHITE}{TOWN} local authority
 
STR_LOCAL_AUTHORITY_COMPANY_RATINGS                             :{BLACK}Transport company ratings:
 
STR_LOCAL_AUTHORITY_COMPANY_RATING                              :{YELLOW}{COMPANY} {COMPANYNUM}: {ORANGE}{STRING}
 
STR_LOCAL_AUTHORITY_ACTIONS_TITLE                               :{BLACK}Actions available:
 
STR_LOCAL_AUTHORITY_ACTIONS_TOOLTIP                             :{BLACK}List of things to do at this town - click on item for more details
 
STR_LOCAL_AUTHORITY_DO_IT_BUTTON                                :{BLACK}Do it
 
STR_LOCAL_AUTHORITY_DO_IT_TOOLTIP                               :{BLACK}Carry out the highlighted action in the list above
 

	
 
STR_LOCAL_AUTHORITY_ACTION_SMALL_ADVERTISING_CAMPAIGN           :Small advertising campaign
 
STR_LOCAL_AUTHORITY_ACTION_MEDIUM_ADVERTISING_CAMPAIGN          :Medium advertising campaign
 
STR_LOCAL_AUTHORITY_ACTION_LARGE_ADVERTISING_CAMPAIGN           :Large advertising campaign
 
STR_LOCAL_AUTHORITY_ACTION_ROAD_RECONSTRUCTION                  :Fund local road reconstruction
 
STR_LOCAL_AUTHORITY_ACTION_STATUE_OF_COMPANY                    :Build statue of company owner
 
STR_LOCAL_AUTHORITY_ACTION_NEW_BUILDINGS                        :Fund new buildings
 
STR_LOCAL_AUTHORITY_ACTION_EXCLUSIVE_TRANSPORT                  :Buy exclusive transport rights
 
STR_LOCAL_AUTHORITY_ACTION_BRIBE                                :Bribe the local authority
 

	
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_SMALL_ADVERTISING            :{YELLOW}Initiate a small local advertising campaign, to attract more passengers and cargo to your transport services.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_MEDIUM_ADVERTISING           :{YELLOW}Initiate a medium local advertising campaign, to attract more passengers and cargo to your transport services.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_LARGE_ADVERTISING            :{YELLOW}Initiate a large local advertising campaign, to attract more passengers and cargo to your transport services.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_ROAD_RECONSTRUCTION          :{YELLOW}Fund the reconstruction of the urban road network. Causes considerable disruption to road traffic for up to 6 months.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_STATUE_OF_COMPANY            :{YELLOW}Build a statue in honour of your company.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_NEW_BUILDINGS                :{YELLOW}Fund the construction of new commercial buildings in the town.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_EXCLUSIVE_TRANSPORT          :{YELLOW}Buy 1 year's exclusive transport rights in town. Town authority will only allow passengers and cargo to use your company's stations.{}Cost: {CURRENCY}
 
STR_LOCAL_AUTHORITY_ACTION_TOOLTIP_BRIBE                        :{YELLOW}Bribe the local authority to increase your rating, at the risk of a severe penalty if caught.{}Cost: {CURRENCY}
 

	
 
# Subsidies window
 
STR_SUBSIDIES_CAPTION                                           :{WHITE}Subsidies
 
STR_SUBSIDIES_OFFERED_TITLE                                     :{BLACK}Subsidies on offer for services taking:
 
STR_SUBSIDIES_OFFERED_FROM_TO                                   :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} (by {DATE_SHORT})
 
STR_SUBSIDIES_NONE                                              :{ORANGE}- None -
 
STR_SUBSIDIES_SUBSIDISED_TITLE                                  :{BLACK}Services already subsidised:
 
STR_SUBSIDIES_SUBSIDISED_FROM_TO                                :{ORANGE}{STRING} from {STRING2} to {STRING2}{YELLOW} ({COMPANY}{YELLOW}, until {DATE_SHORT})
 
STR_SUBSIDIES_TOOLTIP_CLICK_ON_SERVICE_TO_CENTER                :{BLACK}Click on service to centre view on industry/town. Ctrl+Click opens a new viewport on industry/town location
 

	
 
# Station list window
 
STR_STATION_LIST_TOOLTIP                                        :{BLACK}Station names - click on name to centre view on station. Ctrl+Click opens a new viewport on station location
 
STR_STATION_LIST_USE_CTRL_TO_SELECT_MORE                        :{BLACK}Hold Ctrl to select more than one item
 
STR_STATION_LIST_CAPTION                                        :{WHITE}{COMPANY} - {COMMA} Station{P "" s}
 
STR_STATION_LIST_STATION                                        :{YELLOW}{STATION} {STATIONFEATURES}
 
STR_STATION_LIST_WAYPOINT                                       :{YELLOW}{WAYPOINT}
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 {
 
	GRFError(StringID severity, StringID message = 0);
 
	GRFError(const GRFError &error);
 
	~GRFError();
 

	
 
	char *custom_message;  ///< Custom message (if present)
 
	char *data;            ///< Additional data for message and custom_message
 
	StringID message;      ///< Default message
 
	StringID severity;     ///< Info / Warning / Error / Fatal
 
	uint8 num_params;      ///< Number of additinal parameters for message and custom_message (0, 1 or 2)
 
	uint32 param_value[2]; ///< Values of GRF parameters to show for message and custom_message
 
};
 

	
 
/** The possible types of a newgrf parameter. */
 
enum GRFParameterType {
 
	PTYPE_UINT_ENUM, ///< The parameter allows a range of numbers, each of which can have a special name
 
	PTYPE_BOOL,      ///< The parameter is either 0 or 1
 
	PTYPE_END,       ///< Invalid parameter type
 
};
 

	
 
/** Information about one grf parameter. */
 
struct GRFParameterInfo {
 
	GRFParameterInfo(uint nr);
 
	GRFParameterInfo(GRFParameterInfo &info);
 
	~GRFParameterInfo();
 
	struct GRFText *name;  ///< The name of this parameter
 
	struct GRFText *desc;  ///< The description of this parameter
 
	GRFParameterType type; ///< The type of this parameter
 
	uint32 min_value;      ///< The minimal value this parameter can have
 
	uint32 max_value;      ///< The maximal value of this parameter
 
	byte param_nr;         ///< GRF parameter to store content in
 
	byte first_bit;        ///< First bit to use in the GRF parameter
 
	byte num_bit;          ///< Number of bits to use for this parameter
 
	SmallMap<uint32, struct GRFText *, 8> value_names; ///< Names for each value.
 

	
 
	uint32 GetValue(struct GRFConfig *config) const;
 
	void SetValue(struct GRFConfig *config, uint32 value);
 
};
 

	
 
/** Information about GRF, used in the game and (part of it) in savegames */
 
struct GRFConfig : ZeroedMemoryAllocator {
 
	GRFConfig(const char *filename = NULL);
 
	GRFConfig(const GRFConfig &config);
 
	~GRFConfig();
 

	
 
	GRFIdentifier ident;       ///< grfid and md5sum to uniquely identify newgrfs
 
	uint8 original_md5sum[16]; ///< MD5 checksum of original file if only a 'compatible' file was loaded
 
	char *filename;            ///< Filename - either with or without full path
 
	struct GRFText *name;      ///< NOSAVE: GRF name (Action 0x08)
src/rail_cmd.cpp
Show inline comments
 
@@ -1287,193 +1287,192 @@ CommandCost CmdBuildSignalTrack(TileInde
 
/**
 
 * Remove signals
 
 * @param tile coordinates where signal is being deleted from
 
 * @param flags operation to perform
 
 * @param p1 various bitstuffed elements, only track information is used
 
 *           - (bit  0- 2) - track-orientation, valid values: 0-5 (Track enum)
 
 *           - (bit  3)    - override signal/semaphore, or pre/exit/combo signal (CTRL-toggle)
 
 *           - (bit  4)    - 0 = signals, 1 = semaphores
 
 * @param p2 unused
 
 * @param text unused
 
 * @return the cost of this operation or an error
 
 */
 
CommandCost CmdRemoveSingleSignal(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
 
{
 
	Track track = Extract<Track, 0, 3>(p1);
 

	
 
	if (!ValParamTrackOrientation(track) || !IsPlainRailTile(tile) || !HasTrack(tile, track)) {
 
		return_cmd_error(STR_ERROR_THERE_IS_NO_RAILROAD_TRACK);
 
	}
 
	if (!HasSignalOnTrack(tile, track)) {
 
		return_cmd_error(STR_ERROR_THERE_ARE_NO_SIGNALS);
 
	}
 
	CommandCost ret = EnsureNoTrainOnTrack(tile, track);
 
	if (ret.Failed()) return ret;
 

	
 
	/* Only water can remove signals from anyone */
 
	if (_current_company != OWNER_WATER) {
 
		CommandCost ret = CheckTileOwnership(tile);
 
		if (ret.Failed()) return ret;
 
	}
 

	
 
	/* Do it? */
 
	if (flags & DC_EXEC) {
 
		Train *v = NULL;
 
		if (HasReservedTracks(tile, TrackToTrackBits(track))) {
 
			v = GetTrainForReservation(tile, track);
 
		} else if (IsPbsSignal(GetSignalType(tile, track))) {
 
			/* PBS signal, might be the end of a path reservation. */
 
			Trackdir td = TrackToTrackdir(track);
 
			for (int i = 0; v == NULL && i < 2; i++, td = ReverseTrackdir(td)) {
 
				/* Only test the active signal side. */
 
				if (!HasSignalOnTrackdir(tile, ReverseTrackdir(td))) continue;
 
				TileIndex next = TileAddByDiagDir(tile, TrackdirToExitdir(td));
 
				TrackBits tracks = TrackdirBitsToTrackBits(TrackdirReachesTrackdirs(td));
 
				if (HasReservedTracks(next, tracks)) {
 
					v = GetTrainForReservation(next, TrackBitsToTrack(GetReservedTrackbits(next) & tracks));
 
				}
 
			}
 
		}
 
		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;
 
				case MP_ROAD:
 
					if (!IsLevelCrossing(tile)) continue;
 
					if (RailNoLevelCrossings(totype)) {
 
						error.MakeError(STR_ERROR_CROSSING_DISALLOWED);
 
						continue;
 
					}
 
					break;
 
				case MP_TUNNELBRIDGE:
 
					if (GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) continue;
 
					break;
 
				default: continue;
 
			}
 

	
 
			/* Original railtype we are converting from */
 
			RailType type = GetRailType(tile);
 

	
 
			/* Converting to the same type or converting 'hidden' elrail -> rail */
 
			if (type == totype || (_settings_game.vehicle.disable_elrails && totype == RAILTYPE_RAIL && type == RAILTYPE_ELECTRIC)) continue;
 

	
 
			/* Trying to convert other's rail */
 
			CommandCost ret = CheckTileOwnership(tile);
 
			if (ret.Failed()) {
 
				error = ret;
 
				continue;
 
			}
 

	
 
			SmallVector<Train *, 2> vehicles_affected;
 

	
 
			/* Vehicle on the tile when not converting Rail <-> ElRail
 
			 * Tunnels and bridges have special check later */
 
			if (tt != MP_TUNNELBRIDGE) {
 
				if (!IsCompatibleRail(type, totype)) {
 
					CommandCost ret = EnsureNoVehicleOnGround(tile);
 
					if (ret.Failed()) {
 
						error = ret;
 
						continue;
 
					}
 
				}
 
				if (flags & DC_EXEC) { // we can safely convert, too
 
					TrackBits reserved = GetReservedTrackbits(tile);
 
					Track     track;
 
					while ((track = RemoveFirstTrack(&reserved)) != INVALID_TRACK) {
 
						Train *v = GetTrainForReservation(tile, track);
 
						if (v != NULL && !HasPowerOnRail(v->railtype, totype)) {
 
							/* No power on new rail type, reroute. */
 
							FreeTrainTrackReservation(v);
 
							*vehicles_affected.Append() = v;
 
						}
 
@@ -1490,194 +1489,193 @@ CommandCost CmdConvertRail(TileIndex til
 
				case MP_RAILWAY:
 
					switch (GetRailTileType(tile)) {
 
						case RAIL_TILE_DEPOT:
 
							if (flags & DC_EXEC) {
 
								/* notify YAPF about the track layout change */
 
								YapfNotifyTrackLayoutChange(tile, GetRailDepotTrack(tile));
 

	
 
								/* Update build vehicle window related to this depot */
 
								InvalidateWindowData(WC_VEHICLE_DEPOT, tile);
 
								InvalidateWindowData(WC_BUILD_VEHICLE, tile);
 
							}
 
							cost.AddCost(RailConvertCost(type, totype));
 
							break;
 

	
 
						default: // RAIL_TILE_NORMAL, RAIL_TILE_SIGNALS
 
							if (flags & DC_EXEC) {
 
								/* notify YAPF about the track layout change */
 
								TrackBits tracks = GetTrackBits(tile);
 
								while (tracks != TRACK_BIT_NONE) {
 
									YapfNotifyTrackLayoutChange(tile, RemoveFirstTrack(&tracks));
 
								}
 
							}
 
							cost.AddCost(RailConvertCost(type, totype) * CountBits(GetTrackBits(tile)));
 
							break;
 
					}
 
					break;
 

	
 
				case MP_TUNNELBRIDGE: {
 
					TileIndex endtile = GetOtherTunnelBridgeEnd(tile);
 

	
 
					/* If both ends of tunnel/bridge are in the range, do not try to convert twice -
 
					 * it would cause assert because of different test and exec runs */
 
					if (endtile < tile && TileX(endtile) >= sx && TileX(endtile) <= ex &&
 
							TileY(endtile) >= sy && TileY(endtile) <= ey) continue;
 

	
 
					/* When not coverting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */
 
					if (!IsCompatibleRail(GetRailType(tile), totype)) {
 
						CommandCost ret = TunnelBridgeIsFree(tile, endtile);
 
						if (ret.Failed()) {
 
							error = ret;
 
							continue;
 
						}
 
					}
 

	
 
					if (flags & DC_EXEC) {
 
						Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile));
 
						if (HasTunnelBridgeReservation(tile)) {
 
							Train *v = GetTrainForReservation(tile, track);
 
							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);
 
		} else {
 
			return_cmd_error(STR_ERROR_BUILDING_MUST_BE_DEMOLISHED);
 
		}
 
	}
 

	
 
	switch (GetRailTileType(tile)) {
 
		case RAIL_TILE_SIGNALS:
 
		case RAIL_TILE_NORMAL: {
 
			Slope tileh = GetTileSlope(tile, NULL);
 
			/* Is there flat water on the lower halftile, that gets cleared expensively? */
 
			bool water_ground = (GetRailGroundType(tile) == RAIL_GROUND_WATER && IsSlopeWithOneCornerRaised(tileh));
 

	
 
			TrackBits tracks = GetTrackBits(tile);
 
			while (tracks != TRACK_BIT_NONE) {
 
				Track track = RemoveFirstTrack(&tracks);
 
				CommandCost ret = DoCommand(tile, 0, track, flags, CMD_REMOVE_SINGLE_RAIL);
 
				if (ret.Failed()) return ret;
 
				cost.AddCost(ret);
 
			}
 

	
 
			/* when bankrupting, don't make water dirty, there could be a ship on lower halftile */
 
			if (water_ground && !(flags & DC_BANKRUPT)) {
 
				CommandCost ret = EnsureNoVehicleOnGround(tile);
 
				if (ret.Failed()) return ret;
 

	
 
				/* The track was removed, and left a coast tile. Now also clear the water. */
 
				if (flags & DC_EXEC) DoClearSquare(tile);
 
				cost.AddCost(_price[PR_CLEAR_WATER]);
 
			}
 

	
 
			return cost;
 
		}
 

	
 
		case RAIL_TILE_DEPOT:
 
			return RemoveTrainDepot(tile, flags);
 

	
 
		default:
 
			return CMD_ERROR;
 
	}
 
}
 

	
 
/**
 
 * Get surface height in point (x,y)
 
 * On tiles with halftile foundations move (x,y) to a safe point wrt. track
 
 */
 
static uint GetSaveSlopeZ(uint x, uint y, Track track)
 
{
 
	switch (track) {
src/train.h
Show inline comments
 
@@ -52,192 +52,194 @@ enum TrainForceProceeding {
 
	TFP_STUCK  = 1,    ///< Proceed till next signal, but ignore being stuck till then. This includes force leaving depots.
 
	TFP_SIGNAL = 2,    ///< Ignore next signal, after the signal ignore being stucked.
 
};
 
typedef SimpleTinyEnumT<TrainForceProceeding, byte> TrainForceProceedingByte;
 

	
 
byte FreightWagonMult(CargoID cargo);
 

	
 
void CheckTrainsLengths();
 

	
 
void FreeTrainTrackReservation(const Train *v, TileIndex origin = INVALID_TILE, Trackdir orig_td = INVALID_TRACKDIR);
 
bool TryPathReserve(Train *v, bool mark_as_stuck = false, bool first_tile_okay = false);
 

	
 
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length);
 

	
 
/** Variables that are cached to improve performance and such */
 
struct TrainCache {
 
	/* Cached wagon override spritegroup */
 
	const struct SpriteGroup *cached_override;
 

	
 
	uint16 last_speed; // NOSAVE: only used in UI
 

	
 
	/* cached values, recalculated on load and each time a vehicle is added to/removed from the consist. */
 
	uint16 cached_total_length; ///< Length of the whole train, valid only for first engine.
 
	uint8 cached_veh_length;    ///< length of this vehicle in units of 1/8 of normal length, cached because this can be set by a callback
 
	bool cached_tilt;           ///< train can tilt; feature provides a bonus in curves
 

	
 
	/* cached max. speed / acceleration data */
 
	uint16 cached_max_speed;    ///< max speed of the consist. (minimum of the max speed of all vehicles in the consist)
 
	int cached_max_curve_speed; ///< max consist speed limited by curves
 

	
 
	/**
 
	 * Position/type of visual effect.
 
	 * bit 0 - 3 = position of effect relative to vehicle. (0 = front, 8 = centre, 15 = rear)
 
	 * bit 4 - 5 = type of effect. (0 = default for engine class, 1 = steam, 2 = diesel, 3 = electric)
 
	 * bit     6 = disable visual effect.
 
	 * bit     7 = disable powered wagons.
 
	 */
 
	byte cached_vis_effect;
 
	byte user_def_data;
 

	
 
	EngineID first_engine;  ///< cached EngineID of the front vehicle. INVALID_ENGINE for the front vehicle itself.
 
};
 

	
 
/**
 
 * 'Train' is either a loco or a wagon.
 
 */
 
struct Train : public GroundVehicle<Train, VEH_TRAIN> {
 
	TrainCache tcache;
 

	
 
	/* 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
 
	 */
 
	FORCEINLINE void ClearWagon() { ClrBit(this->subtype, TS_WAGON); }
 

	
 
	/**
 
	 * Set engine status
 
	 */
 
	FORCEINLINE void SetEngine() { SetBit(this->subtype, TS_ENGINE); }
 

	
 
	/**
 
	 * Clear engine status
 
	 */
 
	FORCEINLINE void ClearEngine() { ClrBit(this->subtype, TS_ENGINE); }
 

	
 
	/**
 
	 * Set if a vehicle is a free wagon
 
	 */
 
	FORCEINLINE void SetFreeWagon() { SetBit(this->subtype, TS_FREE_WAGON); }
 

	
 
	/**
 
	 * Clear a vehicle from being a free wagon
 
	 */
 
	FORCEINLINE void ClearFreeWagon() { ClrBit(this->subtype, TS_FREE_WAGON); }
 

	
 
	/**
 
	 * Set if a vehicle is a multiheaded engine
 
	 */
 
	FORCEINLINE void SetMultiheaded() { SetBit(this->subtype, TS_MULTIHEADED); }
 

	
 
	/**
 
	 * Clear multiheaded engine property
 
	 */
 
	FORCEINLINE void ClearMultiheaded() { ClrBit(this->subtype, TS_MULTIHEADED); }
 

	
 

	
 
	/**
 
	 * Check if train is a front engine
 
	 * @return Returns true if train is a front engine
 
	 */
 
	FORCEINLINE bool IsFrontEngine() const { return HasBit(this->subtype, TS_FRONT); }
 

	
 
	/**
 
	 * Check if train is a free wagon (got no engine in front of it)
 
	 * @return Returns true if train is a free wagon
 
	 */
 
	FORCEINLINE bool IsFreeWagon() const { return HasBit(this->subtype, TS_FREE_WAGON); }
 

	
 
	/**
 
	 * Check if a vehicle is an engine (can be first in a train)
 
	 * @return Returns true if vehicle is an engine
 
@@ -279,193 +281,193 @@ struct Train : public GroundVehicle<Trai
 
	 * Get the next part of a multi-part engine.
 
	 * Will only work on a multi-part engine (this->EngineHasArticPart() == true),
 
	 * Result is undefined for normal engine.
 
	 * @return next part of articulated engine
 
	 */
 
	FORCEINLINE Train *GetNextArticPart() const
 
	{
 
		assert(this->HasArticulatedPart());
 
		return this->Next();
 
	}
 

	
 
	/**
 
	 * Get the first part of a multi-part engine.
 
	 * @return First part of the engine.
 
	 */
 
	FORCEINLINE Train *GetFirstEnginePart()
 
	{
 
		Train *v = this;
 
		while (v->IsArticulatedPart()) v = v->Previous();
 
		return v;
 
	}
 

	
 
	/**
 
	 * Get the first part of a multi-part engine.
 
	 * @return First part of the engine.
 
	 */
 
	FORCEINLINE const Train *GetFirstEnginePart() const
 
	{
 
		const Train *v = this;
 
		while (v->IsArticulatedPart()) v = v->Previous();
 
		return v;
 
	}
 

	
 
	/**
 
	 * Get the last part of a multi-part engine.
 
	 * @return Last part of the engine.
 
	 */
 
	FORCEINLINE Train *GetLastEnginePart()
 
	{
 
		Train *v = this;
 
		while (v->HasArticulatedPart()) v = v->GetNextArticPart();
 
		return v;
 
	}
 

	
 
	/**
 
	 * Get the next real (non-articulated part) vehicle in the consist.
 
	 * @return Next vehicle in the consist.
 
	 */
 
	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;
 
		}
 

	
 
		return weight;
 
	}
 

	
 
	/**
 
	 * Allows to know the tractive effort value that this vehicle will use.
 
	 * @return Tractive effort value from the engine.
 
	 */
 
	FORCEINLINE byte GetTractiveEffort() const
 
	{
 
		return GetVehicleProperty(this, PROP_TRAIN_TRACTIVE_EFFORT, RailVehInfo(this->engine_type)->tractive_effort);
 
	}
 

	
 
	/**
 
	 * Gets the area used for calculating air drag.
 
	 * @return Area of the engine.
 
	 */
 
	FORCEINLINE byte GetAirDragArea() const
 
	{
 
		return 120;
 
	}
 

	
 
	/**
 
	 * Gets the air drag coefficient of this vehicle.
 
	 * @return Air drag value from the engine.
 
	 */
 
	FORCEINLINE byte GetAirDrag() const
 
	{
 
		return RailVehInfo(this->engine_type)->air_drag;
 
	}
 

	
 
	/**
 
	 * Checks the current acceleration status of this vehicle.
 
	 * @return Acceleration status.
 
	 */
 
	FORCEINLINE AccelStatus GetAccelerationStatus() const
 
	{
 
		return (this->vehstatus & VS_STOPPED) || HasBit(this->flags, VRF_REVERSING) || HasBit(this->flags, VRF_TRAIN_STUCK) ? AS_BRAKE : AS_ACCEL;
 
	}
 

	
 
	/**
 
	 * Calculates the current speed of this vehicle.
 
	 * @return Current speed in mph.
 
	 */
 
	FORCEINLINE uint16 GetCurrentSpeed() const
 
	{
 
		return this->cur_speed * 10 / 16;
src/train_cmd.cpp
Show inline comments
 
@@ -37,278 +37,300 @@
 
#include "network/network.h"
 
#include "spritecache.h"
 
#include "core/random_func.hpp"
 
#include "company_base.h"
 
#include "engine_base.h"
 
#include "engine_func.h"
 
#include "newgrf.h"
 

	
 
#include "table/strings.h"
 
#include "table/train_cmd.h"
 

	
 
static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
 
static bool TrainCheckIfLineEnds(Train *v);
 
static void TrainController(Train *v, Vehicle *nomove);
 
static TileIndex TrainApproachingCrossingTile(const Train *v);
 
static void CheckIfTrainNeedsService(Train *v);
 
static void CheckNextTrainTile(Train *v);
 

	
 
static const byte _vehicle_initial_x_fract[4] = {10, 8, 4,  8};
 
static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
 

	
 

	
 
/**
 
 * Determine the side in which the train will leave the tile
 
 *
 
 * @param direction vehicle direction
 
 * @param track vehicle track bits
 
 * @return side of tile the train will leave
 
 */
 
static inline DiagDirection TrainExitDir(Direction direction, TrackBits track)
 
{
 
	static const TrackBits state_dir_table[DIAGDIR_END] = { TRACK_BIT_RIGHT, TRACK_BIT_LOWER, TRACK_BIT_LEFT, TRACK_BIT_UPPER };
 

	
 
	DiagDirection diagdir = DirToDiagDir(direction);
 

	
 
	/* Determine the diagonal direction in which we will exit this tile */
 
	if (!HasBit(direction, 0) && track != state_dir_table[diagdir]) {
 
		diagdir = ChangeDiagDir(diagdir, DIAGDIRDIFF_90LEFT);
 
	}
 

	
 
	return diagdir;
 
}
 

	
 

	
 
/**
 
 * Return the cargo weight multiplier to use for a rail vehicle
 
 * @param cargo Cargo type to get multiplier for
 
 * @return Cargo weight multiplier
 
 */
 
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();
 
		u->InvalidateNewGRFCache();
 
	}
 

	
 
	/* store consist weight/max speed in cache */
 
	this->tcache.cached_max_speed = max_speed;
 
	this->tcache.cached_tilt = train_can_tilt;
 
	this->tcache.cached_max_curve_speed = this->GetCurveSpeedLimit();
 

	
 
	/* recalculate cached weights and power too (we do this *after* the rest, so it is known which wagons are powered and need extra weight added) */
 
	this->CargoChanged();
 

	
 
	if (this->IsFrontEngine()) {
 
		this->UpdateAcceleration();
 
		SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
 
		InvalidateWindowData(WC_VEHICLE_REFIT, this->index);
 
	}
 
}
 

	
 
/**
 
 * Get the stop location of (the center) of the front vehicle of a train at
 
 * a platform of a station.
 
 * @param station_id     the ID of the station where we're stopping
 
 * @param tile           the tile where the vehicle currently is
 
 * @param v              the vehicle to get the stop location of
 
 * @param station_ahead  'return' the amount of 1/16th tiles in front of the train
 
 * @param station_length 'return' the station length in 1/16th tiles
 
 * @return the location, calculated from the begin of the station to stop at.
 
 */
 
int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, int *station_ahead, int *station_length)
 
{
 
	const Station *st = Station::Get(station_id);
 
	*station_ahead  = st->GetPlatformLength(tile, DirToDiagDir(v->direction)) * TILE_SIZE;
 
	*station_length = st->GetPlatformLength(tile) * TILE_SIZE;
 

	
 
	/* Default to the middle of the station for stations stops that are not in
 
	 * the order list like intermediate stations when non-stop is disabled */
 
	OrderStopLocation osl = OSL_PLATFORM_MIDDLE;
 
	if (v->tcache.cached_total_length >= *station_length) {
 
		/* The train is longer than the station, make it stop at the far end of the platform */
 
		osl = OSL_PLATFORM_FAR_END;
 
	} else if (v->current_order.IsType(OT_GOTO_STATION) && v->current_order.GetDestination() == station_id) {
 
		osl = v->current_order.GetStopLocation();
 
	}
 

	
 
	/* The stop location of the FRONT! of the train */
 
	int stop;
 
	switch (osl) {
 
		default: NOT_REACHED();
 
@@ -1473,193 +1495,193 @@ static inline void SetLastSpeed(Train *v
 
	int old = v->tcache.last_speed;
 
	if (spd != old) {
 
		v->tcache.last_speed = spd;
 
		if (_settings_client.gui.vehicle_speed || (old == 0) != (spd == 0)) {
 
			SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		}
 
	}
 
}
 

	
 
/** Mark a train as stuck and stop it if it isn't stopped right now. */
 
static void MarkTrainAsStuck(Train *v)
 
{
 
	if (!HasBit(v->flags, VRF_TRAIN_STUCK)) {
 
		/* It is the first time the problem occurred, set the "train stuck" flag. */
 
		SetBit(v->flags, VRF_TRAIN_STUCK);
 

	
 
		v->wait_counter = 0;
 

	
 
		/* Stop train */
 
		v->cur_speed = 0;
 
		v->subspeed = 0;
 
		SetLastSpeed(v, 0);
 

	
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	}
 
}
 

	
 
static void SwapTrainFlags(uint16 *swap_flag1, uint16 *swap_flag2)
 
{
 
	uint16 flag1 = *swap_flag1;
 
	uint16 flag2 = *swap_flag2;
 

	
 
	/* Clear the flags */
 
	ClrBit(*swap_flag1, GVF_GOINGUP_BIT);
 
	ClrBit(*swap_flag1, GVF_GOINGDOWN_BIT);
 
	ClrBit(*swap_flag2, GVF_GOINGUP_BIT);
 
	ClrBit(*swap_flag2, GVF_GOINGDOWN_BIT);
 

	
 
	/* Reverse the rail-flags (if needed) */
 
	if (HasBit(flag1, GVF_GOINGUP_BIT)) {
 
		SetBit(*swap_flag2, GVF_GOINGDOWN_BIT);
 
	} else if (HasBit(flag1, GVF_GOINGDOWN_BIT)) {
 
		SetBit(*swap_flag2, GVF_GOINGUP_BIT);
 
	}
 
	if (HasBit(flag2, GVF_GOINGUP_BIT)) {
 
		SetBit(*swap_flag1, GVF_GOINGDOWN_BIT);
 
	} else if (HasBit(flag2, GVF_GOINGDOWN_BIT)) {
 
		SetBit(*swap_flag1, GVF_GOINGUP_BIT);
 
	}
 
}
 

	
 
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);
 

	
 
	if (HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum)) return true;
 

	
 
	dir = ReverseDiagDir(dir);
 
	tile_from = tile + TileOffsByDiagDir(dir);
 

	
 
	return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum);
 
}
 

	
 

	
 
/**
 
 * Sets correct crossing state
 
 * @param tile tile to update
 
 * @param sound should we play sound?
 
 * @pre tile is a rail-road crossing
 
 */
 
void UpdateLevelCrossing(TileIndex tile, bool sound)
 
{
 
	assert(IsLevelCrossingTile(tile));
 

	
 
	/* train on crossing || train approaching crossing || reserved */
 
	bool new_state = HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile) || HasCrossingReservation(tile);
 

	
 
	if (new_state != IsCrossingBarred(tile)) {
 
		if (new_state && sound) {
 
			SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
 
		}
 
		SetCrossingBarred(tile, new_state);
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 

	
 
/**
 
 * Bars crossing and plays ding-ding sound if not barred already
 
 * @param tile tile with crossing
 
 * @pre tile is a rail-road crossing
 
 */
 
static inline void MaybeBarCrossingWithSound(TileIndex tile)
 
{
 
	if (!IsCrossingBarred(tile)) {
 
		BarCrossing(tile);
 
		SndPlayTileFx(SND_0E_LEVEL_CROSSING, tile);
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 

	
 
/**
 
@@ -3274,194 +3296,193 @@ static void TrainController(Train *v, Ve
 
							v->subspeed = 0;
 
							v->progress = 255 - 100;
 
							if (_settings_game.pf.wait_oneway_signal == 255 || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return;
 
						} else if (HasSignalOnTrackdir(gp.new_tile, i)) {
 
							v->cur_speed = 0;
 
							v->subspeed = 0;
 
							v->progress = 255 - 10;
 
							if (_settings_game.pf.wait_twoway_signal == 255 || ++v->wait_counter < _settings_game.pf.wait_twoway_signal * 73) {
 
								DiagDirection exitdir = TrackdirToExitdir(i);
 
								TileIndex o_tile = TileAddByDiagDir(gp.new_tile, exitdir);
 

	
 
								exitdir = ReverseDiagDir(exitdir);
 

	
 
								/* check if a train is waiting on the other side */
 
								if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return;
 
							}
 
						}
 

	
 
						/* If we would reverse but are currently in a PBS block and
 
						 * reversing of stuck trains is disabled, don't reverse.
 
						 * This does not apply if the reason for reversing is a one-way
 
						 * signal blocking us, because a train would then be stuck forever. */
 
						if (_settings_game.pf.wait_for_pbs_path == 255 && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) &&
 
								UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
 
							v->wait_counter = 0;
 
							return;
 
						}
 
						goto reverse_train_direction;
 
					} else {
 
						TryReserveRailTrack(gp.new_tile, TrackBitsToTrack(chosen_track));
 
					}
 
				} else {
 
					/* The wagon is active, simply follow the prev vehicle. */
 
					if (prev->tile == gp.new_tile) {
 
						/* Choose the same track as prev */
 
						if (prev->track == TRACK_BIT_WORMHOLE) {
 
							/* Vehicles entering tunnels enter the wormhole earlier than for bridges.
 
							 * However, just choose the track into the wormhole. */
 
							assert(IsTunnel(prev->tile));
 
							chosen_track = bits;
 
						} else {
 
							chosen_track = prev->track;
 
						}
 
					} else {
 
						/* Choose the track that leads to the tile where prev is.
 
						 * This case is active if 'prev' is already on the second next tile, when 'v' just enters the next tile.
 
						 * I.e. when the tile between them has only space for a single vehicle like
 
						 *  1) horizontal/vertical track tiles and
 
						 *  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()) {
 
					TryReserveRailTrack(gp.new_tile, DiagDirToDiagTrack(GetTunnelBridgeDirection(gp.new_tile)));
 
					CheckNextTrainTile(v);
 
				}
 
			} else {
 
				v->x_pos = gp.x;
 
				v->y_pos = gp.y;
 
				VehicleMove(v, !(v->vehstatus & VS_HIDDEN));
 
				continue;
 
			}
 
		}
 

	
 
		/* update image of train, as well as delta XY */
 
		v->UpdateDeltaXY(v->direction);
 

	
 
		v->x_pos = gp.x;
 
		v->y_pos = gp.y;
 

	
 
		/* update the Z position of the vehicle */
 
		byte old_z = v->UpdateInclination(gp.new_tile != gp.old_tile, false);
 

	
 
		if (prev == NULL) {
 
			/* This is the first vehicle in the train */
 
			AffectSpeedByZChange(v, old_z);
 
		}
 

	
 
		if (update_signals_crossing) {
 
			if (v->IsFrontEngine()) {
 
				if (TrainMovedChangeSignals(gp.new_tile, enterdir)) {
 
					/* We are entering a block with PBS signals right now, but
 
					 * not through a PBS signal. This means we don't have a
 
					 * reservation right now. As a conventional signal will only
 
					 * ever be green if no other train is in the block, getting
 
					 * a path should always be possible. If the player built
 
					 * such a strange network that it is not possible, the train
 
					 * will be marked as stuck and the player has to deal with
 
					 * the problem. */
 
					if ((!HasReservedTracks(gp.new_tile, v->track) &&
 
							!TryReserveRailTrack(gp.new_tile, FindFirstTrack(v->track))) ||
 
							!TryPathReserve(v)) {
 
						MarkTrainAsStuck(v);
 
					}
 
				}
 
			}
 

	
 
			/* Signals can only change when the first
 
			 * (above) or the last vehicle moves. */
 
			if (v->Next() == NULL) {
 
				TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
0 comments (0 inline, 0 general)