File diff r13023:9f6499c8d4fb → r13024:48c81d0b078a
src/train_cmd.cpp
Show inline comments
 
@@ -121,26 +121,26 @@ void TrainPowerChanged(Train *v)
 

	
 
		if (HasBit(u->flags, VRF_POWEREDWAGON) && HasPowerOnRail(v->railtype, railtype)) {
 
			total_power += RailVehInfo(u->tcache.first_engine)->pow_wag_power;
 
		}
 
	}
 

	
 
	if (v->tcache.cached_power != total_power || v->tcache.cached_max_te != max_te) {
 
		/* If it has no power (no catenary), stop the train */
 
		if (total_power == 0) v->vehstatus |= VS_STOPPED;
 

	
 
		v->tcache.cached_power = total_power;
 
		v->tcache.cached_max_te = max_te;
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	}
 
}
 

	
 

	
 
/**
 
 * Recalculates the cached weight of a train and its vehicles. Should be called each time the cargo on
 
 * the consist changes.
 
 * @param v First vehicle of the consist.
 
 */
 
static void TrainCargoChanged(Train *v)
 
{
 
	uint32 weight = 0;
 
@@ -346,25 +346,25 @@ void TrainConsistChanged(Train *v, bool 
 
	}
 

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

	
 
	/* 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) */
 
	TrainCargoChanged(v);
 

	
 
	if (v->IsFrontEngine()) {
 
		UpdateTrainAcceleration(v);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 
	}
 
}
 

	
 
enum AccelType {
 
	AM_ACCEL,
 
	AM_BRAKE
 
};
 

	
 
/**
 
 * 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
 
@@ -759,25 +759,25 @@ static CommandCost CmdBuildRailWagon(Eng
 
		v->random_bits = VehicleRandomBits();
 

	
 
		v->group_id = DEFAULT_GROUP;
 

	
 
		AddArticulatedParts(v, VEH_TRAIN);
 

	
 
		_new_vehicle_id = v->index;
 

	
 
		VehicleMove(v, false);
 
		TrainConsistChanged(v->First(), false);
 
		UpdateTrainGroupID(v->First());
 

	
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
		if (IsLocalCompany()) {
 
			InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Train window
 
		}
 
		Company::Get(_current_company)->num_engines[engine]++;
 

	
 
		CheckConsistencyOfArticulatedVehicle(v);
 
	}
 

	
 
	return value;
 
}
 

	
 
/** Move all free vehicles in the depot to the train */
 
@@ -930,25 +930,25 @@ CommandCost CmdBuildRailVehicle(TileInde
 
			AddArticulatedParts(v, VEH_TRAIN);
 
		}
 

	
 
		TrainConsistChanged(v, false);
 
		UpdateTrainGroupID(v);
 

	
 
		if (!HasBit(p2, 1) && !(flags & DC_AUTOREPLACE)) { // check if the cars should be added to the new vehicle
 
			NormalizeTrainVehInDepot(v);
 
		}
 

	
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
		InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
 
		InvalidateWindow(WC_COMPANY, v->owner);
 
		SetWindowDirty(WC_COMPANY, v->owner);
 
		if (IsLocalCompany()) {
 
			InvalidateAutoreplaceWindow(v->engine_type, v->group_id); // updates the replace Train window
 
		}
 

	
 
		Company::Get(_current_company)->num_engines[p1]++;
 

	
 
		CheckConsistencyOfArticulatedVehicle(v);
 
	}
 

	
 
	return value;
 
}
 

	
 
