(svn r11802) -Fix [FS#716]: do not crash trains when leaving depot to a very long track

-Codechange: use dedicated pathfinder for signal updating, resulting in better performance and possible future improvements
This commit is contained in:
smatz
2008-01-09 23:00:59 +00:00
parent c004cc1fd9
commit bc4ed3eafd
16 changed files with 643 additions and 244 deletions

View File

@@ -42,6 +42,7 @@
#include "window_func.h"
#include "vehicle_func.h"
#include "sound_func.h"
#include "signal_func.h"
const byte _track_sloped_sprites[14] = {
@@ -84,7 +85,7 @@ const byte _track_sloped_sprites[14] = {
*/
static void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
void *EnsureNoTrainOnTrackProc(Vehicle *v, void *data)
{
TrackBits rail_bits = *(TrackBits *)data;
@@ -740,7 +741,7 @@ CommandCost CmdBuildTrainDepot(TileIndex tile, uint32 flags, uint32 p1, uint32 p
d->town_index = ClosestTownFromTile(tile, (uint)-1)->index;
UpdateSignalsOnSegment(tile, dir);
UpdateSignalsOnSegment(tile, INVALID_DIAGDIR);
YapfNotifyTrackLayoutChange(tile, TrackdirToTrack(DiagdirToDiagTrackdir(dir)));
d_auto_delete.Detach();
}
@@ -1846,227 +1847,6 @@ void DrawDefaultWaypointSprite(int x, int y, RailType railtype)
DrawTileSequence(x, y, dts->ground_sprite + offset, dts->seq, 0);
}
struct SetSignalsData {
int cur;
int cur_stack;
bool stop;
bool has_presignal;
/* presignal info */
int presignal_exits;
int presignal_exits_free;
/* these are used to keep track of the signals that change. */
TrackdirByte bit[NUM_SSD_ENTRY];
TileIndex tile[NUM_SSD_ENTRY];
/* these are used to keep track of the stack that modifies presignals recursively */
TileIndex next_tile[NUM_SSD_STACK];
DiagDirectionByte next_dir[NUM_SSD_STACK];
};
static bool SetSignalsEnumProc(TileIndex tile, void* data, Trackdir trackdir, uint length, byte* state)
{
SetSignalsData* ssd = (SetSignalsData*)data;
Track track = TrackdirToTrack(trackdir);
if (!IsTileType(tile, MP_RAILWAY)) return false;
/* the tile has signals? */
if (HasSignalOnTrack(tile, track)) {
if (HasSignalOnTrackdir(tile, ReverseTrackdir(trackdir))) {
/* yes, add the signal to the list of signals */
if (ssd->cur != NUM_SSD_ENTRY) {
ssd->tile[ssd->cur] = tile; // remember the tile index
ssd->bit[ssd->cur] = trackdir; // and the controlling bit number
ssd->cur++;
}
/* remember if this block has a presignal. */
ssd->has_presignal |= IsPresignalEntry(tile, track);
}
if (HasSignalOnTrackdir(tile, trackdir) && IsPresignalExit(tile, track)) {
/* this is an exit signal that points out from the segment */
ssd->presignal_exits++;
if (GetSignalStateByTrackdir(tile, trackdir) != SIGNAL_STATE_RED)
ssd->presignal_exits_free++;
}
return true;
} else if (IsTileDepotType(tile, TRANSPORT_RAIL)) {
return true; // don't look further if the tile is a depot
}
return false;
}
static void *SignalVehicleCheckProc(Vehicle *v, void *data)
{
uint track = *(uint*)data;
if (v->type != VEH_TRAIN) return NULL;
/* Are we on the same piece of track? */
if (track & v->u.rail.track * 0x101) return v;
return NULL;
}
/* Special check for SetSignalsAfterProc, to see if there is a vehicle on this tile */
static bool SignalVehicleCheck(TileIndex tile, uint track)
{
if (IsTileType(tile, MP_TUNNELBRIDGE)) {
/* Locate vehicles in tunnels or on bridges */
return GetVehicleTunnelBridge(tile, GetOtherTunnelBridgeEnd(tile)) != NULL;
} else {
return VehicleFromPos(tile, &track, &SignalVehicleCheckProc) != NULL;
}
}
static void SetSignalsAfterProc(TrackPathFinder *tpf)
{
SetSignalsData *ssd = (SetSignalsData*)tpf->userdata;
const TrackPathFinderLink* link;
uint offs;
uint i;
ssd->stop = false;
/* Go through all the PF tiles */
for (i = 0; i < lengthof(tpf->hash_head); i++) {
/* Empty hash item */
if (tpf->hash_head[i] == 0) continue;
/* If 0x8000 is not set, there is only 1 item */
if (!(tpf->hash_head[i] & 0x8000)) {
/* Check if there is a vehicle on this tile */
if (SignalVehicleCheck(tpf->hash_tile[i], tpf->hash_head[i])) {
ssd->stop = true;
return;
}
} else {
/* There are multiple items, where hash_tile points to the first item in the list */
offs = tpf->hash_tile[i];
do {
/* Find the next item */
link = PATHFIND_GET_LINK_PTR(tpf, offs);
/* Check if there is a vehicle on this tile */
if (SignalVehicleCheck(link->tile, link->flags)) {
ssd->stop = true;
return;
}
/* Goto the next item */
} while ((offs = link->next) != 0xFFFF);
}
}
}
static void ChangeSignalStates(SetSignalsData *ssd)
{
int i;
/* thinking about presignals...
* the presignal is green if,
* if no train is in the segment AND
* there is at least one green exit signal OR
* there are no exit signals in the segment */
/* then mark the signals in the segment accordingly */
for (i = 0; i != ssd->cur; i++) {
TileIndex tile = ssd->tile[i];
byte bit = SignalAgainstTrackdir(ssd->bit[i]);
uint signals = GetSignalStates(tile);
Track track = TrackdirToTrack(ssd->bit[i]);
/* presignals don't turn green if there is at least one presignal exit and none are free */
if (IsPresignalEntry(tile, track)) {
int ex = ssd->presignal_exits, exfree = ssd->presignal_exits_free;
/* subtract for dual combo signals so they don't count themselves */
if (IsPresignalExit(tile, track) && HasSignalOnTrackdir(tile, ssd->bit[i])) {
ex--;
if (GetSignalStateByTrackdir(tile, ssd->bit[i]) != SIGNAL_STATE_RED) exfree--;
}
/* if we have exits and none are free, make red. */
if (ex && !exfree) goto make_red;
}
/* check if the signal is unaffected. */
if (ssd->stop) {
make_red:
/* turn red */
if ((bit & signals) == 0) continue;
} else {
/* turn green */
if ((bit & signals) != 0) continue;
}
/* Update signals on the other side of this exit-combo signal; it changed. */
if (IsPresignalExit(tile, track)) {
if (ssd->cur_stack != NUM_SSD_STACK) {
ssd->next_tile[ssd->cur_stack] = tile;
ssd->next_dir[ssd->cur_stack] = TrackdirToExitdir(ssd->bit[i]);
ssd->cur_stack++;
} else {
DEBUG(misc, 0, "NUM_SSD_STACK too small"); /// @todo WTF is this???
}
}
/* it changed, so toggle it */
SetSignalStates(tile, signals ^ bit);
MarkTileDirtyByTile(tile);
}
}
bool UpdateSignalsOnSegment(TileIndex tile, DiagDirection direction)
{
SetSignalsData ssd;
int result = -1;
ssd.cur_stack = 0;
for (;;) {
/* go through one segment and update all signals pointing into that segment. */
ssd.cur = ssd.presignal_exits = ssd.presignal_exits_free = 0;
ssd.has_presignal = false;
FollowTrack(tile, 0xC000 | TRANSPORT_RAIL, 0, direction, SetSignalsEnumProc, SetSignalsAfterProc, &ssd);
ChangeSignalStates(&ssd);
/* remember the result only for the first iteration. */
if (result < 0) {
/* stay in depot while segment is occupied or while all presignal exits are blocked */
result = ssd.stop || (ssd.presignal_exits > 0 && ssd.presignal_exits_free == 0);
}
/* if any exit signals were changed, we need to keep going to modify the stuff behind those. */
if (ssd.cur_stack == 0) break;
/* one or more exit signals were changed, so we need to update another segment too. */
tile = ssd.next_tile[--ssd.cur_stack];
direction = ssd.next_dir[ssd.cur_stack];
}
return result != 0;
}
void SetSignalsOnBothDir(TileIndex tile, byte track)
{
static const DiagDirection _search_dir_1[] = {
DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_NE, DIAGDIR_SE, DIAGDIR_SW, DIAGDIR_SE
};
static const DiagDirection _search_dir_2[] = {
DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NW, DIAGDIR_SW, DIAGDIR_NW, DIAGDIR_NE
};
UpdateSignalsOnSegment(tile, _search_dir_1[track]);
UpdateSignalsOnSegment(tile, _search_dir_2[track]);
}
static uint GetSlopeZ_Track(TileIndex tile, uint x, uint y)
{
uint z;