# HG changeset patch # User rubidium # Date 2008-09-07 11:23:10 # Node ID 2e8288935c093a1f1288fb83fcd394b5074d9fe3 # Parent 5b3f8b8c9cc3b8f615a0ff1e5842790401c824dc (svn r14258) -Codechange: rework the way to query the vehicle hash to make sure it always results in the same irregardless of the order of the hash-linked-list. -Fix: desync in PBS reservation following, vehicle flooding and road vehicle overtake/follow code. diff --git a/src/pbs.cpp b/src/pbs.cpp --- a/src/pbs.cpp +++ b/src/pbs.cpp @@ -206,12 +206,29 @@ static PBSTileInfo FollowReservation(Own return PBSTileInfo(tile, trackdir, false); } -/** Callback for VehicleFromPos to find a train on a specific track. */ +/** + * Helper struct for finding the best matching vehicle on a specific track. + */ +struct FindTrainOnTrackInfo { + PBSTileInfo res; ///< Information about the track. + Vehicle *best; ///< The currently "best" vehicle we have found. + + /** Init the best location to NULL always! */ + FindTrainOnTrackInfo() : best(NULL) {} +}; + +/** Callback for Has/FindVehicleOnPos to find a train on a specific track. */ static Vehicle *FindTrainOnTrackEnum(Vehicle *v, void *data) { - PBSTileInfo info = *(PBSTileInfo *)data; + FindTrainOnTrackInfo *info = (FindTrainOnTrackInfo *)data; + + if (v->type == VEH_TRAIN && !(v->vehstatus & VS_CRASHED) && HasBit((TrackBits)v->u.rail.track, TrackdirToTrack(info->res.trackdir))) { + v = v->First(); - if (v->type == VEH_TRAIN && !(v->vehstatus & VS_CRASHED) && HasBit((TrackBits)v->u.rail.track, TrackdirToTrack(info.trackdir))) return v; + /* ALWAYS return the lowest ID (anti-desync!) */ + if (info->best == NULL || v->index < info->best->index) info->best = v; + return v; + } return NULL; } @@ -223,7 +240,7 @@ static Vehicle *FindTrainOnTrackEnum(Veh * @param train_on_res Is set to a train we might encounter * @returns The last tile of the reservation or the current train tile if no reservation present. */ -PBSTileInfo FollowTrainReservation(const Vehicle *v, Vehicle **train_on_res) +PBSTileInfo FollowTrainReservation(const Vehicle *v, bool *train_on_res) { assert(v->type == VEH_TRAIN); @@ -232,10 +249,11 @@ PBSTileInfo FollowTrainReservation(const if (IsRailDepotTile(tile) && !GetRailDepotReservation(tile)) return PBSTileInfo(tile, trackdir, false); - PBSTileInfo res = FollowReservation(v->owner, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes, tile, trackdir); - res.okay = IsSafeWaitingPosition(v, res.tile, res.trackdir, true, _settings_game.pf.forbid_90_deg); - if (train_on_res != NULL) *train_on_res = VehicleFromPos(res.tile, &res, FindTrainOnTrackEnum); - return res; + FindTrainOnTrackInfo ftoti; + ftoti.res = FollowReservation(v->owner, GetRailTypeInfo(v->u.rail.railtype)->compatible_railtypes, tile, trackdir); + ftoti.res.okay = IsSafeWaitingPosition(v, ftoti.res.tile, ftoti.res.trackdir, true, _settings_game.pf.forbid_90_deg); + if (train_on_res != NULL) *train_on_res = HasVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum); + return ftoti.res; } /** @@ -256,24 +274,25 @@ Vehicle *GetTrainForReservation(TileInde * have a train on it. We need FollowReservation to ignore one-way signals * here, as one of the two search directions will be the "wrong" way. */ for (int i = 0; i < 2; ++i, trackdir = ReverseTrackdir(trackdir)) { - PBSTileInfo dest = FollowReservation(GetTileOwner(tile), rts, tile, trackdir, true); + FindTrainOnTrackInfo ftoti; + ftoti.res = FollowReservation(GetTileOwner(tile), rts, tile, trackdir, true); - Vehicle *v = VehicleFromPos(dest.tile, &dest, FindTrainOnTrackEnum); - if (v != NULL) return v->First(); + FindVehicleOnPos(ftoti.res.tile, &ftoti, FindTrainOnTrackEnum); + if (ftoti.best != NULL) return ftoti.best; /* Special case for stations: check the whole platform for a vehicle. */ - if (IsRailwayStationTile(dest.tile)) { - TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(dest.trackdir))); - for (TileIndex st_tile = dest.tile + diff; IsCompatibleTrainStationTile(st_tile, dest.tile); st_tile += diff) { - v = VehicleFromPos(st_tile, &dest, FindTrainOnTrackEnum); - if (v != NULL) return v->First(); + if (IsRailwayStationTile(ftoti.res.tile)) { + TileIndexDiff diff = TileOffsByDiagDir(TrackdirToExitdir(ReverseTrackdir(ftoti.res.trackdir))); + for (TileIndex st_tile = ftoti.res.tile + diff; IsCompatibleTrainStationTile(st_tile, ftoti.res.tile); st_tile += diff) { + FindVehicleOnPos(st_tile, &ftoti, FindTrainOnTrackEnum); + if (ftoti.best != NULL) return ftoti.best; } } /* Special case for bridges/tunnels: check the other end as well. */ - if (IsTileType(dest.tile, MP_TUNNELBRIDGE)) { - v = VehicleFromPos(GetOtherTunnelBridgeEnd(dest.tile), &dest, FindTrainOnTrackEnum); - if (v != NULL) return v->First(); + if (IsTileType(ftoti.res.tile, MP_TUNNELBRIDGE)) { + FindVehicleOnPos(GetOtherTunnelBridgeEnd(ftoti.res.tile), &ftoti, FindTrainOnTrackEnum); + if (ftoti.best != NULL) return ftoti.best; } } diff --git a/src/pbs.h b/src/pbs.h --- a/src/pbs.h +++ b/src/pbs.h @@ -27,7 +27,7 @@ struct PBSTileInfo { PBSTileInfo(TileIndex _t, Trackdir _td, bool _okay) : tile(_t), trackdir(_td), okay(_okay) {} }; -PBSTileInfo FollowTrainReservation(const Vehicle *v, Vehicle **train_on_res = NULL); +PBSTileInfo FollowTrainReservation(const Vehicle *v, bool *train_on_res = NULL); bool IsSafeWaitingPosition(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool include_line_end, bool forbid_90deg = false); bool IsWaitingPositionFree(const Vehicle *v, TileIndex tile, Trackdir trackdir, bool forbid_90deg = false); diff --git a/src/rail_cmd.cpp b/src/rail_cmd.cpp --- a/src/rail_cmd.cpp +++ b/src/rail_cmd.cpp @@ -130,7 +130,7 @@ static bool EnsureNoTrainOnTrack(TileInd { TrackBits rail_bits = TrackToTrackBits(track); - return VehicleFromPos(tile, &rail_bits, &EnsureNoTrainOnTrackProc) == NULL; + return !HasVehicleOnPos(tile, &rail_bits, &EnsureNoTrainOnTrackProc); } static bool CheckTrackCombination(TileIndex tile, TrackBits to_build, uint flags) @@ -1334,7 +1334,7 @@ CommandCost CmdConvertRail(TileIndex til SetRailType(tile, totype); MarkTileDirtyByTile(tile); /* update power of train engines on this tile */ - VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); + FindVehicleOnPos(tile, NULL, &UpdateTrainPowerProc); } } @@ -1384,7 +1384,7 @@ CommandCost CmdConvertRail(TileIndex til /* When not coverting rail <-> el. rail, any vehicle cannot be in tunnel/bridge */ if (!IsCompatibleRail(GetRailType(tile), totype) && - GetVehicleTunnelBridge(tile, endtile) != NULL) continue; + !HasVehicleOnTunnelBridge(tile, endtile)) continue; if (flags & DC_EXEC) { Track track = DiagDirToDiagTrack(GetTunnelBridgeDirection(tile)); @@ -1398,8 +1398,8 @@ CommandCost CmdConvertRail(TileIndex til SetRailType(tile, totype); SetRailType(endtile, totype); - VehicleFromPos(tile, NULL, &UpdateTrainPowerProc); - VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc); + FindVehicleOnPos(tile, NULL, &UpdateTrainPowerProc); + FindVehicleOnPos(endtile, NULL, &UpdateTrainPowerProc); YapfNotifyTrackLayoutChange(tile, track); YapfNotifyTrackLayoutChange(endtile, track); diff --git a/src/road_cmd.cpp b/src/road_cmd.cpp --- a/src/road_cmd.cpp +++ b/src/road_cmd.cpp @@ -223,7 +223,7 @@ static CommandCost RemoveRoad(TileIndex case MP_TUNNELBRIDGE: if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) return CMD_ERROR; - if (GetVehicleTunnelBridge(tile, GetOtherTunnelBridgeEnd(tile)) != NULL) return CMD_ERROR; + if (HasVehicleOnTunnelBridge(tile, GetOtherTunnelBridgeEnd(tile))) return CMD_ERROR; break; default: @@ -589,7 +589,7 @@ CommandCost CmdBuildRoad(TileIndex tile, if (MirrorRoadBits(DiagDirToRoadBits(GetTunnelBridgeDirection(tile))) != pieces) return CMD_ERROR; if (HasTileRoadType(tile, rt)) return_cmd_error(STR_1007_ALREADY_BUILT); /* Don't allow adding roadtype to the bridge/tunnel when vehicles are already driving on it */ - if (GetVehicleTunnelBridge(tile, GetOtherTunnelBridgeEnd(tile)) != NULL) return CMD_ERROR; + if (HasVehicleOnTunnelBridge(tile, GetOtherTunnelBridgeEnd(tile))) return CMD_ERROR; break; default: { diff --git a/src/roadveh_cmd.cpp b/src/roadveh_cmd.cpp --- a/src/roadveh_cmd.cpp +++ b/src/roadveh_cmd.cpp @@ -646,7 +646,7 @@ static void RoadVehCheckTrainCrash(Vehic if (!IsLevelCrossingTile(tile)) continue; - if (VehicleFromPosXY(v->x_pos, v->y_pos, u, EnumCheckRoadVehCrashTrain) != NULL) { + if (HasVehicleOnPosXY(v->x_pos, v->y_pos, u, EnumCheckRoadVehCrashTrain)) { RoadVehCrash(v); return; } @@ -725,7 +725,9 @@ static void StartRoadVehSound(const Vehi struct RoadVehFindData { int x; int y; - const Vehicle* veh; + const Vehicle *veh; + Vehicle *best; + uint best_diff; Direction dir; }; @@ -734,28 +736,34 @@ static Vehicle *EnumCheckRoadVehClose(Ve static const int8 dist_x[] = { -4, -8, -4, -1, 4, 8, 4, 1 }; static const int8 dist_y[] = { -4, -1, 4, 8, 4, 1, -4, -8 }; - const RoadVehFindData *rvf = (RoadVehFindData*)data; + RoadVehFindData *rvf = (RoadVehFindData*)data; short x_diff = v->x_pos - rvf->x; short y_diff = v->y_pos - rvf->y; - return - v->type == VEH_ROAD && - !v->IsInDepot() && - abs(v->z_pos - rvf->veh->z_pos) < 6 && - v->direction == rvf->dir && - rvf->veh->First() != v->First() && - (dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) && - (dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) && - (dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) && - (dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0)) ? - v : NULL; + if (v->type == VEH_ROAD && + !v->IsInDepot() && + abs(v->z_pos - rvf->veh->z_pos) < 6 && + v->direction == rvf->dir && + rvf->veh->First() != v->First() && + (dist_x[v->direction] >= 0 || (x_diff > dist_x[v->direction] && x_diff <= 0)) && + (dist_x[v->direction] <= 0 || (x_diff < dist_x[v->direction] && x_diff >= 0)) && + (dist_y[v->direction] >= 0 || (y_diff > dist_y[v->direction] && y_diff <= 0)) && + (dist_y[v->direction] <= 0 || (y_diff < dist_y[v->direction] && y_diff >= 0))) { + uint diff = abs(x_diff) + abs(y_diff); + + if (diff < rvf->best_diff || (diff == rvf->best_diff && v->index < rvf->best->index)) { + rvf->best = v; + rvf->best_diff = diff; + } + } + + return NULL; } -static Vehicle* RoadVehFindCloseTo(Vehicle* v, int x, int y, Direction dir) +static Vehicle *RoadVehFindCloseTo(Vehicle *v, int x, int y, Direction dir) { RoadVehFindData rvf; - Vehicle *u; Vehicle *front = v->First(); if (front->u.road.reverse_ctr != 0) return NULL; @@ -764,25 +772,27 @@ static Vehicle* RoadVehFindCloseTo(Vehic rvf.y = y; rvf.dir = dir; rvf.veh = v; + rvf.best_diff = UINT_MAX; + if (front->u.road.state == RVSB_WORMHOLE) { - u = VehicleFromPos(v->tile, &rvf, EnumCheckRoadVehClose); - if (u == NULL) u = (Vehicle*)VehicleFromPos(GetOtherTunnelBridgeEnd(v->tile), &rvf, EnumCheckRoadVehClose); + FindVehicleOnPos(v->tile, &rvf, EnumCheckRoadVehClose); + FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &rvf, EnumCheckRoadVehClose); } else { - u = VehicleFromPosXY(x, y, &rvf, EnumCheckRoadVehClose); + FindVehicleOnPosXY(x, y, &rvf, EnumCheckRoadVehClose); } /* This code protects a roadvehicle from being blocked for ever * If more than 1480 / 74 days a road vehicle is blocked, it will * drive just through it. The ultimate backup-code of TTD. * It can be disabled. */ - if (u == NULL) { + if (rvf.best_diff == UINT_MAX) { front->u.road.blocked_ctr = 0; return NULL; } if (++front->u.road.blocked_ctr > 1480) return NULL; - return u; + return rvf.best; } static void RoadVehArrivesAt(const Vehicle* v, Station* st) @@ -903,7 +913,7 @@ static bool CheckRoadBlockedForOvertakin if (!HasBit(trackdirbits, od->trackdir) || (trackbits & ~TRACK_BIT_CROSS) || (red_signals != TRACKDIR_BIT_NONE)) return true; /* Are there more vehicles on the tile except the two vehicles involved in overtaking */ - return VehicleFromPos(od->tile, od, EnumFindVehBlockingOvertake) != NULL; + return HasVehicleOnPos(od->tile, od, EnumFindVehBlockingOvertake); } static void RoadVehCheckOvertake(Vehicle *v, Vehicle *u) diff --git a/src/signal.cpp b/src/signal.cpp --- a/src/signal.cpp +++ b/src/signal.cpp @@ -285,13 +285,13 @@ static SigFlags ExploreSegment(Owner own if (IsRailDepot(tile)) { if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; exitdir = GetRailDepotDirection(tile); tile += TileOffsByDiagDir(exitdir); enterdir = ReverseDiagDir(exitdir); break; } else if (enterdir == GetRailDepotDirection(tile)) { // entered a depot - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; continue; } else { continue; @@ -300,7 +300,7 @@ static SigFlags ExploreSegment(Owner own if (GetRailTileType(tile) == RAIL_TILE_WAYPOINT) { if (GetWaypointAxis(tile) != DiagDirToAxis(enterdir)) continue; - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile += TileOffsByDiagDir(exitdir); /* enterdir and exitdir stay the same */ break; @@ -311,10 +311,10 @@ static SigFlags ExploreSegment(Owner own if (tracks == TRACK_BIT_HORZ || tracks == TRACK_BIT_VERT) { // there is exactly one incidating track, no need to check tracks = tracks_masked; - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, &tracks, &EnsureNoTrainOnTrackProc) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, &tracks, &EnsureNoTrainOnTrackProc)) flags |= SF_TRAIN; } else { if (tracks_masked == TRACK_BIT_NONE) continue; // no incidating track - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; } if (HasSignals(tile)) { // there is exactly one track - not zero, because there is exit from this tile @@ -366,7 +366,7 @@ static SigFlags ExploreSegment(Owner own if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile += TileOffsByDiagDir(exitdir); break; @@ -375,7 +375,7 @@ static SigFlags ExploreSegment(Owner own if (GetTileOwner(tile) != owner) continue; if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile += TileOffsByDiagDir(exitdir); break; @@ -385,13 +385,13 @@ static SigFlags ExploreSegment(Owner own DiagDirection dir = GetTunnelBridgeDirection(tile); if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; enterdir = dir; exitdir = ReverseDiagDir(dir); tile += TileOffsByDiagDir(exitdir); // just skip to next tile } else { // NOT incoming from the wormhole! if (ReverseDiagDir(enterdir) != dir) continue; - if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL) flags |= SF_TRAIN; + if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN; tile = GetOtherTunnelBridgeEnd(tile); // just skip to exit tile enterdir = INVALID_DIAGDIR; exitdir = INVALID_DIAGDIR; diff --git a/src/station_cmd.cpp b/src/station_cmd.cpp --- a/src/station_cmd.cpp +++ b/src/station_cmd.cpp @@ -1506,7 +1506,7 @@ static CommandCost RemoveRoadStop(Statio /* don't do the check for drive-through road stops when company bankrupts */ if (IsDriveThroughStopTile(tile) && (flags & DC_BANKRUPT)) { /* remove the 'going through road stop' status from all vehicles on that tile */ - if (flags & DC_EXEC) VehicleFromPos(tile, NULL, &ClearRoadStopStatusEnum); + if (flags & DC_EXEC) FindVehicleOnPos(tile, NULL, &ClearRoadStopStatusEnum); } else { if (!EnsureNoVehicleOnGround(tile)) return CMD_ERROR; } diff --git a/src/train_cmd.cpp b/src/train_cmd.cpp --- a/src/train_cmd.cpp +++ b/src/train_cmd.cpp @@ -1694,24 +1694,22 @@ static Vehicle *TrainApproachingCrossing /** * Finds a vehicle approaching rail-road crossing * @param tile tile to test - * @return pointer to vehicle approaching the crossing + * @return true if a vehicle is approaching the crossing * @pre tile is a rail-road crossing */ -static Vehicle *TrainApproachingCrossing(TileIndex tile) +static bool TrainApproachingCrossing(TileIndex tile) { assert(IsLevelCrossingTile(tile)); DiagDirection dir = AxisToDiagDir(GetCrossingRailAxis(tile)); TileIndex tile_from = tile + TileOffsByDiagDir(dir); - Vehicle *v = VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum); - - if (v != NULL) return v; + if (HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum)) return true; dir = ReverseDiagDir(dir); tile_from = tile + TileOffsByDiagDir(dir); - return VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum); + return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum); } @@ -1726,7 +1724,7 @@ void UpdateLevelCrossing(TileIndex tile, assert(IsLevelCrossingTile(tile)); /* train on crossing || train approaching crossing || reserved */ - bool new_state = VehicleFromPos(tile, NULL, &TrainOnTileEnum) != NULL || TrainApproachingCrossing(tile) || GetCrossingReservation(tile); + bool new_state = HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile) || GetCrossingReservation(tile); if (new_state != IsCrossingBarred(tile)) { if (new_state && sound) { @@ -3028,7 +3026,7 @@ bool TryPathReserve(Vehicle *v, bool mar } } - Vehicle *other_train = NULL; + bool other_train = false; PBSTileInfo origin = FollowTrainReservation(v, &other_train); /* If we have a reserved path and the path ends at a safe tile, we are finished already. */ if (origin.okay && (v->tile != origin.tile || first_tile_okay)) { @@ -3041,7 +3039,7 @@ bool TryPathReserve(Vehicle *v, bool mar * This can only happen when tracks and signals are changed. A crash * is probably imminent, don't do any further reservation because * it might cause stale reservations. */ - if (other_train != NULL && v->tile != origin.tile) { + if (other_train && v->tile != origin.tile) { if (mark_as_stuck) MarkTrainAsStuck(v); return false; } @@ -3518,10 +3516,10 @@ static void CheckTrainCollision(Vehicle /* find colliding vehicles */ if (v->u.rail.track == TRACK_BIT_WORMHOLE) { - VehicleFromPos(v->tile, &tcc, FindTrainCollideEnum); - VehicleFromPos(GetOtherTunnelBridgeEnd(v->tile), &tcc, FindTrainCollideEnum); + FindVehicleOnPos(v->tile, &tcc, FindTrainCollideEnum); + FindVehicleOnPos(GetOtherTunnelBridgeEnd(v->tile), &tcc, FindTrainCollideEnum); } else { - VehicleFromPosXY(v->x_pos, v->y_pos, &tcc, FindTrainCollideEnum); + FindVehicleOnPosXY(v->x_pos, v->y_pos, &tcc, FindTrainCollideEnum); } /* any dead -> no crash */ @@ -3652,7 +3650,7 @@ static void TrainController(Vehicle *v, exitdir = ReverseDiagDir(exitdir); /* check if a train is waiting on the other side */ - if (VehicleFromPos(o_tile, &exitdir, &CheckVehicleAtSignal) == NULL) return; + if (!HasVehicleOnPos(o_tile, &exitdir, &CheckVehicleAtSignal)) return; } } @@ -3819,9 +3817,9 @@ reverse_train_direction: } /** Collect trackbits of all crashed train vehicles on a tile - * @param v Vehicle passed from VehicleFromPos() + * @param v Vehicle passed from Find/HasVehicleOnPos() * @param data trackdirbits for the result - * @return NULL to not abort VehicleFromPos() + * @return NULL to iterate over all vehicles on the tile. */ static Vehicle *CollectTrackbitsFromCrashedVehiclesEnum(Vehicle *v, void *data) { @@ -3894,7 +3892,7 @@ static void DeleteLastWagon(Vehicle *v) /* If there are still crashed vehicles on the tile, give the track reservation to them */ TrackBits remaining_trackbits = TRACK_BIT_NONE; - VehicleFromPos(tile, &remaining_trackbits, CollectTrackbitsFromCrashedVehiclesEnum); + FindVehicleOnPos(tile, &remaining_trackbits, CollectTrackbitsFromCrashedVehiclesEnum); /* It is important that these two are the first in the loop, as reservation cannot deal with every trackbit combination */ assert(TRACK_BEGIN == TRACK_X && TRACK_Y == TRACK_BEGIN + 1); diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -605,7 +605,7 @@ static CommandCost DoClearTunnel(TileInd endtile = GetOtherTunnelEnd(tile); - if (GetVehicleTunnelBridge(tile, endtile) != NULL) return CMD_ERROR; + if (HasVehicleOnTunnelBridge(tile, endtile)) return CMD_ERROR; _build_tunnel_endtile = endtile; @@ -670,7 +670,7 @@ static CommandCost DoClearBridge(TileInd endtile = GetOtherBridgeEnd(tile); - if (GetVehicleTunnelBridge(tile, endtile) != NULL) return CMD_ERROR; + if (HasVehicleOnTunnelBridge(tile, endtile)) return CMD_ERROR; direction = GetTunnelBridgeDirection(tile); delta = TileOffsByDiagDir(direction); diff --git a/src/vehicle.cpp b/src/vehicle.cpp --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -168,42 +168,12 @@ static Vehicle *EnsureNoVehicleProcZ(Veh return v; } -Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z) -{ - return (Vehicle*)VehicleFromPos(tile, &z, &EnsureNoVehicleProcZ); -} - bool EnsureNoVehicleOnGround(TileIndex tile) { - return FindVehicleOnTileZ(tile, GetTileMaxZ(tile)) == NULL; + byte z = GetTileMaxZ(tile); + return !HasVehicleOnPos(tile, &z, &EnsureNoVehicleProcZ); } -Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z, bool without_crashed) -{ - int x1 = TileX(from); - int y1 = TileY(from); - int x2 = TileX(to); - int y2 = TileY(to); - Vehicle *veh; - - /* Make sure x1 < x2 or y1 < y2 */ - if (x1 > x2 || y1 > y2) { - Swap(x1, x2); - Swap(y1, y2); - } - FOR_ALL_VEHICLES(veh) { - if (without_crashed && (veh->vehstatus & VS_CRASHED) != 0) continue; - if ((veh->type == VEH_TRAIN || veh->type == VEH_ROAD) && (z == 0xFF || veh->z_pos == z)) { - if ((veh->x_pos >> 4) >= x1 && (veh->x_pos >> 4) <= x2 && - (veh->y_pos >> 4) >= y1 && (veh->y_pos >> 4) <= y2) { - return veh; - } - } - } - return NULL; -} - - /** Procedure called for every vehicle found in tunnel/bridge in the hash map */ static Vehicle *GetVehicleTunnelBridgeProc(Vehicle *v, void *data) { @@ -217,14 +187,12 @@ static Vehicle *GetVehicleTunnelBridgePr * Finds vehicle in tunnel / bridge * @param tile first end * @param endtile second end - * @return pointer to vehicle found + * @return true if the bridge has a vehicle */ -Vehicle *GetVehicleTunnelBridge(TileIndex tile, TileIndex endtile) +bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile) { - Vehicle *v = (Vehicle*)VehicleFromPos(tile, NULL, &GetVehicleTunnelBridgeProc); - if (v != NULL) return v; - - return (Vehicle*)VehicleFromPos(endtile, NULL, &GetVehicleTunnelBridgeProc); + return HasVehicleOnPos(tile, NULL, &GetVehicleTunnelBridgeProc) || + HasVehicleOnPos(endtile, NULL, &GetVehicleTunnelBridgeProc); } @@ -384,14 +352,14 @@ const int HASH_RES = 0; static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE]; -static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc) +static Vehicle *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc, bool find_first) { for (int y = yl; ; y = (y + (1 << HASH_BITS)) & (HASH_MASK << HASH_BITS)) { for (int x = xl; ; x = (x + 1) & HASH_MASK) { Vehicle *v = _new_vehicle_position_hash[(x + y) & TOTAL_HASH_MASK]; for (; v != NULL; v = v->next_new_hash) { Vehicle *a = proc(v, data); - if (a != NULL) return a; + if (find_first && a != NULL) return a; } if (x == xu) break; } @@ -402,7 +370,18 @@ static Vehicle *VehicleFromHash(int xl, } -Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc) +/** + * Helper function for FindVehicleOnPos/HasVehicleOnPos. + * @note Do not call this function directly! + * @param x The X location on the map + * @param y The Y location on the map + * @param data Arbitrary data passed to proc + * @param proc The proc that determines whether a vehicle will be "found". + * @param find_first Whether to return on the first found or iterate over + * all vehicles + * @return the best matching or first vehicle (depending on find_first). + */ +static Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first) { const int COLL_DIST = 6; @@ -412,11 +391,55 @@ Vehicle *VehicleFromPosXY(int x, int y, int yl = GB((y - COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS; int yu = GB((y + COLL_DIST) / TILE_SIZE, HASH_RES, HASH_BITS) << HASH_BITS; - return VehicleFromHash(xl, yl, xu, yu, data, proc); + return VehicleFromHash(xl, yl, xu, yu, data, proc, find_first); +} + +/** + * Find a vehicle from a specific location. It will call proc for ALL vehicles + * on the tile and YOU must make SURE that the "best one" is stored in the + * data value and is ALWAYS the same regardless of the order of the vehicles + * where proc was called on! + * When you fail to do this properly you create an almost untraceable DESYNC! + * @note The return value of proc will be ignored. + * @note Use this when you have the intention that all vehicles + * should be iterated over. + * @param x The X location on the map + * @param y The Y location on the map + * @param data Arbitrary data passed to proc + * @param proc The proc that determines whether a vehicle will be "found". + */ +void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc) +{ + VehicleFromPosXY(x, y, data, proc, false); } - -Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc) +/** + * Checks whether a vehicle in on a specific location. It will call proc for + * vehicles until it returns non-NULL. + * @note Use FindVehicleOnPosXY when you have the intention that all vehicles + * should be iterated over. + * @param x The X location on the map + * @param y The Y location on the map + * @param data Arbitrary data passed to proc + * @param proc The proc that determines whether a vehicle will be "found". + * @return True if proc returned non-NULL. + */ +bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc) +{ + return VehicleFromPosXY(x, y, data, proc, true) != NULL; +} + +/** + * Helper function for FindVehicleOnPos/HasVehicleOnPos. + * @note Do not call this function directly! + * @param tile The location on the map + * @param data Arbitrary data passed to proc + * @param proc The proc that determines whether a vehicle will be "found". + * @param find_first Whether to return on the first found or iterate over + * all vehicles + * @return the best matching or first vehicle (depending on find_first). + */ +static Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc, bool find_first) { int x = GB(TileX(tile), HASH_RES, HASH_BITS); int y = GB(TileY(tile), HASH_RES, HASH_BITS) << HASH_BITS; @@ -426,12 +449,46 @@ Vehicle *VehicleFromPos(TileIndex tile, if (v->tile != tile) continue; Vehicle *a = proc(v, data); - if (a != NULL) return a; + if (find_first && a != NULL) return a; } return NULL; } +/** + * Find a vehicle from a specific location. It will call proc for ALL vehicles + * on the tile and YOU must make SURE that the "best one" is stored in the + * data value and is ALWAYS the same regardless of the order of the vehicles + * where proc was called on! + * When you fail to do this properly you create an almost untraceable DESYNC! + * @note The return value of proc will be ignored. + * @note Use this when you have the intention that all vehicles + * should be iterated over. + * @param tile The location on the map + * @param data Arbitrary data passed to proc + * @param proc The proc that determines whether a vehicle will be "found". + */ +void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc) +{ + VehicleFromPos(tile, data, proc, false); +} + +/** + * Checks whether a vehicle in on a specific location. It will call proc for + * vehicles until it returns non-NULL. + * @note Use FindVehicleOnPos when you have the intention that all vehicles + * should be iterated over. + * @param tile The location on the map + * @param data Arbitrary data passed to proc + * @param proc The proc that determines whether a vehicle will be "found". + * @return True if proc returned non-NULL. + */ +bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc) +{ + return VehicleFromPos(tile, data, proc, true) != NULL; +} + + static void UpdateNewVehiclePosHash(Vehicle *v, bool remove) { Vehicle **old_hash = v->old_new_hash; diff --git a/src/vehicle_func.h b/src/vehicle_func.h --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -28,10 +28,11 @@ const Vehicle *GetLastVehicleInChain(con uint CountVehiclesInChain(const Vehicle *v); bool IsEngineCountable(const Vehicle *v); void DeleteVehicleChain(Vehicle *v); -Vehicle *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc); -Vehicle *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc); +void FindVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc); +void FindVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc); +bool HasVehicleOnPos(TileIndex tile, void *data, VehicleFromPosProc *proc); +bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc); void CallVehicleTicks(); -Vehicle *FindVehicleOnTileZ(TileIndex tile, byte z); uint8 CalcPercentVehicleFilled(const Vehicle *v, StringID *color); void InitializeTrains(); @@ -50,8 +51,7 @@ SpriteID GetRotorImage(const Vehicle *v) uint32 VehicleEnterTile(Vehicle *v, TileIndex tile, int x, int y); StringID VehicleInTheWayErrMsg(const Vehicle* v); -Vehicle *FindVehicleBetween(TileIndex from, TileIndex to, byte z, bool without_crashed = false); -Vehicle *GetVehicleTunnelBridge(TileIndex tile, TileIndex endtile); +bool HasVehicleOnTunnelBridge(TileIndex tile, TileIndex endtile); void DecreaseVehicleValue(Vehicle *v); void CheckVehicleBreakdown(Vehicle *v); diff --git a/src/water_cmd.cpp b/src/water_cmd.cpp --- a/src/water_cmd.cpp +++ b/src/water_cmd.cpp @@ -37,6 +37,7 @@ #include "tree_map.h" #include "station_base.h" #include "airport.h" +#include "aircraft.h" #include "newgrf_cargo.h" #include "effectvehicle_func.h" #include "oldpool_func.h" @@ -801,28 +802,47 @@ static void AnimateTile_Water(TileIndex /* not used */ } +static void FloodVehicle(Vehicle *v); + +/** + * Flood a vehicle if we are allowed to flood it, i.e. when it is on the ground. + * @param v The vehicle to test for flooding. + * @param data The z of level to flood. + * @return NULL as we always want to remove everything. + */ +static Vehicle *FloodVehicleProc(Vehicle *v, void *data) +{ + byte z = *(byte*)data; + + if (v->type == VEH_DISASTER || (v->type == VEH_AIRCRAFT && v->subtype == AIR_SHADOW)) return NULL; + if (v->z_pos > z || (v->vehstatus & VS_CRASHED) != 0) return NULL; + + FloodVehicle(v); + return NULL; +} /** * Finds a vehicle to flood. * It does not find vehicles that are already crashed on bridges, i.e. flooded. * @param tile the tile where to find a vehicle to flood - * @return a vehicle too flood or NULL when there is no vehicle too flood. */ -static Vehicle *FindFloodableVehicleOnTile(TileIndex tile) +static void FloodVehicles(TileIndex tile) { + byte z = 0; + if (IsTileType(tile, MP_STATION) && IsAirport(tile)) { const Station *st = GetStationByTile(tile); const AirportFTAClass *airport = st->Airport(); + z = 1 + airport->delta_z; for (uint x = 0; x < airport->size_x; x++) { for (uint y = 0; y < airport->size_y; y++) { tile = TILE_ADDXY(st->airport_tile, x, y); - Vehicle *v = FindVehicleOnTileZ(tile, 1 + airport->delta_z); - if (v != NULL && (v->vehstatus & VS_CRASHED) == 0) return v; + FindVehicleOnPos(tile, &z, &FloodVehicleProc); } } /* No vehicle could be flooded on this airport anymore */ - return NULL; + return; } /* if non-uniform stations are disabled, flood some train in this train station (if there is any) */ @@ -831,31 +851,23 @@ static Vehicle *FindFloodableVehicleOnTi BEGIN_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile) if (st->TileBelongsToRailStation(t)) { - Vehicle *v = FindVehicleOnTileZ(t, 0); - if (v != NULL && (v->vehstatus & VS_CRASHED) == 0) return v; + FindVehicleOnPos(tile, &z, &FloodVehicleProc); } END_TILE_LOOP(t, st->trainst_w, st->trainst_h, st->train_tile) - return NULL; + return; } - if (!IsBridgeTile(tile)) return FindVehicleOnTileZ(tile, 0); + if (!IsBridgeTile(tile)) { + FindVehicleOnPos(tile, &z, &FloodVehicleProc); + return; + } TileIndex end = GetOtherBridgeEnd(tile); - byte z = GetBridgeHeight(tile); - Vehicle *v; - - /* check the start tile first since as this is closest to the water */ - v = FindVehicleOnTileZ(tile, z); - if (v != NULL && (v->vehstatus & VS_CRASHED) == 0) return v; + z = GetBridgeHeight(tile); - /* check a vehicle in between both bridge heads */ - v = FindVehicleBetween(tile, end, z, true); - if (v != NULL) return v; - - /* check the end tile last to give fleeing vehicles a chance to escape */ - v = FindVehicleOnTileZ(end, z); - return (v != NULL && (v->vehstatus & VS_CRASHED) == 0) ? v : NULL; + FindVehicleOnPos(tile, &z, &FloodVehicleProc); + FindVehicleOnPos(end, &z, &FloodVehicleProc); } static void FloodVehicle(Vehicle *v) @@ -989,12 +1001,8 @@ static void DoFloodTile(TileIndex target switch (GetTileType(target)) { case MP_RAILWAY: { if (!IsPlainRailTile(target)) break; - + FloodVehicles(target); flooded = FloodHalftile(target); - - Vehicle *v = FindFloodableVehicleOnTile(target); - if (v != NULL) FloodVehicle(v); - break; } @@ -1019,8 +1027,7 @@ static void DoFloodTile(TileIndex target } } else { /* Flood vehicles */ - Vehicle *v = FindFloodableVehicleOnTile(target); - if (v != NULL) FloodVehicle(v); + FloodVehicles(target); /* flood flat tile */ if (CmdSucceeded(DoCommand(target, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR))) {