Changeset - r8143:59f1aeab7b39
[Not reviewed]
master
0 1 0
smatz - 16 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
 
@@ -1549,101 +1549,105 @@ static void DisableTrainCrossing(TileInd
 
 * 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);
 
		}
 

	
 
@@ -2576,55 +2580,48 @@ static byte AfterSetTrainPos(Vehicle *v,
 
				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)
 
@@ -2948,67 +2945,68 @@ static void TrainController(Vehicle *v, 
 
				}
 

	
 
				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?? */
0 comments (0 inline, 0 general)