Changeset - r8143:59f1aeab7b39
[Not reviewed]
master
0 1 0
smatz - 17 years ago 2007-12-27 13:25:23
smatz@openttd.org
(svn r11705) -Fix [FS#1557]: trains could have sprites with wrong direction when reversing, also was inconsistent with save/load process (possible desyncs)
1 file changed with 12 insertions and 14 deletions:
0 comments (0 inline, 0 general)
src/train_cmd.cpp
Show inline comments
 
@@ -1525,149 +1525,153 @@ static void ReverseTrainSwapVeh(Vehicle 
 

	
 
	/* Update train's power incase tiles were different rail type */
 
	TrainPowerChanged(v);
 
}
 

	
 
/* Check if the vehicle is a train and is on the tile we are testing */
 
static void *TestTrainOnCrossing(Vehicle *v, void *data)
 
{
 
	if (v->type != VEH_TRAIN) return NULL;
 
	return v;
 
}
 

	
 
static void DisableTrainCrossing(TileIndex tile)
 
{
 
	if (IsLevelCrossingTile(tile) &&
 
			IsCrossingBarred(tile) &&
 
			VehicleFromPos(tile, NULL, &TestTrainOnCrossing) == NULL) { // empty?
 
		UnbarCrossing(tile);
 
		MarkTileDirtyByTile(tile);
 
	}
 
}
 

	
 
/**
 
 * Advances wagons for train reversing, needed for variable length wagons.
 
 * Needs to be called once before the train is reversed, and once after it.
 
 * @param v First vehicle in chain
 
 * @param before Set to true for the call before reversing, false otherwise
 
 */
 
static void AdvanceWagons(Vehicle *v, bool before)
 
{
 
	Vehicle *base = v;
 
	Vehicle *first = base->Next();
 
	uint length = CountVehiclesInChain(v);
 

	
 
	while (length > 2) {
 
		/* find pairwise matching wagon
 
		 * start<>end, start+1<>end-1, ... */
 
		Vehicle *last = first;
 
		for (uint i = length - 3; i > 0; i--) last = last->Next();
 

	
 
		int differential = last->u.rail.cached_veh_length - base->u.rail.cached_veh_length;
 
		if (before) differential *= -1;
 

	
 
		if (differential > 0) {
 
			/* disconnect last car to make sure only this subset moves */
 
			Vehicle *tempnext = last->Next();
 
			last->SetNext(NULL);
 

	
 
			/* do not update images now because the wagons are disconnected
 
			 * and that could cause problems with NewGRFs */
 
			for (int i = 0; i < differential; i++) TrainController(first, false);
 

	
 
			last->SetNext(tempnext);
 
		}
 

	
 
		base = first;
 
		first = first->Next();
 
		length -= 2;
 
	}
 
}
 

	
 

	
 
static void ReverseTrainDirection(Vehicle *v)
 
{
 
	if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	/* Check if we were approaching a rail/road-crossing */
 
	{
 
		TileIndex tile = v->tile;
 
		DiagDirection dir = DirToDiagDir(v->direction);
 

	
 
		/* Determine the diagonal direction in which we will exit this tile */
 
		if (!(v->direction & 1) && v->u.rail.track != _state_dir_table[dir]) {
 
			dir = ChangeDiagDir(dir, DIAGDIRDIFF_90LEFT);
 
		}
 
		/* Calculate next tile */
 
		tile += TileOffsByDiagDir(dir);
 

	
 
		/* Check if the train left a rail/road-crossing */
 
		DisableTrainCrossing(tile);
 
	}
 

	
 
	/* count number of vehicles */
 
	int r = -1;
 
	const Vehicle *u = v;
 
	do r++; while ((u = u->Next()) != NULL);
 
	int r = 0;  ///< number of vehicles - 1
 
	for (const Vehicle *u = v; (u = u->Next()) != NULL;) { r++; }
 

	
 
	AdvanceWagons(v, true);
 

	
 
	/* swap start<>end, start+1<>end-1, ... */
 
	int l = 0;
 
	do {
 
		ReverseTrainSwapVeh(v, l++, r--);
 
	} while (l <= r);
 

	
 
	AdvanceWagons(v, false);
 

	
 
	if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
 
		InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
 
	}
 

	
 
	/* update all images */
 
	for (Vehicle *u = v; u != NULL; u = u->Next()) { u->cur_image = u->GetImage(u->direction); }
 

	
 
	ClrBit(v->u.rail.flags, VRF_REVERSING);
 
}
 

	
 
