1
0
Fork 0

(svn r11900) -Fix: set correctly crossing state after train reversal, train leaving crossing, train crash

Fixes several ways to leave crossing red forever or to leave it unbarred when there is a train on crossing
release/0.6
smatz 2008-01-17 17:57:39 +00:00
parent 35f3421842
commit c74cf439fa
1 changed files with 149 additions and 47 deletions

View File

@ -53,6 +53,7 @@
static bool TrainCheckIfLineEnds(Vehicle *v); static bool TrainCheckIfLineEnds(Vehicle *v);
static void TrainController(Vehicle *v, bool update_image); static void TrainController(Vehicle *v, bool update_image);
static TileIndex TrainApproachingCrossingTile(const Vehicle *v);
static const byte _vehicle_initial_x_fract[4] = {10, 8, 4, 8}; static const byte _vehicle_initial_x_fract[4] = {10, 8, 4, 8};
static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10}; static const byte _vehicle_initial_y_fract[4] = { 8, 4, 8, 10};
@ -1624,23 +1625,81 @@ static void ReverseTrainSwapVeh(Vehicle *v, int l, int r)
TrainPowerChanged(v); TrainPowerChanged(v);
} }
/* Check if the vehicle is a train and is on the tile we are testing */
static void *TestTrainOnCrossing(Vehicle *v, void *data) /**
* Check if the vehicle is a train
* @param v vehicle on tile
* @return v if it is a train, NULL otherwise
*/
static void *TrainOnTileEnum(Vehicle *v, void *)
{ {
if (v->type != VEH_TRAIN) return NULL; return (v->type == VEH_TRAIN) ? v : NULL;
}
/**
* Checks if a train is approaching a rail-road crossing
* @param v vehicle on tile
* @param data tile with crossing we are testing
* @return v if it is approaching a crossing, NULL otherwise
*/
static void *TrainApproachingCrossingEnum(Vehicle *v, void *data)
{
/* not a train || not front engine || crashed */
if (v->type != VEH_TRAIN || !IsFrontEngine(v) || v->vehstatus & VS_CRASHED) return NULL;
TileIndex tile = *(TileIndex*)data;
if (TrainApproachingCrossingTile(v) != tile) return NULL;
return v; return v;
} }
static void DisableTrainCrossing(TileIndex tile)
/**
* Finds a vehicle approaching rail-road crossing
* @param tile tile to test
* @return pointer to vehicle approaching the crossing
* @pre tile is a rail-road crossing
*/
static Vehicle *TrainApproachingCrossing(TileIndex tile)
{ {
if (IsLevelCrossingTile(tile) && assert(IsLevelCrossingTile(tile));
IsCrossingBarred(tile) &&
VehicleFromPos(tile, NULL, &TestTrainOnCrossing) == NULL) { // empty? DiagDirection dir = AxisToDiagDir(OtherAxis(GetCrossingRoadAxis(tile)));
UnbarCrossing(tile); TileIndex tile_from = tile + TileOffsByDiagDir(dir);
MarkTileDirtyByTile(tile);
} Vehicle *v = (Vehicle *)VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum);
if (v != NULL) return v;
dir = ReverseDiagDir(dir);
tile_from = tile + TileOffsByDiagDir(dir);
return (Vehicle *)VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum);
} }
/**
* Sets correct crossing state
* @param tile tile to update
* @pre tile is a rail-road crossing
*/
void UpdateTrainCrossing(TileIndex tile)
{
assert(IsLevelCrossingTile(tile));
UnbarCrossing(tile);
/* train on crossing || train approaching crossing */
if (VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile)) {
BarCrossing(tile);
}
MarkTileDirtyByTile(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. * Needs to be called once before the train is reversed, and once after it.
@ -1688,14 +1747,7 @@ static void ReverseTrainDirection(Vehicle *v)
} }
/* Check if we were approaching a rail/road-crossing */ /* Check if we were approaching a rail/road-crossing */
{ TileIndex crossing = TrainApproachingCrossingTile(v);
/* Determine the diagonal direction in which we will exit this tile */
DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
/* Calculate next tile */
TileIndex tile = v->tile + TileOffsByDiagDir(dir);
/* Check if the train left a rail/road-crossing */
DisableTrainCrossing(tile);
}
/* count number of vehicles */ /* count number of vehicles */
int r = 0; ///< number of vehicles - 1 int r = 0; ///< number of vehicles - 1
@ -1719,6 +1771,13 @@ static void ReverseTrainDirection(Vehicle *v)
for (Vehicle *u = v; u != NULL; u = u->Next()) { u->cur_image = u->GetImage(u->direction); } for (Vehicle *u = v; u != NULL; u = u->Next()) { u->cur_image = u->GetImage(u->direction); }
ClrBit(v->u.rail.flags, VRF_REVERSING); ClrBit(v->u.rail.flags, VRF_REVERSING);
/* update crossing we were approaching */
if (crossing != INVALID_TILE) UpdateTrainCrossing(crossing);
/* maybe we are approaching crossing now, after reversal */
crossing = TrainApproachingCrossingTile(v);
if (crossing != INVALID_TILE) UpdateTrainCrossing(crossing);
} }
/** Reverse train. /** Reverse train.
@ -2777,6 +2836,9 @@ static void SetVehicleCrashed(Vehicle *v)
{ {
if (v->u.rail.crash_anim_pos != 0) return; if (v->u.rail.crash_anim_pos != 0) return;
/* we may need to update crossing we were approaching */
TileIndex crossing = TrainApproachingCrossingTile(v);
v->u.rail.crash_anim_pos++; v->u.rail.crash_anim_pos++;
InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR); InvalidateWindowWidget(WC_VEHICLE_VIEW, v->index, STATUS_BAR);
@ -2792,6 +2854,9 @@ static void SetVehicleCrashed(Vehicle *v)
v->vehstatus |= VS_CRASHED; v->vehstatus |= VS_CRASHED;
MarkSingleVehicleDirty(v); MarkSingleVehicleDirty(v);
END_ENUM_WAGONS(v) END_ENUM_WAGONS(v)
/* must be updated after the train has been marked crashed */
if (crossing != INVALID_TILE) UpdateTrainCrossing(crossing);
} }
static uint CountPassengersInTrain(const Vehicle* v) static uint CountPassengersInTrain(const Vehicle* v)
@ -2899,7 +2964,7 @@ static void TrainController(Vehicle *v, bool update_image)
/* For every vehicle after and including the given vehicle */ /* For every vehicle after and including the given vehicle */
for (prev = v->Previous(); v != NULL; prev = v, v = v->Next()) { for (prev = v->Previous(); v != NULL; prev = v, v = v->Next()) {
DiagDirection enterdir = DIAGDIR_BEGIN; DiagDirection enterdir = DIAGDIR_BEGIN;
bool update_signals = false; bool update_signals_crossing = false; // will we update signals or crossing state?
BeginVehicleMove(v); BeginVehicleMove(v);
GetNewVehiclePosResult gp = GetNewVehiclePos(v); GetNewVehiclePosResult gp = GetNewVehiclePos(v);
@ -3022,11 +3087,6 @@ static void TrainController(Vehicle *v, bool update_image)
goto invalid_rail; goto invalid_rail;
} }
if (IsLevelCrossingTile(v->tile) && v->Next() == NULL) {
UnbarCrossing(v->tile);
MarkTileDirtyByTile(v->tile);
}
if (IsFrontEngine(v)) v->load_unload_time_rem = 0; if (IsFrontEngine(v)) v->load_unload_time_rem = 0;
if (!HasBit(r, VETS_ENTERED_WORMHOLE)) { if (!HasBit(r, VETS_ENTERED_WORMHOLE)) {
@ -3042,7 +3102,7 @@ static void TrainController(Vehicle *v, bool update_image)
/* We need to update signal status, but after the vehicle position hash /* We need to update signal status, but after the vehicle position hash
* has been updated by AfterSetTrainPos() */ * has been updated by AfterSetTrainPos() */
update_signals = true; update_signals_crossing = true;
if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir); if (prev == NULL) AffectSpeedByDirChange(v, chosen_dir);
@ -3081,12 +3141,15 @@ static void TrainController(Vehicle *v, bool update_image)
AffectSpeedByZChange(v, old_z); AffectSpeedByZChange(v, old_z);
} }
if (update_signals) { if (update_signals_crossing) {
if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir); if (IsFrontEngine(v)) TrainMovedChangeSignals(gp.new_tile, enterdir);
/* Signals can only change when the first /* Signals can only change when the first
* (above) or the last vehicle moves. */ * (above) or the last vehicle moves. */
if (v->Next() == NULL) TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir)); if (v->Next() == NULL) {
TrainMovedChangeSignals(gp.old_tile, ReverseDiagDir(enterdir));
if (IsLevelCrossingTile(gp.old_tile)) UpdateTrainCrossing(gp.old_tile);
}
} }
} }
return; return;
@ -3147,9 +3210,8 @@ static void DeleteLastWagon(Vehicle *v)
delete v; delete v;
v = NULL; // make sure nobody will won't try to read 'v' anymore v = NULL; // make sure nobody will won't try to read 'v' anymore
/* Check if the wagon was on a road/rail-crossing and disable it if no /* check if the wagon was on a road/rail-crossing */
* others are on it */ if (IsLevelCrossingTile(tile)) UpdateTrainCrossing(tile);
DisableTrainCrossing(tile);
/* Update signals */ /* Update signals */
if (IsTileType(tile, MP_TUNNELBRIDGE) || IsTileDepotType(tile, TRANSPORT_RAIL)) { if (IsTileType(tile, MP_TUNNELBRIDGE) || IsTileDepotType(tile, TRANSPORT_RAIL)) {
@ -3295,6 +3357,61 @@ static bool TrainApproachingLineEnd(Vehicle *v, bool signal)
} }
/**
* Determines whether train would like to leave the tile
* @param v train to test
* @return true iff vehicle is NOT entering or inside a depot or tunnel/bridge
*/
static bool TrainCanLeaveTile(const Vehicle *v)
{
/* Exit if inside a tunnel/bridge or a depot */
if (v->u.rail.track == TRACK_BIT_WORMHOLE || v->u.rail.track == TRACK_BIT_DEPOT) return false;
TileIndex tile = v->tile;
/* entering a tunnel/bridge? */
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
DiagDirection dir = GetTunnelBridgeDirection(tile);
if (DiagDirToDir(dir) == v->direction) return false;
}
/* entering a depot? */
if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile));
if (DiagDirToDir(dir) == v->direction) return false;
}
return true;
}
/**
* Determines whether train is approaching a rail-road crossing
* (thus making it barred)
* @param v front engine of train
* @return TileIndex of crossing the train is approaching, else INVALID_TILE
* @pre v in non-crashed front engine
*/
static TileIndex TrainApproachingCrossingTile(const Vehicle *v)
{
assert(IsFrontEngine(v));
assert(!(v->vehstatus & VS_CRASHED));
if (!TrainCanLeaveTile(v)) return INVALID_TILE;
DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
TileIndex tile = v->tile + TileOffsByDiagDir(dir);
/* not a crossing || wrong axis || wrong railtype || wrong owner */
if (!IsLevelCrossingTile(tile) || DiagDirToAxis(dir) == GetCrossingRoadAxis(tile) ||
!CheckCompatibleRail(v, tile) || GetTileOwner(tile) != v->owner) {
return INVALID_TILE;
}
return tile;
}
/** /**
* Checks for line end. Also, bars crossing at next tile if needed * Checks for line end. Also, bars crossing at next tile if needed
* *
@ -3315,27 +3432,12 @@ static bool TrainCheckIfLineEnds(Vehicle *v)
v->vehstatus &= ~VS_TRAIN_SLOWING; v->vehstatus &= ~VS_TRAIN_SLOWING;
} }
/* Exit if inside a tunnel/bridge or a depot */ if (!TrainCanLeaveTile(v)) return true;
if (v->u.rail.track == TRACK_BIT_WORMHOLE || v->u.rail.track == TRACK_BIT_DEPOT) return true;
TileIndex tile = v->tile;
/* entering a tunnel/bridge? */
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
DiagDirection dir = GetTunnelBridgeDirection(tile);
if (DiagDirToDir(dir) == v->direction) return true;
}
/* entering a depot? */
if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
DiagDirection dir = ReverseDiagDir(GetRailDepotDirection(tile));
if (DiagDirToDir(dir) == v->direction) return true;
}
/* Determine the non-diagonal direction in which we will exit this tile */ /* Determine the non-diagonal direction in which we will exit this tile */
DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track); DiagDirection dir = TrainExitDir(v->direction, v->u.rail.track);
/* Calculate next tile */ /* Calculate next tile */
tile += TileOffsByDiagDir(dir); TileIndex tile = v->tile + TileOffsByDiagDir(dir);
/* Determine the track status on the next tile */ /* Determine the track status on the next tile */
uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _reachable_tracks[dir]; uint32 ts = GetTileTrackStatus(tile, TRANSPORT_RAIL, 0) & _reachable_tracks[dir];