diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 94529fc456..288faf2373 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -2046,7 +2046,7 @@ bool AfterLoadGame() return false; } - const Tunnel *t = new Tunnel(start_tile, end_tile, false); + const Tunnel *t = new Tunnel(start_tile, end_tile, TileHeight(start_tile), false); SetTunnelIndex(start_tile, t->index); SetTunnelIndex(end_tile, t->index); @@ -2055,7 +2055,6 @@ bool AfterLoadGame() } } - /* Move the signal variant back up one bit for PBS. We don't convert the old PBS * format here, as an old layout wouldn't work properly anyway. To be safe, we * clear any possible PBS reservations as well. */ diff --git a/src/saveload/tunnel_sl.cpp b/src/saveload/tunnel_sl.cpp index 470de84590..d50b4bcd83 100644 --- a/src/saveload/tunnel_sl.cpp +++ b/src/saveload/tunnel_sl.cpp @@ -20,6 +20,7 @@ static const SaveLoad _tunnel_desc[] = { SLE_CONDVAR(Tunnel, tile_n, SLE_UINT32, 0, SL_MAX_VERSION), SLE_CONDVAR(Tunnel, tile_s, SLE_UINT32, 0, SL_MAX_VERSION), + SLE_CONDVAR(Tunnel, height, SLE_UINT8, 0, SL_MAX_VERSION), SLE_CONDVAR(Tunnel, is_chunnel, SLE_BOOL, 0, SL_MAX_VERSION), SLE_END() }; diff --git a/src/tunnel_base.h b/src/tunnel_base.h index 92408abde8..06196c2359 100644 --- a/src/tunnel_base.h +++ b/src/tunnel_base.h @@ -24,12 +24,13 @@ struct Tunnel : TunnelPool::PoolItem<&_tunnel_pool> { TileIndex tile_n; // North tile of tunnel. TileIndex tile_s; // South tile of tunnel. - bool is_chunnel; + uint8 height; // Tunnel height + bool is_chunnel; // Whether this tunnel is a chunnel Tunnel() {} ~Tunnel(); - Tunnel(TileIndex tile_n, TileIndex tile_s, bool is_chunnel) : tile_n(tile_n), tile_s(tile_s), is_chunnel(is_chunnel) + Tunnel(TileIndex tile_n, TileIndex tile_s, uint8 height, bool is_chunnel) : tile_n(tile_n), tile_s(tile_s), height(height), is_chunnel(is_chunnel) { this->UpdateIndexes(); } diff --git a/src/tunnel_map.cpp b/src/tunnel_map.cpp index cd049f7397..7b546848a4 100644 --- a/src/tunnel_map.cpp +++ b/src/tunnel_map.cpp @@ -22,6 +22,21 @@ TunnelPool _tunnel_pool("Tunnel"); INSTANTIATE_POOL_METHODS(Tunnel) static std::unordered_map tunnel_tile_index_map; +static std::unordered_multimap tunnel_axis_height_index; + +static uint64 GetTunnelAxisHeightCacheKey(TileIndex tile, uint8 height, bool y_axis) { + if (y_axis) { + // tunnel extends along Y axis (DIAGDIR_SE from north end), has same X values + return TileX(tile) | (((uint64) height) << 24) | (((uint64) 1) << 32); + } else { + // tunnel extends along X axis (DIAGDIR_SW from north end), has same Y values + return TileY(tile) | (((uint64) height) << 24); + } +} + +static inline uint64 GetTunnelAxisHeightCacheKey(const Tunnel* t) { + return GetTunnelAxisHeightCacheKey(t->tile_n, t->height, t->tile_s - t->tile_n > MapMaxX()); +} /** * Clean up a tunnel tile @@ -34,6 +49,17 @@ Tunnel::~Tunnel() tunnel_tile_index_map.erase(this->tile_n); tunnel_tile_index_map.erase(this->tile_s); } + + auto range = tunnel_axis_height_index.equal_range(GetTunnelAxisHeightCacheKey(this)); + bool have_erased = false; + for (auto it = range.first; it != range.second; ++it) { + if (it->second == this) { + tunnel_axis_height_index.erase(it); + have_erased = true; + break; + } + } + assert(have_erased); } /** @@ -45,6 +71,8 @@ void Tunnel::UpdateIndexes() tunnel_tile_index_map[this->tile_n] = this->index; tunnel_tile_index_map[this->tile_s] = this->index; } + + tunnel_axis_height_index.emplace(GetTunnelAxisHeightCacheKey(this), this); } /** @@ -53,6 +81,7 @@ void Tunnel::UpdateIndexes() void Tunnel::PreCleanPool() { tunnel_tile_index_map.clear(); + tunnel_axis_height_index.clear(); } TunnelID GetTunnelIndexByLookup(TileIndex t) @@ -74,6 +103,24 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) return t->tile_n == tile ? t->tile_s : t->tile_n; } +static inline bool IsTunnelInWaySingleAxis(TileIndex tile, int z, bool chunnel_allowed, bool y_axis, TileIndexDiff tile_diff) +{ + const auto tunnels = tunnel_axis_height_index.equal_range(GetTunnelAxisHeightCacheKey(tile, z, y_axis)); + for (auto it = tunnels.first; it != tunnels.second; ++it) { + const Tunnel *t = it->second; + if (t->tile_n > tile || tile > t->tile_s) continue; + + if (t->is_chunnel && chunnel_allowed) { + /* Only if tunnel was built over water is terraforming is allowed between portals. */ + const TileIndexDiff delta = tile_diff * 4; // 4 tiles ramp. + if (tile < t->tile_n + delta || t->tile_s - delta < tile) return true; + continue; + } + return true; + } + return false; +} + /** * Is there a tunnel in the way in any direction? * @param tile the tile to search from. @@ -83,26 +130,5 @@ TileIndex GetOtherTunnelEnd(TileIndex tile) */ bool IsTunnelInWay(TileIndex tile, int z, bool chunnel_allowed) { - uint x = TileX(tile); - uint y = TileY(tile); - - Tunnel *t; - FOR_ALL_TUNNELS(t) { - if (t->tile_n > tile || tile > t->tile_s) continue; - - if (t->tile_s - t->tile_n > MapMaxX()){ - if (TileX(t->tile_n) != x || (int)TileHeight(t->tile_n) != z) continue; // dir DIAGDIR_SE - } else { - if (TileY(t->tile_n) != y || (int)TileHeight(t->tile_n) != z) continue; // dir DIAGDIR_SW - } - - if (t->is_chunnel && chunnel_allowed) { - /* Only if tunnel was build over water terraforming is allowed between portals. */ - TileIndexDiff delta = GetTunnelBridgeDirection(t->tile_n) == DIAGDIR_SE ? TileOffsByDiagDir(DIAGDIR_SE) * 4 : 4; // 4 tiles ramp. - if (tile < t->tile_n + delta || t->tile_s - delta < tile) return true; - continue; - } - return true; - } - return false; + return IsTunnelInWaySingleAxis(tile, z, chunnel_allowed, false, 1) || IsTunnelInWaySingleAxis(tile, z, chunnel_allowed, true, TileOffsByDiagDir(DIAGDIR_SE)); } diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 0bd2e54bb8..30df9ce9ef 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -814,7 +814,7 @@ CommandCost CmdBuildTunnel(TileIndex start_tile, DoCommandFlag flags, uint32 p1, if(start_tile > end_tile) Swap(tn, ts); if (!Tunnel::CanAllocateItem()) return_cmd_error(STR_ERROR_TUNNEL_TOO_MANY); - const Tunnel *t = new Tunnel(tn, ts, is_chunnel); + const Tunnel *t = new Tunnel(tn, ts, TileHeight(tn), is_chunnel); if (transport_type == TRANSPORT_RAIL) { if (!IsTunnelTile(start_tile) && c != NULL) c->infrastructure.rail[railtype] += num_pieces;