/** Reverse train.
 
 * @param tile unused
 
 * @param flags type of operation
 
 * @param p1 train to reverse
 
 * @param p2 if true, reverse a unit in a train (needs to be in a depot)
 
 */
 
CommandCost CmdReverseTrainDirection(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
 
{
 
	if (!IsValidVehicleID(p1)) return CMD_ERROR;
 

	
 
	Vehicle *v = GetVehicle(p1);
 

	
 
	if (v->type != VEH_TRAIN || !CheckOwnership(v->owner)) return CMD_ERROR;
 

	
 
	if (p2) {
 
		/* turn a single unit around */
 

	
 
		if (IsMultiheaded(v) || HasBit(EngInfo(v->engine_type)->callbackmask, CBM_VEHICLE_ARTIC_ENGINE)) {
 
			return_cmd_error(STR_ONLY_TURN_SINGLE_UNIT);
 
		}
 

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

	
 
		if (flags & DC_EXEC) {
 
			ToggleBit(v->u.rail.flags, VRF_REVERSE_DIRECTION);
 
			InvalidateWindow(WC_VEHICLE_DEPOT, v->tile);
 
			InvalidateWindow(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) {
 
			if (_patches.realistic_acceleration && v->cur_speed != 0) {
 
				ToggleBit(v->u.rail.flags, VRF_REVERSING);
 
			} else {
 
				v->cur_speed = 0;
 
				SetLastSpeed(v, 0);
 
				ReverseTrainDirection(v);
 
			}
 
		}
 
@@ -2552,103 +2556,96 @@ static void TrainEnterStation(Vehicle *v
 

	
 
static byte AfterSetTrainPos(Vehicle *v, bool new_tile)
 
{
 
	byte old_z = v->z_pos;
 
	v->z_pos = GetSlopeZ(v->x_pos, v->y_pos);
 

	
 
	if (new_tile) {
 
		ClrBit(v->u.rail.flags, VRF_GOINGUP);
 
		ClrBit(v->u.rail.flags, VRF_GOINGDOWN);
 

	
 
		if (v->u.rail.track == TRACK_BIT_X || v->u.rail.track == TRACK_BIT_Y) {
 
			/* Any track that isn't TRACK_BIT_X or TRACK_BIT_Y cannot be sloped.
 
			 * To check whether the current tile is sloped, and in which
 
			 * direction it is sloped, we get the 'z' at the center of
 
			 * the tile (middle_z) and the edge of the tile (old_z),
 
			 * which we then can compare. */
 
			static const int HALF_TILE_SIZE = TILE_SIZE / 2;
 
			static const int INV_TILE_SIZE_MASK = ~(TILE_SIZE - 1);
 

	
 
			byte middle_z = GetSlopeZ((v->x_pos & INV_TILE_SIZE_MASK) | HALF_TILE_SIZE, (v->y_pos & INV_TILE_SIZE_MASK) | HALF_TILE_SIZE);
 

	
 
			/* For some reason tunnel tiles are always given as sloped :(
 
			 * But they are not sloped... */
 
			if (middle_z != v->z_pos && !IsTunnelTile(TileVirtXY(v->x_pos, v->y_pos))) {
 
				SetBit(v->u.rail.flags, (middle_z > old_z) ? VRF_GOINGUP : VRF_GOINGDOWN);
 
			}
 
		}
 
	}
 

	
 
	VehiclePositionChanged(v);
 
	EndVehicleMove(v);
 
	return old_z;
 
}
 

	
 
static const Direction _new_vehicle_direction_table[11] = {
 
	DIR_N , DIR_NW, DIR_W , INVALID_DIR,
 
	DIR_NE, DIR_N , DIR_SW, INVALID_DIR,
 
	DIR_E , DIR_SE, DIR_S
 
};
 

	
 
static Direction GetNewVehicleDirectionByTile(TileIndex new_tile, TileIndex old_tile)
 
{
 
	uint offs = (TileY(new_tile) - TileY(old_tile) + 1) * 4 +
 
							TileX(new_tile) - TileX(old_tile) + 1;
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static Direction GetNewVehicleDirection(const Vehicle *v, int x, int y)
 
{
 
	uint offs = (y - v->y_pos + 1) * 4 + (x - v->x_pos + 1);
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
static int GetDirectionToVehicle(const Vehicle *v, int x, int y)
 
{
 
	byte offs;
 

	
 
	x -= v->x_pos;
 
	if (x >= 0) {
 
		offs = (x > 2) ? 0 : 1;
 
	} else {
 
		offs = (x < -2) ? 2 : 1;
 
	}
 

	
 
	y -= v->y_pos;
 
	if (y >= 0) {
 
		offs += ((y > 2) ? 0 : 1) * 4;
 
	} else {
 
		offs += ((y < -2) ? 2 : 1) * 4;
 
	}
 

	
 
	assert(offs < 11);
 
	return _new_vehicle_direction_table[offs];
 
}
 

	
 
/* Check if the vehicle is compatible with the specified tile */
 
static bool CheckCompatibleRail(const Vehicle *v, TileIndex tile)
 
{
 
	return
 
		IsTileOwner(tile, v->owner) && (
 
			!IsFrontEngine(v) ||
 
			HasBit(v->u.rail.compatible_railtypes, GetRailType(tile))
 
		);
 
}
 

	
 
struct RailtypeSlowdownParams {
 
	byte small_turn, large_turn;
 
	byte z_up; // fraction to remove when moving up
 
	byte z_down; // fraction to remove when moving down
 
};
 

	
 
static const RailtypeSlowdownParams _railtype_slowdown[] = {
 
	// normal accel
 
	{256 / 4, 256 / 2, 256 / 4, 2}, ///< normal
 
	{256 / 4, 256 / 2, 256 / 4, 2}, ///< electrified
 
	{256 / 4, 256 / 2, 256 / 4, 2}, ///< monorail
 
	{0,       256 / 2, 256 / 4, 2}, ///< maglev
 
};
 

	
 
/** Modify the speed of the vehicle due to a turn */
 
static void AffectSpeedByDirChange(Vehicle* v, Direction new_dir)
 
@@ -2924,115 +2921,116 @@ static void TrainController(Vehicle *v, 
 
					chosen_track = (TrackBits)(byte)(_matching_tracks[GetDirectionToVehicle(prev, gp.x, gp.y)] & 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 (IsLevelCrossingTile(v->tile) && v->Next() == NULL) {
 
					UnbarCrossing(v->tile);
 
					MarkTileDirtyByTile(v->tile);
 
				}
 

	
 
				if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
 

	
 
				if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
 
					v->tile = gp.new_tile;
 

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

	
 
					v->u.rail.track = chosen_track;
 
					assert(v->u.rail.track);
 
				}
 

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

	
 
				if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
 

	
 
				v->direction = chosen_dir;
 
			}
 
		} else {
 
			/* In tunnel or on a bridge */
 
			/* 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, GetBridge(GetBridgeType(v->tile))->speed);
 
			}
 

	
 
			if (!(IsTunnelTile(gp.new_tile) || IsBridgeTile(gp.new_tile)) || !HasBit(VehicleEnterTile(v, gp.new_tile, gp.x, gp.y), VETS_ENTERED_WORMHOLE)) {
 
				v->x_pos = gp.x;
 
				v->y_pos = gp.y;
 
				VehiclePositionChanged(v);
 
				if (!(v->vehstatus & VS_HIDDEN)) EndVehicleMove(v);
 
				continue;
 
			}
 
		}
 

	
 
		/* update image of train, as well as delta XY */
 
		Direction newdir = GetNewVehicleDirection(v, gp.x, gp.y);
 
		v->UpdateDeltaXY(newdir);
 
		if (update_image) v->cur_image = v->GetImage(newdir);
 
		v->UpdateDeltaXY(v->direction);
 
		if (update_image) v->cur_image = v->GetImage(v->direction);
 

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

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

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

	
 
		if (update_signals) {
 
			if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
 

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

	
 
invalid_rail:
 
	/* We've reached end of line?? */
 
	if (prev != NULL) error("!Disconnecting train");
 

	
 
reverse_train_direction:
 
	v->load_unload_time_rem = 0;
 
	v->cur_speed = 0;
 
	v->subspeed = 0;
 
	ReverseTrainDirection(v);
 
}
 

	
 
/**
 
 * Deletes/Clears the last wagon of a crashed train. It takes the engine of the
 
 * train, then goes to the last wagon and deletes that. Each call to this function
 
 * will remove the last wagon of a crashed train. If this wagon was on a crossing,
 
 * or inside a tunnel/bridge, recalculate the signals as they might need updating
 
 * @param v the Vehicle of which last wagon is to be removed
 
 */
 
static void DeleteLastWagon(Vehicle *v)
 
{
 
	/* Go to the last wagon and delete the link pointing there
 
	 * *u is then the one-before-last wagon, and *v the last
 
	 * one which will physicially be removed */
 
	Vehicle *u = v;
 
	for (; v->Next() != NULL; v = v->Next()) u = v;
 
	u->SetNext(NULL);
0 comments (0 inline, 0 general)