@@ -1397,42 +1397,42 @@ CommandCost CmdMoveRailVehicle(TileIndex
 
			const GroupID tmp_g = src_head->group_id;
 
			CmdMoveRailVehicle(0, flags, src_head->index | (INVALID_VEHICLE << 16), 1, text);
 
			SetTrainGroupID(src_head, tmp_g);
 
			src_head = NULL; // don't do anything more to this train since the new call will do it
 
		}
 

	
 
		if (src_head != NULL) {
 
			NormaliseTrainConsist(src_head);
 
			TrainConsistChanged(src_head, false);
 
			UpdateTrainGroupID(src_head);
 
			if (src_head->IsFrontEngine()) {
 
				/* Update the refit button and window */
 
				InvalidateWindow(WC_VEHICLE_REFIT, src_head->index);
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, src_head->index, VVW_WIDGET_REFIT_VEH);
 
				SetWindowDirty(WC_VEHICLE_REFIT, src_head->index);
 
				SetWindowWidgetDirty(WC_VEHICLE_VIEW, src_head->index, VVW_WIDGET_REFIT_VEH);
 
			}
 
			/* Update the depot window */
 
			InvalidateWindow(WC_VEHICLE_DEPOT, src_head->tile);
 
			SetWindowDirty(WC_VEHICLE_DEPOT, src_head->tile);
 
		}
 

	
 
		if (dst_head != NULL) {
 
			NormaliseTrainConsist(dst_head);
 
			TrainConsistChanged(dst_head, false);
 
			UpdateTrainGroupID(dst_head);
 
			if (dst_head->IsFrontEngine()) {
 
				/* Update the refit button and window */
 
				InvalidateWindowWidget(WC_VEHICLE_VIEW, dst_head->index, VVW_WIDGET_REFIT_VEH);
 
				InvalidateWindow(WC_VEHICLE_REFIT, dst_head->index);
 
				SetWindowWidgetDirty(WC_VEHICLE_VIEW, dst_head->index, VVW_WIDGET_REFIT_VEH);
 
				SetWindowDirty(WC_VEHICLE_REFIT, dst_head->index);
 
			}
 
			/* Update the depot window */
 
			InvalidateWindow(WC_VEHICLE_DEPOT, dst_head->tile);
 
			SetWindowDirty(WC_VEHICLE_DEPOT, dst_head->tile);
 
		}
 

	
 
		InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
 
	}
 

	
 
	return CommandCost();
 
}
 

	
 
/** Sell a (single) train wagon/engine.
 
 * @param tile unused
 
 * @param flags type of operation
 
 * @param p1 the wagon/engine index
 
@@ -1461,25 +1461,25 @@ CommandCost CmdSellRailWagon(TileIndex t
 
	}
 

	
 
	if (v->IsRearDualheaded()) return_cmd_error(STR_ERROR_REAR_ENGINE_FOLLOW_FRONT);
 

	
 
	if (flags & DC_EXEC) {
 
		if (v == first && first->IsFrontEngine()) {
 
			DeleteWindowById(WC_VEHICLE_VIEW, first->index);
 
			DeleteWindowById(WC_VEHICLE_ORDERS, first->index);
 
			DeleteWindowById(WC_VEHICLE_REFIT, first->index);
 
			DeleteWindowById(WC_VEHICLE_DETAILS, first->index);
 
			DeleteWindowById(WC_VEHICLE_TIMETABLE, first->index);
 
		}
 
		InvalidateWindow(WC_VEHICLE_DEPOT, first->tile);
 
		SetWindowDirty(WC_VEHICLE_DEPOT, first->tile);
 
		InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
 
	}
 

	
 
	CommandCost cost(EXPENSES_NEW_VEHICLES);
 
	switch (p2) {
 
		case 0: { // Delete given wagon
 
			bool switch_engine = false;    // update second wagon to engine?
 

	
 
			/* 1. Delete the engine, if it is dualheaded also delete the matching
 
			 * rear engine of the loco (from the point of deletion onwards) */
 
			Train *rear = (v->IsMultiheaded() &&
 
				v->IsEngine()) ? v->other_multiheaded_part : NULL;
 
