(svn r23290) -Fix [FS#2379,FS#3569]: Change the centre of train vehicles to depend on the vehicle length instead of being fixed at 4/8th of the original vehicle length to make sure shortened vehicles don't block tiles they shouldn't block.

This commit is contained in:
michi_cc 2011-11-21 20:51:43 +00:00
parent 3748cab608
commit 4d2a9e384c
10 changed files with 230 additions and 59 deletions

View File

@ -239,4 +239,15 @@ static inline bool IsValidAxis(Axis d)
return d < AXIS_END;
}
/**
* Checks if a given Direction is diagonal.
*
* @param dir The given direction.
* @return True if the direction is diagonal.
*/
static inline bool IsDiagonalDirection(Direction dir)
{
return (dir & 1) != 0;
}
#endif /* DIRECTION_FUNC_H */

View File

@ -2711,7 +2711,7 @@ static const int8 _deltacoord_leaveoffset[8] = {
int TicksToLeaveDepot(const Train *v)
{
DiagDirection dir = GetRailDepotDirection(v->tile);
int length = v->gcache.cached_veh_length;
int length = v->CalcNextVehicleOffset();
switch (dir) {
case DIAGDIR_NE: return ((int)(v->x_pos & 0x0F) - ((_fractcoords_enter[dir] & 0x0F) - (length + 1)));
@ -2738,9 +2738,8 @@ static VehicleEnterTileStatus VehicleEnter_Track(Vehicle *u, TileIndex tile, int
/* depot direction */
DiagDirection dir = GetRailDepotDirection(tile);
/* calculate the point where the following wagon should be activated
* this depends on the length of the current vehicle */
int length = v->gcache.cached_veh_length;
/* Calculate the point where the following wagon should be activated. */
int length = v->CalcNextVehicleOffset();
byte fract_coord_leave =
((_fractcoords_enter[dir] & 0x0F) + // x

View File

@ -2667,6 +2667,9 @@ bool AfterLoadGame()
}
}
/* The center of train vehicles was changed, fix up spacing. */
if (IsSavegameVersionBefore(164)) FixupTrainLengths();
/* When any NewGRF has been changed the availability of some vehicles might
* have been changed too. e->company_avail must be set to 0 in that case
* which is done by StartupEngines(). */

View File

@ -228,8 +228,9 @@
* 161 22567
* 162 22713
* 163 22767
* 164 23290
*/
extern const uint16 SAVEGAME_VERSION = 163; ///< Current savegame version of OpenTTD.
extern const uint16 SAVEGAME_VERSION = 164; ///< Current savegame version of OpenTTD.
SavegameType _savegame_type; ///< type of savegame we are loading

View File

@ -27,6 +27,7 @@ void MoveWaypointsToBaseStations();
const SaveLoad *GetBaseStationDescription();
void AfterLoadVehicles(bool part_of_load);
void FixupTrainLengths();
void AfterLoadStations();
void AfterLoadRoadStops();
void AfterLoadLabelMaps();

View File

@ -249,8 +249,6 @@ void AfterLoadVehicles(bool part_of_load)
if (v->Next() != NULL) v->Next()->previous = v;
if (v->NextShared() != NULL) v->NextShared()->previous_shared = v;
v->UpdateDeltaXY(v->direction);
if (part_of_load) v->fill_percent_te_id = INVALID_TE_ID;
v->first = NULL;
if (v->IsGroundVehicle()) v->GetGroundVehicleCache()->first_engine = INVALID_ENGINE;
@ -442,11 +440,99 @@ void AfterLoadVehicles(bool part_of_load)
default: break;
}
v->UpdateDeltaXY(v->direction);
v->coord.left = INVALID_COORD;
VehicleMove(v, false);
}
}
bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // From train_cmd.cpp
void ReverseTrainDirection(Train *v);
void ReverseTrainSwapVeh(Train *v, int l, int r);
/** Fixup old train spacing. */
void FixupTrainLengths()
{
/* Vehicle center was moved from 4 units behind the front to half the length
* behind the front. Move vehicles so they end up on the same spot. */
Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (v->type == VEH_TRAIN && v->IsPrimaryVehicle()) {
/* The vehicle center is now more to the front depending on vehicle length,
* so we need to move all vehicles forward to cover the difference to the
* old center, otherwise wagon spacing in trains would be broken upon load. */
for (Train *u = Train::From(v); u != NULL; u = u->Next()) {
if (u->track == TRACK_BIT_DEPOT || (u->vehstatus & VS_CRASHED)) continue;
Train *next = u->Next();
/* Try to pull the vehicle half its length forward. */
int diff = (VEHICLE_LENGTH - u->gcache.cached_veh_length) / 2;
int done;
for (done = 0; done < diff; done++) {
if (!TrainController(u, next, false)) break;
}
if (next != NULL && done < diff && u->IsFrontEngine()) {
/* Pulling the front vehicle forwards failed, we either encountered a dead-end
* or a red signal. To fix this, we try to move the whole train the required
* space backwards and re-do the fix up of the front vehicle. */
/* Ignore any signals when backtracking. */
TrainForceProceeding old_tfp = u->force_proceed;
u->force_proceed = TFP_SIGNAL;
/* Swap start<>end, start+1<>end-1, ... */
int r = CountVehiclesInChain(u) - 1; // number of vehicles - 1
int l = 0;
do ReverseTrainSwapVeh(u, l++, r--); while (l <= r);
/* We moved the first vehicle which is now the last. Move it back to the
* original position as we will fix up the last vehicle later in the loop. */
for (int i = 0; i < done; i++) TrainController(u->Last(), NULL);
/* Move the train backwards to get space for the first vehicle. As the stopping
* distance from a line end is rounded up, move the train one unit more to cater
* for front vehicles with odd lengths. */
int moved;
for (moved = 0; moved < diff + 1; moved++) {
if (!TrainController(u, NULL, false)) break;
}
/* Swap start<>end, start+1<>end-1, ... again. */
r = CountVehiclesInChain(u) - 1; // number of vehicles - 1
l = 0;
do ReverseTrainSwapVeh(u, l++, r--); while (l <= r);
u->force_proceed = old_tfp;
/* Tracks are too short to fix the train length. The player has to fix the
* train in a depot. Bail out so we don't damage the vehicle chain any more. */
if (moved < diff + 1) break;
/* Re-do the correction for the first vehicle. */
for (done = 0; done < diff; done++) TrainController(u, next, false);
/* We moved one unit more backwards than needed for even-length front vehicles,
* try to move that unit forward again. We don't care if this step fails. */
TrainController(u, NULL, false);
}
/* If the next wagon is still in a depot, check if it shouldn't be outside already. */
if (next != NULL && next->track == TRACK_BIT_DEPOT) {
int d = TicksToLeaveDepot(u);
if (d <= 0) {
/* Next vehicle should have left the depot already, show it and pull forward. */
next->vehstatus &= ~VS_HIDDEN;
next->track = TrackToTrackBits(GetRailDepotTrack(next->tile));
for (int i = 0; i >= d; i--) TrainController(next, NULL);
}
}
}
}
}
}
static uint8 _cargo_days;
static uint16 _cargo_source;
static uint32 _cargo_source_xy;

