diff --git a/src/vehicle.cpp b/src/vehicle.cpp index f33092ffa6..aba44f4b08 100644 --- a/src/vehicle.cpp +++ b/src/vehicle.cpp @@ -502,6 +502,52 @@ bool HasVehicleOnPosXY(int x, int y, void *data, VehicleFromPosProc *proc) return VehicleFromPosXY(x, y, data, proc, true) != nullptr; } +/** + * Iterator constructor. + * Find first vehicle near (x, y). + */ +VehiclesNearTileXY::Iterator::Iterator(int32_t x, int32_t y) +{ + const int COLL_DIST = 6; + + /* Hash area to scan */ + this->hxmin = this->hx = GetTileHash1D((x - COLL_DIST) / TILE_SIZE); + this->hxmax = GetTileHash1D((x + COLL_DIST) / TILE_SIZE); + this->hymin = this->hy = GetTileHash1D((y - COLL_DIST) / TILE_SIZE); + this->hymax = GetTileHash1D((y + COLL_DIST) / TILE_SIZE); + + this->current_veh = _vehicle_tile_hash[ComposeTileHash(this->hx, this->hy)]; + this->SkipEmptyBuckets(); +} + +/** + * Advance the internal state to the next potential vehicle. + */ +void VehiclesNearTileXY::Iterator::Increment() +{ + assert(this->current_veh != nullptr); + this->current_veh = this->current_veh->hash_tile_next; + this->SkipEmptyBuckets(); +} + +/** + * Advance the internal state until we reach a non-empty bucket, or the end. + */ +void VehiclesNearTileXY::Iterator::SkipEmptyBuckets() +{ + while (this->current_veh == nullptr) { + if (this->hx != this->hxmax) { + this->hx = IncTileHash1D(this->hx); + } else if (this->hy != this->hymax) { + this->hx = this->hxmin; + this->hy = IncTileHash1D(this->hy); + } else { + return; + } + this->current_veh = _vehicle_tile_hash[ComposeTileHash(this->hx, this->hy)]; + } +} + /** * Iterator constructor. * Find first vehicle on tile. diff --git a/src/vehicle_func.h b/src/vehicle_func.h index 7a3ae6147b..245561af28 100644 --- a/src/vehicle_func.h +++ b/src/vehicle_func.h @@ -111,6 +111,73 @@ bool HasVehicleOnTile(TileIndex tile, UnaryPred &&predicate) return false; } +/** + * Iterate over all vehicles near a given world coordinate. + * @warning This only works for vehicles with proper Vehicle::Tile, so only ground vehicles outside wormholes. + * @warning The order is non-deterministic. You have to make sure, that your processing is not order dependant. + */ +class VehiclesNearTileXY { +public: + /** + * Forward iterator + */ + class Iterator { + public: + using value_type = Vehicle *; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + using pointer = void; + using reference = void; + + explicit Iterator(int32_t x, int32_t y); + + bool operator==(const Iterator &rhs) const { return this->current_veh == rhs.current_veh; } + bool operator==(const std::default_sentinel_t &) const { return this->current_veh == nullptr; } + + Vehicle *operator*() const { return this->current_veh; } + + Iterator &operator++() + { + this->Increment(); + return *this; + } + + Iterator operator++(int) + { + Iterator result = *this; + ++*this; + return result; + } + private: + uint hxmin, hxmax, hymin, hymax; + uint hx, hy; + Vehicle *current_veh; + + void Increment(); + void SkipEmptyBuckets(); + }; + + explicit VehiclesNearTileXY(int32_t x, int32_t y) : start(x, y) {} + Iterator begin() const { return this->start; } + std::default_sentinel_t end() const { return std::default_sentinel_t(); } +private: + Iterator start; +}; + +/** + * Loop over vehicles near a given world coordinate, and check whether a predicate is true for any of them. + * The predicate must have the signature: bool Predicate(const Vehicle *); + * @warning This only works for vehicles with proper Vehicle::Tile, so only ground vehicles outside wormholes. + */ +template +bool HasVehicleNearTileXY(int32_t x, int32_t y, UnaryPred &&predicate) +{ + for (const auto *v : VehiclesNearTileXY(x, y)) { + if (predicate(v)) return true; + } + return false; +} + typedef Vehicle *VehicleFromPosProc(Vehicle *v, void *data); void VehicleServiceInDepot(Vehicle *v);