@@ -1537,25 +1537,25 @@ CommandCost CmdSellRailWagon(TileIndex t
 
				first = UnlinkWagon(v, first);
 
				delete v;
 

	
 
				/* 4 If the second wagon was an engine, update it to front_engine
 
				 * which UnlinkWagon() has changed to TS_Free_Car */
 
				if (switch_engine) first->SetFrontEngine();
 

	
 
				/* 5. If the train still exists, update its acceleration, window, etc. */
 
				if (first != NULL) {
 
					NormaliseTrainConsist(first);
 
					TrainConsistChanged(first, false);
 
					UpdateTrainGroupID(first);
 
					if (first->IsFrontEngine()) InvalidateWindow(WC_VEHICLE_REFIT, first->index);
 
					if (first->IsFrontEngine()) SetWindowDirty(WC_VEHICLE_REFIT, first->index);
 
				}
 

	
 
			}
 
		} break;
 
		case 1: { // Delete wagon and all wagons after it given certain criteria
 
			/* Start deleting every vehicle after the selected one
 
			 * If we encounter a matching rear-engine to a front-engine
 
			 * earlier in the chain (before deletion), leave it alone */
 
			for (Train *tmp; v != NULL; v = tmp) {
 
				tmp = v->GetNextVehicle();
 

	
 
				if (v->IsMultiheaded()) {
 
@@ -1588,25 +1588,25 @@ CommandCost CmdSellRailWagon(TileIndex t
 
				cost.AddCost(-v->value);
 
				if (flags & DC_EXEC) {
 
					first = UnlinkWagon(v, first);
 
					delete v;
 
				}
 
			}
 

	
 
			/* 3. If it is still a valid train after selling, update its acceleration and cached values */
 
			if ((flags & DC_EXEC) && first != NULL) {
 
				NormaliseTrainConsist(first);
 
				TrainConsistChanged(first, false);
 
				UpdateTrainGroupID(first);
 
				InvalidateWindow(WC_VEHICLE_REFIT, first->index);
 
				SetWindowDirty(WC_VEHICLE_REFIT, first->index);
 
			}
 
		} break;
 
	}
 
	return cost;
 
}
 

	
 
void Train::UpdateDeltaXY(Direction direction)
 