View File

@ -148,6 +148,18 @@ struct Train : public GroundVehicle<Train, VEH_TRAIN> {
return v;
}
/**
* Calculate the offset from this vehicle's center to the following center taking the vehicle lengths into account.
* @return Offset from center to center.
*/
int CalcNextVehicleOffset() const
{
/* For vehicles with odd lengths the part before the center will be one unit
* longer than the part after the center. This means we have to round up the
* length of the next vehicle but may not round the length of the current
* vehicle. */
return this->gcache.cached_veh_length / 2 + (this->Next() != NULL ? this->Next()->gcache.cached_veh_length + 1 : 0) / 2;
}
protected: // These functions should not be called outside acceleration code.

View File

@ -41,8 +41,8 @@
#include "table/train_cmd.h"
static Track ChooseTrainTrack(Train *v, TileIndex tile, DiagDirection enterdir, TrackBits tracks, bool force_res, bool *got_reservation, bool mark_stuck);
static bool TrainCheckIfLineEnds(Train *v);
static void TrainController(Train *v, Vehicle *nomove);
static bool TrainCheckIfLineEnds(Train *v, bool reverse = true);
bool TrainController(Train *v, Vehicle *nomove, bool reverse = true); // Also used in vehicle_sl.cpp.
static TileIndex TrainApproachingCrossingTile(const Train *v);
static void CheckIfTrainNeedsService(Train *v);
static void CheckNextTrainTile(Train *v);
@ -95,7 +95,7 @@ void CheckTrainsLengths()
for (const Train *u = v, *w = v->Next(); w != NULL; u = w, w = w->Next()) {
if (u->track != TRACK_BIT_DEPOT) {
if ((w->track != TRACK_BIT_DEPOT &&
max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->gcache.cached_veh_length) ||
max(abs(u->x_pos - w->x_pos), abs(u->y_pos - w->y_pos)) != u->CalcNextVehicleOffset()) ||
(w->track == TRACK_BIT_DEPOT && TicksToLeaveDepot(u) <= 0)) {
SetDParam(0, v->index);
SetDParam(1, v->owner);
@ -305,11 +305,8 @@ int GetTrainStopLocation(StationID station_id, TileIndex tile, const Train *v, i
}
/* Subtract half the front vehicle length of the train so we get the real
* stop location of the train.
* Actually, the center of all vehicles is half a normal vehicle's length
* from the front of the vehicle, so even in case the vehicle is 1/8th
* long, the center is still at 1/2 of VEHICLE_LENGTH. Basically FS#3569. */
return stop - VEHICLE_LENGTH / 2;
* stop location of the train. */
return stop - (v->gcache.cached_veh_length + 1) / 2;
}
@ -1382,25 +1379,66 @@ CommandCost CmdSellRailWagon(DoCommandFlag flags, Vehicle *t, uint16 data, uint3
void Train::UpdateDeltaXY(Direction direction)
{
#define MKIT(a, b, c, d) ((a & 0xFF) << 24) | ((b & 0xFF) << 16) | ((c & 0xFF) << 8) | ((d & 0xFF) << 0)
static const uint32 _delta_xy_table[8] = {
MKIT(3, 3, -1, -1),
MKIT(3, 7, -1, -3),
MKIT(3, 3, -1, -1),
MKIT(7, 3, -3, -1),
MKIT(3, 3, -1, -1),
MKIT(3, 7, -1, -3),
MKIT(3, 3, -1, -1),
MKIT(7, 3, -3, -1),
};
#undef MKIT
/* Set common defaults. */
this->x_offs = -1;
this->y_offs = -1;
this->x_extent = 3;
this->y_extent = 3;
this->z_extent = 6;
this->x_bb_offs = 0;
this->y_bb_offs = 0;
uint32 x = _delta_xy_table[direction];
this->x_offs = GB(x, 0, 8);
this->y_offs = GB(x, 8, 8);
this->x_extent = GB(x, 16, 8);
this->y_extent = GB(x, 24, 8);
this->z_extent = 6;
if (!IsDiagonalDirection(direction)) {
static const int _sign_table[] =
{
// x, y
-1, -1, // DIR_N
-1, 1, // DIR_E
1, 1, // DIR_S
1, -1, // DIR_W
};
int half_shorten = (VEHICLE_LENGTH - this->gcache.cached_veh_length) / 2;
/* For all straight directions, move the bound box to the centre of the vehicle, but keep the size. */
this->x_offs -= half_shorten * _sign_table[direction];
this->y_offs -= half_shorten * _sign_table[direction + 1];
this->x_extent += this->x_bb_offs = half_shorten * _sign_table[direction];
this->y_extent += this->y_bb_offs = half_shorten * _sign_table[direction + 1];
} else {
switch (direction) {
/* Shorten southern corner of the bounding box according the vehicle length
* and center the bounding box on the vehicle. */
case DIR_NE:
this->x_offs = 1 - (this->gcache.cached_veh_length + 1) / 2;
this->x_extent = this->gcache.cached_veh_length - 1;
this->x_bb_offs = -1;
break;
case DIR_NW:
this->y_offs = 1 - (this->gcache.cached_veh_length + 1) / 2;
this->y_extent = this->gcache.cached_veh_length - 1;
this->y_bb_offs = -1;
break;
/* Move northern corner of the bounding box down according to vehicle length
* and center the bounding box on the vehicle. */
case DIR_SW:
this->x_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH;
this->x_extent = VEHICLE_LENGTH - 1;
this->x_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1;
break;
case DIR_SE:
this->y_offs = 1 + (this->gcache.cached_veh_length + 1) / 2 - VEHICLE_LENGTH;
this->y_extent = VEHICLE_LENGTH - 1;
this->y_bb_offs = VEHICLE_LENGTH - this->gcache.cached_veh_length - 1;
break;
default:
NOT_REACHED();
}
}
}
/** Mark a train as stuck and stop it if it isn't stopped right now. */
@ -1478,7 +1516,7 @@ static void UpdateStatusAfterSwap(Train *v)
v->UpdateViewport(true, true);
}
static void ReverseTrainSwapVeh(Train *v, int l, int r)
void ReverseTrainSwapVeh(Train *v, int l, int r)
{
Train *a, *b;
@ -1626,7 +1664,7 @@ static void AdvanceWagonsBeforeSwap(Train *v)
last = last->Previous();
first = first->Next();
int differential = base->gcache.cached_veh_length - last->gcache.cached_veh_length;
int differential = base->CalcNextVehicleOffset() - last->CalcNextVehicleOffset();
/* do not update images now
* negative differential will be handled in AdvanceWagonsAfterSwap() */
@ -1686,7 +1724,7 @@ static void AdvanceWagonsAfterSwap(Train *v)
last = last->Previous();
first = first->Next();
int differential = last->gcache.cached_veh_length - base->gcache.cached_veh_length;
int differential = last->CalcNextVehicleOffset() - base->CalcNextVehicleOffset();
/* do not update images now */
for (int i = 0; i < differential; i++) TrainController(first, (nomove ? last->Next() : NULL));
@ -1697,7 +1735,7 @@ static void AdvanceWagonsAfterSwap(Train *v)
}
static void ReverseTrainDirection(Train *v)
void ReverseTrainDirection(Train *v)
{
if (IsRailDepotTile(v->tile)) {
InvalidateWindowData(WC_VEHICLE_DEPOT, v->tile);
@ -2840,7 +2878,8 @@ static Vehicle *FindTrainCollideEnum(Vehicle *v, void *data)
if (hash & ~15) return NULL;
/* Slower check using multiplication */
if (x_diff * x_diff + y_diff * y_diff > 25) return NULL;
int min_diff = (Train::From(v)->gcache.cached_veh_length + 1) / 2 + (tcc->v->gcache.cached_veh_length + 1) / 2 - 1;
if (x_diff * x_diff + y_diff * y_diff > min_diff * min_diff) return NULL;
/* Happens when there is a train under bridge next to bridge head */
if (abs(v->z_pos - tcc->v->z_pos) > 5) return NULL;
@ -2906,7 +2945,14 @@ static Vehicle *CheckTrainAtSignal(Vehicle *v, void *data)
return t;
}
static void TrainController(Train *v, Vehicle *nomove)
/**
* Move a vehicle chain one movement stop forwards.
* @param v First vehicle to move.
* @param nomove Stop moving this and all following vehicles.
* @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic.
* @return True if the vehicle could be moved forward, false otherwise.
*/
bool TrainController(Train *v, Vehicle *nomove, bool reverse)
{
Train *first = v->First();
Train *prev;
@ -2930,7 +2976,7 @@ static void TrainController(Train *v, Vehicle *nomove)
/* Not inside depot */
/* Reverse when we are at the end of the track already, do not move to the new position */
if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v)) return;
if (v->IsFrontEngine() && !TrainCheckIfLineEnds(v, reverse)) return false;
uint32 r = VehicleEnterTile(v, gp.new_tile, gp.x, gp.y);
if (HasBit(r, VETS_CANNOT_ENTER)) {
@ -2997,13 +3043,13 @@ static void TrainController(Train *v, Vehicle *nomove)
Trackdir i = FindFirstTrackdir(trackdirbits);
/* Don't handle stuck trains here. */
if (HasBit(v->flags, VRF_TRAIN_STUCK)) return;
if (HasBit(v->flags, VRF_TRAIN_STUCK)) return false;
if (!HasSignalOnTrackdir(gp.new_tile, ReverseTrackdir(i))) {
v->cur_speed = 0;
v->subspeed = 0;
v->progress = 255 - 100;
if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return;
if (!_settings_game.pf.reverse_at_signals || ++v->wait_counter < _settings_game.pf.wait_oneway_signal * 20) return false;
} else if (HasSignalOnTrackdir(gp.new_tile, i)) {
v->cur_speed = 0;
v->subspeed = 0;
@ -3015,7 +3061,7 @@ static void TrainController(Train *v, Vehicle *nomove)
exitdir = ReverseDiagDir(exitdir);
/* check if a train is waiting on the other side */
if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return;
if (!HasVehicleOnPos(o_tile, &exitdir, &CheckTrainAtSignal)) return false;
}
}
@ -3026,7 +3072,7 @@ static void TrainController(Train *v, Vehicle *nomove)
if (!_settings_game.pf.reverse_at_signals && !HasOnewaySignalBlockingTrackdir(gp.new_tile, i) &&
UpdateSignalsOnSegment(v->tile, enterdir, v->owner) == SIGSEG_PBS) {
v->wait_counter = 0;
return;
return false;
}
goto reverse_train_direction;
} else {
@ -3209,17 +3255,21 @@ static void TrainController(Train *v, Vehicle *nomove)
if (direction_changed) first->tcache.cached_max_curve_speed = first->GetCurveSpeedLimit();
return;
return true;
invalid_rail:
/* We've reached end of line?? */
if (prev != NULL) error("Disconnecting train");
reverse_train_direction:
v->wait_counter = 0;
v->cur_speed = 0;
v->subspeed = 0;
ReverseTrainDirection(v);
if (reverse) {
v->wait_counter = 0;
v->cur_speed = 0;
v->subspeed = 0;
ReverseTrainDirection(v);
}
return false;
}
/**
@ -3389,9 +3439,10 @@ static const uint16 _breakdown_speeds[16] = {
*
* @param v front train engine
* @param signal not line end, just a red signal
* @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic.
* @return true iff we did NOT have to reverse
*/
static bool TrainApproachingLineEnd(Train *v, bool signal)
static bool TrainApproachingLineEnd(Train *v, bool signal, bool reverse)
{
/* Calc position within the current tile */
uint x = v->x_pos & 0xF;
@ -3413,13 +3464,12 @@ static bool TrainApproachingLineEnd(Train *v, bool signal)
/* Do not reverse when approaching red signal. Make sure the vehicle's front
* does not cross the tile boundary when we do reverse, but as the vehicle's
* location is based on their center, use half a vehicle's length as offset.
* Actually, the center of all vehicles is half a normal vehicle's length
* from the front of the vehicle, so even in case the vehicle is 1/8th
* long, the center is still at 1/2 of VEHICLE_LENGTH. Basically FS#3569. */
if (!signal && x + VEHICLE_LENGTH / 2 >= TILE_SIZE) {
* Multiply the half-length by two for straight directions to compensate that
* we only get odd x offsets there. */
if (!signal && x + (v->gcache.cached_veh_length + 1) / 2 * (IsDiagonalDirection(v->direction) ? 1 : 2) >= TILE_SIZE) {
/* we are too near the tile end, reverse now */
v->cur_speed = 0;
ReverseTrainDirection(v);
if (reverse) ReverseTrainDirection(v);
return false;
}
@ -3491,9 +3541,10 @@ static TileIndex TrainApproachingCrossingTile(const Train *v)
* Checks for line end. Also, bars crossing at next tile if needed
*
* @param v vehicle we are checking
* @param reverse Set to false to not execute the vehicle reversing. This does not change any other logic.
* @return true iff we did NOT have to reverse
*/
static bool TrainCheckIfLineEnds(Train *v)
static bool TrainCheckIfLineEnds(Train *v, bool reverse)
{
/* First, handle broken down train */
@ -3531,11 +3582,11 @@ static bool TrainCheckIfLineEnds(Train *v)
/* no suitable trackbits at all || unusable rail (wrong type or owner) */
if (bits == TRACK_BIT_NONE || !CheckCompatibleRail(v, tile)) {
return TrainApproachingLineEnd(v, false);
return TrainApproachingLineEnd(v, false, reverse);
}
/* approaching red signal */
if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true);
if ((trackdirbits & red_signals) != 0) return TrainApproachingLineEnd(v, true, reverse);
/* approaching a rail/road crossing? then make it red */
if (IsLevelCrossingTile(tile)) MaybeBarCrossingWithSound(tile);

View File

@ -962,7 +962,7 @@ static void DoDrawVehicle(const Vehicle *v)
}
AddSortableSpriteToDraw(image, pal, v->x_pos + v->x_offs, v->y_pos + v->y_offs,
v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed);
v->x_extent, v->y_extent, v->z_extent, v->z_pos, shadowed, v->x_bb_offs, v->y_bb_offs);
}
/**
@ -2213,6 +2213,11 @@ void Vehicle::ShowVisualEffect() const
continue;
}
/* The effect offset is relative to a point 4 units behind the vehicle's
* front (which is the center of an 8/8 vehicle). Shorter vehicles need a
* correction factor. */
if (v->type == VEH_TRAIN) effect_offset += (VEHICLE_LENGTH - Train::From(v)->gcache.cached_veh_length) / 2;
int x = _vehicle_smoke_pos[v->direction] * effect_offset;
int y = _vehicle_smoke_pos[(v->direction + 2) % 8] * effect_offset;

View File

@ -194,6 +194,8 @@ public:
byte x_extent; ///< x-extent of vehicle bounding box
byte y_extent; ///< y-extent of vehicle bounding box
byte z_extent; ///< z-extent of vehicle bounding box
int8 x_bb_offs; ///< x offset of vehicle bounding box
int8 y_bb_offs; ///< y offset of vehicle bounding box
int8 x_offs; ///< x offset for vehicle sprite
int8 y_offs; ///< y offset for vehicle sprite
EngineID engine_type; ///< The type of engine used for this vehicle.