Repository: cataclysmbnteam/Cataclysm-BN
Pull request #6215: perf: parallelize overmap generation / overmap tile searches
Author: login: usagirei
Labels: name: src
description: changes related to source code.
name: JSON
description: related to game datas in JSON format.
name: tests
description: changes related to tests
Created: 2025-03-19T02:05:39Z | Merged: 2025-03-31T01:53:01Z

## Purpose of change (The Why)

Using maps is slow for many reasons: 
* It may generate multiple overmaps 
* Searching for overmap tile features is done in an outward spiral, optionally over each map layer
  * This needs to generate the "coordinate spiral" first which can be memory / cpu intensive, due to the size of the vector growing exponentially with the search range. A lot of wasted time if you search a large range but the tile you want is close by.
  * The overmap was looked up for every single overmap tile being checked. Expensive.

## Describe the solution (The How)

Optimize the performance bottlenecks and throw multithreading at it, Included but not limited to:

* Reduce memory allocation and wasted cpu usage of closest_point_first by not generating the whole search space at once, and using an iterator that spits out coordinates on demand
* Make find_all search for tiles in small chunks of tiles on multiple threads at once
   * Each "scan task" consolidates multiple coordinates on a specific overmap, and keeps hold of it during the whole scan, instead of getting looking up the overmap for every overmap tile
* Make find_closest / find_random methods a find_all internally and take advantage of the optimizations
* Adding more filtering / constraint options to omt_find_params, in order to reduce search space / return early
* Constraining missions and other functions that called find_closest/find_random without range constraints

## Describe alternatives you've considered

Cry in slow code

## Testing

* Launch game.
* Spawn maps and use them.
   * Using maps now should be virtually instantaneous
* Spawn npcs and generate missions.
   * The cattail jelly mission fires off a scan for a nearby existing forest_water tile, so do the kill jabberwock on a nearby thick_forest   
   * NPCs by themselves outside an area should trigger their "move to somewhere to fill needs" behaviour, which also triggers the optimized code

## Additional context

Need to test on platforms other than windows, since I don't have access to those I can't test locally.

As a followup of these changes, there are some places that can make use of the iterator style closest_points_first (named closest_points_generator) for potential reductions of memory usage / cpu time, since the generating a vector might not be needed if its just used for iterating in a forward manner

## Checklist

<!--
NOTE: Please grant permission for repository maintainers to edit your PR.  It is EXTREMELY common for PRs to be held up due to trivial changes being requested and the author being unavailable to make them.  In web UI, you can do it by clicking the "Allow edits and access to secrets by maintainers" checkbox next to "Create Pull Request" button at the bottom of the editor, or by clicking the same checkbox in the sidebar after PR has been created.

NOTE: Please read your emails. Anyone mentioned on Github with an @ will receive an email, any activity on your work will also send emails. This is more reliable than being notified on our Discord, you will always get an email.
--->

### Mandatory

- [x] I wrote the PR title in [conventional commit format](https://docs.cataclysmbn.org/en/contribute/changelog_guidelines/).
- [x] I ran the [code formatter](https://docs.cataclysmbn.org/en/contribute/contributing/#code-style).
- [x] I linked any relevant issues using [github keyword syntax](https://docs.cataclysmbn.org/en/contribute/contributing/#pull-request-notes) like `closes #1234` in [Summary of the PR](#purpose-of-change-the-why) so it can be closed automatically.
- [x] I've [committed my changes to new branch that isn't `main`](https://docs.cataclysmbn.org/en/contribute/contributing/#make-your-changes) so it won't cause conflict when updating `main` branch later.

<!--
please remove sections irrelevant to this PR.

### Optional

- [ ] This PR ports commits from DDA or other cataclysm forks.
  - [ ] I have attributed original authors in the commit messages adding [`Co-Authored-By`](https://docs.github.com/pull-requests/committing-changes-to-your-project/creating-and-editing-commits/creating-a-commit-with-multiple-authors) in the commit message.
  - [ ] I have linked the URL of original PR(s) in the description.
- [ ] This is a C++ PR that modifies JSON loading or behavior.
  - [ ] I have documented the changes in the appropriate location in the `doc/` folder.
  - [ ] If documentation for this feature does not exist, please write it or at least note its lack in PR description.
  - [ ] New localizable fields need to be added to the `lang/bn_extract_json_strings.sh` script if it does not support them yet.
  - [ ] If applicable, add checks on game load that would validate the loaded data.
  - [ ] If it modifies format of save files, please add migration from the old format.
- [ ] This is a PR that modifies build process or code organization.
  - [ ] Please document the changes in the appropriate location in the `doc/` folder.
  - [ ] If documentation for this feature or process does not exist, please write it.
  - [ ] If the change alters versions of software required to build or work with the game, please document it.
- [ ] This is a PR that removes JSON entities.
  - [ ] The removed JSON entities have new entries in `data/json/obsoletion/` folder or use some other migration process for existing saves.
-->



Diff:
diff --git a/data/json/npcs/missiondef.json b/data/json/npcs/missiondef.json
index 9772dc2c2a5e..bda74cfa9da1 100644
--- a/data/json/npcs/missiondef.json
+++ b/data/json/npcs/missiondef.json
@@ -9,7 +9,7 @@
     "value": 50000,
     "start": {
       "effect": [ "follow", { "u_buy_item": "sarcophagus_access_code" } ],
-      "assign_mission_target": { "om_terrain": "haz_sar_1_1", "om_special": "Hazardous Waste Sarcophagus", "reveal_radius": 3 }
+      "assign_mission_target": { "om_terrain": "haz_sar_1_1", "om_special": "Hazardous Waste Sarcophagus", "reveal_radius": 3, "z": 0 }
     },
     "origins": [ "ORIGIN_SECONDARY" ],
     "destination": "haz_sar_b_1",
@@ -336,7 +336,7 @@
     "item": "software_blood_data",
     "start": {
       "effect": [ { "u_buy_item": "vacutainer" }, { "u_buy_item": "usb_drive" } ],
-      "assign_mission_target": { "om_terrain": "hospital_2", "om_special": "hospital", "reveal_radius": 3 }
+      "assign_mission_target": { "om_terrain": "hospital_2", "om_special": "hospital", "reveal_radius": 3, "z": 0 }
     },
     "origins": [ "ORIGIN_SECONDARY" ],
     "dialogue": {
@@ -359,7 +359,9 @@
     "difficulty": 2,
     "value": 150000,
     "item": "etched_skull",
-    "start": { "assign_mission_target": { "om_terrain": "cabin_strange", "om_special": "Strange Cabin", "reveal_radius": 3 } },
+    "start": {
+      "assign_mission_target": { "om_terrain": "cabin_strange", "om_special": "Strange Cabin", "reveal_radius": 3, "z": 0 }
+    },
     "origins": [ "ORIGIN_SECONDARY" ],
     "followup": "MISSION_INVESTIGATE_PRISON_VISIONARY",
     "dialogue": {
@@ -383,7 +385,7 @@
     "value": 150000,
     "item": "visions_solitude",
     "start": {
-      "assign_mission_target": { "om_terrain": "prison_1_4", "om_special": "Prison", "reveal_radius": 3 },
+      "assign_mission_target": { "om_terrain": "prison_1_4", "om_special": "Prison", "reveal_radius": 3, "z": 0 },
       "update_mapgen": {
         "set": [ { "point": "terrain", "id": "t_door_bar_o", "x": 12, "y": 19 } ],
         "place_fields": [
@@ -575,7 +577,7 @@
     "goal": "MGOAL_GO_TO_TYPE",
     "difficulty": 2,
     "value": 60000,
-    "start": { "effect": "follow_only", "assign_mission_target": { "om_terrain": "fema_entrance", "reveal_radius": 3 } },
+    "start": { "effect": "follow_only", "assign_mission_target": { "om_terrain": "fema_entrance", "reveal_radius": 3, "z": 0 } },
     "origins": [ "ORIGIN_SECONDARY" ],
     "followup": "MISSION_REACH_FARM_HOUSE",
     "destination": "fema_entrance",
@@ -600,7 +602,7 @@
     "destination": "s_air_hangars",
     "difficulty": 1,
     "value": 0,
-    "start": { "assign_mission_target": { "om_terrain": "s_air_hangars", "reveal_radius": 5, "om_special": "o_airport" } },
+    "start": { "assign_mission_target": { "om_terrain": "s_air_hangars", "reveal_radius": 5, "om_special": "o_airport", "z": 0 } },
     "origins": [ "ORIGIN_GAME_START" ]
   },
   {
@@ -628,7 +630,8 @@
         "om_special": "Mansion_Wild",
         "reveal_radius": 1,
         "search_range": 200,
-        "min_distance": 45
+        "min_distance": 45,
+        "z": 0
       }
     },
     "origins": [ "ORIGIN_GAME_START" ]
@@ -805,7 +808,7 @@
     "origins": [ "ORIGIN_OPENER_NPC", "ORIGIN_ANY_NPC" ],
     "start": {
       "effect": [ { "u_buy_item": "duffelbag" } ],
-      "assign_mission_target": { "om_terrain": "forest_water", "reveal_radius": 3 }
+      "assign_mission_target": { "om_terrain": "forest_water", "reveal_radius": 3, "z": 0 }
     },
     "end": {
       "effect": [
diff --git a/src/coordinates.h b/src/coordinates.h
index 27dd853da691..ce832bf387fd 100644
--- a/src/coordinates.h
+++ b/src/coordinates.h
@@ -587,28 +587,6 @@ Tripoint midpoint( const half_open_cuboid<Tripoint> &box )
     return midpoint( box.p_min, box.p_max );
 }
 
-template<typename Point, coords::origin Origin, coords::scale Scale>
-std::vector<coords::coord_point<Point, Origin, Scale>>
-        closest_points_first( const coords::coord_point<Point, Origin, Scale> &loc,
-                              int min_dist, int max_dist )
-{
-    std::vector<Point> raw_result = closest_points_first( loc.raw(), min_dist, max_dist );
-    std::vector<coords::coord_point<Point, Origin, Scale>> result;
-    result.reserve( raw_result.size() );
-    std::transform( raw_result.begin(), raw_result.end(), std::back_inserter( result ),
-    []( const Point & p ) {
-        return coords::coord_point<Point, Origin, Scale>( p );
-    } );
-    return result;
-}
-template<typename Point, coords::origin Origin, coords::scale Scale>
-std::vector<coords::coord_point<Point, Origin, Scale>>
-        closest_points_first( const coords::coord_point<Point, Origin, Scale> &loc,
-                              int max_dist )
-{
-    return closest_points_first( loc, 0, max_dist );
-}
-
 /* find appropriate subdivided coordinates for absolute tile coordinate.
  * This is less obvious than one might think, for negative coordinates, so this
  * was created to give a definitive answer.
diff --git a/src/game.cpp b/src/game.cpp
index 5bacb91865d5..77df89f4056e 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -779,13 +779,13 @@ vehicle *game::place_vehicle_nearby(
         }
     }
     for( const std::string &search_type : search_types ) {
+        // TODO: Pull-up find_params and use that scan result instead
+        // find nearest road
         omt_find_params find_params;
-        find_params.must_see = false;
-        find_params.cant_see = false;
         find_params.types.emplace_back( search_type, ot_match_type::type );
-        // find nearest road
-        find_params.min_distance = min_distance;
-        find_params.search_range = max_distance;
+        find_params.search_range = { min_distance, max_distance };
+        find_params.search_layers = std::nullopt;
+
         // if player spawns underground, park their car on the surface.
         const tripoint_abs_omt omt_origin( origin, 0 );
         for( const tripoint_abs_omt &goal : overmap_buffer.find_all( omt_origin, find_params ) ) {
@@ -6992,7 +6992,7 @@ std::vector<map_item_stack> game::find_nearby_items( int iRadius )
 
     for( int i = 1; i <= range; i++ ) {
         int z = i % 2 ? center_z - i / 2 : center_z + i / 2;
-        for( auto &points_p_it : closest_points_first( {u.pos().xy(), z}, iRadius ) ) {
+        for( auto &points_p_it : closest_points_first<tripoint>( {u.pos().xy(), z}, iRadius ) ) {
             if( points_p_it.y >= u.posy() - iRadius && points_p_it.y <= u.posy() + iRadius &&
                 u.sees( points_p_it ) &&
                 m.sees_some_items( points_p_it, u ) ) {
diff --git a/src/iuse_actor.cpp b/src/iuse_actor.cpp
index 33626143b1c8..8e1135782475 100644
--- a/src/iuse_actor.cpp
+++ b/src/iuse_actor.cpp
@@ -62,12 +62,13 @@
 #include "mutation.h"
 #include "options.h"
 #include "output.h"
-#include "omdata.h"
+#include "overmap.h"
 #include "overmap_ui.h"
 #include "overmapbuffer.h"
 #include "player.h"
 #include "player_activity.h"
 #include "pldata.h"
+#include "popup.h"
 #include "point.h"
 #include "recipe.h"
 #include "recipe_dictionary.h"
@@ -1349,48 +1350,69 @@ void reveal_map_actor::load( const JsonObject &obj )
 void reveal_map_actor::reveal_targets( const tripoint_abs_omt &map ) const
 {
     omt_find_params params{};
-    params.search_range = radius;
+    params.search_range = { 0, radius };
+    params.search_layers = omt_find_all_layers;
     params.types = omt_types;
-    params.must_see = false;
     params.existing_only = false;
+    params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
 
-    for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-        const auto places = overmap_buffer.find_all( tripoint_abs_omt( map.xy(), z ), params );
-        for( auto &place : places ) {
-            overmap_buffer.reveal( place, 0 );
+    /*
+    * Stagger parallel map generation starting from center (0), outwards
+    * so the generated maps have a neighbor to latch onto when generating roads/rivers
+    * 5 4 3 2 3 4 5
+    * 4 3 2 1 2 3 4
+    * 3 2 1 0 1 2 3
+    * 4 3 2 1 2 3 4
+    * 5 4 3 2 3 4 5
+    */
+
+    const point_abs_om origin_om_pos = project_to<coords::om>( map.xy() );
+
+    // Generate a Square fitting the requested map radius
+    const point_abs_omt omt_bb_min = map.xy() - point_rel_omt{ radius, radius };
+    const point_abs_omt omt_bb_max = map.xy() + point_rel_omt{ radius, radius };
+
+    // OM Corners of bounding box
+    const point_abs_om om_bb_min = project_to<coords::om>( omt_bb_min );
+    const point_abs_om om_bb_max = project_to<coords::om>( omt_bb_max );
+
+    // Iterate through range [om_bb_min, om_bb_max] to get the OM we want, then sort by manhattan distance
+    std::map<int, std::vector<point_abs_om>> om_to_generate;
+    for( int x = om_bb_min.x(); x <= om_bb_max.x(); ++x ) {
+        for( int y = om_bb_min.y(); y <= om_bb_max.y(); ++y ) {
+            auto dist = manhattan_dist( origin_om_pos, { x, y } );
+            auto &vec =
+                om_to_generate[dist]; // if the vector for this distance doesn't exist it will be created empty
+            vec.emplace_back( x, y );
         }
     }
+
+    for( const auto& [_, to_gen] : om_to_generate ) {
+        overmap_buffer.generate( to_gen );
+    }
+
+    const auto places = overmap_buffer.find_all( map, params );
+    for( auto &place : places ) {
+        overmap_buffer.reveal( place, 0 );
+    }
 }
 
 void reveal_map_actor::show_revealed( player &p, item &item, const tripoint_abs_omt &center ) const
 {
     omt_find_params params{};
-    params.search_range = radius;
+    params.search_range = { 0, radius };
     params.types = omt_types_view;
-    params.must_see = false;
+    params.exclude_types = omt_types_view_exclude;
     params.existing_only = true;
+    // TODO: Add support for variable reveal z-range to reveal_map iuse_action JSON
+    params.search_layers = omt_find_all_layers;
+    params.explored = false;
+    params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
 
-    const auto should_show = [&]( const tripoint_abs_omt & pt ) {
-        if( overmap_buffer.is_explored( pt ) ) {
-            return false;
-        }
-
-        const auto pred = [&]( const std::pair<std::string, ot_match_type> &x ) {
-            return overmap_buffer.check_ot_existing( x.first, x.second, pt );
-        };
+    const auto places = overmap_buffer.find_all( center, params );
 
-        if( std::any_of( omt_types_view_exclude.cbegin(), omt_types_view_exclude.cend(), pred ) ) {
-            return false;
-        }
-
-        return true;
-    };
-
-    std::vector<tripoint_abs_omt> places ;
-    for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-        const auto tmp = overmap_buffer.find_all( tripoint_abs_omt( center.xy(), z ), params );
-        std::copy_if( tmp.cbegin(), tmp.cend(), std::back_inserter( places ), should_show );
-    }
+    // Delete popup after search is done, before showing uilist
+    params.popup = nullptr;
 
     // Group tiles by name
     std::multimap<std::string, tripoint_abs_omt> mm;
diff --git a/src/iuse_software_minesweeper.cpp b/src/iuse_software_minesweeper.cpp
index 7789ac624030..b78043469fee 100644
--- a/src/iuse_software_minesweeper.cpp
+++ b/src/iuse_software_minesweeper.cpp
@@ -104,7 +104,7 @@ void minesweeper_game::new_level()
     for( int y = 0; y < level.y; y++ ) {
         for( int x = 0; x < level.x; x++ ) {
             if( mLevel[y][x] == bomb ) {
-                for( point p : closest_points_first( {x, y}, 1 ) ) {
+                for( point p : closest_points_first<point>( {x, y}, 1 ) ) {
                     if( p.x >= 0 && p.x < level.x && p.y >= 0 && p.y < level.y ) {
                         if( mLevel[p.y][p.x] != bomb ) {
                             mLevel[p.y][p.x]++;
diff --git a/src/mission_start.cpp b/src/mission_start.cpp
index 63d46e3e22a9..80ef41a75e91 100644
--- a/src/mission_start.cpp
+++ b/src/mission_start.cpp
@@ -95,17 +95,19 @@ void mission_start::kill_horde_master( mission *miss )
     // Npc joins you
     p->set_attitude( NPCATT_FOLLOW );
     // Pick one of the below locations for the horde to haunt
+
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "office_tower_1", ot_match_type::type );
+    find_params.types.emplace_back( "hotel_tower_1_8", ot_match_type::type );
+    // TODO: 'school_5' tile type doesn't seem to exist, and certainly not on z-layer 0
+    // find_params.types.emplace_back( "school_5", ot_match_type::type );
+    find_params.types.emplace_back( "forest_thick", ot_match_type::type );
+    find_params.search_range = { 0, 0 };
+    find_params.search_layers = { 0, 0 };
+
     const tripoint_abs_omt center = p->global_omt_location();
-    tripoint_abs_omt site = overmap_buffer.find_closest( center, "office_tower_1", 0, false );
-    if( site == overmap::invalid_tripoint ) {
-        site = overmap_buffer.find_closest( center, "hotel_tower_1_8", 0, false );
-    }
-    if( site == overmap::invalid_tripoint ) {
-        site = overmap_buffer.find_closest( center, "school_5", 0, false );
-    }
-    if( site == overmap::invalid_tripoint ) {
-        site = overmap_buffer.find_closest( center, "forest_thick", 0, false );
-    }
+    tripoint_abs_omt site = overmap_buffer.find_closest( center, find_params );
+
     miss->target = site;
     overmap_buffer.reveal( site, 6 );
     tinymap tile;
@@ -201,7 +203,6 @@ void mission_start::place_npc_software( mission *miss )
         miss->item_id = itype_software_hacking;
     } else if( dev->myclass == NC_DOCTOR ) {
         miss->item_id = itype_software_medical;
-        type = "s_pharm";
         miss->follow_up = mission_type_id( "MISSION_GET_ZOMBIE_BLOOD_ANAL" );
     } else if( dev->myclass == NC_SCIENTIST ) {
         miss->item_id = itype_software_math;
@@ -213,7 +214,12 @@ void mission_start::place_npc_software( mission *miss )
     if( type == "house" ) {
         place = mission_util::random_house_in_closest_city();
     } else {
-        place = overmap_buffer.find_closest( dev->global_omt_location(), type, 0, false );
+        omt_find_params find_params{};
+        find_params.types.emplace_back( "s_pharm", ot_match_type::type );
+        find_params.search_range = { 0, 0 };
+        find_params.search_layers = { 0, 0 };
+
+        place = overmap_buffer.find_closest( dev->global_omt_location(), find_params );
     }
     miss->target = place;
     overmap_buffer.reveal( place, 6 );
@@ -266,11 +272,14 @@ void mission_start::place_deposit_box( mission *miss )
     }
     // Npc joins you
     p->set_attitude( NPCATT_FOLLOW );
-    tripoint_abs_omt site =
-        overmap_buffer.find_closest( p->global_omt_location(), "bank", 0, false );
-    if( site == overmap::invalid_tripoint ) {
-        site = overmap_buffer.find_closest( p->global_omt_location(), "office_tower_1", 0, false );
-    }
+
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "bank", ot_match_type::type );
+    find_params.types.emplace_back( "office_tower_1", ot_match_type::type );
+    find_params.search_range = { 0, 0 };
+    find_params.search_layers = { 0, 0 };
+
+    tripoint_abs_omt site = overmap_buffer.find_closest( p->global_omt_location(), find_params );
 
     if( site == overmap::invalid_tripoint ) {
         site = p->global_omt_location();
@@ -627,10 +636,14 @@ void mission_start::reveal_refugee_center( mission *miss )
         return;
     }
 
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "road", ot_match_type::type );
+    find_params.search_range = { 0, 3 };
+    find_params.search_layers = { 0, 0 };
+
     const tripoint_abs_omt source_road = overmap_buffer.find_closest(
-            get_player_character().global_omt_location(), "road",
-            3, false );
-    const tripoint_abs_omt dest_road = overmap_buffer.find_closest( *target_pos, "road", 3, false );
+            get_player_character().global_omt_location(), find_params );
+    const tripoint_abs_omt dest_road = overmap_buffer.find_closest( *target_pos, find_params );
 
     omt_route_params params;
     params.radius = 1;
@@ -676,7 +689,13 @@ void mission_start::create_lab_console( mission *miss )
     // Pick a lab that has spaces on z = -1: e.g., in hidden labs.
     tripoint_abs_omt loc = player_character.global_omt_location();
     loc.z() = -1;
-    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, "lab", 0, false );
+
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "lab", ot_match_type::type );
+    find_params.search_range = { 0, 0 };
+    find_params.search_layers = std::nullopt;
+
+    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, find_params );
 
     create_lab_consoles( miss, place, "lab", 2, translate_marker( "Workstation" ),
                          translate_marker( "Download Memory Contents" ) );
@@ -710,7 +729,13 @@ void mission_start::create_ice_lab_console( mission *miss )
     // Pick an ice lab with spaces on z = -4.
     tripoint_abs_omt loc = player_character.global_omt_location();
     loc.z() = -4;
-    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, "ice_lab", 0, false );
+
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "ice_lab", ot_match_type::type );
+    find_params.search_range = { 0, 0 };
+    find_params.search_layers = std::nullopt;
+
+    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, find_params );
 
     create_lab_consoles( miss, place, "ice_lab", 3, translate_marker( "Durable Storage Archive" ),
                          translate_marker( "Download Archives" ) );
@@ -726,7 +751,13 @@ void mission_start::reveal_lab_train_depot( mission *miss )
     // Find and prepare lab location.
     tripoint_abs_omt loc = player_character.global_omt_location();
     loc.z() = -4;  // tunnels are at z = -4
-    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, "lab_train_depot", 0, false );
+
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "lab_train_depot", ot_match_type::type );
+    find_params.search_range = { 0, 0 };
+    find_params.search_layers = std::nullopt;
+
+    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, find_params );
 
     tinymap compmap;
     compmap.load( project_to<coords::sm>( place ), false );
