From ac4ec8c2d2120d308d6710104ef0d04a02ae5424 Mon Sep 17 00:00:00 2001 From: smatz Date: Wed, 19 Mar 2008 20:50:19 +0000 Subject: (svn r12386) -Fix [FS#1841](r2428): train could break apart when reversed while partially in a depot --- src/rail.h | 1 + src/rail_cmd.cpp | 23 +++++++++++++++ src/train_cmd.cpp | 83 +++++++++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 93 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/rail.h b/src/rail.h index 5f91490c9..eb31bb4ab 100644 --- a/src/rail.h +++ b/src/rail.h @@ -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. diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp index 118551c2e..724a7213d 100644 --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -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; diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp index 643828a4b..a5cffd820 100644 --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -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); -- cgit v1.2.3-54-g00ecf