(svn r12386) -Fix [FS#1841](r2428): train could break apart when reversed while partially in a depot

pull/155/head
smatz 16 years ago
parent 606bc6fc14
commit 3ab59da68a

@ -181,6 +181,7 @@ void *UpdateTrainPowerProc(Vehicle *v, void *data);
void DrawTrainDepotSprite(int x, int y, int image, RailType railtype);
void DrawDefaultWaypointSprite(int x, int y, RailType railtype);
void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data);
int TicksToLeaveDepot(const Vehicle *v);
/**
* Draws overhead wires and pylons for electric railways.

@ -2230,6 +2230,29 @@ static const signed char _deltacoord_leaveoffset[8] = {
0, 1, 0, -1 /* y */
};
/** Compute number of ticks when next wagon will leave a depot.
* Negative means next wagon should have left depot n ticks before.
* @param v vehicle outside (leaving) the depot
* @return number of ticks when the next wagon will leave
*/
int TicksToLeaveDepot(const Vehicle *v)
{
DiagDirection dir = GetRailDepotDirection(v->tile);
int length = v->u.rail.cached_veh_length;
switch (dir) {
case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
case DIAGDIR_SE: return -((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) + (length + 1)));
case DIAGDIR_SW: return -((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) + (length + 1)));
default:
case DIAGDIR_NW: return ((int)(v->y_pos & 0x0F) - ((_fractcoords_enter[dir] >> 4) - (length + 1)));
}
return 0; // make compilers happy
}
static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *v, TileIndex tile, int x, int y)
{
byte fract_coord;

@ -1716,31 +1716,86 @@ static inline void MaybeBarCrossingWithSound(TileIndex 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.
* This one is called before the train is reversed.
* @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)
static void AdvanceWagonsBeforeSwap(Vehicle *v)
{
Vehicle *base = v;
Vehicle *first = base->Next();
Vehicle *first = base; // first vehicle to move
Vehicle *last = GetLastVehicleInChain(v); // last vehicle to move
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();
last = last->Previous();
first = first->Next();
int differential = last->u.rail.cached_veh_length - base->u.rail.cached_veh_length;
if (before) differential *= -1;
int differential = base->u.rail.cached_veh_length - last->u.rail.cached_veh_length;
/* do not update images now
* negative differential will are handled in the second run */
* negative differential will be handled in AdvanceWagonsAfterSwap() */
for (int i = 0; i < differential; i++) TrainController(first, last->Next(), false);
base = first;
base = first; // == base->Next()
length -= 2;
}
}
/**
* Advances wagons for train reversing, needed for variable length wagons.
* This one is called after the train is reversed.
* @param v First vehicle in chain
*/
static void AdvanceWagonsAfterSwap(Vehicle *v)
{
/* first of all, fix the situation when the train was entering a depot */
Vehicle *dep = v; // last vehicle in front of just left depot
while (dep->Next() != NULL && (dep->u.rail.track == TRACK_BIT_DEPOT || dep->Next()->u.rail.track != TRACK_BIT_DEPOT)) {
dep = dep->Next(); // find first vehicle outside of a depot, with next vehicle inside a depot
}
Vehicle *leave = dep->Next(); // first vehicle in a depot we are leaving now
if (leave != NULL) {
/* 'pull' next wagon out of the depot, so we won't miss it (it could stay in depot forever) */
int d = TicksToLeaveDepot(dep);
if (d <= 0) {
leave->vehstatus &= ~VS_HIDDEN; // move it out of the depot
leave->u.rail.track = AxisToTrackBits(DiagDirToAxis(GetRailDepotDirection(leave->tile)));
for (int i = 0; i >= d; i--) TrainController(leave, NULL, false); // maybe move it, and maybe let another wagon leave
}
} else {
dep = NULL; // no vehicle in a depot, so no vehicle leaving a depot
}
Vehicle *base = v;
Vehicle *first = base; // first vehicle to move
Vehicle *last = GetLastVehicleInChain(v); // last vehicle to move
uint length = CountVehiclesInChain(v);
/* we have to make sure all wagons that leave a depot because of train reversing are moved coorectly
* they have already correct spacing, so we have to make sure they are moved how they should */
bool nomove = (dep == NULL); // if there is no vehicle leaving a depot, limit the number of wagons moved immediatelly
while (length > 2) {
/* we reached vehicle (originally) in front of a depot, stop now
* (we would move wagons that are alredy moved with new wagon length) */
if (base == dep) break;
/* the last wagon was that one leaving a depot, so do not move it anymore */
if (last == dep) nomove = true;
last = last->Previous();
first = first->Next();
int differential = last->u.rail.cached_veh_length - base->u.rail.cached_veh_length;
/* do not update images now */
for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : NULL), false);
base = first; // == base->Next()
length -= 2;
}
}
@ -1759,7 +1814,7 @@ static void ReverseTrainDirection(Vehicle *v)
int r = 0; ///< number of vehicles - 1
for (const Vehicle *u = v; (u = u->Next()) != NULL;) { r++; }
AdvanceWagons(v, true);
AdvanceWagonsBeforeSwap(v);
/* swap start<>end, start+1<>end-1, ... */
int l = 0;
@ -1767,7 +1822,7 @@ static void ReverseTrainDirection(Vehicle *v)
ReverseTrainSwapVeh(v, l++, r--);
} while (l <= r);
AdvanceWagons(v, false);
AdvanceWagonsAfterSwap(v);
if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);

Loading…
Cancel
Save