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
 
@@ -2445,48 +2445,49 @@ STR_NEWGRF_ERROR_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
 

	
src/newgrf_config.h
Show inline comments
 
@@ -20,48 +20,49 @@
 
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.
src/rail_cmd.cpp
Show inline comments
 
@@ -1359,49 +1359,48 @@ CommandCost CmdRemoveSingleSignal(TileIn
 
 * @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);
 
@@ -1562,50 +1561,49 @@ CommandCost CmdConvertRail(TileIndex til
 

	
 
					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));
src/train.h
Show inline comments
 
@@ -124,48 +124,50 @@ struct Train : public GroundVehicle<Trai
 
	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); }
 
@@ -351,49 +353,49 @@ struct Train : public GroundVehicle<Trai
 
	 */
 
	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
 
	{
src/train_cmd.cpp
Show inline comments
 
@@ -109,74 +109,96 @@ static void RailVehicleLengthChanged(con
 
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);
 

	
 
@@ -194,49 +216,49 @@ void Train::ConsistChanged(bool same_len
 
		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 */
 
@@ -1545,49 +1567,49 @@ static void ReverseTrainSwapVeh(Train *v
 
		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;
 

	
 
@@ -3346,50 +3368,49 @@ static void TrainController(Train *v, Ve
 
				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. */
0 comments (0 inline, 0 general)