{
 
#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
 
	static const uint32 _delta_xy_table[8] = {
 
		MKIT(3, 3, -1, -1),
 
		MKIT(3, 7, -1, -3),
 
@@ -1624,47 +1624,47 @@ void Train::UpdateDeltaXY(Direction dire
 
	this->y_offs        = GB(x,  8, 8);
 
	this->x_extent      = GB(x, 16, 8);
 
	this->y_extent      = GB(x, 24, 8);
 
	this->z_extent      = 6;
 
}
 

	
 
static inline void SetLastSpeed(Train *v, int spd)
 
{
 
	int old = v->tcache.last_speed;
 
	if (spd != old) {
 
		v->tcache.last_speed = spd;
 
		if (_settings_client.gui.vehicle_speed || (old == 0) != (spd == 0)) {
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
			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 occured, set the "train stuck" flag. */
 
		SetBit(v->flags, VRF_TRAIN_STUCK);
 

	
 
		/* When loading the vehicle is already stopped. No need to change that. */
 
		if (v->current_order.IsType(OT_LOADING)) return;
 

	
 
		v->load_unload_time_rem = 0;
 

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

	
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		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, VRF_GOINGUP);
 
	ClrBit(*swap_flag1, VRF_GOINGDOWN);
 
	ClrBit(*swap_flag2, VRF_GOINGUP);
 
@@ -1951,25 +1951,25 @@ static void ReverseTrainDirection(Train 
 
	for (Vehicle *u = v; u != NULL; u = u->Next()) u->UpdateViewport(false, false);
 

	
 
	/* update crossing we were approaching */
 
	if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
 

	
 
	/* maybe we are approaching crossing now, after reversal */
 
	crossing = TrainApproachingCrossingTile(v);
 
	if (crossing != INVALID_TILE) MaybeBarCrossingWithSound(crossing);
 

	
 
	/* If we are inside a depot after reversing, don't bother with path reserving. */
 
	if (v->track == TRACK_BIT_DEPOT) {
 
		/* Can't be stuck here as inside a depot is always a safe tile. */
 
		if (HasBit(v->flags, VRF_TRAIN_STUCK)) InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		if (HasBit(v->flags, VRF_TRAIN_STUCK)) SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		ClrBit(v->flags, VRF_TRAIN_STUCK);
 
		return;
 
	}
 

	
 
	/* TrainExitDir does not always produce the desired dir for depots and
 
	 * tunnels/bridges that is needed for UpdateSignalsOnSegment. */
 
	DiagDirection dir = TrainExitDir(v->direction, v->track);
 
	if (IsRailDepotTile(v->tile) || IsTileType(v->tile, MP_TUNNELBRIDGE)) dir = INVALID_DIAGDIR;
 

	
 
	if (UpdateSignalsOnSegment(v->tile, dir, v->owner) == SIGSEG_PBS || _settings_game.pf.reserve_paths) {
 
		/* If we are currently on a tile with conventional signals, we can't treat the
 
		 * current tile as a safe tile or we would enter a PBS block without a reservation. */
 
@@ -2009,26 +2009,26 @@ CommandCost CmdReverseTrainDirection(Til
 
		if (v->IsMultiheaded() || HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) {
 
			return_cmd_error(STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS);
 
		}
 

	
 
		Train *front = v->First();
 
		/* make sure the vehicle is stopped in the depot */
 
		if (CheckTrainStoppedInDepot(front) < 0) {
 
			return_cmd_error(STR_ERROR_TRAINS_CAN_ONLY_BE_ALTERED_INSIDE_A_DEPOT);
 
		}
 

	
 
		if (flags & DC_EXEC) {
 
			ToggleBit(v->flags, VRF_REVERSE_DIRECTION);
 
			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
			InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
			SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
			SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 
		}
 
	} else {
 
		/* turn the whole train around */
 
		if ((v->vehstatus & VS_CRASHED) || v->breakdown_ctr != 0) return CMD_ERROR;
 

	
 
		if (flags & DC_EXEC) {
 
			/* Properly leave the station if we are loading and won't be loading anymore */
 
			if (v->current_order.IsType(OT_LOADING)) {
 
				const Vehicle *last = v;
 
				while (last->Next() != NULL) last = last->Next();
 

	
 
				/* not a station || different station --> leave the station */
 
@@ -2139,26 +2139,26 @@ CommandCost CmdRefitRailVehicle(TileInde
 
			}
 

	
 
			if (new_cid != v->cargo_type) {
 
				cost.AddCost(GetRefitCost(v->engine_type));
 
			}
 

	
 
			num += amount;
 
			if (flags & DC_EXEC) {
 
				v->cargo.Truncate((v->cargo_type == new_cid) ? amount : 0);
 
				v->cargo_type = new_cid;
 
				v->cargo_cap = amount;
 
				v->cargo_subtype = new_subtype;
 
				InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
				InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
				SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 
				SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
				InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
 
			}
 
		}
 
	} while ((v = v->Next()) != NULL && !only_this);
 

	
 
	_returned_refit_capacity = num;
 

	
 
	/* Update the train's cached variables */
 
	if (flags & DC_EXEC) TrainConsistChanged(Train::Get(p1)->First(), false);
 

	
 
	return cost;
 
}
 
@@ -2428,71 +2428,71 @@ static void CheckNextTrainTile(Train *v)
 
}
 

	
 
static bool CheckTrainStayInDepot(Train *v)
 
{
 
	/* bail out if not all wagons are in the same depot or not in a depot at all */
 
	for (const Train *u = v; u != NULL; u = u->Next()) {
 
		if (u->track != TRACK_BIT_DEPOT || u->tile != v->tile) return false;
 
	}
 

	
 
	/* if the train got no power, then keep it in the depot */
 
	if (v->tcache.cached_power == 0) {
 
		v->vehstatus |= VS_STOPPED;
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
		return true;
 
	}
 

	
 
	SigSegState seg_state;
 

	
 
	if (v->force_proceed == 0) {
 
		/* force proceed was not pressed */
 
		if (++v->load_unload_time_rem < 37) {
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			SetWindowClassesDirty(WC_TRAINS_LIST);
 
			return true;
 
		}
 

	
 
		v->load_unload_time_rem = 0;
 

	
 
		seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
 
		if (seg_state == SIGSEG_FULL || HasDepotReservation(v->tile)) {
 
			/* Full and no PBS signal in block or depot reserved, can't exit. */
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			SetWindowClassesDirty(WC_TRAINS_LIST);
 
			return true;
 
		}
 
	} else {
 
		seg_state = _settings_game.pf.reserve_paths ? SIGSEG_PBS : UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
 
	}
 

	
 
	/* We are leaving a depot, but have to go to the exact same one; re-enter */
 
	if (v->current_order.IsType(OT_GOTO_DEPOT) && v->tile == v->dest_tile) {
 
		/* We need to have a reservation for this to work. */
 
		if (HasDepotReservation(v->tile)) return true;
 
		SetDepotReservation(v->tile, true);
 
		VehicleEnterDepot(v);
 
		return true;
 
	}
 

	
 
	/* Only leave when we can reserve a path to our destination. */
 
	if (seg_state == SIGSEG_PBS && !TryPathReserve(v) && v->force_proceed == 0) {
 
		/* No path and no force proceed. */
 
		InvalidateWindowClasses(WC_TRAINS_LIST);
 
		SetWindowClassesDirty(WC_TRAINS_LIST);
 
		MarkTrainAsStuck(v);
 
		return true;
 
	}
 

	
 
	SetDepotReservation(v->tile, true);
 
	if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
 

	
 
	VehicleServiceInDepot(v);
 
	InvalidateWindowClasses(WC_TRAINS_LIST);
 
	SetWindowClassesDirty(WC_TRAINS_LIST);
 
	v->PlayLeaveStationSound();
 

	
 
	v->track = TRACK_BIT_X;
 
	if (v->direction & 2) v->track = TRACK_BIT_Y;
 

	
 
	v->vehstatus &= ~VS_HIDDEN;
 
	v->cur_speed = 0;
 

	
 
	v->UpdateDeltaXY(v->direction);
 
	v->cur_image = v->GetImage(v->direction);
 
	VehicleMove(v, false);
 
	UpdateSignalsOnSegment(v->tile, INVALID_DIAGDIR, v->owner);
 
@@ -3160,25 +3160,25 @@ bool TryPathReserve(Train *v, bool mark_
 
	/* The path we are driving on is alread blocked by some other train.
 
	 * This can only happen in certain situations when mixing path and
 
	 * block signals or when changing tracks and/or signals.
 
	 * Exit here as doing any further reservations will probably just
 
	 * make matters worse. */
 
	if (other_train && v->tile != origin.tile) {
 
		if (mark_as_stuck) MarkTrainAsStuck(v);
 
		return false;
 
	}
 
	/* If we have a reserved path and the path ends at a safe tile, we are finished already. */
 
	if (origin.okay && (v->tile != origin.tile || first_tile_okay)) {
 
		/* Can't be stuck then. */
 
		if (HasBit(v->flags, VRF_TRAIN_STUCK)) InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		if (HasBit(v->flags, VRF_TRAIN_STUCK)) SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		ClrBit(v->flags, VRF_TRAIN_STUCK);
 
		return true;
 
	}
 

	
 
	/* If we are in a depot, tentativly reserve the depot. */
 
	if (v->track == TRACK_BIT_DEPOT) {
 
		SetDepotReservation(v->tile, true);
 
		if (_settings_client.gui.show_track_reservation) MarkTileDirtyByTile(v->tile);
 
	}
 

	
 
	DiagDirection exitdir = TrackdirToExitdir(origin.trackdir);
 
	TileIndex     new_tile = TileAddByDiagDir(origin.tile, exitdir);
 
@@ -3188,25 +3188,25 @@ bool TryPathReserve(Train *v, bool mark_
 

	
 
	bool res_made = false;
 
	ChooseTrainTrack(v, new_tile, exitdir, reachable, true, &res_made, mark_as_stuck);
 

	
 
	if (!res_made) {
 
		/* Free the depot reservation as well. */
 
		if (v->track == TRACK_BIT_DEPOT) SetDepotReservation(v->tile, false);
 
		return false;
 
	}
 

	
 
	if (HasBit(v->flags, VRF_TRAIN_STUCK)) {
 
		v->load_unload_time_rem = 0;
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	}
 
	ClrBit(v->flags, VRF_TRAIN_STUCK);
 
	return true;
 
}
 

	
 

	
 
static bool CheckReverseTrain(Train *v)
 
{
 
	if (_settings_game.difficulty.line_reverse_mode != 0 ||
 
			v->track == TRACK_BIT_DEPOT || v->track == TRACK_BIT_WORMHOLE ||
 
			!(v->direction & 1)) {
 
		return false;
 
@@ -3527,29 +3527,29 @@ static void SetVehicleCrashed(Train *v)
 
				/* ClearPathReservation will not free the wormhole exit
 
				 * if the train has just entered the wormhole. */
 
				SetTunnelBridgeReservation(GetOtherTunnelBridgeEnd(u->tile), false);
 
			}
 
		}
 
	}
 

	
 
	/* we may need to update crossing we were approaching */
 
	TileIndex crossing = TrainApproachingCrossingTile(v);
 

	
 
	v->crash_anim_pos++;
 

	
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
	SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 

	
 
	if (v->track == TRACK_BIT_DEPOT) {
 
		InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
		SetWindowDirty(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	InvalidateWindowClassesData(WC_TRAINS_LIST, 0);
 

	
 
	for (; v != NULL; v = v->Next()) {
 
		v->vehstatus |= VS_CRASHED;
 
		MarkSingleVehicleDirty(v);
 
	}
 

	
 
	/* must be updated after the train has been marked crashed */
 
	if (crossing != INVALID_TILE) UpdateLevelCrossing(crossing);
 
}
 
@@ -4015,25 +4015,25 @@ static void DeleteLastWagon(Train *v)
 
	 * *u is then the one-before-last wagon, and *v the last
 
	 * one which will physicially be removed */
 
	Train *u = v;
 
	for (; v->Next() != NULL; v = v->Next()) u = v;
 
	u->SetNext(NULL);
 

	
 
	if (first != v) {
 
		/* Recalculate cached train properties */
 
		TrainConsistChanged(first, false);
 
		/* Update the depot window if the first vehicle is in depot -
 
		 * if v == first, then it is updated in PreDestructor() */
 
		if (first->track == TRACK_BIT_DEPOT) {
 
			InvalidateWindow(WC_VEHICLE_DEPOT, first->tile);
 
			SetWindowDirty(WC_VEHICLE_DEPOT, first->tile);
 
		}
 
	}
 

	
 
	/* 'v' shouldn't be accessed after it has been deleted */
 
	TrackBits trackbits = v->track;
 
	TileIndex tile = v->tile;
 
	Owner owner = v->owner;
 

	
 
	delete v;
 
	v = NULL; // make sure nobody will try to read 'v' anymore
 

	
 
	if (trackbits == TRACK_BIT_WORMHOLE) {
 
@@ -4112,58 +4112,58 @@ static bool HandleCrashedTrain(Train *v)
 
					GB(r,  0, 3) + 5,
 
					EV_EXPLOSION_SMALL);
 
				break;
 
			}
 
		} while ((u = u->Next()) != NULL);
 
	}
 

	
 
	if (state <= 240 && !(v->tick_counter & 3)) ChangeTrainDirRandomly(v);
 

	
 
	if (state >= 4440 && !(v->tick_counter & 0x1F)) {
 
		bool ret = v->Next() != NULL;
 
		DeleteLastWagon(v);
 
		InvalidateWindow(WC_REPLACE_VEHICLE, (v->group_id << 16) | VEH_TRAIN);
 
		SetWindowDirty(WC_REPLACE_VEHICLE, (v->group_id << 16) | VEH_TRAIN);
 
		return ret;
 
	}
 

	
 
	return true;
 
}
 

	
 
static void HandleBrokenTrain(Train *v)
 
{
 
	if (v->breakdown_ctr != 1) {
 
		v->breakdown_ctr = 1;
 
		v->cur_speed = 0;
 

	
 
		if (v->breakdowns_since_last_service != 255)
 
			v->breakdowns_since_last_service++;
 

	
 
		InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
		InvalidateWindow(WC_VEHICLE_DETAILS, v->index);
 
		SetWindowDirty(WC_VEHICLE_VIEW, v->index);
 
		SetWindowDirty(WC_VEHICLE_DETAILS, v->index);
 

	
 
		if (!PlayVehicleSound(v, VSE_BREAKDOWN)) {
 
			SndPlayVehicleFx((_settings_game.game_creation.landscape != LT_TOYLAND) ?
 
				SND_10_TRAIN_BREAKDOWN : SND_3A_COMEDY_BREAKDOWN_2, v);
 
		}
 

	
 
		if (!(v->vehstatus & VS_HIDDEN)) {
 
			EffectVehicle *u = CreateEffectVehicleRel(v, 4, 4, 5, EV_BREAKDOWN_SMOKE);
 
			if (u != NULL) u->animation_state = v->breakdown_delay * 2;
 
		}
 
	}
 

	
 
	if (!(v->tick_counter & 3)) {
 
		if (!--v->breakdown_delay) {
 
			v->breakdown_ctr = 0;
 
			InvalidateWindow(WC_VEHICLE_VIEW, v->index);
 
			SetWindowDirty(WC_VEHICLE_VIEW, v->index);
 
		}
 
	}
 
}
 

	
 
/** Maximum speeds for train that is broken down or approaching line end */
 
static const uint16 _breakdown_speeds[16] = {
 
	225, 210, 195, 180, 165, 150, 135, 120, 105, 90, 75, 60, 45, 30, 15, 15
 
};
 

	
 

	
 
/**
 
 * Train is approaching line end, slow down and possibly reverse
 
@@ -4321,25 +4321,25 @@ static bool TrainCheckIfLineEnds(Train *
 

	
 

	
 
static bool TrainLocoHandler(Train *v, bool mode)
 
{
 
	/* train has crashed? */
 
	if (v->vehstatus & VS_CRASHED) {
 
		return mode ? true : HandleCrashedTrain(v); // 'this' can be deleted here
 
	}
 

	
 
	if (v->force_proceed != 0) {
 
		v->force_proceed--;
 
		ClrBit(v->flags, VRF_TRAIN_STUCK);
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	}
 

	
 
	/* train is broken down? */
 
	if (v->breakdown_ctr != 0) {
 
		if (v->breakdown_ctr <= 2) {
 
			HandleBrokenTrain(v);
 
			return true;
 
		}
 
		if (!v->current_order.IsType(OT_LOADING)) v->breakdown_ctr--;
 
	}
 

	
 
	if (HasBit(v->flags, VRF_REVERSING) && v->cur_speed == 0) {
 
@@ -4390,39 +4390,39 @@ static bool TrainLocoHandler(Train *v, b
 
					AddVehicleNewsItem(
 
						STR_NEWS_TRAIN_IS_STUCK,
 
						NS_ADVICE,
 
						v->index
 
					);
 
				}
 
				v->load_unload_time_rem = 0;
 
			}
 
			/* Exit if force proceed not pressed, else reset stuck flag anyway. */
 
			if (v->force_proceed == 0) return true;
 
			ClrBit(v->flags, VRF_TRAIN_STUCK);
 
			v->load_unload_time_rem = 0;
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
			SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		}
 
	}
 

	
 
	if (v->current_order.IsType(OT_LEAVESTATION)) {
 
		v->current_order.Free();
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		return true;
 
	}
 

	
 
	int j = UpdateTrainSpeed(v);
 

	
 
	/* we need to invalidate the widget if we are stopping from 'Stopping 0 km/h' to 'Stopped' */
 
	if (v->cur_speed == 0 && v->tcache.last_speed == 0 && (v->vehstatus & VS_STOPPED)) {
 
		InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	}
 

	
 
	int adv_spd = (v->direction & 1) ? 192 : 256;
 
	if (j < adv_spd) {
 
		/* if the vehicle has speed 0, update the last_speed field. */
 
		if (v->cur_speed == 0) SetLastSpeed(v, v->cur_speed);
 
	} else {
 
		TrainCheckIfLineEnds(v);
 
		/* Loop until the train has finished moving. */
 
		for (;;) {
 
			j -= adv_spd;
 
			TrainController(v, NULL);
 
@@ -4512,40 +4512,40 @@ static void CheckIfTrainNeedsService(Tra
 
		VehicleServiceInDepot(v);
 
		return;
 
	}
 

	
 
	TrainFindDepotData tfdd = FindClosestTrainDepot(v, MAX_ACCEPTABLE_DEPOT_DIST);
 
	/* Only go to the depot if it is not too far out of our way. */
 
	if (tfdd.best_length == UINT_MAX || tfdd.best_length > MAX_ACCEPTABLE_DEPOT_DIST) {
 
		if (v->current_order.IsType(OT_GOTO_DEPOT)) {
 
			/* If we were already heading for a depot but it has
 
			 * suddenly moved farther away, we continue our normal
 
			 * schedule? */
 
			v->current_order.MakeDummy();
 
			InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
			SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
		}
 
		return;
 
	}
 

	
 
	DepotID depot = GetDepotIndex(tfdd.tile);
 

	
 
	if (v->current_order.IsType(OT_GOTO_DEPOT) &&
 
			v->current_order.GetDestination() != depot &&
 
			!Chance16(3, 16)) {
 
		return;
 
	}
 

	
 
	v->current_order.MakeGoToDepot(depot, ODTFB_SERVICE);
 
	v->dest_tile = tfdd.tile;
 
	InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
	SetWindowWidgetDirty(WC_VEHICLE_VIEW, v->index, VVW_WIDGET_START_STOP_VEH);
 
}
 

	
 
void Train::OnNewDay()
 
{
 
	if ((++this->day_counter & 7) == 0) DecreaseVehicleValue(this);
 

	
 
	if (this->IsFrontEngine()) {
 
		CheckVehicleBreakdown(this);
 
		AgeVehicle(this);
 

	
 
		CheckIfTrainNeedsService(this);
 

	
 
@@ -4557,26 +4557,26 @@ void Train::OnNewDay()
 
			if (tile != INVALID_TILE) this->dest_tile = tile;
 
		}
 

	
 
		if (this->running_ticks != 0) {
 
			/* running costs */
 
			CommandCost cost(EXPENSES_TRAIN_RUN, this->GetRunningCost() * this->running_ticks / (DAYS_IN_YEAR  * DAY_TICKS));
 

	
 
			this->profit_this_year -= cost.GetCost();
 
			this->running_ticks = 0;
 

	
 
			SubtractMoneyFromCompanyFract(this->owner, cost);
 

	
 
			InvalidateWindow(WC_VEHICLE_DETAILS, this->index);
 
			InvalidateWindowClasses(WC_TRAINS_LIST);
 
			SetWindowDirty(WC_VEHICLE_DETAILS, this->index);
 
			SetWindowClassesDirty(WC_TRAINS_LIST);
 
		}
 
	} else if (this->IsEngine()) {
 
		/* Also age engines that aren't front engines */
 
		AgeVehicle(this);
 
	}
 
}
 

	
 
Trackdir Train::GetVehicleTrackdir() const
 
{
 
	if (this->vehstatus & VS_CRASHED) return INVALID_TRACKDIR;
 

	
 
	if (this->track == TRACK_BIT_DEPOT) {