1
0
Fork 0

(svn r14259) [0.6] -Backport from trunk:

- Fix: desync due to randomly ordered vehicle hash by flooding and road vehicle overtake/following (r14258)
release/0.6
rubidium 2008-09-07 11:54:00 +00:00
parent 16492fe7b1
commit 0222967c41
10 changed files with 204 additions and 132 deletions

View File

@ -109,7 +109,7 @@ static bool EnsureNoTrainOnTrack(TileIndex tile, Track track)
{
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)
@ -1232,7 +1232,7 @@ CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
SetRailType(tile, totype);
MarkTileDirtyByTile(tile);
/* update power of train engines on this tile */
VehicleFromPos(tile, NULL, &UpdateTrainPowerProc);
FindVehicleOnPos(tile, NULL, &UpdateTrainPowerProc);
}
}
@ -1282,14 +1282,14 @@ CommandCost CmdConvertRail(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
/* 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) {
SetRailType(tile, totype);
SetRailType(endtile, totype);
VehicleFromPos(tile, NULL, &UpdateTrainPowerProc);
VehicleFromPos(endtile, NULL, &UpdateTrainPowerProc);
FindVehicleOnPos(tile, NULL, &UpdateTrainPowerProc);
FindVehicleOnPos(endtile, NULL, &UpdateTrainPowerProc);
Track track = AxisToTrack(DiagDirToAxis(GetTunnelBridgeDirection(tile)));

View File

@ -126,7 +126,7 @@ static CommandCost RemoveRoad(TileIndex tile, uint32 flags, RoadBits pieces, Roa
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:
@ -532,7 +532,7 @@ CommandCost CmdBuildRoad(TileIndex tile, uint32 flags, uint32 p1, uint32 p2)
if (GetTunnelBridgeTransportType(tile) != TRANSPORT_ROAD) 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:

View File

@ -714,7 +714,7 @@ static void RoadVehCheckTrainCrash(Vehicle *v)
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;
}
@ -851,37 +851,45 @@ static void StartRoadVehSound(const Vehicle* v)
struct RoadVehFindData {
int x;
int y;
const Vehicle* veh;
const Vehicle *veh;
Vehicle *best;
uint best_diff;
Direction dir;
};
static void* EnumCheckRoadVehClose(Vehicle *v, void* data)
static void *EnumCheckRoadVehClose(Vehicle *v, void* data)
{
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;
@ -890,25 +898,27 @@ static Vehicle* RoadVehFindCloseTo(Vehicle* v, int x, int y, Direction dir)
rvf.y = y;
rvf.dir = dir;
rvf.veh = v;
rvf.best_diff = UINT_MAX;
if (front->u.road.state == RVSB_WORMHOLE) {
u = (Vehicle*)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 = (Vehicle*)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)
@ -1035,7 +1045,7 @@ static bool CheckRoadBlockedForOvertaking(OvertakeData *od)
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)

View File

@ -284,13 +284,13 @@ static SigFlags ExploreSegment(Owner owner)
if (IsRailDepot(tile)) {
if (enterdir == INVALID_DIAGDIR) { // from 'inside' - train just entered or left the depot
if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) 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)) flags |= SF_TRAIN;
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
continue;
} else {
continue;
@ -299,7 +299,7 @@ static SigFlags ExploreSegment(Owner owner)
if (GetRailTileType(tile) == RAIL_TILE_WAYPOINT) {
if (GetWaypointAxis(tile) != DiagDirToAxis(enterdir)) continue;
if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
tile += TileOffsByDiagDir(exitdir);
/* enterdir and exitdir stay the same */
break;
@ -310,10 +310,10 @@ static SigFlags ExploreSegment(Owner owner)
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)) 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)) 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
@ -358,7 +358,7 @@ static SigFlags ExploreSegment(Owner owner)
if (DiagDirToAxis(enterdir) != GetRailStationAxis(tile)) continue; // different axis
if (IsStationTileBlocked(tile)) continue; // 'eye-candy' station tile
if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
tile += TileOffsByDiagDir(exitdir);
break;
@ -367,7 +367,7 @@ static SigFlags ExploreSegment(Owner owner)
if (GetTileOwner(tile) != owner) continue;
if (DiagDirToAxis(enterdir) == GetCrossingRoadAxis(tile)) continue; // different axis
if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
if (!(flags & SF_TRAIN) && HasVehicleOnPos(tile, NULL, &TrainOnTileEnum)) flags |= SF_TRAIN;
tile += TileOffsByDiagDir(exitdir);
break;
@ -377,13 +377,13 @@ static SigFlags ExploreSegment(Owner owner)
DiagDirection dir = GetTunnelBridgeDirection(tile);
if (enterdir == INVALID_DIAGDIR) { // incoming from the wormhole
if (!(flags & SF_TRAIN) && VehicleFromPos(tile, NULL, &TrainOnTileEnum)) 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)) 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;