diff --git a/src/mission_util.cpp b/src/mission_util.cpp
index ef48c11ef6dd..404c727b1ef1 100644
--- a/src/mission_util.cpp
+++ b/src/mission_util.cpp
@@ -33,8 +33,13 @@
 static tripoint_abs_omt reveal_destination( const std::string &type )
 {
     const tripoint_abs_omt your_pos = get_player_character().global_omt_location();
-    const tripoint_abs_omt center_pos =
-        overmap_buffer.find_random( your_pos, type, rng( 40, 80 ), false );
+
+    omt_find_params params{};
+    params.types.emplace_back( type, ot_match_type::type );
+    params.search_range = { 0, rng( 40, 80 ) };
+    params.search_layers = std::nullopt;
+
+    const tripoint_abs_omt center_pos = overmap_buffer.find_random( your_pos, params );
 
     if( center_pos != overmap::invalid_tripoint ) {
         overmap_buffer.reveal( center_pos, 2 );
@@ -73,13 +78,18 @@ static void reveal_any_target( mission *miss, const std::vector<std::string> &om
 bool mission_util::reveal_road( const tripoint_abs_omt &source, const tripoint_abs_omt &dest,
                                 overmapbuffer &omb )
 {
-    const tripoint_abs_omt source_road = overmap_buffer.find_closest( source, "road", 3, false );
-    const tripoint_abs_omt dest_road = overmap_buffer.find_closest( dest, "road", 3, false );
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "road", ot_match_type::type );
+    find_params.search_range = { 0, 3 };
+    find_params.search_layers = { 0, 0 };
 
-    omt_route_params params;
-    params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
+    const tripoint_abs_omt source_road = overmap_buffer.find_closest( source, find_params );
+    const tripoint_abs_omt dest_road = overmap_buffer.find_closest( dest, find_params );
 
-    return omb.reveal_route( source_road, dest_road, params );
+    omt_route_params route_params;
+    route_params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
+
+    return omb.reveal_route( source_road, dest_road, route_params );
 }
 
 /**
@@ -88,10 +98,17 @@ bool mission_util::reveal_road( const tripoint_abs_omt &source, const tripoint_a
 tripoint_abs_omt mission_util::reveal_om_ter( const std::string &omter, int reveal_rad,
         bool must_see, int target_z )
 {
+    omt_find_params find_params{};
+    find_params.types.emplace_back( omter, ot_match_type::type );
+    find_params.search_range = { 0, 0 };
+    // TODO: Determine if search_layers can be safely constrained to { target_z, target_z }
+    find_params.search_layers = omt_find_all_layers;
+    find_params.seen = must_see ? std::make_optional( true ) : std::nullopt;
+
     // Missions are normally on z-level 0, but allow an optional argument.
     tripoint_abs_omt loc = get_player_character().global_omt_location();
     loc.z() = target_z;
-    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, omter, 0, must_see );
+    const tripoint_abs_omt place = overmap_buffer.find_closest( loc, find_params );
     if( place != overmap::invalid_tripoint && reveal_rad >= 0 ) {
         overmap_buffer.reveal( place, reveal_rad );
     }
@@ -132,17 +149,18 @@ tripoint_abs_omt mission_util::random_house_in_closest_city()
 tripoint_abs_omt mission_util::target_closest_lab_entrance(
     const tripoint_abs_omt &origin, int reveal_rad, mission *miss )
 {
-    tripoint_abs_omt testpoint = origin;
+    omt_find_params find_params;
+    find_params.types.emplace_back( "lab_stairs", ot_match_type::contains );
+    find_params.search_range = { 0, 0 };
+    find_params.search_layers = std::nullopt;
+
     // Get the surface locations for labs and for spaces above hidden lab stairs.
-    testpoint.z() = 0;
-    tripoint_abs_omt surface = overmap_buffer.find_closest( testpoint, "lab_stairs", 0, false,
-                               ot_match_type::contains );
+    tripoint_abs_omt surface = overmap_buffer.find_closest( tripoint_abs_omt( origin.xy(), 0 ),
+                               find_params );
 
-    testpoint.z() = -1;
-    tripoint_abs_omt underground =
-        overmap_buffer.find_closest( testpoint, "hidden_lab_stairs", 0, false,
-                                     ot_match_type::contains );
-    underground.z() = 0;
+    find_params.types.front().first = "hidden_lab_stairs";
+    tripoint_abs_omt underground = overmap_buffer.find_closest( tripoint_abs_omt( origin.xy(), -1 ),
+                                   find_params );
 
     tripoint_abs_omt closest;
     if( square_dist( surface.xy(), origin.xy() ) <= square_dist( underground.xy(), origin.xy() ) ) {
@@ -151,7 +169,13 @@ tripoint_abs_omt mission_util::target_closest_lab_entrance(
         closest = underground;
     }
 
-    if( closest != overmap::invalid_tripoint && reveal_rad >= 0 ) {
+    if( closest == overmap::invalid_tripoint ) {
+        return closest;
+    }
+
+    // Reveal / Target surface regardless
+    closest = tripoint_abs_omt( closest.xy(), 0 );
+    if( reveal_rad >= 0 ) {
         overmap_buffer.reveal( closest, reveal_rad );
     }
     miss->set_target( closest );
@@ -163,21 +187,30 @@ static std::optional<tripoint_abs_omt> find_or_create_om_terrain(
 {
     tripoint_abs_omt target_pos = overmap::invalid_tripoint;
 
-    omt_find_params find_params;
-    std::vector<std::pair<std::string, ot_match_type>> temp_types;
-    std::pair<std::string, ot_match_type> temp_pair;
-    temp_pair.first = params.overmap_terrain;
-    temp_pair.second = params.overmap_terrain_match_type;
-    temp_types.push_back( temp_pair );
-    find_params.types = temp_types;
-    find_params.search_range = params.search_range;
-    find_params.min_distance = params.min_distance;
-    find_params.must_see = params.must_see;
-    find_params.cant_see = params.cant_see;
-    find_params.existing_only = true;
-    find_params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
+    auto make_find_params = [&]( bool existing_only ) {
+        omt_find_params find_params;
+        find_params.types.emplace_back( params.overmap_terrain, params.overmap_terrain_match_type );
+        find_params.search_range = { params.min_distance, params.search_range };
+        find_params.existing_only = existing_only;
+        find_params.seen = params.must_see
+                           ? std::make_optional( true )
+                           : params.cant_see
+                           ? std::make_optional( false )
+                           : std::nullopt;
+        find_params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
+        if( params.z.has_value() ) {
+            find_params.search_layers = std::make_pair( params.z.value(), params.z.value() );
+        } else {
+            // TODO: Change to omt_find_all_layers if things don't work as expected
+            find_params.search_layers = std::nullopt;
+        }
+        return find_params;
+    };
+
+    omt_find_params find_params = make_find_params( true );
 
     auto get_target_position = [&]() {
+
         // Either find a random or closest match, based on the criteria.
         if( params.random ) {
             target_pos = overmap_buffer.find_random( origin_pos, find_params );
@@ -187,8 +220,7 @@ static std::optional<tripoint_abs_omt> find_or_create_om_terrain(
 
         // If we didn't find a match, and we're allowed to create new terrain, and the player didn't
         // have to see the location beforehand, then we can attempt to create the new terrain.
-        if( target_pos == overmap::invalid_tripoint && params.create_if_necessary &&
-            !params.must_see ) {
+        if( target_pos == overmap::invalid_tripoint && params.create_if_necessary && !params.must_see ) {
             // If this terrain is part of an overmap special...
             if( params.overmap_special ) {
                 // ...then attempt to place the whole special.
@@ -197,18 +229,18 @@ static std::optional<tripoint_abs_omt> find_or_create_om_terrain(
                 // If we succeeded in placing the special, then try and find the particular location
                 // we're interested in.
                 if( placed ) {
-                    find_params.must_see = false;
+                    find_params.seen = std::nullopt;
                     target_pos = overmap_buffer.find_closest( origin_pos, find_params );
                 }
             } else if( params.replaceable_overmap_terrain ) {
                 // This terrain wasn't part of an overmap special, but we do have a replacement
                 // terrain specified. Find a random location of that replacement type.
-                find_params.must_see = false;
+                find_params.seen = std::nullopt;
                 find_params.types.front().first = *params.replaceable_overmap_terrain;
                 target_pos = overmap_buffer.find_random( origin_pos, find_params );
 
                 // We didn't find it, so allow this search to create new overmaps and try again.
-                find_params.existing_only = true;
+                find_params.existing_only = false;
                 if( target_pos == overmap::invalid_tripoint ) {
                     target_pos = overmap_buffer.find_random( origin_pos, find_params );
                 }
@@ -228,7 +260,7 @@ static std::optional<tripoint_abs_omt> find_or_create_om_terrain(
     if( target_pos == overmap::invalid_tripoint ) {
         // If it's invalid, then that means we couldn't find it or create it (if allowed) on
         // our current overmap. We'll now go ahead and try again but allow it to create new overmaps.
-        find_params.existing_only = false;
+        find_params = make_find_params( false );
         get_target_position();
     }
 
@@ -236,7 +268,7 @@ static std::optional<tripoint_abs_omt> find_or_create_om_terrain(
     // on any overmap (new or existing) within the allowed search range.
     if( target_pos == overmap::invalid_tripoint ) {
         debugmsg( "Unable to find and assign mission target %s within %d tiles.",
-                  params.overmap_terrain, find_params.search_range );
+                  params.overmap_terrain, find_params.search_range.second );
         return std::nullopt;
     }
     return target_pos;
@@ -334,7 +366,13 @@ tripoint_abs_omt mission_util::target_om_ter_random( const std::string &omter, i
         loc = g->u.global_omt_location();
     }
 
-    auto places = overmap_buffer.find_all( loc, omter, range, must_see );
+    omt_find_params find_params{};
+    find_params.types.emplace_back( omter, ot_match_type::type );
+    find_params.search_range = { 0, range };
+    find_params.search_layers = std::nullopt;
+    find_params.seen = must_see ? std::make_optional( true ) : std::nullopt;
+
+    auto places = overmap_buffer.find_all( loc, find_params );
     if( places.empty() ) {
         return g->u.global_omt_location();
     }
diff --git a/src/npcmove.cpp b/src/npcmove.cpp
index 9cc02f81ea00..42bb23dd2784 100644
--- a/src/npcmove.cpp
+++ b/src/npcmove.cpp
@@ -4143,8 +4143,9 @@ void npc::set_omt_destination()
             // note: no shuffle of `find_params.types` is needed, because `find_closest`
             // disregards `types` order anyway, and already returns random result among
             // those having equal minimal distance
-            find_params.search_range = 75;
-            find_params.existing_only = false;
+            find_params.search_range = { 0, 75 };
+            find_params.search_layers = omt_find_all_layers;
+
             goal = overmap_buffer.find_closest( surface_omt_loc, find_params );
             npc_need_goal_cache &cache = goal_cache[fulfill];
             cache.goal = goal;
diff --git a/src/npctalk_funcs.cpp b/src/npctalk_funcs.cpp
index b40f0a912b97..12ae85ce2d34 100644
--- a/src/npctalk_funcs.cpp
+++ b/src/npctalk_funcs.cpp
@@ -606,9 +606,13 @@ void talk_function::morale_chat_activity( npc &p )
 
 void talk_function::buy_10_logs( npc &p )
 {
-    std::vector<tripoint_abs_omt> places =
-        overmap_buffer.find_all( get_player_character().global_omt_location(), "ranch_camp_67", 1,
-                                 false );
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "ranch_camp_67", ot_match_type::type );
+    find_params.search_range = { 0, 1 };
+    find_params.search_layers = { 0, 0 };
+
+    std::vector<tripoint_abs_omt> places = overmap_buffer.find_all(
+            get_player_character().global_omt_location(), find_params );
     if( places.empty() ) {
         debugmsg( "Couldn't find %s", "ranch_camp_67" );
         return;
@@ -633,9 +637,13 @@ void talk_function::buy_10_logs( npc &p )
 
 void talk_function::buy_100_logs( npc &p )
 {
+    omt_find_params find_params{};
+    find_params.types.emplace_back( "ranch_camp_67", ot_match_type::type );
+    find_params.search_range = { 0, 1 };
+    find_params.search_layers = { 0, 0 };
+
     std::vector<tripoint_abs_omt> places =
-        overmap_buffer.find_all( get_player_character().global_omt_location(), "ranch_camp_67", 1,
-                                 false );
+        overmap_buffer.find_all( get_player_character().global_omt_location(), find_params );
     if( places.empty() ) {
         debugmsg( "Couldn't find %s", "ranch_camp_67" );
         return;
diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp
index 57b0aefd4cd5..9c0c71e88246 100644
--- a/src/overmapbuffer.cpp
+++ b/src/overmapbuffer.cpp
@@ -8,6 +8,7 @@
 #include <map>
 #include <optional>
 #include <queue>
+#include <future>
 
 #include "avatar.h"
 #include "calendar.h"
@@ -48,10 +49,14 @@
 
 class map_extra;
 
+template<typename _Mutex>
+using write_lock = std::unique_lock< _Mutex >;
+template<typename _Mutex>
+using read_lock = std::shared_lock< _Mutex >;
+
 overmapbuffer overmap_buffer;
 
 overmapbuffer::overmapbuffer()
-    : last_requested_overmap( nullptr )
 {
 }
 
@@ -67,40 +72,84 @@ omt_find_params::~omt_find_params() = default;
 
 omt_route_params::~omt_route_params() = default;
 
-
 overmap &overmapbuffer::get( const point_abs_om &p )
 {
-    if( last_requested_overmap != nullptr && last_requested_overmap->pos() == p ) {
-        return *last_requested_overmap;
+    {
+        read_lock<std::shared_mutex> _l( mutex );
+        const auto it = overmaps.find( p );
+        if( it != overmaps.end() ) {
+            return *it->second.get();
+        }
     }
 
-    const auto it = overmaps.find( p );
-    if( it != overmaps.end() ) {
-        return *( last_requested_overmap = it->second.get() );
-    }
+    overmap *new_om;
+    {
+        write_lock<std::shared_mutex> _l( mutex );
+        // Search for it again, but now with a lock since another thread could've loaded this overmap tile first
+        const auto it = overmaps.find( p );
+        if( it != overmaps.end() ) {
+            return *it->second.get();
+        }
 
-    // That constructor loads an existing overmap or creates a new one.
-    overmap &new_om = *( overmaps[ p ] = std::make_unique<overmap>( p ) );
-    new_om.populate();
+        // That constructor loads an existing overmap or creates a new one.
+        assert( overmaps.find( p ) == overmaps.end() );
+        overmaps[p] = std::make_unique<overmap>( p );
+        new_om = overmaps[p].get();
+    }
     // Note: fix_mongroups might load other overmaps, so overmaps.back() is not
     // necessarily the overmap at (x,y)
-    fix_mongroups( new_om );
-    fix_npcs( new_om );
+    new_om->populate();
+    fix_mongroups( *new_om );
+    fix_npcs( *new_om );
 
-    last_requested_overmap = &new_om;
-    return new_om;
+    return *new_om;
 }
 
 void overmapbuffer::create_custom_overmap( const point_abs_om &p, overmap_special_batch &specials )
 {
-    if( last_requested_overmap != nullptr ) {
-        auto om_iter = overmaps.find( p );
-        if( om_iter != overmaps.end() && om_iter->second.get() == last_requested_overmap ) {
-            last_requested_overmap = nullptr;
+    overmap *new_om;
+    {
+        write_lock<std::shared_mutex> _l( mutex );
+        overmaps[p] = std::make_unique<overmap>( p );
+        new_om = overmaps[p].get();
+    }
+    new_om->populate( specials );
+}
+
+void overmapbuffer::generate( const std::vector<point_abs_om> &locs )
+{
+    using overmap_loc = std::pair<point_abs_om, std::unique_ptr<overmap>>;
+
+    std::vector<std::future<overmap_loc>> async_data;
+    for( auto &loc : locs ) {
+        if( overmap_buffer.has( loc ) ) {
+            continue;
+        }
+
+        auto gen_func = [&]() {
+            auto map = std::make_unique<overmap>( loc );
+            map->populate();
+            fix_mongroups( *map );
+            fix_npcs( *map );
+            return std::make_pair( loc, std::move( map ) );
+        };
+        async_data.push_back( std::async( std::launch::async, gen_func ) );
+    }
+
+    auto popup = make_shared_fast<throbber_popup>( _( "Please wait..." ) );
+    for( auto &f : async_data ) {
+        while( f.wait_for( std::chrono::milliseconds( 10 ) ) != std::future_status::ready ) {
+            popup->refresh();
+        }
+    }
+
+    {
+        write_lock<std::shared_mutex> _l( mutex );
+        for( auto &m : async_data ) {
+            auto result = m.get();
+            overmaps[result.first] = std::move( result.second );
         }
     }
-    overmap &new_om = *( overmaps[ p ] = std::make_unique<overmap>( p ) );
-    new_om.populate( specials );
 }
 
 void overmapbuffer::fix_mongroups( overmap &new_overmap )
@@ -190,6 +239,8 @@ void overmapbuffer::fix_npcs( overmap &new_overmap )
 
 void overmapbuffer::save()
 {
+    read_lock<std::shared_mutex> _l( mutex );
+
     for( auto &omp : overmaps ) {
         // Note: this may throw io errors from std::ofstream
         omp.second->save();
@@ -198,10 +249,11 @@ void overmapbuffer::save()
 
 void overmapbuffer::clear()
 {
+    write_lock<std::shared_mutex> _l( mutex );
+
     overmaps.clear();
     known_non_existing.clear();
     placed_unique_specials.clear();
-    last_requested_overmap = nullptr;
 }
 
 const regional_settings &overmapbuffer::get_settings( const tripoint_abs_omt &p )
@@ -248,17 +300,18 @@ void overmapbuffer::delete_extra( const tripoint_abs_omt &p )
 
 overmap *overmapbuffer::get_existing( const point_abs_om &p )
 {
-    if( last_requested_overmap && last_requested_overmap->pos() == p ) {
-        return last_requested_overmap;
-    }
-    const auto it = overmaps.find( p );
-    if( it != overmaps.end() ) {
-        return last_requested_overmap = it->second.get();
-    }
-    if( known_non_existing.contains( p ) ) {
-        // This overmap does not exist on disk (this has already been
-        // checked in a previous call of this function).
-        return nullptr;
+    {
+        read_lock<std::shared_mutex> _l( mutex );
+        const auto it = overmaps.find( p );
+        if( it != overmaps.end() ) {
+            return it->second.get();
+        }
+
+        if( known_non_existing.contains( p ) ) {
+            // This overmap does not exist on disk (this has already been
+            // checked in a previous call of this function).
+            return nullptr;
+        }
     }
     if( g->get_active_world() && g->get_active_world()->overmap_exists( p ) ) {
         // File exists, load it normally (the get function
@@ -271,7 +324,10 @@ overmap *overmapbuffer::get_existing( const point_abs_om &p )
     // return early.
     // If the overmap had been created in the mean time, the previous
     // loop would have found and returned it.
-    known_non_existing.insert( p );
+    {
+        write_lock<std::shared_mutex> _l( mutex );
+        known_non_existing.insert( p );
+    }
     return nullptr;
 }
 
@@ -953,62 +1009,60 @@ bool overmapbuffer::contains_unique_special( const overmap_special_id &id ) cons
     return placed_unique_specials.contains( id );
 }
 
-static omt_find_params assign_params(
-    const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    omt_find_params params;
-    std::vector<std::pair<std::string, ot_match_type>> temp_types;
-    std::pair<std::string, ot_match_type> temp_pair;
-    temp_pair.first = type;
-    temp_pair.second = match_type;
-    temp_types.push_back( temp_pair );
-    params.types = temp_types;
-    params.search_range = radius;
-    params.must_see = must_be_seen;
-    params.existing_only = existing_overmaps_only;
-    params.om_special = om_special;
-    return params;
-}
-
 bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
         const omt_find_params &params )
 {
-    bool type_matches = false;
+    overmap_with_local_coords om_loc;
     if( params.existing_only ) {
-        for( const std::pair<std::string, ot_match_type> &elem : params.types ) {
-            type_matches = check_ot_existing( elem.first, elem.second, location );
-            if( type_matches ) {
-                break;
-            }
-        }
+        om_loc = get_existing_om_global( location );
     } else {
-        for( const std::pair<std::string, ot_match_type> &elem : params.types ) {
-            type_matches = check_ot( elem.first, elem.second, location );
-            if( type_matches ) {
-                break;
-            }
-        }
+        om_loc = get_om_global( location );
     }
-    if( !type_matches ) {
+
+    return is_findable_location( om_loc, params );
+}
+
+bool overmapbuffer::is_findable_location( const overmap_with_local_coords &om_loc,
+        const omt_find_params &params )
+{
+    if( om_loc.om == nullptr ) {
         return false;
     }
 
-    if( params.must_see && !seen( location ) ) {
+    const auto is_seen = om_loc.om->seen( om_loc.local );
+    if( params.seen.has_value() && params.seen.value() != is_seen ) {
         return false;
     }
-    if( params.cant_see && seen( location ) ) {
+
+    const auto is_explored = om_loc.om->explored( om_loc.local );
+    if( params.explored.has_value() && params.explored.value() != is_explored ) {
         return false;
     }
 
-    if( params.om_special ) {
-        bool meets_om_special = false;
-        if( params.existing_only ) {
-            meets_om_special = check_overmap_special_type_existing( *params.om_special, location );
-        } else {
-            meets_om_special = check_overmap_special_type( *params.om_special, location );
+    bool is_excluded = false;
+    for( const std::pair<std::string, ot_match_type> &elem : params.exclude_types ) {
+        is_excluded = om_loc.om->check_ot( elem.first, elem.second, om_loc.local );
+        if( is_excluded ) {
+            break;
         }
+    }
+    if( is_excluded ) {
+        return false;
+    }
+
+    bool is_included = false;
+    for( const std::pair<std::string, ot_match_type> &elem : params.types ) {
+        is_included = om_loc.om->check_ot( elem.first, elem.second, om_loc.local );
+        if( is_included ) {
+            break;
+        }
+    }
+    if( !is_included ) {
+        return false;
+    }
+
+    if( params.om_special ) {
+        bool meets_om_special = om_loc.om->check_overmap_special_type( *params.om_special, om_loc.local );
         if( !meets_om_special ) {
             return false;
         }
@@ -1017,131 +1071,323 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
 }
 
-tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    // Check the origin before searching adjacent tiles!
-    if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
-        return origin;
+    const auto concurrency = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    if( concurrency == 1 ) {
+        return find_all_sync( origin, params );
+    } else {
+        return find_all_async( origin, params );
     }
+}
 
-    // By default search overmaps within a radius of 4,
-    // i.e. C = current overmap, X = overmaps searched:
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXCXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    //
-    // See overmap::place_specials for how we attempt to insure specials are placed within this
-    // range.  The actual number is 5 because 1 covers the current overmap,
-    // and each additional one expends the search to the next concentric circle of overmaps.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_sync( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
+{
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
 
-    std::vector<tripoint_abs_omt> result;
-    std::optional<int> found_dist;
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+    std::vector<tripoint_abs_omt> find_result;
+    find_result.reserve( params.max_results.value_or( 256 ) );
+    while( true ) {
 
-    for( const point_abs_omt &loc_xy : closest_points_first( origin.xy(), min_dist, max_dist ) ) {
-        const int dist_xy = square_dist( origin.xy(), loc_xy );
+        if( params.popup ) {
+            params.popup->refresh();
+        }
 
-        if( found_dist && *found_dist < dist_xy ) {
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
             break;
         }
 
-        for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-            const tripoint_abs_omt loc( loc_xy, z );
-            const int dist = square_dist( origin, loc );
+        auto& [task_om, task_omts] = task_data.value();
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
 
-            if( found_dist && *found_dist < dist ) {
-                continue;
-            }
+        overmap *om_loc = &get( task_om );
 
-            if( is_findable_location( loc, params ) ) {
-                found_dist = dist;
-                result.push_back( loc );
+        bool done = false;
+        for( const auto &loc : task_omts ) {
+            overmap_with_local_coords q{ om_loc, loc.second };
+            if( is_findable_location( q, params ) ) {
+                find_result.push_back( loc.first );
             }
-
-            counter += 1;
-            if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
-                params.popup->refresh();
-                num_overmaps = overmaps.size();
-                counter = 0;
+            if( params.max_results.has_value() &&
+                find_result.size() == static_cast<size_t>( params.max_results.value() ) ) {
+                done = true;
+                break;
             }
         }
+
+        if( done ) {
+            break;
+        }
     }
 
-    return random_entry( result, overmap::invalid_tripoint );
+    return find_result;
 }
 
-std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_async( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    std::vector<tripoint_abs_omt> result;
-    // dist == 0 means search a whole overmap diameter.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX;
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
+
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
+
+    std::deque<std::future<std::vector<tripoint_abs_omt>>> tasks;
+
+    std::vector<tripoint_abs_omt> find_result;
+    int free_tasks = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    auto try_finish_task = []( std::future<std::vector<tripoint_abs_omt>> &task,
+    std::vector<tripoint_abs_omt> &dst, omt_find_params params ) -> bool {
+        if( task.wait_for( std::chrono::milliseconds( 0 ) ) == std::future_status::ready )
+        {
+            auto task_result = task.get();
+
+            if( !params.max_results.has_value() ||
+                dst.size() < static_cast<size_t>( params.max_results.value() ) ) {
+                std::copy( task_result.begin(), task_result.end(), std::back_inserter( dst ) );
+                if( params.max_results.has_value() && dst.size() > params.max_results.value() ) {
+                    dst.resize( params.max_results.value() );
+                }
+            }
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+            return true;
+        }
+        return false;
+    };
 
-    for( const tripoint_abs_omt &loc : closest_points_first( origin, min_dist, max_dist ) ) {
-        if( is_findable_location( loc, params ) ) {
-            result.push_back( loc );
+    while( true ) {
+        if( params.popup ) {
+            params.popup->refresh();
         }
 
-        counter += 1;
-        if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
+        if( !tasks.empty() ) {
+            if( try_finish_task( tasks.front(), find_result, params ) ) {
+                tasks.pop_front();
+                ++free_tasks;
+            }
+            if( params.max_results.has_value() &&
+                find_result.size() >= params.max_results.value() ) {
+                break;
+            }
+        }
+
+        if( free_tasks == 0 ) {
+            continue;
+        }
+
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
+            break;
+        }
+
+        auto& [task_om, task_omts] = task_data.value();
+
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
+
+        auto task_func = [&]( point_abs_om l,
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> locals ) {
+            std::vector<tripoint_abs_omt> result;
+
+            overmap *om_loc;
+            if( params.existing_only ) {
+                om_loc = get_existing( l );
+            } else {
+                om_loc = &get( l );
+            }
+            if( !om_loc ) {
+                return result;
+            }
+
+            for( const auto &loc : locals ) {
+                overmap_with_local_coords q{ om_loc, loc.second };
+                if( is_findable_location( q, params ) ) {
+                    result.push_back( loc.first );
+                }
+                if( params.max_results.has_value() && result.size() == params.max_results.value() ) {
+                    break;
+                }
+            }
+
+            return result;
+        };
+
+        auto task = std::async( std::launch::async, task_func, task_om, std::move( task_omts ) );
+
+        tasks.push_back( std::move( task ) );
+
+        --free_tasks;
+    }
+
+    while( !tasks.empty() ) {
+        if( params.popup ) {
             params.popup->refresh();
-            num_overmaps = overmaps.size();
-            counter = 0;
+        }
+
+        if( try_finish_task( tasks.front(), find_result, params ) ) {
+            tasks.pop_front();
+            ++free_tasks;
         }
     }
 
-    return result;
+    return find_result;
 }
 
-std::vector<tripoint_abs_omt> overmapbuffer::find_all(
-    const tripoint_abs_omt &origin, const std::string &type, int dist, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
+tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+        const omt_find_params &pp )
 {
-    const omt_find_params params = assign_params( type, dist, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_all( origin, params );
+    // Check the origin before searching adjacent tiles!
+    if( pp.search_range.first == 0 && is_findable_location( origin, pp ) ) {
+        return origin;
+    }
+
+    // By default search overmaps within a radius of 4,
+    // i.e. C = current overmap, X = overmaps searched:
+    //
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXCXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    //
+    // See overmap::place_specials for how we attempt to insure specials are placed within this
+    // range.  The actual number is 5 because 1 covers the current overmap,
+    // and each additional one expends the search to the next concentric circle of overmaps.
+
+    omt_find_params params = pp;
+    if( params.search_range.second == 0 ) {
+        params.search_range.second = OMAPX * 5;
+    }
+    if( !params.max_results.has_value() ) {
+        // Limit potential random sample size, scaling with number of terrain types
+        params.max_results = 10 * pp.types.size();
+    }
+
+    auto scan_result = find_all( origin, params );
+
+    if( scan_result.empty() ) {
+        return overmap::invalid_tripoint;
+    }
+
+    std::vector<tripoint_abs_omt> near_points;
+    int min_dist = INT_MAX;
+
+    // Only care about the absolute nearest points, so only keep those points
+    for( auto &p : scan_result ) {
+        int dist = square_dist( origin, p );
+        if( dist < min_dist ) {
+            near_points.clear();
+            min_dist = dist;
+        } else if( dist > min_dist ) {
+            continue;
+        }
+        near_points.emplace_back( p );
+    }
+
+    // rng is inclusive
+    auto random_idx = rng( 0, near_points.size() - 1 );
+    return near_points[random_idx];
 }
 
 tripoint_abs_omt overmapbuffer::find_random( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    return random_entry( find_all( origin, params ), overmap::invalid_tripoint );
-}
-
-tripoint_abs_omt overmapbuffer::find_random(
-    const tripoint_abs_omt &origin, const std::string &type, int dist, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, dist, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_random( origin, params );
+    auto found = find_all( origin, params );
+    return random_entry( found, overmap::invalid_tripoint );
 }
 
 shared_ptr_fast<npc> overmapbuffer::find_npc( character_id id )
diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h
index b26635acd74f..87643ed1c1ab 100644
--- a/src/overmapbuffer.h
+++ b/src/overmapbuffer.h
@@ -12,6 +12,7 @@
 #include <unordered_set>
 #include <utility>
 #include <vector>
+#include <shared_mutex>
 
 #include "coordinates.h"
 #include "enums.h"
@@ -111,29 +112,38 @@ struct overmap_with_local_coords {
  * Standard arguments for finding overmap terrain
  * @param origin Location of search
  * @param types vector of Terrain type/matching rule to use to find the type
- * @param search_range The maximum search distance.  If 0, OMAPX is used.
+ * @param exclude_types vector of Terrain type/matching rule to use to find the type
+ * @param search_range The [minimum, maximum] search distance. If maximum is 0, a function specific default is used.
+ * @param search_layers If set, overrides the search layers to this range. Layer from search origin otherwise.
  * @param min_distance Matches within min_distance are ignored.
- * @param must_see If true, only terrain seen by the player should be searched.
- * @param cant_see If true, only terrain not seen by the player should be searched
- * @param existing_overmaps_only If true, will restrict searches to existing overmaps only. This
+ * @param seen If true, only terrain seen by the player should be searched.
+ * If false, only terrain not seen by the player should be searched.
+ * @param explored If true, only terrain marked as explored by the player should be searched.
+ * If false, only terrain not marked as explored by the player should be searched.
+ * @param existing_only If true, will restrict searches to existing overmaps only. This
  * is particularly useful if we want to attempt to add a missing overmap special to an existing
  * overmap rather than creating many overmaps in an attempt to find it.
  * @param om_special If set, the terrain must be part of the specified overmap special.
  * @param popup If set, the popup will be periodically updated to indicate ongoing search.
+ * @param max_results, If set, limits the search result to at most n entries
 */
 struct omt_find_params {
     ~omt_find_params();
 
     std::vector<std::pair<std::string, ot_match_type>> types;
-    int search_range = 0;
-    int min_distance = 0;
-    bool must_see = false;
-    bool cant_see = false;
+    std::vector<std::pair<std::string, ot_match_type>> exclude_types;
+    std::pair<int, int> search_range = { 0, 0 };
+    std::optional<std::pair<int, int>> search_layers = std::nullopt;
+    std::optional<bool> seen = std::nullopt;
+    std::optional<bool> explored = std::nullopt;
     bool existing_only = false;
     std::optional<overmap_special_id> om_special = std::nullopt;
     shared_ptr_fast<throbber_popup> popup = nullptr;
+    std::optional<int> max_results = std::nullopt;
 };
 
+constexpr const std::pair<int, int> omt_find_all_layers = { -OVERMAP_DEPTH, OVERMAP_HEIGHT };
+
 /**
  * Standard arguments for finding overmap route
  * @param radius Radius of revealed terrain around the route
@@ -162,6 +172,11 @@ class overmapbuffer
         void clear();
         void create_custom_overmap( const point_abs_om &, overmap_special_batch &specials );
 
+        /**
+        * Generates overmap tiles, if missing
+        */
+        void generate( const std::vector<point_abs_om> &locs );
+
         /**
          * Returns the overmap terrain at the given OMT coordinates.
          * Creates a new overmap if necessary.
@@ -301,19 +316,18 @@ class overmapbuffer
 
         /**
          * Find all places with the specific overmap terrain type.
-         * The function only searches on the z-level indicated by
-         * origin.
          * This function may create a new overmap if needed.
          * @param origin Location of search
          * see omt_find_params for definitions of the terms
          */
         std::vector<tripoint_abs_omt> find_all( const tripoint_abs_omt &origin,
                                                 const omt_find_params &params );
-        std::vector<tripoint_abs_omt> find_all(
-            const tripoint_abs_omt &origin, const std::string &type,
-            int dist, bool must_be_seen, ot_match_type match_type = ot_match_type::type,
-            bool existing_overmaps_only = false,
-            const std::optional<overmap_special_id> &om_special = std::nullopt );
+    private:
+        std::vector<tripoint_abs_omt> find_all_async( const tripoint_abs_omt &origin,
+                const omt_find_params &params );
+        std::vector<tripoint_abs_omt> find_all_sync( const tripoint_abs_omt &origin,
+                const omt_find_params &params );
+    public:
 
         /**
          * Returns a random point of specific terrain type among those found in certain search
@@ -324,11 +338,16 @@ class overmapbuffer
          */
         tripoint_abs_omt find_random( const tripoint_abs_omt &origin,
                                       const omt_find_params &params );
-        tripoint_abs_omt find_random(
-            const tripoint_abs_omt &origin, const std::string &type, int dist,
-            bool must_be_seen, ot_match_type match_type = ot_match_type::type,
-            bool existing_overmaps_only = false,
-            const std::optional<overmap_special_id> &om_special = std::nullopt );
+
+        /**
+         * Returns the closest point of terrain type.
+         * @param origin Location of search
+         * see omt_find_params for definitions of the terms
+         */
+        tripoint_abs_omt find_closest( const tripoint_abs_omt &origin,
+                                       const omt_find_params &params );
+
+
         /**
          * Mark a square area around center on Z-level z
          * as seen.
@@ -347,17 +366,6 @@ class overmapbuffer
             const tripoint_abs_omt &src, const tripoint_abs_omt &dest, overmap_path_params params );
         bool reveal_route( const tripoint_abs_omt &source, const tripoint_abs_omt &dest,
                            const omt_route_params &params );
-        /**
-         * Returns the closest point of terrain type.
-         * @param origin Location of search
-         * see omt_find_params for definitions of the terms
-         */
-        tripoint_abs_omt find_closest( const tripoint_abs_omt &origin,
-                                       const omt_find_params &params );
-        tripoint_abs_omt find_closest(
-            const tripoint_abs_omt &origin, const std::string &type, int radius, bool must_be_seen,
-            ot_match_type match_type = ot_match_type::type, bool existing_overmaps_only = false,
-            const std::optional<overmap_special_id> &om_special = std::nullopt );
 
         /* These functions return the overmap that contains the given
          * overmap terrain coordinate, and the local coordinates of that point
@@ -500,6 +508,7 @@ class overmapbuffer
                             int min_radius, int max_radius );
 
     private:
+        std::shared_mutex mutex;
         /**
          * Common function used by the find_closest/all/random to determine if the location is
          * findable based on the specified criteria.
@@ -507,15 +516,15 @@ class overmapbuffer
          * see omt_find_params for definitions of the terms
          */
         bool is_findable_location( const tripoint_abs_omt &location, const omt_find_params &params );
+        bool is_findable_location( const overmap_with_local_coords &map_loc,
+                                   const omt_find_params &params );
 
         std::unordered_map< point_abs_om, std::unique_ptr< overmap > > overmaps;
         /**
          * Set of overmap coordinates of overmaps that are known
          * to not exist on disk. See @ref get_existing for usage.
          */
-        mutable std::set<point_abs_om> known_non_existing;
-        // Cached result of previous call to overmapbuffer::get_existing
-        overmap mutable *last_requested_overmap;
+        std::set<point_abs_om> known_non_existing;
 
         // Set of globally unique overmap specials that have already been placed
         std::unordered_set<overmap_special_id> placed_unique_specials;
diff --git a/src/point.cpp b/src/point.cpp
index cc06d35d6735..3ccd5d15263f 100644
--- a/src/point.cpp
+++ b/src/point.cpp
@@ -48,69 +48,74 @@ std::ostream &operator<<( std::ostream &os, const tripoint &pos )
     return os << "(" << pos.x << "," << pos.y << "," << pos.z << ")";
 }
 
-std::vector<tripoint> closest_points_first( const tripoint &center, int max_dist )
+namespace detail
 {
-    return closest_points_first( center, 0, max_dist );
+spiral_generator_impl::spiral_generator_impl( point center, int min_dist, int max_dist )
+    : center( center )
+    , min_dist( min_dist )
+    , max_dist( max_dist )
+    , min_edge( min_dist * 2 + 1 )
+    , max_edge( max_dist * 2 + 1 )
+    , n( ( max_edge * max_edge ) - ( min_edge - 2 ) * ( min_edge - 2 ) )
+    , is_center_included( min_dist == 0 )
+{
+    i = is_center_included ? -1 : 0;
+    x = std::max( min_dist, 1 );
+    y = 1 - x;
+    dx = 1;
+    dy = 0;
+
+    p = is_center_included
+        ? center
+        : center + point{ x, y };
 }
 
-std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist, int max_dist )
+spiral_generator_impl spiral_generator_impl::exhausted( point center, int min_dist, int max_dist )
 {
-    const std::vector<point> points = closest_points_first( center.xy(), min_dist, max_dist );
-
-    std::vector<tripoint> result;
-    result.reserve( points.size() );
-
-    for( point p : points ) {
-        result.emplace_back( p, center.z );
-    }
-
-    return result;
+    auto q = spiral_generator_impl( center, min_dist, max_dist );
+    q.i = q.n;
+    return q;
 }
 
-std::vector<point> closest_points_first( point center, int max_dist )
+spiral_generator_impl::operator bool() const noexcept
 {
-    return closest_points_first( center, 0, max_dist );
+    return i < n;
 }
 
-std::vector<point> closest_points_first( point center, int min_dist, int max_dist )
+bool spiral_generator_impl::next()
 {
-    min_dist = std::max( min_dist, 0 );
-    max_dist = std::max( max_dist, 0 );
-
-    if( min_dist > max_dist ) {
-        return {};
+    if( i >= n ) {
+        return false;
     }
 
-    const int min_edge = min_dist * 2 + 1;
-    const int max_edge = max_dist * 2 + 1;
-
-    const int n = max_edge * max_edge - ( min_edge - 2 ) * ( min_edge - 2 );
-    const bool is_center_included = min_dist == 0;
-
-    std::vector<point> result;
-    result.reserve( n + ( is_center_included ? 1 : 0 ) );
-
-    if( is_center_included ) {
-        result.push_back( center );
+    if( i++ < 0 ) {
+        p = center + point{ x, y };
+        return true;
     }
 
-    int x = std::max( min_dist, 1 );
-    int y = 1 - x;
+    if( x == y || ( x < 0 && x == -y ) || ( x > 0 && x == 1 - y ) ) {
+        std::swap( dx, dy );
+        dx = -dx;
+    }
 
-    int dx = 1;
-    int dy = 0;
+    x += dx;
+    y += dy;
 
-    for( int i = 0; i < n; i++ ) {
-        result.push_back( center + point{ x, y } );
+    p = center + point{ x, y };
 
-        if( x == y || ( x < 0 && x == -y ) || ( x > 0 && x == 1 - y ) ) {
-            std::swap( dx, dy );
-            dx = -dx;
-        }
+    return true;
+}
 
-        x += dx;
-        y += dy;
-    }
+const point &spiral_generator_impl::current() const
+{
+    return p;
+}
 
-    return result;
+bool spiral_generator_impl::operator==( const spiral_generator_impl &other ) const
+{
+    return i == other.i
+           && center == other.center
+           && min_dist == other.min_dist
+           && max_dist == other.max_dist;
 }
+} // namespace detail
diff --git a/src/point.h b/src/point.h
index 324b9bf161a1..73a61a3bd86e 100644
--- a/src/point.h
+++ b/src/point.h
@@ -15,6 +15,7 @@
 #include <ostream>
 #include <string>
 #include <vector>
+#include <optional>
 
 #else
 
@@ -28,6 +29,8 @@ class ostream;
 
 #endif // CATA_NO_STL
 
+#include "point_traits.h"
+
 class JsonIn;
 class JsonOut;
 
@@ -302,11 +305,175 @@ struct sphere {
  * Following functions return points in a spiral pattern starting at center_x/center_y until it hits the radius. Clockwise fashion.
  * Credit to Tom J Nowell; http://stackoverflow.com/a/1555236/1269969
  */
-std::vector<tripoint> closest_points_first( const tripoint &center, int max_dist );
-std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist, int max_dist );
 
-std::vector<point> closest_points_first( point center, int max_dist );
-std::vector<point> closest_points_first( point center, int min_dist, int max_dist );
+namespace detail
+{
+class spiral_generator_impl
+{
+    private:
+        const point center;
+        const int min_dist;
+        const int max_dist;
+        const int min_edge;
+        const int max_edge;
+        const int n;
+        const bool is_center_included;
+
+        int i;
+        int x, y;
+        int dx, dy;
+        point p;
+    public:
+        spiral_generator_impl( point center, int min_dist, int max_dist );
+        static spiral_generator_impl exhausted( point center, int min_dist, int max_dist );
+
+        explicit operator bool() const noexcept;
+        bool next();
+        const point &current() const;
+
+        bool operator==( const spiral_generator_impl &other ) const;
+};
+
+template<typename _Point, int _Dim = _Point::dimension>
+_Point convert_point( point p, _Point ref )
+{
+    if constexpr( _Dim == 3 ) {
+        return _Point{ p.x, p.y, point_traits<_Point>::z( ref ) };
+    } else {
+        return _Point{ p.x, p.y };
+    }
+}
+
+} // namespace detail
+
+template<typename _Point>
+class spiral_generator
+{
+    private:
+        const _Point center;
+        const int min_dist;
+        const int max_dist;
+    public:
+        class iterator
+        {
+                friend class spiral_generator;
+            private:
+                detail::spiral_generator_impl generator;
+                const _Point origin;
+                _Point current;
+            public:
+                using value_type = _Point;
+                using difference_type = std::ptrdiff_t;
+                using pointer = value_type *;
+                using reference = value_type &;
+                using iterator_category = std::forward_iterator_tag;
+
+                iterator( const spiral_generator &g, bool );
+                iterator( const spiral_generator &g );
+                explicit operator bool() const noexcept;
+                iterator &operator++();
+
+                const _Point &operator*() const;
+
+                bool operator==( const iterator &other ) const;
+        };
+
+        spiral_generator( _Point center, int min_dist, int max_dist );
+        iterator begin() const;
+        iterator end() const;
+
+};
+
+template<typename _Point>
+inline spiral_generator<_Point> closest_points_generator( _Point center, int min_dist,
+        int max_dist )
+{
+    return spiral_generator<_Point>( center, min_dist, max_dist );
+}
+
+template<typename _Point>
+inline std::vector<_Point> closest_points_first( _Point center, int min_dist, int max_dist )
+{
+    auto gen = closest_points_generator( center, min_dist, max_dist );
+    return std::vector( gen.begin(), gen.end() );
+}
+
+template<typename _Point>
+inline spiral_generator<_Point> closest_points_generator( _Point center, int max_dist )
+{
+    return closest_points_generator( center, 0, max_dist );
+}
+
+template<typename _Point>
+inline std::vector<_Point> closest_points_first( _Point center, int max_dist )
+{
+    return closest_points_first( center, 0, max_dist );
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::iterator::iterator( const spiral_generator &g, bool )
+    : generator( detail::spiral_generator_impl::exhausted( point{ point_traits<_Point>::x( g.center ), point_traits<_Point>::y( g.center ) },
+                 g.min_dist, g.max_dist ) )
+    , origin( g.center )
+    , current( detail::convert_point( generator.current(), g.center ) )
+{
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::iterator::iterator( const spiral_generator &g )
+    : generator( point{ point_traits<_Point>::x( g.center ), point_traits<_Point>::y( g.center ) },
+                 g.min_dist, g.max_dist )
+    , origin( g.center )
+    , current( detail::convert_point( generator.current(), g.center ) )
+{
+
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::iterator::operator bool() const noexcept
+{
+    return static_cast<bool>( generator );
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::iterator &spiral_generator<_Point>::iterator::operator++()
+{
+    if( generator.next() ) {
+        current = detail::convert_point( generator.current(), origin );
+    }
+    return *this;
+}
+
+template<typename _Point>
+inline bool spiral_generator<_Point>::iterator::operator==( const iterator &other ) const
+{
+    return  generator == other.generator;
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::spiral_generator( _Point center, int min_dist, int max_dist )
+    : center( center ), min_dist( min_dist ), max_dist( max_dist )
+{
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::iterator spiral_generator<_Point>::begin() const
+{
+    return iterator( *this );
+}
+
+template<typename _Point>
+inline spiral_generator<_Point>::iterator spiral_generator<_Point>::end() const
+{
+    return iterator( *this, false );
+}
+
+template<typename _Point>
+inline const _Point &spiral_generator<_Point>::iterator::operator*() const
+{
+    return current;
+}
+
 
 static constexpr tripoint tripoint_min { INT_MIN, INT_MIN, INT_MIN };
 static constexpr tripoint tripoint_max{ INT_MAX, INT_MAX, INT_MAX };
diff --git a/src/start_location.cpp b/src/start_location.cpp
index 9cce01939f5e..df3754438117 100644
--- a/src/start_location.cpp
+++ b/src/start_location.cpp
@@ -250,9 +250,13 @@ tripoint_abs_omt start_location::find_player_initial_location() const
             const tripoint_abs_omt abs_mid = project_combine( omp, om_mid );
             if( overmap_buffer.place_special( special.id, abs_mid, 0, OMAPX / 2 ) ) {
 
+                omt_find_params find_params{};
+                find_params.types.emplace_back( loc.first, loc.second );
+                find_params.search_range = { 0, OMAPX / 2 };
+                find_params.search_layers = omt_find_all_layers;
+
                 // Now try to find what we spawned
-                const tripoint_abs_omt start = overmap_buffer.find_closest( abs_mid, loc.first,
-                                               OMAPX / 2, false, loc.second );
+                const tripoint_abs_omt start = overmap_buffer.find_closest( abs_mid, find_params );
                 if( start != overmap::invalid_tripoint ) {
                     return start;
                 }
diff --git a/tests/map_extra_test.cpp b/tests/map_extra_test.cpp
index dbf1767ddda0..ebd19848067f 100644
--- a/tests/map_extra_test.cpp
+++ b/tests/map_extra_test.cpp
@@ -29,7 +29,9 @@ TEST_CASE( "mx_minefield real spawn", "[.][map_extra][overmap]" )
     // Find all of the bridges within a 180 OMT radius of this location.
     omt_find_params find_params;
     find_params.types = {{"bridgehead_ground", ot_match_type::type}};
-    find_params.search_range = 180;
+    find_params.search_range = { 0, 180 };
+    find_params.search_layers = std::nullopt;
+
     const std::vector<tripoint_abs_omt> bridges = overmap_buffer.find_all( origin, find_params );
 
     // The rest of this check is pointless if there are no bridges.


Reviews:
user: login: scarf005
body: haven't reviewed all of them but let's use C++17 and C++20 features more
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T00:06:43Z
user: login: OrenAudeles
body: Looks good. It does what it says on the tin, increases performance when doing large overmap searches. It runs at around the same performance as a (non-async) cacher I've been working on. My cacher to speed up `is_ot_match` / `ot_check` calls doesn't play nice with the async code right now so I couldn't test how fast `find_all` could be with both enabled.

In `iuse_actor.cpp` there are some inefficiencies that will be exacerbated as search radius increases but for `radius = 180` it just adds around 2ms to a function taking 250 - 500ms. Not a big deal.

A lot of time is spent in overmap generation, and timings swings around wildly because of how random overmap generation can be.

Performance tanks to worse than without this PR if `free_tasks` is limited to 1 (simulating very low end hardware). But with just a few tasks available it's a performance improvement so probably not necessary to think too much about it for now.
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-25T09:37:34Z
user: login: usagirei
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-25T20:49:41Z
user: login: OrenAudeles
body: Looks good, works for me (tm).
- The multimap use in `overmapbuffer::find_closest` is fine but not really necessary. Using a vector there and only keeping track of the nearest items will be slightly better for memory use if there are a lot of results. May not be relevant in practice if there are only a few dozen results due to being clamped by `params.max_results = 10 * pp.types.size();`
- Some easy to fix compiler warnings
- `omt_find_params::post_filter` needs to be used in the synchronous find_all to match the async find_all
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-03-30T21:13:02Z
user: login: OrenAudeles
body: LGTM. All of the blockers I had in mind have been handled so I think this is good to go. I'll leave it unmerged for now so that another reviewer can come through and catch anything they have concerns about or approve and merge.
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-30T22:24:29Z
user: login: chaosvolt
body: 1. Compiled and load-tested.
2. Started a megacity world and examined the evac shelter's computer to try and find the refugee center. It freezes up pretty badly but that's probably to be expected, center generated over 600 map tiles away.
3. Used a restaurant guide, also a pretty bad lagspike on this world. Returned 229 restaurants in range so uh, prolly also expected.
4. Repeated these two tests with a normal world with default city settings, both worked fine and took barely any time. The evac center one was notable since it took less than a second despite the center being even farther away, just shy of 700 tiles.
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-31T01:29:16Z

Review comments:
diff_hunk: @@ -1349,48 +1349,74 @@ void reveal_map_actor::load( const JsonObject &obj )
 void reveal_map_actor::reveal_targets( const tripoint_abs_omt &map ) const
 {
     omt_find_params params{};
-    params.search_range = radius;
+    params.search_range = { 0, radius };
+    params.search_layers = omt_find_all_layers;
     params.types = omt_types;
-    params.must_see = false;
     params.existing_only = false;
 
-    for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-        const auto places = overmap_buffer.find_all( tripoint_abs_omt( map.xy(), z ), params );
-        for( auto &place : places ) {
-            overmap_buffer.reveal( place, 0 );
+    point_abs_om origin_om_pos;
+    point_om_omt origin_local;
+    std::tie( origin_om_pos, origin_local ) = project_remain<coords::om>( map.xy() );
path: src/iuse_actor.cpp
user: login: scarf005
body: ```suggestion
    const auto [ origin_om_pos, origin_local ] = project_remain<coords::om>( map.xy() );
```

let's use https://en.cppreference.com/w/cpp/language/structured_binding
created_at: 2025-03-20T00:02:04Z
author_association: MEMBER
diff_hunk: @@ -1349,48 +1349,74 @@ void reveal_map_actor::load( const JsonObject &obj )
 void reveal_map_actor::reveal_targets( const tripoint_abs_omt &map ) const
 {
     omt_find_params params{};
-    params.search_range = radius;
+    params.search_range = { 0, radius };
+    params.search_layers = omt_find_all_layers;
     params.types = omt_types;
-    params.must_see = false;
     params.existing_only = false;
 
-    for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-        const auto places = overmap_buffer.find_all( tripoint_abs_omt( map.xy(), z ), params );
-        for( auto &place : places ) {
-            overmap_buffer.reveal( place, 0 );
+    point_abs_om origin_om_pos;
+    point_om_omt origin_local;
+    std::tie( origin_om_pos, origin_local ) = project_remain<coords::om>( map.xy() );
+
+    int max_dist = 0;
+    std::unordered_set<point_abs_om> visited;
+    std::multimap<int, point_abs_om> by_dist;
+
+    /*
+    * Collect all unique overmap locations, grouped by manhattan distance
+    */
+    for( auto &p : closest_point_generator( map.raw().xy(), 0, radius ) ) {
+        const tripoint tp( p, map.z() );
+        const tripoint_abs_omt w( tp );
+
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( w.xy() );
path: src/iuse_actor.cpp
user: login: scarf005
body: ```suggestion
        const auto [ om_pos, local ] = project_remain<coords::om>( w.xy() );
```
also here
created_at: 2025-03-20T00:02:51Z
author_association: MEMBER
diff_hunk: @@ -1349,48 +1349,74 @@ void reveal_map_actor::load( const JsonObject &obj )
 void reveal_map_actor::reveal_targets( const tripoint_abs_omt &map ) const
 {
     omt_find_params params{};
-    params.search_range = radius;
+    params.search_range = { 0, radius };
+    params.search_layers = omt_find_all_layers;
     params.types = omt_types;
-    params.must_see = false;
     params.existing_only = false;
 
-    for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-        const auto places = overmap_buffer.find_all( tripoint_abs_omt( map.xy(), z ), params );
-        for( auto &place : places ) {
-            overmap_buffer.reveal( place, 0 );
+    point_abs_om origin_om_pos;
+    point_om_omt origin_local;
+    std::tie( origin_om_pos, origin_local ) = project_remain<coords::om>( map.xy() );
+
+    int max_dist = 0;
+    std::unordered_set<point_abs_om> visited;
+    std::multimap<int, point_abs_om> by_dist;
+
+    /*
+    * Collect all unique overmap locations, grouped by manhattan distance
+    */
+    for( auto &p : closest_point_generator( map.raw().xy(), 0, radius ) ) {
+        const tripoint tp( p, map.z() );
+        const tripoint_abs_omt w( tp );
+
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( w.xy() );
+        auto dist = manhattan_dist( origin_om_pos, om_pos );
+        max_dist = std::max( max_dist, dist );
+        if( visited.insert( om_pos ).second ) {
+            by_dist.emplace( dist, om_pos );
         }
     }
+
+    /*
+    * Stagger parallel map generation starting from center (0), outwards
+    * so the generated maps have a neighbor to latch onto when generating roads/rivers
+    * 5 4 3 2 3 4 5
+    * 4 3 2 1 2 3 4
+    * 3 2 1 0 1 2 3
+    * 4 3 2 1 2 3 4
+    * 5 4 3 2 3 4 5
+    */
+
+    std::vector<point_abs_om> to_gen;
+    for( int i = 0; i <= max_dist; i++ ) {
+        to_gen.clear();
+        auto range = by_dist.equal_range( i );
+        std::transform( range.first, range.second,
+        std::back_inserter( to_gen ), []( const std::pair<int, point_abs_om> &x ) {
+            return x.second;
+        } );
path: src/iuse_actor.cpp
user: login: scarf005
body: ```suggestion
        std::ranges::transform( range,
        std::back_inserter( to_gen ), []( const std::pair<int, point_abs_om> &x ) {
            return x.second;
        } );
```
could we use https://en.cppreference.com/w/cpp/algorithm/ranges/transform ?
created_at: 2025-03-20T00:04:57Z
author_association: MEMBER
diff_hunk: @@ -1349,48 +1350,73 @@ void reveal_map_actor::load( const JsonObject &obj )
 void reveal_map_actor::reveal_targets( const tripoint_abs_omt &map ) const
 {
     omt_find_params params{};
-    params.search_range = radius;
+    params.search_range = { 0, radius };
+    params.search_layers = omt_find_all_layers;
     params.types = omt_types;
-    params.must_see = false;
     params.existing_only = false;
+    params.popup = make_shared_fast<throbber_popup>( _( "Please wait…" ) );
 
-    for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-        const auto places = overmap_buffer.find_all( tripoint_abs_omt( map.xy(), z ), params );
-        for( auto &place : places ) {
-            overmap_buffer.reveal( place, 0 );
+    const auto [ origin_om_pos, origin_local ] = project_remain<coords::om>( map.xy() );
+
+    int max_dist = 0;
+    std::unordered_set<point_abs_om> visited;
+    std::multimap<int, point_abs_om> by_dist;
+
+    /*
+    * Collect all unique overmap locations, grouped by manhattan distance
+    */
+    for( auto &tp : closest_points_generator( map, 0, radius ) ) {
+        const auto [ om_pos, local ] = project_remain<coords::om>( tp.xy() );
+        const auto dist = manhattan_dist( origin_om_pos, om_pos );
+        max_dist = std::max( max_dist, dist );
+        if( visited.insert( om_pos ).second ) {
+            by_dist.emplace( dist, om_pos );
         }
     }
+
+    /*
+    * Stagger parallel map generation starting from center (0), outwards
+    * so the generated maps have a neighbor to latch onto when generating roads/rivers
+    * 5 4 3 2 3 4 5
+    * 4 3 2 1 2 3 4
+    * 3 2 1 0 1 2 3
+    * 4 3 2 1 2 3 4
+    * 5 4 3 2 3 4 5
+    */
+
+    std::vector<point_abs_om> to_gen;
+    for( int i = 0; i <= max_dist; i++ ) {
+        to_gen.clear();
+        auto range = by_dist.equal_range( i );
+        std::transform( range.first, range.second,
+        std::back_inserter( to_gen ), []( const std::pair<int, point_abs_om> &x ) {
+            return x.second;
+        } );
+        overmap_buffer.generate( to_gen );
+    }
path: src/iuse_actor.cpp
user: login: OrenAudeles
body: It's largely unnecessary to iterate individual OMT coordinates to construct the overmap coordinates to try generating.

From this comment I suppose we are wanting a rectangle of overmaps, and the following suggestions will be working on that idea.
```cpp
    /*
    * Stagger parallel map generation starting from center (0), outwards
    * so the generated maps have a neighbor to latch onto when generating roads/rivers
    * 5 4 3 2 3 4 5
    * 4 3 2 1 2 3 4
    * 3 2 1 0 1 2 3
    * 4 3 2 1 2 3 4
    * 5 4 3 2 3 4 5
    */
```

Using the radius and the source OMT we can build a bounding box with corners at
```cpp
const point_abs_omt omt_bb_min = map.xy() - point_rel_omt{radius, radius};
const point_abs_omt omt_bb_max = map.xy() + point_rel_omt{radius, radius};
```
We can then convert both of those into overmap coordinates
```cpp
// OM Corners of bounding box
const point_abs_om om_bb_min = project_to<coords::om>( omt_bb_min );
const point_abs_om om_bb_max = project_to<coords::om>( omt_bb_max );
```
Note, we do not need to use `project_remain` here since we don't care about the local OMT coordinate at all.
Having the corners of the bounding box we can iterate through it to gather relevant overmaps
```cpp
// Iterate through range [om_bb_min, om_bb_max] to get the OM we want, then sort by manhattan distance
std::map<int, std::vector<point_abs_om>> om_to_generate;
for( int x = om_bb_min.x(); x <= om_bb_max.x(); ++x ) {
    for( int y = om_bb_min.y(); y <= om_bb_max.y(); ++y ) {
        auto dist = manhattan_dist( origin_om_pos, {x, y} );
        auto &vec = om_to_generate[dist]; // if the vector for this distance doesn't exist it will be created empty
        vec.emplace_back( x, y );
    }
}
```
Finally, we need to iterate the distances and generate the overmaps if necessary
```cpp
for( const auto &[_, to_gen] : om_to_generate ) {
    overmap_buffer.generate( to_gen );
}
```
And that's it. There are some small differences in the order within each distance vector between implementations but order is not important. What is important is that this is super fast, roughly 50-100x faster on my machine for `radius = 180`, but the absolute benefit is rather small saving ~2ms in a several hundred ms function. It might have more impact if radius is much larger than 180 OMT.
created_at: 2025-03-25T08:58:58Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,26 +1057,208 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
+}
+
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
 {
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
+
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
+
+    std::deque<std::future<std::vector<tripoint_abs_omt>>> tasks;
+
+    std::vector<tripoint_abs_omt> find_result;
+    int free_tasks = std::max( 1u, std::thread::hardware_concurrency() - 1 );
path: src/overmapbuffer.cpp
user: login: OrenAudeles
body: Tested forcing 1 free task for a "minimum platform spec" analogue and this function does horribly it's amazing how bad it is.
Pre-PR: ~700ms
free tasks = 15: ~200 - 250ms 
free tasks = 1 : ~1000ms

I'm not entirely sure why it dies so hard with 1 task, I don't have enough experience working with async / futures. It's not unusable but does seem like a bit of a performance regression for potato tier low end platforms.
created_at: 2025-03-25T09:14:51Z
author_association: COLLABORATOR
diff_hunk: @@ -48,69 +48,74 @@ std::ostream &operator<<( std::ostream &os, const tripoint &pos )
     return os << "(" << pos.x << "," << pos.y << "," << pos.z << ")";
 }
 
-std::vector<tripoint> closest_points_first( const tripoint &center, int max_dist )
+namespace detail
 {
-    return closest_points_first( center, 0, max_dist );
+spiral_generator_impl::spiral_generator_impl( point center, int min_dist, int max_dist )
+    : center( center )
+    , min_dist( min_dist )
+    , max_dist( max_dist )
+    , min_edge( min_dist * 2 + 1 )
+    , max_edge( max_dist * 2 + 1 )
+    , n( ( max_edge * max_edge ) - ( min_edge - 2 ) * ( min_edge - 2 ) )
+    , is_center_included( min_dist == 0 )
+{
+    i = is_center_included ? -1 : 0;
+    x = std::max( min_dist, 1 );
+    y = 1 - x;
+    dx = 1;
+    dy = 0;
+
+    p = is_center_included
+        ? center
+        : center + point{ x, y };
 }
 
-std::vector<tripoint> closest_points_first( const tripoint &center, int min_dist, int max_dist )
+spiral_generator_impl spiral_generator_impl::exhausted( point center, int min_dist, int max_dist )
 {
-    const std::vector<point> points = closest_points_first( center.xy(), min_dist, max_dist );
-
-    std::vector<tripoint> result;
-    result.reserve( points.size() );
-
-    for( point p : points ) {
-        result.emplace_back( p, center.z );
-    }
-
-    return result;
+    auto q = spiral_generator_impl( center, min_dist, max_dist );
+    q.i = q.n;
+    return q;
 }
 
-std::vector<point> closest_points_first( point center, int max_dist )
+spiral_generator_impl::operator bool() const noexcept
 {
-    return closest_points_first( center, 0, max_dist );
+    return i < n;
 }
 
-std::vector<point> closest_points_first( point center, int min_dist, int max_dist )
+bool spiral_generator_impl::next()
 {
-    min_dist = std::max( min_dist, 0 );
-    max_dist = std::max( max_dist, 0 );
-
-    if( min_dist > max_dist ) {
-        return {};
+    if( i >= n ) {
+        return false;
     }
 
-    const int min_edge = min_dist * 2 + 1;
-    const int max_edge = max_dist * 2 + 1;
-
-    const int n = max_edge * max_edge - ( min_edge - 2 ) * ( min_edge - 2 );
-    const bool is_center_included = min_dist == 0;
-
-    std::vector<point> result;
-    result.reserve( n + ( is_center_included ? 1 : 0 ) );
-
-    if( is_center_included ) {
-        result.push_back( center );
+    if( i++ < 0 ) {
+        p = center + point{ x, y };
+        return true;
     }
 
-    int x = std::max( min_dist, 1 );
-    int y = 1 - x;
+    if( x == y || ( x < 0 && x == -y ) || ( x > 0 && x == 1 - y ) ) {
+        std::swap( dx, dy );
+        dx = -dx;
+    }
 
-    int dx = 1;
-    int dy = 0;
+    x += dx;
+    y += dy;
 
-    for( int i = 0; i < n; i++ ) {
-        result.push_back( center + point{ x, y } );
+    p = center + point{ x, y };
 
-        if( x == y || ( x < 0 && x == -y ) || ( x > 0 && x == 1 - y ) ) {
-            std::swap( dx, dy );
-            dx = -dx;
-        }
+    return true;
+}
 
-        x += dx;
-        y += dy;
-    }
+const point &spiral_generator_impl::current() const
+{
+    return p;
+}
 
-    return result;
+bool spiral_generator_impl::operator==( const spiral_generator_impl &other ) const
+{
+    return i == other.i
+           && center == other.center
+           && min_dist == other.min_dist
+           && max_dist == other.max_dist;
+}
 }
path: src/point.cpp
user: login: OrenAudeles
body: Needs a closing comment to make clang-tidy shut up
```cpp
} // namespace detail
```
created_at: 2025-03-25T09:19:54Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,26 +1057,208 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
+}
+
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
 {
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
+
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
+
+    std::deque<std::future<std::vector<tripoint_abs_omt>>> tasks;
+
+    std::vector<tripoint_abs_omt> find_result;
+    int free_tasks = std::max( 1u, std::thread::hardware_concurrency() - 1 );
path: src/overmapbuffer.cpp
user: login: usagirei
body: On my machine it wasn't as egregious, but it was still slower, so i added a synchronous version of the call for the next batch of commits

Measured the just calls to `find_all` at `reveal_map_actor::show_revealed`:

| Test Case | time |
|-|-|
| pre-PR | ~750 ms |
| async with 1 task | ~550 ms |
| sync | ~430 ms |
| async with 2 tasks | ~260 ms |
| async with 4 tasks | ~140 ms |
| async with 8 tasks | ~93 ms  |
| async with 15 tasks | ~69 ms  |
created_at: 2025-03-25T20:49:41Z
author_association: COLLABORATOR
diff_hunk: @@ -334,7 +366,12 @@ tripoint_abs_omt mission_util::target_om_ter_random( const std::string &omter, i
         loc = g->u.global_omt_location();
     }
 
-    auto places = overmap_buffer.find_all( loc, omter, range, must_see );
+    omt_find_params find_params{};
path: src/mission_util.cpp
user: login: OrenAudeles
body: [Commenting here because github doesn't like me putting this as a review comment on a line not part of the diff!]
Is it intentional that function parameter `bool must_see` (L363) is no longer used?

The function is only used in `mission_start.cpp` and the parameter is only ever set to `false` during calls so it's probably not necessary at all.

Not a critical change, but changing from `bool must_see` to just `bool` would shut up the unused parameter warnings.
created_at: 2025-03-30T20:14:40Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,131 +1071,328 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
 }
 
-tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    // Check the origin before searching adjacent tiles!
-    if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
-        return origin;
+    const auto concurrency = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    if( concurrency == 1 ) {
+        return find_all_sync( origin, params );
+    } else {
+        return find_all_async( origin, params );
     }
+}
 
-    // By default search overmaps within a radius of 4,
-    // i.e. C = current overmap, X = overmaps searched:
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXCXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    //
-    // See overmap::place_specials for how we attempt to insure specials are placed within this
-    // range.  The actual number is 5 because 1 covers the current overmap,
-    // and each additional one expends the search to the next concentric circle of overmaps.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_sync( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
+{
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
 
-    std::vector<tripoint_abs_omt> result;
-    std::optional<int> found_dist;
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
path: src/overmapbuffer.cpp
user: login: OrenAudeles
body: ```suggestion
```
Remove unused variable
created_at: 2025-03-30T20:25:03Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,131 +1071,328 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
 }
 
-tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    // Check the origin before searching adjacent tiles!
-    if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
-        return origin;
+    const auto concurrency = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    if( concurrency == 1 ) {
+        return find_all_sync( origin, params );
+    } else {
+        return find_all_async( origin, params );
     }
+}
 
-    // By default search overmaps within a radius of 4,
-    // i.e. C = current overmap, X = overmaps searched:
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXCXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    //
-    // See overmap::place_specials for how we attempt to insure specials are placed within this
-    // range.  The actual number is 5 because 1 covers the current overmap,
-    // and each additional one expends the search to the next concentric circle of overmaps.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_sync( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
+{
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
 
-    std::vector<tripoint_abs_omt> result;
-    std::optional<int> found_dist;
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
 
-    for( const point_abs_omt &loc_xy : closest_points_first( origin.xy(), min_dist, max_dist ) ) {
-        const int dist_xy = square_dist( origin.xy(), loc_xy );
+    std::vector<tripoint_abs_omt> find_result;
+    find_result.reserve( params.max_results.value_or( 256 ) );
+    while( true ) {
 
-        if( found_dist && *found_dist < dist_xy ) {
+        if( params.popup ) {
+            params.popup->refresh();
+        }
+
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
             break;
         }
 
-        for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-            const tripoint_abs_omt loc( loc_xy, z );
-            const int dist = square_dist( origin, loc );
+        auto& [task_om, task_omts] = task_data.value();
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
 
-            if( found_dist && *found_dist < dist ) {
-                continue;
-            }
+        overmap *om_loc = &get( task_om );
 
-            if( is_findable_location( loc, params ) ) {
-                found_dist = dist;
-                result.push_back( loc );
+        bool done = false;
+        for( const auto &loc : task_omts ) {
+            overmap_with_local_coords q{ om_loc, loc.second };
+            if( is_findable_location( q, params ) ) {
+                find_result.push_back( loc.first );
             }
-
-            counter += 1;
-            if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
-                params.popup->refresh();
-                num_overmaps = overmaps.size();
-                counter = 0;
+            if( params.max_results.has_value() && find_result.size() == params.max_results.value() ) {
path: src/overmapbuffer.cpp
user: login: OrenAudeles
body: ```suggestion
            if( params.max_results.has_value() && find_result.size() == static_cast<size_t>( params.max_results.value() ) ) {
```
Shuts up CI warning `comparison of integer expressions of different signedness`
created_at: 2025-03-30T20:27:26Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,131 +1071,328 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
 }
 
-tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    // Check the origin before searching adjacent tiles!
-    if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
-        return origin;
+    const auto concurrency = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    if( concurrency == 1 ) {
+        return find_all_sync( origin, params );
+    } else {
+        return find_all_async( origin, params );
     }
+}
 
-    // By default search overmaps within a radius of 4,
-    // i.e. C = current overmap, X = overmaps searched:
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXCXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    //
-    // See overmap::place_specials for how we attempt to insure specials are placed within this
-    // range.  The actual number is 5 because 1 covers the current overmap,
-    // and each additional one expends the search to the next concentric circle of overmaps.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_sync( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
+{
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
 
-    std::vector<tripoint_abs_omt> result;
-    std::optional<int> found_dist;
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
 
-    for( const point_abs_omt &loc_xy : closest_points_first( origin.xy(), min_dist, max_dist ) ) {
-        const int dist_xy = square_dist( origin.xy(), loc_xy );
+    std::vector<tripoint_abs_omt> find_result;
+    find_result.reserve( params.max_results.value_or( 256 ) );
+    while( true ) {
 
-        if( found_dist && *found_dist < dist_xy ) {
+        if( params.popup ) {
+            params.popup->refresh();
+        }
+
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
             break;
         }
 
-        for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-            const tripoint_abs_omt loc( loc_xy, z );
-            const int dist = square_dist( origin, loc );
+        auto& [task_om, task_omts] = task_data.value();
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
 
-            if( found_dist && *found_dist < dist ) {
-                continue;
-            }
+        overmap *om_loc = &get( task_om );
 
-            if( is_findable_location( loc, params ) ) {
-                found_dist = dist;
-                result.push_back( loc );
+        bool done = false;
+        for( const auto &loc : task_omts ) {
+            overmap_with_local_coords q{ om_loc, loc.second };
+            if( is_findable_location( q, params ) ) {
+                find_result.push_back( loc.first );
             }
-
-            counter += 1;
-            if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
-                params.popup->refresh();
-                num_overmaps = overmaps.size();
-                counter = 0;
+            if( params.max_results.has_value() && find_result.size() == params.max_results.value() ) {
+                done = true;
+                break;
             }
         }
+
+        if( done ) {
+            break;
+        }
     }
 
-    return random_entry( result, overmap::invalid_tripoint );
+    return find_result;
 }
 
-std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_async( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    std::vector<tripoint_abs_omt> result;
-    // dist == 0 means search a whole overmap diameter.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX;
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
+
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
+
+    std::deque<std::future<std::vector<tripoint_abs_omt>>> tasks;
+
+    std::vector<tripoint_abs_omt> find_result;
+    int free_tasks = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    auto try_finish_task = []( std::future<std::vector<tripoint_abs_omt>> &task,
+    std::vector<tripoint_abs_omt> &dst, omt_find_params params ) -> bool {
+        if( task.wait_for( std::chrono::milliseconds( 0 ) ) == std::future_status::ready )
+        {
+            auto task_result = task.get();
+
+            if( !params.max_results.has_value() || dst.size() < params.max_results.value() ) {
path: src/overmapbuffer.cpp
user: login: OrenAudeles
body: ```suggestion
            if( !params.max_results.has_value() || dst.size() < static_cast<size_t>( params.max_results.value() ) ) {
```
Shuts up CI warning `comparison of integer expressions of different signedness`
created_at: 2025-03-30T20:29:15Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,131 +1071,328 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
 }
 
-tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    // Check the origin before searching adjacent tiles!
-    if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
-        return origin;
+    const auto concurrency = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    if( concurrency == 1 ) {
+        return find_all_sync( origin, params );
+    } else {
+        return find_all_async( origin, params );
     }
+}
 
-    // By default search overmaps within a radius of 4,
-    // i.e. C = current overmap, X = overmaps searched:
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXCXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    //
-    // See overmap::place_specials for how we attempt to insure specials are placed within this
-    // range.  The actual number is 5 because 1 covers the current overmap,
-    // and each additional one expends the search to the next concentric circle of overmaps.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_sync( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
+{
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
 
-    std::vector<tripoint_abs_omt> result;
-    std::optional<int> found_dist;
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
 
-    for( const point_abs_omt &loc_xy : closest_points_first( origin.xy(), min_dist, max_dist ) ) {
-        const int dist_xy = square_dist( origin.xy(), loc_xy );
+    std::vector<tripoint_abs_omt> find_result;
+    find_result.reserve( params.max_results.value_or( 256 ) );
+    while( true ) {
 
-        if( found_dist && *found_dist < dist_xy ) {
+        if( params.popup ) {
+            params.popup->refresh();
+        }
+
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
             break;
         }
 
-        for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-            const tripoint_abs_omt loc( loc_xy, z );
-            const int dist = square_dist( origin, loc );
+        auto& [task_om, task_omts] = task_data.value();
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
 
-            if( found_dist && *found_dist < dist ) {
-                continue;
-            }
+        overmap *om_loc = &get( task_om );
 
-            if( is_findable_location( loc, params ) ) {
-                found_dist = dist;
-                result.push_back( loc );
+        bool done = false;
+        for( const auto &loc : task_omts ) {
+            overmap_with_local_coords q{ om_loc, loc.second };
+            if( is_findable_location( q, params ) ) {
+                find_result.push_back( loc.first );
             }
-
-            counter += 1;
-            if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
-                params.popup->refresh();
-                num_overmaps = overmaps.size();
-                counter = 0;
+            if( params.max_results.has_value() && find_result.size() == params.max_results.value() ) {
+                done = true;
+                break;
             }
         }
+
+        if( done ) {
+            break;
+        }
     }
 
-    return random_entry( result, overmap::invalid_tripoint );
+    return find_result;
 }
 
-std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_async( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    std::vector<tripoint_abs_omt> result;
-    // dist == 0 means search a whole overmap diameter.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX;
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
+
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
+
+    std::deque<std::future<std::vector<tripoint_abs_omt>>> tasks;
+
+    std::vector<tripoint_abs_omt> find_result;
+    int free_tasks = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    auto try_finish_task = []( std::future<std::vector<tripoint_abs_omt>> &task,
+    std::vector<tripoint_abs_omt> &dst, omt_find_params params ) -> bool {
+        if( task.wait_for( std::chrono::milliseconds( 0 ) ) == std::future_status::ready )
+        {
+            auto task_result = task.get();
+
+            if( !params.max_results.has_value() || dst.size() < params.max_results.value() ) {
+                if( params.post_filter ) {
path: src/overmapbuffer.cpp
user: login: OrenAudeles
body: I didn't see anywhere that `post_filter` is set, and these are the only references to the variable. If it's intended to be used in the future its use will need to be reflected in the synchronous version of this function as well (somewhere around L1192)
created_at: 2025-03-30T20:34:49Z
author_association: COLLABORATOR
diff_hunk: @@ -1017,131 +1071,328 @@ bool overmapbuffer::is_findable_location( const tripoint_abs_omt &location,
     return true;
 }
 
-tripoint_abs_omt overmapbuffer::find_closest(
-    const tripoint_abs_omt &origin, const std::string &type, int const radius, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
-{
-    const omt_find_params params = assign_params( type, radius, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_closest( origin, params );
+namespace
+{
+using find_task = std::pair
+                  <point_abs_om, std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>>>;
+
+struct find_task_generator {
+    const spiral_generator<point> _gen;
+    const int _min_z;
+    const int _max_z;
+    const int _max_coords;
+    const int _n_steps;
+    spiral_generator<point>::iterator _it;
+    const spiral_generator<point>::iterator _end;
+    std::pair<point_abs_om, point_om_omt> _current;
+    bool _done;
+
+    auto get_om_loc() {
+        auto &p = *_it;
+        tripoint_abs_omt loc( p.x, p.y, 0 );
+        point_abs_om om_pos;
+        point_om_omt local;
+        std::tie( om_pos, local ) = project_remain<coords::om>( loc.xy() );
+        return std::make_pair( om_pos, local );
+    }
+
+    find_task_generator( point p, int mind, int maxd, int minz, int maxz, int chunk )
+        : _gen( p, mind, maxd )
+        , _min_z( minz )
+        , _max_z( maxz )
+        , _max_coords( chunk )
+        , _n_steps( chunk / ( maxz - minz + 1 ) )
+        , _it( _gen.begin() )
+        , _end( _gen.end() ) {
+        _current = get_om_loc();
+        _done = _end == _it;
+    }
+
+    std::optional<find_task> operator()() {
+        if( _done ) {
+            return std::nullopt;
+        }
+
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> v;
+        v.reserve( _max_coords );
+        point_abs_om om_loc = _current.first;
+
+        for( int n = 0; n < _n_steps; n++ ) {
+            auto &p = *_it;
+            for( int z = _min_z; z <= _max_z; z++ ) {
+                tripoint_abs_omt abs( p.x, p.y, z );
+                tripoint_om_omt om( _current.second, z );
+                v.emplace_back( abs, om );
+            }
+
+            ++_it;
+            _current = get_om_loc();
+            _done = _end == _it;
+            if( _done || _current.first != om_loc ) {
+                break;
+            }
+        }
+
+        return std::make_pair( om_loc, std::move( v ) );
+    }
+};
 }
 
-tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    // Check the origin before searching adjacent tiles!
-    if( params.min_distance == 0 && is_findable_location( origin, params ) ) {
-        return origin;
+    const auto concurrency = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    if( concurrency == 1 ) {
+        return find_all_sync( origin, params );
+    } else {
+        return find_all_async( origin, params );
     }
+}
 
-    // By default search overmaps within a radius of 4,
-    // i.e. C = current overmap, X = overmaps searched:
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXCXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    // XXXXXXXXX
-    //
-    // See overmap::place_specials for how we attempt to insure specials are placed within this
-    // range.  The actual number is 5 because 1 covers the current overmap,
-    // and each additional one expends the search to the next concentric circle of overmaps.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX * 5;
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_sync( const tripoint_abs_omt &origin,
+        const omt_find_params &params )
+{
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
 
-    std::vector<tripoint_abs_omt> result;
-    std::optional<int> found_dist;
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
 
-    for( const point_abs_omt &loc_xy : closest_points_first( origin.xy(), min_dist, max_dist ) ) {
-        const int dist_xy = square_dist( origin.xy(), loc_xy );
+    std::vector<tripoint_abs_omt> find_result;
+    find_result.reserve( params.max_results.value_or( 256 ) );
+    while( true ) {
 
-        if( found_dist && *found_dist < dist_xy ) {
+        if( params.popup ) {
+            params.popup->refresh();
+        }
+
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
             break;
         }
 
-        for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) {
-            const tripoint_abs_omt loc( loc_xy, z );
-            const int dist = square_dist( origin, loc );
+        auto& [task_om, task_omts] = task_data.value();
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
 
-            if( found_dist && *found_dist < dist ) {
-                continue;
-            }
+        overmap *om_loc = &get( task_om );
 
-            if( is_findable_location( loc, params ) ) {
-                found_dist = dist;
-                result.push_back( loc );
+        bool done = false;
+        for( const auto &loc : task_omts ) {
+            overmap_with_local_coords q{ om_loc, loc.second };
+            if( is_findable_location( q, params ) ) {
+                find_result.push_back( loc.first );
             }
-
-            counter += 1;
-            if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
-                params.popup->refresh();
-                num_overmaps = overmaps.size();
-                counter = 0;
+            if( params.max_results.has_value() && find_result.size() == params.max_results.value() ) {
+                done = true;
+                break;
             }
         }
+
+        if( done ) {
+            break;
+        }
     }
 
-    return random_entry( result, overmap::invalid_tripoint );
+    return find_result;
 }
 
-std::vector<tripoint_abs_omt> overmapbuffer::find_all( const tripoint_abs_omt &origin,
+std::vector<tripoint_abs_omt> overmapbuffer::find_all_async( const tripoint_abs_omt &origin,
         const omt_find_params &params )
 {
-    std::vector<tripoint_abs_omt> result;
-    // dist == 0 means search a whole overmap diameter.
-    const int min_dist = params.min_distance;
-    const int max_dist = params.search_range ? params.search_range : OMAPX;
+    // max_dist == 0 means search a whole overmap diameter.
+    const int min_dist = params.search_range.first;
+    const int max_dist = params.search_range.second ? params.search_range.second : OMAPX;
+
+    // empty search_layers means origin.z
+    const auto search_layers = params.search_layers.value_or( std::make_pair( origin.z(),
+                               origin.z() ) );
+    const int min_layer = search_layers.first;
+    const int max_layer = search_layers.second;
+    const int num_layers = max_layer - min_layer + 1;
+
+    find_task_generator gen( origin.raw().xy(), min_dist, max_dist, min_layer, max_layer, 256 );
+
+    std::deque<std::future<std::vector<tripoint_abs_omt>>> tasks;
+
+    std::vector<tripoint_abs_omt> find_result;
+    int free_tasks = std::max( 1u, std::thread::hardware_concurrency() - 1 );
+    auto try_finish_task = []( std::future<std::vector<tripoint_abs_omt>> &task,
+    std::vector<tripoint_abs_omt> &dst, omt_find_params params ) -> bool {
+        if( task.wait_for( std::chrono::milliseconds( 0 ) ) == std::future_status::ready )
+        {
+            auto task_result = task.get();
+
+            if( !params.max_results.has_value() || dst.size() < params.max_results.value() ) {
+                if( params.post_filter ) {
+                    std::copy_if( task_result.begin(), task_result.end(), std::back_inserter( dst ),
+                                  params.post_filter );
+                } else {
+                    std::copy( task_result.begin(), task_result.end(), std::back_inserter( dst ) );
+                }
 
-    size_t num_overmaps = overmaps.size();
-    size_t counter = 0;
+                if( params.max_results.has_value() && dst.size() > params.max_results.value() ) {
+                    dst.resize( params.max_results.value() );
+                }
+            }
 
-    for( const tripoint_abs_omt &loc : closest_points_first( origin, min_dist, max_dist ) ) {
-        if( is_findable_location( loc, params ) ) {
-            result.push_back( loc );
+            return true;
         }
+        return false;
+    };
 
-        counter += 1;
-        if( params.popup && ( num_overmaps != overmaps.size() || counter == 512 ) ) {
+    while( true ) {
+        if( params.popup ) {
             params.popup->refresh();
-            num_overmaps = overmaps.size();
-            counter = 0;
         }
+
+        if( !tasks.empty() ) {
+            if( try_finish_task( tasks.front(), find_result, params ) ) {
+                tasks.pop_front();
+                ++free_tasks;
+            }
+            if( params.max_results.has_value() &&
+                find_result.size() >= params.max_results.value() ) {
+                break;
+            }
+        }
+
+        if( free_tasks == 0 ) {
+            continue;
+        }
+
+        auto task_data = gen();
+        if( !task_data.has_value() ) {
+            break;
+        }
+
+        auto& [task_om, task_omts] = task_data.value();
+
+        if( params.existing_only && !has( task_om ) ) {
+            continue;
+        }
+
+        auto task_func = [&]( point_abs_om l,
+        std::vector<std::pair<tripoint_abs_omt, tripoint_om_omt>> locals ) {
+            std::vector<tripoint_abs_omt> result;
+
+            overmap *om_loc;
+            if( params.existing_only ) {
+                om_loc = get_existing( l );
+            } else {
+                om_loc = &get( l );
+            }
+            if( !om_loc ) {
+                return result;
+            }
+
+            for( const auto &loc : locals ) {
+                overmap_with_local_coords q{ om_loc, loc.second };
+                if( is_findable_location( q, params ) ) {
+                    result.push_back( loc.first );
+                }
+                if( params.max_results.has_value() && result.size() == params.max_results.value() ) {
+                    break;
+                }
+            }
+
+            return result;
+        };
+
+        auto task = std::async( std::launch::async, task_func, task_om, std::move( task_omts ) );
+
+        tasks.push_back( std::move( task ) );
+
+        --free_tasks;
     }
 
-    return result;
+    while( !tasks.empty() ) {
+        if( params.popup ) {
+            params.popup->refresh();
+        }
+
+        if( try_finish_task( tasks.front(), find_result, params ) ) {
+            tasks.pop_front();
+            ++free_tasks;
+        }
+    }
+
+    return find_result;
 }
 
-std::vector<tripoint_abs_omt> overmapbuffer::find_all(
-    const tripoint_abs_omt &origin, const std::string &type, int dist, bool must_be_seen,
-    ot_match_type match_type, bool existing_overmaps_only,
-    const std::optional<overmap_special_id> &om_special )
+
+tripoint_abs_omt overmapbuffer::find_closest( const tripoint_abs_omt &origin,
+        const omt_find_params &pp )
 {
-    const omt_find_params params = assign_params( type, dist, must_be_seen, match_type,
-                                   existing_overmaps_only, om_special );
-    return find_all( origin, params );
+    // Check the origin before searching adjacent tiles!
+    if( pp.search_range.first == 0 && is_findable_location( origin, pp ) ) {
+        return origin;
+    }
+
+    // By default search overmaps within a radius of 4,
+    // i.e. C = current overmap, X = overmaps searched:
+    //
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXCXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    // XXXXXXXXX
+    //
+    // See overmap::place_specials for how we attempt to insure specials are placed within this
+    // range.  The actual number is 5 because 1 covers the current overmap,
+    // and each additional one expends the search to the next concentric circle of overmaps.
+
+    omt_find_params params = pp;
+    if( params.search_range.second == 0 ) {
+        params.search_range.second = OMAPX * 5;
+    }
+    if( !params.max_results.has_value() ) {
+        // Limit potential random sample size, scaling with number of terrain types
+        params.max_results = 10 * pp.types.size();
+    }
+
+    std::vector<tripoint_abs_omt> near_points;
+    std::optional<int> found_dist;
+
+    std::multimap<int, tripoint_abs_omt> points_by_dist;
+
+    auto scan_result = find_all( origin, params );
+    if( scan_result.size() == 0 ) {
+        return overmap::invalid_tripoint;
+    }
+
+    for( auto &p : scan_result ) {
+        const int dist = square_dist( origin, p );
+        points_by_dist.insert( { dist, p } );
+    }
path: src/overmapbuffer.cpp
user: login: OrenAudeles
body: Instead of using a multimap here you could do something like the following:
```cpp
auto scan_result = find_all( origin, params );
if( scan_result.empty() ) {
    return overmap::invalid_tripoint;
}
std::vector<tripoint_abs_omt> near_points;
int min_dist = INT_MAX;

// Only care about the absolute nearest points, so only keep those points
for( auto &p : scan_result ) {
    int dist = square_dist( origin, p );
    if( dist < min_dist ) {
        near_points.clear();
        min_dist = dist;
    }
    near_points.emplace_back( p );
}

return near_points[ rng( 0, near_points.size() ) ];
```
created_at: 2025-03-30T20:47:00Z
author_association: COLLABORATOR

PR comments:
user: login: autofix-ci[bot]
created_at: 2025-03-19T02:06:31Z
body: Autofix has formatted [code style violation][code-style] in this PR.

<details><summary>I edit commits locally (e.g: git, github desktop) and want to keep autofix</summary>

1. Run `git pull`. this will merge the automated commit into your local copy of the PR branch.
2. Continue working.

</details>

<details><summary>I do not want the automated commit</summary>

1. [Format your code locally](https://docs.cataclysmbn.org/en/contribute/contributing/#code-style), then commit it.
2. Run `git push --force` to force push your branch. This will overwrite the automated commit on remote with your local one.
3. Continue working.

</details>

If you don't do this, your following commits will be based on the old commit, and cause **MERGE CONFLICT**.

[code-style]: https://docs.cataclysmbn.org/en/contribute/contributing/#code-style
author_association: CONTRIBUTOR
user: login: OrenAudeles
created_at: 2025-03-19T11:26:08Z
body: How much does performance increase if async was stripped away?
author_association: COLLABORATOR
user: login: usagirei
created_at: 2025-03-19T17:28:27Z
body: would need to measure, but there should be other optimizations in place that also benefit a single threaded approach.
author_association: COLLABORATOR
user: login: usagirei
created_at: 2025-03-21T02:05:51Z
body: Need people to ensure it works under Unix/Mac/Android as well
author_association: COLLABORATOR