mirror of https://github.com/OpenTTD/OpenTTD
(svn r12386) -Fix [FS#1841](r2428): train could break apart when reversed while partially in a depot
parent
ab7542e912
commit
ac4ec8c2d2
|
@ -181,6 +181,7 @@ void *UpdateTrainPowerProc(Vehicle *v, void *data);
|
||||||
void DrawTrainDepotSprite(int x, int y, int image, RailType railtype);
|
void DrawTrainDepotSprite(int x, int y, int image, RailType railtype);
|
||||||
void DrawDefaultWaypointSprite(int x, int y, RailType railtype);
|
void DrawDefaultWaypointSprite(int x, int y, RailType railtype);
|
||||||
void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data);
|
void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data);
|
||||||
|
int TicksToLeaveDepot(const Vehicle *v);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws overhead wires and pylons for electric railways.
|
* Draws overhead wires and pylons for electric railways.
|
||||||
|
|
|
@ -2230,6 +2230,29 @@ static const signed char _deltacoord_leaveoffset[8] = {
|
||||||
0, 1, 0, -1 /* y */
|
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)
|
static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *v, TileIndex tile, int x, int y)
|
||||||
{
|
{
|
||||||
byte fract_coord;
|
byte fract_coord;
|
||||||
|
|
|
@ -1716,31 +1716,86 @@ static inline void MaybeBarCrossingWithSound(TileIndex tile)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Advances wagons for train reversing, needed for variable length wagons.
|
* 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 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 *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);
|
uint length = CountVehiclesInChain(v);
|
||||||
|
|
||||||
while (length > 2) {
|
while (length > 2) {
|
||||||
/* find pairwise matching wagon
|
last = last->Previous();
|
||||||
* start<>end, start+1<>end-1, ... */
|
first = first->Next();
|
||||||
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;
|
int differential = base->u.rail.cached_veh_length - last->u.rail.cached_veh_length;
|
||||||
if (before) differential *= -1;
|
|
||||||
|
|
||||||
/* do not update images now
|
/* 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);
|
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();
|
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;
|
length -= 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1759,7 +1814,7 @@ static void ReverseTrainDirection(Vehicle *v)
|
||||||
int r = 0; ///< number of vehicles - 1
|
int r = 0; ///< number of vehicles - 1
|
||||||
for (const Vehicle *u = v; (u = u->Next()) != NULL;) { r++; }
|
for (const Vehicle *u = v; (u = u->Next()) != NULL;) { r++; }
|
||||||
|
|
||||||
AdvanceWagons(v, true);
|
AdvanceWagonsBeforeSwap(v);
|
||||||
|
|
||||||
/* swap start<>end, start+1<>end-1, ... */
|
/* swap start<>end, start+1<>end-1, ... */
|
||||||
int l = 0;
|
int l = 0;
|
||||||
|
@ -1767,7 +1822,7 @@ static void ReverseTrainDirection(Vehicle *v)
|
||||||
ReverseTrainSwapVeh(v, l++, r--);
|
ReverseTrainSwapVeh(v, l++, r--);
|
||||||
} while (l <= r);
|
} while (l <= r);
|
||||||
|
|
||||||
AdvanceWagons(v, false);
|
AdvanceWagonsAfterSwap(v);
|
||||||
|
|
||||||
if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
|
if (IsTileDepotType(v->tile, TRANSPORT_RAIL)) {
|
||||||
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
|
||||||
|
|
Loading…
Reference in New Issue