diff --git a/src/saveload/afterload.cpp b/src/saveload/afterload.cpp index 3d007071f5..8123d86e43 100644 --- a/src/saveload/afterload.cpp +++ b/src/saveload/afterload.cpp @@ -3774,6 +3774,7 @@ bool AfterLoadGame() /* This needs to be done after conversion. */ RebuildViewportKdtree(); + ViewportMapBuildTunnelCache(); /* Road stops is 'only' updating some caches */ AfterLoadRoadStops(); diff --git a/src/settings.cpp b/src/settings.cpp index 7c3a400103..e365911913 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -1244,6 +1244,13 @@ static bool CheckYapfRailSignalPenalties(int32) return true; } +static bool ViewportMapShowTunnelModeChanged(int32 p1) +{ + extern void ViewportMapBuildTunnelCache(); + ViewportMapBuildTunnelCache(); + return RedrawScreen(p1); +} + /** Checks if any settings are set to incorrect values, and sets them to correct values in that case. */ static void ValidateSettings() { diff --git a/src/table/settings.ini b/src/table/settings.ini index 7f99386997..44ac5aa39c 100644 --- a/src/table/settings.ini +++ b/src/table/settings.ini @@ -48,6 +48,7 @@ static bool DayLengthChanged(int32 p1); static bool SimulatedWormholeSignalsChanged(int32 p1); static bool EnableSingleVehSharedOrderGuiChanged(int32 p1); static bool CheckYapfRailSignalPenalties(int32 p1); +static bool ViewportMapShowTunnelModeChanged(int32 p1); static bool UpdateClientName(int32 p1); static bool UpdateServerPassword(int32 p1); @@ -4065,7 +4066,7 @@ var = gui.show_tunnels_on_map flags = SLF_NOT_IN_SAVE | SLF_NO_NETWORK_SYNC def = true str = STR_CONFIG_SETTING_VIEWPORT_MAP_SHOW_TUNNELS -proc = RedrawScreen +proc = ViewportMapShowTunnelModeChanged [SDTC_VAR] var = gui.show_vehicle_route diff --git a/src/tunnelbridge_cmd.cpp b/src/tunnelbridge_cmd.cpp index 1ccba73074..7e9483ab23 100644 --- a/src/tunnelbridge_cmd.cpp +++ b/src/tunnelbridge_cmd.cpp @@ -999,7 +999,9 @@ 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, TileHeight(tn), is_chunnel); + const int height = TileHeight(tn); + const Tunnel *t = new Tunnel(tn, ts, height, is_chunnel); + ViewportMapStoreTunnel(tn, ts, height, true); if (transport_type == TRANSPORT_RAIL) { if (!IsTunnelTile(start_tile) && c != nullptr) c->infrastructure.rail[railtype] += num_pieces; @@ -1080,6 +1082,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) CommandCost ret = CheckAllowRemoveTunnelBridge(tile); if (ret.Failed()) return ret; + const Axis axis = DiagDirToAxis(GetTunnelBridgeDirection(tile)); TileIndex endtile = GetOtherTunnelEnd(tile); ret = TunnelBridgeIsFree(tile, endtile); @@ -1153,8 +1156,7 @@ static CommandCost DoClearTunnel(TileIndex tile, DoCommandFlag flags) DoClearSquare(tile); DoClearSquare(endtile); } - ViewportMapInvalidateTunnelCacheByTile(tile); - ViewportMapInvalidateTunnelCacheByTile(endtile); + ViewportMapInvalidateTunnelCacheByTile(tile < endtile ? tile : endtile, axis); } return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_CLEAR_TUNNEL] * len * (is_chunnel ? 2 : 1)); } diff --git a/src/viewport.cpp b/src/viewport.cpp index a6f865c47d..6ab7cf8df2 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -206,6 +206,15 @@ struct PolylineInfo { uint second_len; ///< size of the second segment - number of track pieces. }; +struct TunnelToMap { + TunnelBridgeToMap tb; + int y_intercept; + uint8 tunnel_z; +}; +struct TunnelToMapStorage { + std::vector tunnels; +}; + struct BridgeSetXComparator { bool operator() (const TileIndex a, const TileIndex b) const { @@ -229,8 +238,8 @@ struct ViewportDrawer { ParentSpriteToDrawVector parent_sprites_to_draw; ParentSpriteToSortVector parent_sprites_to_sort; ///< Parent sprite pointer array used for sorting ChildScreenSpriteToDrawVector child_screen_sprites_to_draw; - TunnelBridgeToMapVector tunnel_to_map; - btree::btree_set tunnel_tiles; + TunnelToMapStorage tunnel_to_map_x; + TunnelToMapStorage tunnel_to_map_y; btree::btree_map bridge_to_map_x; btree::btree_map bridge_to_map_y; @@ -1941,7 +1950,7 @@ static void ViewportMapStoreBridge(const ViewPort * const vp, const TileIndex ti } } -static void ViewportMapStoreTunnel(const ViewPort * const vp, const TileIndex tile) +void ViewportMapStoreTunnel(const TileIndex tile, const TileIndex tile_south, const int tunnel_z, const bool insert_sorted) { extern LegendAndColour _legend_land_owners[NUM_NO_COMPANY_ENTRIES + MAX_COMPANIES + 1]; extern uint _company_to_list_pos[MAX_COMPANIES]; @@ -1951,45 +1960,68 @@ static void ViewportMapStoreTunnel(const ViewPort * const vp, const TileIndex ti const Owner o = GetTileOwner(tile); if (o < MAX_COMPANIES && !_legend_land_owners[_company_to_list_pos[o]].show_on_map) return; - /* Check if already stored */ - if (_vd.tunnel_tiles.count(tile)) return; - - /* It's a new one, add it to the list */ - _vd.tunnel_to_map.emplace_back(); - TunnelBridgeToMap &tbtm = _vd.tunnel_to_map.back(); - TileIndex other_end = GetOtherTunnelBridgeEnd(tile); - _vd.tunnel_tiles.insert(tile); - _vd.tunnel_tiles.insert(other_end); + const Axis axis = (TileX(tile) == TileX(tile_south)) ? AXIS_Y : AXIS_X; + const Point viewport_pt = RemapCoords(TileX(tile) * TILE_SIZE, TileY(tile) * TILE_SIZE, tunnel_z); + int y_intercept; + if (axis == AXIS_X) { + /* NE to SW */ + y_intercept = viewport_pt.y + (viewport_pt.x / 2); + } else { + /* NW to SE */ + y_intercept = viewport_pt.y - (viewport_pt.x / 2); + } + TunnelToMapStorage &storage = (axis == AXIS_X) ? _vd.tunnel_to_map_x : _vd.tunnel_to_map_y; + TunnelToMap *tbtm; + if (insert_sorted) { + auto iter = std::upper_bound(storage.tunnels.begin(), storage.tunnels.end(), y_intercept, [](int a, const TunnelToMap &b) -> bool { + return a < b.y_intercept; + }); + tbtm = &(*(storage.tunnels.emplace(iter))); + } else { + storage.tunnels.emplace_back(); + tbtm = &(storage.tunnels.back()); + } /* ensure deterministic ordering, to avoid render flicker */ - if (other_end > tile) { - tbtm.from_tile = other_end; - tbtm.to_tile = tile; - } else { - tbtm.from_tile = tile; - tbtm.to_tile = other_end; - } + tbtm->tb.from_tile = tile; + tbtm->tb.to_tile = tile_south; + tbtm->y_intercept = y_intercept; + tbtm->tunnel_z = tunnel_z; } void ViewportMapClearTunnelCache() { - _vd.tunnel_to_map.clear(); - _vd.tunnel_tiles.clear(); + _vd.tunnel_to_map_x.tunnels.clear(); + _vd.tunnel_to_map_y.tunnels.clear(); } -void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile) +void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile, const Axis axis) { - if (!_vd.tunnel_tiles.count(tile)) return; - TunnelBridgeToMapVector * const tbtmv = &_vd.tunnel_to_map; - for (auto tbtm = tbtmv->begin(); tbtm != tbtmv->end(); tbtm++) { - if (tbtm->from_tile == tile || tbtm->to_tile == tile) { - *tbtm = tbtmv->back(); - tbtmv->pop_back(); + if (!_settings_client.gui.show_tunnels_on_map) return; + std::vector &tbtmv = (axis == AXIS_X) ? _vd.tunnel_to_map_x.tunnels : _vd.tunnel_to_map_y.tunnels; + for (auto tbtm = tbtmv.begin(); tbtm != tbtmv.end(); tbtm++) { + if (tbtm->tb.from_tile == tile) { + tbtmv.erase(tbtm); return; } } } +void ViewportMapBuildTunnelCache() +{ + ViewportMapClearTunnelCache(); + if (_settings_client.gui.show_tunnels_on_map) { + for (Tunnel *tunnel : Tunnel::Iterate()) { + ViewportMapStoreTunnel(tunnel->tile_n, tunnel->tile_s, tunnel->height, false); + } + auto sorter = [](const TunnelToMap &a, const TunnelToMap &b) -> bool { + return a.y_intercept < b.y_intercept; + }; + std::sort(_vd.tunnel_to_map_x.tunnels.begin(), _vd.tunnel_to_map_x.tunnels.end(), sorter); + std::sort(_vd.tunnel_to_map_y.tunnels.begin(), _vd.tunnel_to_map_y.tunnels.end(), sorter); + } +} + /** * Draw/colour the blocks that have been redrawn. */ @@ -2592,9 +2624,7 @@ static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * c *tile_type = ttype; if (IsBridgeAbove(from_tile)) ViewportMapStoreBridgeAboveTile(vp, from_tile); } else { - if (IsTunnel(from_tile)) { - ViewportMapStoreTunnel(vp, from_tile); - } else { + if (IsBridge(from_tile)) { ViewportMapStoreBridge(vp, from_tile); } switch (GetTunnelBridgeTransportType(from_tile)) { @@ -2629,9 +2659,7 @@ static inline TileIndex ViewportMapGetMostSignificantTileType(const ViewPort * c /* Store bridges and tunnels. */ *tile_type = GetTileType(result); if (*tile_type == MP_TUNNELBRIDGE) { - if (IsTunnel(result)) { - ViewportMapStoreTunnel(vp, result); - } else { + if (IsBridge(result)) { ViewportMapStoreBridge(vp, result); } switch (GetTunnelBridgeTransportType(result)) { @@ -2838,12 +2866,15 @@ void ViewportMapDraw(const ViewPort * const vp) b += incr_b; } while (++j < h); - /* Render tunnels */ - if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map.size() != 0) { - for (const TunnelBridgeToMap &tbtm : _vd.tunnel_to_map) { // For each tunnel - const int tunnel_z = GetTileZ(tbtm.from_tile) * TILE_HEIGHT; - const Point pt_from = RemapCoords(TileX(tbtm.from_tile) * TILE_SIZE, TileY(tbtm.from_tile) * TILE_SIZE, tunnel_z); - const Point pt_to = RemapCoords(TileX(tbtm.to_tile) * TILE_SIZE, TileY(tbtm.to_tile) * TILE_SIZE, tunnel_z); + auto draw_tunnels = [&](const int y_intercept_min, const int y_intercept_max, const TunnelToMapStorage &storage) { + auto iter = std::lower_bound(storage.tunnels.begin(), storage.tunnels.end(), y_intercept_min, [](const TunnelToMap &a, int b) -> bool { + return a.y_intercept < b; + }); + for (; iter != storage.tunnels.end() && iter->y_intercept <= y_intercept_max; ++iter) { + const TunnelToMap &ttm = *iter; + const int tunnel_z = ttm.tunnel_z * TILE_HEIGHT; + const Point pt_from = RemapCoords(TileX(ttm.tb.from_tile) * TILE_SIZE, TileY(ttm.tb.from_tile) * TILE_SIZE, tunnel_z); + const Point pt_to = RemapCoords(TileX(ttm.tb.to_tile) * TILE_SIZE, TileY(ttm.tb.to_tile) * TILE_SIZE, tunnel_z); /* check if tunnel is wholly outside redrawing area */ const int x_from = UnScaleByZoomLower(pt_from.x - _vd.dpi.left, _vd.dpi.zoom); @@ -2853,8 +2884,20 @@ void ViewportMapDraw(const ViewPort * const vp) const int y_to = UnScaleByZoomLower(pt_to.y - _vd.dpi.top, _vd.dpi.zoom); if ((y_from < 0 && y_to < 0) || (y_from > h && y_to > h)) continue; - ViewportMapDrawBridgeTunnel(vp, &tbtm, tunnel_z, true, w, h, blitter); + ViewportMapDrawBridgeTunnel(vp, &ttm.tb, tunnel_z, true, w, h, blitter); } + }; + + /* Render tunnels */ + if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map_x.tunnels.size() != 0) { + const int y_intercept_min = _vd.dpi.top + (_vd.dpi.left / 2); + const int y_intercept_max = _vd.dpi.top + _vd.dpi.height + ((_vd.dpi.left + _vd.dpi.width) / 2); + draw_tunnels(y_intercept_min, y_intercept_max, _vd.tunnel_to_map_x); + } + if (_settings_client.gui.show_tunnels_on_map && _vd.tunnel_to_map_y.tunnels.size() != 0) { + const int y_intercept_min = _vd.dpi.top - ((_vd.dpi.left + _vd.dpi.width) / 2); + const int y_intercept_max = _vd.dpi.top + _vd.dpi.height - (_vd.dpi.left / 2); + draw_tunnels(y_intercept_min, y_intercept_max, _vd.tunnel_to_map_y); } /* Render bridges */ diff --git a/src/viewport_func.h b/src/viewport_func.h index cf4e781583..d42d532ad5 100644 --- a/src/viewport_func.h +++ b/src/viewport_func.h @@ -112,8 +112,10 @@ Point GetViewportStationMiddle(const ViewPort *vp, const Station *st); void ShowTooltipForTile(Window *w, const TileIndex tile); +void ViewportMapStoreTunnel(const TileIndex tile, const TileIndex tile_south, const int tunnel_z, const bool insert_sorted); void ViewportMapClearTunnelCache(); -void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile); +void ViewportMapInvalidateTunnelCacheByTile(const TileIndex tile, const Axis axis); +void ViewportMapBuildTunnelCache(); void DrawTileSelectionRect(const TileInfo *ti, PaletteID pal); void DrawSelectionSprite(SpriteID image, PaletteID pal, const TileInfo *ti, int z_offset, FoundationPart foundation_part, const SubSprite *sub = nullptr);