View File

@ -1477,7 +1477,7 @@ static CommandCost RemoveRoadStop(Station *st, uint32 flags, TileIndex tile)
/* 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;
}

View File

@ -1730,24 +1730,22 @@ static void *TrainApproachingCrossingEnum(Vehicle *v, void *data)
/**
* 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 = (Vehicle *)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 (Vehicle *)VehicleFromPos(tile_from, &tile, &TrainApproachingCrossingEnum);
return HasVehicleOnPos(tile_from, &tile, &TrainApproachingCrossingEnum);
}
@ -1761,8 +1759,8 @@ void UpdateLevelCrossing(TileIndex tile, bool sound)
{
assert(IsLevelCrossingTile(tile));
/* train on crossing || train approaching crossing */
bool new_state = VehicleFromPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
/* train on crossing || train approaching crossing || reserved */
bool new_state = HasVehicleOnPos(tile, NULL, &TrainOnTileEnum) || TrainApproachingCrossing(tile);
if (new_state != IsCrossingBarred(tile)) {
if (new_state && sound) {
@ -3083,10 +3081,10 @@ static void CheckTrainCollision(Vehicle *v)
/* 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 */
@ -3208,7 +3206,7 @@ static void TrainController(Vehicle *v, Vehicle *nomove, bool update_image)
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;
}
}
goto reverse_train_direction;

View File

@ -584,7 +584,7 @@ static CommandCost DoClearTunnel(TileIndex tile, uint32 flags)
endtile = GetOtherTunnelEnd(tile);
if (GetVehicleTunnelBridge(tile, endtile) != NULL) return CMD_ERROR;
if (HasVehicleOnTunnelBridge(tile, endtile)) return CMD_ERROR;
_build_tunnel_endtile = endtile;
@ -641,7 +641,7 @@ static CommandCost DoClearBridge(TileIndex tile, uint32 flags)
endtile = GetOtherBridgeEnd(tile);
if (GetVehicleTunnelBridge(tile, endtile) != NULL) return CMD_ERROR;
if (HasVehicleOnTunnelBridge(tile, endtile)) return CMD_ERROR;
direction = GetTunnelBridgeDirection(tile);
delta = TileOffsByDiagDir(direction);

View File

@ -163,42 +163,12 @@ static void *EnsureNoVehicleProcZ(Vehicle *v, void *data)
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 void *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
{
@ -212,14 +182,12 @@ static void *GetVehicleTunnelBridgeProc(Vehicle *v, void *data)
* 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);
}
@ -370,14 +338,14 @@ const int HASH_RES = 0;
static Vehicle *_new_vehicle_position_hash[TOTAL_HASH_SIZE];
static void *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, VehicleFromPosProc *proc)
static void *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) {
void *a = proc(v, data);
if (a != NULL) return a;
if (find_first && a != NULL) return a;
}
if (x == xu) break;
}
@ -388,7 +356,18 @@ static void *VehicleFromHash(int xl, int yl, int xu, int yu, void *data, Vehicle
}
void *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 void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc, bool find_first)
{
const int COLL_DIST = 6;
@ -398,11 +377,55 @@ void *VehicleFromPosXY(int x, int y, void *data, VehicleFromPosProc *proc)
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);
}
void *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 void *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;
@ -412,12 +435,46 @@ void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc)
if (v->tile != tile) continue;
void *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;

View File

@ -25,10 +25,11 @@ Vehicle *GetLastVehicleInChain(Vehicle *v);
uint CountVehiclesInChain(const Vehicle *v);
bool IsEngineCountable(const Vehicle *v);
void DeleteVehicleChain(Vehicle *v);
void *VehicleFromPos(TileIndex tile, void *data, VehicleFromPosProc *proc);
void *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(Vehicle *v, StringID *color);
void InitializeTrains();
@ -47,8 +48,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);
Vehicle *CheckClickOnVehicle(const ViewPort *vp, int x, int y);

View File

@ -34,6 +34,7 @@
#include "settings_type.h"
#include "clear_map.h"
#include "tree_map.h"
#include "aircraft.h"
#include "table/sprites.h"
#include "table/strings.h"
@ -737,28 +738,47 @@ static void AnimateTile_Water(TileIndex tile)
/* 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 void *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) */
@ -767,31 +787,23 @@ static Vehicle *FindFloodableVehicleOnTile(TileIndex tile)
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;
z = GetBridgeHeight(tile);
/* 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;
/* 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)
@ -921,12 +933,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;
}
@ -951,8 +959,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))) {