From fbdf451ed3fa62b73d57f4a2a2df5a965f0397ff Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Sat, 15 Dec 2018 00:31:19 -0900 Subject: [PATCH 1/7] New methods for getting overmap with coordinates Adds a new struct which contains a pointer to an overmap and a tripoint local to the overmap. Added new methods for getting an overmap with coordinates projected to the local coordinate system when provided with a global overmap terrain tripoint coordinate. Has both get_ and get_existing varieties. --- src/overmapbuffer.cpp | 24 ++++++++++++++++++++++++ src/overmapbuffer.h | 17 +++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 08e2e915fc00a..3bab7bddd0ee4 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -268,6 +268,15 @@ overmap &overmapbuffer::get_om_global( const tripoint &p ) return get( om_pos.x, om_pos.y ); } +overmap_with_local_coordinates overmapbuffer::get_om_global_with_coordinates( const tripoint &p ) +{ + int x = p.x; + int y = p.y; + const point om_pos = omt_to_om_remain( x, y ); + overmap *om = &get( om_pos.x, om_pos.y ); + return { om, tripoint( x, y, p.z ) }; +} + overmap *overmapbuffer::get_existing_om_global( int &x, int &y ) { const point om_pos = omt_to_om_remain( x, y ); @@ -286,6 +295,21 @@ overmap *overmapbuffer::get_existing_om_global( const tripoint &p ) return get_existing( om_pos.x, om_pos.y ); } +cata::optional +overmapbuffer::get_existing_om_global_with_coordinates( + const tripoint &p ) +{ + int x = p.x; + int y = p.y; + const point om_pos = omt_to_om_remain( x, y ); + overmap *om = get_existing( om_pos.x, om_pos.y ); + if( om == nullptr ) { + return cata::nullopt; + } + + return overmap_with_local_coordinates { om, tripoint( x, y, p.z ) }; +} + bool overmapbuffer::has_note( int x, int y, int z ) { const overmap *om = get_existing_om_global( x, y ); diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index 89f2b80eae03a..3d90f7446aba5 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -54,6 +54,11 @@ struct city_reference { int get_distance_from_bounds() const; }; +struct overmap_with_local_coordinates { + overmap *overmap_pointer; + tripoint coordinates; +}; + class overmapbuffer { public: @@ -273,6 +278,18 @@ class overmapbuffer overmap &get_om_global( int &x, int &y ); overmap &get_om_global( const point &p ); overmap &get_om_global( const tripoint &p ); + + /** + * These two functions return the overmap that contains the given + * global overmap terrain coordinate. They additionally will reproject + * and return the provided global overmap terrain coordinate to the + * local coordinate system, so that it can be used with that overmap. + * They follow the same semantics as the get_om_global and + * get_existing_om_global regarding creating new overmaps. + */ + overmap_with_local_coordinates get_om_global_with_coordinates( const tripoint &p ); + cata::optional get_existing_om_global_with_coordinates( + const tripoint &p ); /** * (x,y) are global overmap coordinates (same as @ref get). * @returns true if the buffer has a overmap with From b09a0326ceb462de4702421097089f4d26a83db0 Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Fri, 14 Dec 2018 15:53:39 -0900 Subject: [PATCH 2/7] Add new methods for checking if an omt has been generated Add new methods to overmap and overmap buffer to allow checking whether or not a given overmap terrain location has been generated, which can be used to inform code that wants to change the overmap but only wants to do so if the location hasn't been generated already. --- src/overmap.cpp | 17 +++++++++++++++++ src/overmap.h | 7 +++++++ src/overmapbuffer.cpp | 13 +++++++++++++ src/overmapbuffer.h | 7 ++++++- 4 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/overmap.cpp b/src/overmap.cpp index 5df96b0dbd8ee..9d54d27246507 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -9,6 +9,7 @@ #include "line.h" #include "map.h" #include "map_iterator.h" +#include "mapbuffer.h" #include "mapgen.h" #include "mapgen_functions.h" #include "messages.h" @@ -3778,6 +3779,22 @@ std::shared_ptr overmap::find_npc( const int id ) const return nullptr; } +bool overmap::is_omt_generated( const tripoint &loc ) const +{ + if( !inbounds( loc ) ) { + return false; + } + + // Location is local to this overmap, but we need global submap coordinates + // for the mapbuffer lookup. + tripoint global_sm_loc = omt_to_sm_copy( loc ) + om_to_sm_copy( tripoint( pos().x, pos().y, + loc.z ) ); + + const bool is_generated = MAPBUFFER.lookup_submap( global_sm_loc ) != nullptr; + + return is_generated; +} + overmap_special_id overmap_specials::create_building_from( const string_id &base ) { // @todo: Get rid of the hard-coded ids. diff --git a/src/overmap.h b/src/overmap.h index 6fbcde672014a..e43ed6f77f9b2 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -216,6 +216,13 @@ class overmap */ std::vector find_notes( const int z, const std::string &text ); + /** + * Returns whether or not the location has been generated (e.g. mapgen has run). + * @param loc Location to check. + * @returns True if param @loc has been generated. + */ + bool is_omt_generated( const tripoint &loc ) const; + /** Returns the (0, 0) corner of the overmap in the global coordinates. */ point global_base_point() const; diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 3bab7bddd0ee4..cbab61a12ef41 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -310,6 +310,19 @@ overmapbuffer::get_existing_om_global_with_coordinates( return overmap_with_local_coordinates { om, tripoint( x, y, p.z ) }; } +bool overmapbuffer::is_omt_generated( const tripoint &loc ) +{ + cata::optional om = get_existing_om_global_with_coordinates( loc ); + + // If the overmap doesn't exist, then for sure the local mapgen + // hasn't happened. + if( !om ) { + return false; + } + + return om->overmap_pointer->is_omt_generated( om->coordinates ); +} + bool overmapbuffer::has_note( int x, int y, int z ) { const overmap *om = get_existing_om_global( x, y ); diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index 3d90f7446aba5..1ac9900412e3d 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -112,7 +112,6 @@ class overmapbuffer int get_horde_size( int x, int y, int z ); std::vector get_vehicle( int x, int y, int z ); const regional_settings &get_settings( int x, int y, int z ); - /** * Accessors for horde introspection into overmaps. * Probably also useful for NPC overmap-scale navigation. @@ -303,6 +302,12 @@ class overmapbuffer * (x,y) are global overmap coordinates (same as @ref get). */ overmap *get_existing( int x, int y ); + /** + * Returns whether or not the location has been generated (e.g. mapgen has run). + * @param loc is in world-global omt coordinates. + * @returns True if the location has been generated. + */ + bool is_omt_generated( const tripoint &loc ); typedef std::pair t_point_with_note; typedef std::vector t_notes_vector; From d4ed21f4d3b57757c2947b5039dbc20759eabfc0 Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Fri, 14 Dec 2018 15:54:07 -0900 Subject: [PATCH 3/7] Optionally restrict overmap special pathfinding and placement to unexplored Also allow forcing placement of overmap special even when it doesn't meet criteria (primarily to be used in the debug menu placement of overmap specials). --- src/overmap.cpp | 75 +++++++++++++++++++++++++++++++++---------------- src/overmap.h | 14 ++++----- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/src/overmap.cpp b/src/overmap.cpp index 9d54d27246507..b366670e16c82 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -2352,8 +2352,8 @@ void overmap::place_building( const tripoint &p, om_direction::type dir, const c for( size_t retries = 10; retries > 0; --retries ) { const overmap_special_id building_tid = pick_random_building_to_place( town_dist ); - if( can_place_special( *building_tid, building_pos, building_dir ) ) { - place_special( *building_tid, building_pos, building_dir, town ); + if( can_place_special( *building_tid, building_pos, building_dir, false ) ) { + place_special( *building_tid, building_pos, building_dir, town, false, false ); break; } } @@ -2774,7 +2774,7 @@ void overmap::place_rifts( const int z ) } pf::path overmap::lay_out_connection( const overmap_connection &connection, const point &source, - const point &dest, int z ) const + const point &dest, int z, const bool must_be_unexplored ) const { const auto estimate = [&]( const pf::node & cur, const pf::node * prev ) { const auto &id( get_ter( cur.x, cur.y, z ) ); @@ -2785,8 +2785,21 @@ pf::path overmap::lay_out_connection( const overmap_connection &connection, cons return pf::rejected; // No option for this terrain. } - const bool existing = connection.has( id ); - if( existing && id->is_rotatable() && + const bool existing_connection = connection.has( id ); + + // Only do this check if it needs to be unexplored and there isn't already a connection. + if( must_be_unexplored && !existing_connection ) { + // If this must be unexplored, check if we've already got a submap generated. + const bool existing_submap = is_omt_generated( tripoint( cur.x, cur.y, z ) ); + + // If there is an existing submap, this area has already been explored and this + // isn't a valid placement. + if( existing_submap ) { + return pf::rejected; + } + } + + if( existing_connection && id->is_rotatable() && !om_direction::are_parallel( id->get_dir(), static_cast( cur.dir ) ) ) { return pf::rejected; // Can't intersect. } @@ -2804,7 +2817,7 @@ pf::path overmap::lay_out_connection( const overmap_connection &connection, cons const int dy = dest.y - cur.y; const int dist = subtype->is_orthogonal() ? std::abs( dx ) + std::abs( dy ) : std::sqrt( dx * dx + dy * dy ); - const int existency_mult = existing ? 1 : 5; // Prefer existing connections. + const int existency_mult = existing_connection ? 1 : 5; // Prefer existing connections. return existency_mult * dist + subtype->basic_cost; }; @@ -2916,9 +2929,10 @@ void overmap::build_connection( const overmap_connection &connection, const pf:: } void overmap::build_connection( const point &source, const point &dest, int z, - const overmap_connection &connection ) + const overmap_connection &connection, const bool must_be_unexplored ) { - build_connection( connection, lay_out_connection( connection, source, dest, z ), z ); + build_connection( connection, lay_out_connection( connection, source, dest, z, must_be_unexplored ), + z ); } void overmap::connect_closest_points( const std::vector &points, int z, @@ -2938,7 +2952,7 @@ void overmap::connect_closest_points( const std::vector &points, int z, } } if( closest > 0 ) { - build_connection( points[i], points[k], z, connection ); + build_connection( points[i], points[k], z, connection, false ); } } } @@ -3212,7 +3226,7 @@ bool om_direction::are_parallel( type dir1, type dir2 ) } om_direction::type overmap::random_special_rotation( const overmap_special &special, - const tripoint &p ) const + const tripoint &p, const bool must_be_unexplored ) const { std::vector rotations( om_direction::size ); const auto first = rotations.begin(); @@ -3252,14 +3266,14 @@ om_direction::type overmap::random_special_rotation( const overmap_special &spec // Pick first valid rotation at random. std::random_shuffle( first, last ); const auto rotation = std::find_if( first, last, [&]( om_direction::type elem ) { - return can_place_special( special, p, elem ); + return can_place_special( special, p, elem, must_be_unexplored ); } ); return rotation != last ? *rotation : om_direction::type::invalid; } bool overmap::can_place_special( const overmap_special &special, const tripoint &p, - om_direction::type dir ) const + om_direction::type dir, const bool must_be_unexplored ) const { assert( p != invalid_tripoint ); assert( dir != om_direction::type::invalid ); @@ -3276,6 +3290,17 @@ bool overmap::can_place_special( const overmap_special &special, const tripoint return false; } + if( must_be_unexplored ) { + // If this must be unexplored, check if we've already got a submap generated. + const bool existing_submap = is_omt_generated( rp ); + + // If there is an existing submap, this area has already been explored and this + // isn't a valid placement. + if( existing_submap ) { + return false; + } + } + const oter_id tid = get_ter( rp ); if( rp.z == 0 ) { @@ -3288,11 +3313,13 @@ bool overmap::can_place_special( const overmap_special &special, const tripoint // checks around the selected point to see if the special can be placed there void overmap::place_special( const overmap_special &special, const tripoint &p, - om_direction::type dir, const city &cit ) + om_direction::type dir, const city &cit, const bool must_be_unexplored, const bool force ) { assert( p != invalid_tripoint ); assert( dir != om_direction::type::invalid ); - assert( can_place_special( special, p, dir ) ); + if( !force ) { + assert( can_place_special( special, p, dir, must_be_unexplored ) ); + } const bool blob = special.flags.count( "BLOB" ) > 0; @@ -3319,7 +3346,7 @@ void overmap::place_special( const overmap_special &special, const tripoint &p, for( const auto &elem : special.connections ) { if( elem.connection ) { const tripoint rp( p + om_direction::rotate( elem.p, dir ) ); - build_connection( cit.pos, point( rp.x, rp.y ), elem.p.z, *elem.connection ); + build_connection( cit.pos, point( rp.x, rp.y ), elem.p.z, *elem.connection, must_be_unexplored ); } } } @@ -3338,8 +3365,8 @@ void overmap::place_special( const overmap_special &special, const tripoint &p, // This basement isn't part of the special that we asserted we could place at // the top of this function, so we need to make sure we can place the basement // special before doing so. - if( can_place_special( *basement_tid, basement_p, dir ) ) { - place_special( *basement_tid, basement_p, dir, cit ); + if( can_place_special( *basement_tid, basement_p, dir, must_be_unexplored ) || force ) { + place_special( *basement_tid, basement_p, dir, cit, force, must_be_unexplored ); } } } @@ -3359,7 +3386,7 @@ std::vector overmap::get_sectors() const } bool overmap::place_special_attempt( overmap_special_batch &enabled_specials, - const point §or, const bool place_optional ) + const point §or, const bool place_optional, const bool must_be_unexplored ) { const int x = sector.x; const int y = sector.y; @@ -3380,12 +3407,12 @@ bool overmap::place_special_attempt( overmap_special_batch &enabled_specials, continue; } // See if we can actually place the special there. - const auto rotation = random_special_rotation( special, p ); + const auto rotation = random_special_rotation( special, p, must_be_unexplored ); if( rotation == om_direction::type::invalid ) { continue; } - place_special( special, p, rotation, nearest_city ); + place_special( special, p, rotation, nearest_city, false, must_be_unexplored ); if( ++iter->instances_placed >= special.occurrences.max ) { enabled_specials.erase( iter ); @@ -3398,7 +3425,7 @@ bool overmap::place_special_attempt( overmap_special_batch &enabled_specials, } void overmap::place_specials_pass( overmap_special_batch &enabled_specials, - std::vector §ors, const bool place_optional ) + std::vector §ors, const bool place_optional, const bool must_be_unexplored ) { // Walk over sectors in random order, to minimize "clumping". std::random_shuffle( sectors.begin(), sectors.end() ); @@ -3406,7 +3433,7 @@ void overmap::place_specials_pass( overmap_special_batch &enabled_specials, const size_t attempts = 10; bool placed = false; for( size_t i = 0; i < attempts; ++i ) { - if( place_special_attempt( enabled_specials, *it, place_optional ) ) { + if( place_special_attempt( enabled_specials, *it, place_optional, must_be_unexplored ) ) { placed = true; it = sectors.erase( it ); if( enabled_specials.empty() ) { @@ -3452,7 +3479,7 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) // First, place the mandatory specials to ensure that all minimum instance // counts are met. - place_specials_pass( enabled_specials, sectors, false ); + place_specials_pass( enabled_specials, sectors, false, false ); // Snapshot remaining specials, which will be the optional specials and // any unplaced mandatory specials. By passing a copy into the creation of @@ -3495,7 +3522,7 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) } } // Then fill in non-mandatory specials. - place_specials_pass( enabled_specials, sectors, true ); + place_specials_pass( enabled_specials, sectors, true, false ); // Clean up... // Because we passed a copy of the specials for placement in adjacent overmaps rather than diff --git a/src/overmap.h b/src/overmap.h index e43ed6f77f9b2..83d5904a93c07 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -346,13 +346,13 @@ class overmap // Connection laying pf::path lay_out_connection( const overmap_connection &connection, const point &source, - const point &dest, int z ) const; + const point &dest, int z, const bool must_be_unexplored ) const; pf::path lay_out_street( const overmap_connection &connection, const point &source, om_direction::type dir, size_t len ) const; void build_connection( const overmap_connection &connection, const pf::path &path, int z ); void build_connection( const point &source, const point &dest, int z, - const overmap_connection &connection ); + const overmap_connection &connection, const bool must_be_unexplored ); void connect_closest_points( const std::vector &points, int z, const overmap_connection &connection ); // Polishing @@ -370,13 +370,13 @@ class overmap std::vector get_sectors() const; om_direction::type random_special_rotation( const overmap_special &special, - const tripoint &p ) const; + const tripoint &p, bool must_be_unexplored ) const; bool can_place_special( const overmap_special &special, const tripoint &p, - om_direction::type dir ) const; + om_direction::type dir, const bool must_be_unexplored ) const; void place_special( const overmap_special &special, const tripoint &p, om_direction::type dir, - const city &cit ); + const city &cit, const bool must_be_unexplored, const bool force ); /** * Iterate over the overmap and place the quota of specials. * If the stated minimums are not reached, it will spawn a new nearby overmap @@ -391,7 +391,7 @@ class overmap * @param place_optional restricts attempting to place specials that have met their minimum count in the first pass. */ void place_specials_pass( overmap_special_batch &enabled_specials, - std::vector §ors, bool place_optional ); + std::vector §ors, bool place_optional, const bool must_be_unexplored ); /** * Attempts to place specials within a sector. @@ -400,7 +400,7 @@ class overmap * @param place_optional restricts attempting to place specials that have met their minimum count in the first pass. */ bool place_special_attempt( overmap_special_batch &enabled_specials, - const point §or, bool place_optional ); + const point §or, bool place_optional, const bool must_be_unexplored ); void place_mongroups(); void place_radios(); From 93edbd1b21f3faf995c9836b58d6db36c8ceb106 Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Fri, 14 Dec 2018 16:09:10 -0900 Subject: [PATCH 4/7] Update overmapbuffer terrain finds to optionally consider special --- src/overmapbuffer.cpp | 162 ++++++++++++++++++++++++++++++------------ src/overmapbuffer.h | 64 ++++++++++++++++- 2 files changed, 179 insertions(+), 47 deletions(-) diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index cbab61a12ef41..56f52a1e2f67d 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -659,6 +659,39 @@ bool overmapbuffer::reveal_route( const tripoint &source, const tripoint &dest, return !path.nodes.empty(); } +bool overmapbuffer::check_ot_type_existing( const std::string &type, const tripoint &loc ) +{ + const cata::optional om = get_existing_om_global_with_coordinates( + loc ); + if( !om ) { + return false; + } + return om->overmap_pointer->check_ot_type( type, om->coordinates.x, om->coordinates.y, + om->coordinates.z ); +} + +bool overmapbuffer::check_ot_subtype_existing( const std::string &type, const tripoint &loc ) +{ + const cata::optional om = get_existing_om_global_with_coordinates( + loc ); + if( !om ) { + return false; + } + return om->overmap_pointer->check_ot_subtype( type, om->coordinates.x, om->coordinates.y, + om->coordinates.z ); +} + +bool overmapbuffer::check_overmap_special_type_existing( const overmap_special_id &id, + const tripoint &loc ) +{ + const cata::optional om = get_existing_om_global_with_coordinates( + loc ); + if( !om ) { + return false; + } + return om->overmap_pointer->check_overmap_special_type( id, om->coordinates ); +} + bool overmapbuffer::check_ot_type( const std::string &type, int x, int y, int z ) { overmap &om = get_om_global( x, y ); @@ -671,13 +704,65 @@ bool overmapbuffer::check_ot_subtype( const std::string &type, int x, int y, int return om.check_ot_subtype( type, x, y, z ); } +bool overmapbuffer::check_overmap_special_type( const overmap_special_id &id, const tripoint &loc ) +{ + const overmap_with_local_coordinates om = get_om_global_with_coordinates( loc ); + return om.overmap_pointer->check_overmap_special_type( id, om.coordinates ); +} + +bool overmapbuffer::is_findable_location( const tripoint &location, const std::string &type, + bool must_be_seen, bool allow_subtype_matches, + bool existing_overmaps_only, cata::optional om_special ) +{ + if( existing_overmaps_only ) { + const bool type_matches = allow_subtype_matches + ? check_ot_subtype_existing( type, location ) + : check_ot_type_existing( type, location ); + + if( !type_matches ) { + return false; + } + } else { + const bool type_matches = allow_subtype_matches + ? check_ot_subtype( type, location.x, location.y, location.z ) + : check_ot_type( type, location.x, location.y, location.z ); + + if( !type_matches ) { + return false; + } + } + + const bool meets_seen_criteria = !must_be_seen || seen( location.x, location.y, location.z ); + if( !meets_seen_criteria ) { + return false; + } + + if( existing_overmaps_only ) { + const bool meets_om_special_criteria = !om_special || + check_overmap_special_type_existing( *om_special, location ); + if( !meets_om_special_criteria ) { + return false; + } + } else { + const bool meets_om_special_criteria = !om_special || + check_overmap_special_type( *om_special, location ); + if( !meets_om_special_criteria ) { + return false; + } + } + + + return true; +} + tripoint overmapbuffer::find_closest( const tripoint &origin, const std::string &type, - const int radius, bool must_be_seen, bool allow_subtype_matches ) + int const radius, bool must_be_seen, bool allow_subtype_matches, + bool existing_overmaps_only, + cata::optional om_special ) { // Check the origin before searching adjacent tiles! - if( allow_subtype_matches - ? check_ot_subtype( type, origin.x, origin.y, origin.z ) - : check_ot_type( type, origin.x, origin.y, origin.z ) ) { + if( is_findable_location( origin, type, must_be_seen, allow_subtype_matches, existing_overmaps_only, + om_special ) ) { return origin; } @@ -705,47 +790,31 @@ tripoint overmapbuffer::find_closest( const tripoint &origin, const std::string // south is +y, north is -y for( int i = 0; i < dist * 2; i++ ) { //start at northwest, scan north edge - int x = origin.x - dist + i; - int y = origin.y - dist; - if( allow_subtype_matches - ? check_ot_subtype( type, x, y, z ) - : check_ot_type( type, x, y, z ) ) { - if( !must_be_seen || seen( x, y, z ) ) { - return tripoint( x, y, z ); - } + const tripoint n_loc( origin.x - dist + i, origin.y - dist, z ); + if( is_findable_location( n_loc, type, must_be_seen, allow_subtype_matches, existing_overmaps_only, + om_special ) ) { + return n_loc; } //start at southeast, scan south - x = origin.x + dist - i; - y = origin.y + dist; - if( allow_subtype_matches - ? check_ot_subtype( type, x, y, z ) - : check_ot_type( type, x, y, z ) ) { - if( !must_be_seen || seen( x, y, z ) ) { - return tripoint( x, y, z ); - } + const tripoint s_loc( origin.x + dist - i, origin.y + dist, z ); + if( is_findable_location( s_loc, type, must_be_seen, allow_subtype_matches, existing_overmaps_only, + om_special ) ) { + return s_loc; } //start at southwest, scan west - x = origin.x - dist; - y = origin.y + dist - i; - if( allow_subtype_matches - ? check_ot_subtype( type, x, y, z ) - : check_ot_type( type, x, y, z ) ) { - if( !must_be_seen || seen( x, y, z ) ) { - return tripoint( x, y, z ); - } + const tripoint w_loc( origin.x - dist, origin.y + dist - i, z ); + if( is_findable_location( w_loc, type, must_be_seen, allow_subtype_matches, existing_overmaps_only, + om_special ) ) { + return w_loc; } //start at northeast, scan east - x = origin.x + dist; - y = origin.y - dist + i; - if( allow_subtype_matches - ? check_ot_subtype( type, x, y, z ) - : check_ot_type( type, x, y, z ) ) { - if( !must_be_seen || seen( x, y, z ) ) { - return tripoint( x, y, z ); - } + const tripoint e_loc( origin.x + dist, origin.y - dist + i, z ); + if( is_findable_location( e_loc, type, must_be_seen, allow_subtype_matches, existing_overmaps_only, + om_special ) ) { + return e_loc; } } } @@ -753,18 +822,19 @@ tripoint overmapbuffer::find_closest( const tripoint &origin, const std::string } std::vector overmapbuffer::find_all( const tripoint &origin, const std::string &type, - int dist, bool must_be_seen ) + int dist, bool must_be_seen, bool allow_subtype_matches, + bool existing_overmaps_only, + cata::optional om_special ) { std::vector result; // dist == 0 means search a whole overmap diameter. dist = dist ? dist : OMAPX; for( int x = origin.x - dist; x <= origin.x + dist; x++ ) { for( int y = origin.y - dist; y <= origin.y + dist; y++ ) { - if( must_be_seen && !seen( x, y, origin.z ) ) { - continue; - } - if( check_ot_type( type, x, y, origin.z ) ) { - result.push_back( tripoint( x, y, origin.z ) ); + const tripoint search_loc( x, y, origin.z ); + if( is_findable_location( search_loc, type, must_be_seen, allow_subtype_matches, + existing_overmaps_only, om_special ) ) { + result.push_back( search_loc ); } } } @@ -772,9 +842,13 @@ std::vector overmapbuffer::find_all( const tripoint &origin, const std } tripoint overmapbuffer::find_random( const tripoint &origin, const std::string &type, - int dist, bool must_be_seen ) + int dist, bool must_be_seen, bool allow_subtype_matches, + bool existing_overmaps_only, + cata::optional om_special ) { - return random_entry( find_all( origin, type, dist, must_be_seen ), overmap::invalid_tripoint ); + return random_entry( find_all( origin, type, dist, must_be_seen, allow_subtype_matches, + existing_overmaps_only, + om_special ), overmap::invalid_tripoint ); } std::shared_ptr overmapbuffer::find_npc( int id ) diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index 1ac9900412e3d..d3dc688f35f88 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -5,6 +5,7 @@ #include "enums.h" #include "int_id.h" #include "overmap_types.h" +#include "string_id.h" #include #include @@ -19,6 +20,9 @@ struct om_vehicle; struct oter_t; using oter_id = int_id; +class overmap_special; +using overmap_special_id = string_id; + class overmap; class overmap_special; class overmap_special_batch; @@ -215,9 +219,18 @@ class overmapbuffer * If 0, OMAPX is used. * @param must_be_seen If true, only terrain seen by the player * should be searched. + * @param allow_subtype_matches If true, will allow matching on subtypes for the + * terrain type (e.g. forest will match forest_thick and forest_water). + * @param existing_overmaps_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. */ std::vector find_all( const tripoint &origin, const std::string &type, - int dist, bool must_be_seen ); + int dist, bool must_be_seen, bool allow_subtype_matches = false, + bool existing_overmaps_only = false, + cata::optional om_special = cata::nullopt ); /** * Returns a random point of specific terrain type among those found in certain search radius. @@ -229,9 +242,18 @@ class overmapbuffer * @param origin uses overmap terrain coordinates. * @param must_be_seen If true, only terrain seen by the player * should be searched. + * @param allow_subtype_matches If true, will allow matching on subtypes for the + * terrain type (e.g. forest will match forest_thick and forest_water). + * @param existing_overmaps_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. */ tripoint find_random( const tripoint &origin, const std::string &type, - int dist, bool must_be_seen ); + int dist, bool must_be_seen, bool allow_subtype_matches = false, + bool existing_overmaps_only = false, + cata::optional om_special = cata::nullopt ); /** * Mark a square area around center on Z-level z @@ -258,9 +280,18 @@ class overmapbuffer * @param origin uses overmap terrain coordinates. * @param must_be_seen If true, only terrain seen by the player * should be searched. + * @param allow_subtype_matches If true, will allow matching on subtypes for the + * terrain type (e.g. forest will match forest_thick and forest_water). + * @param existing_overmaps_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. */ tripoint find_closest( const tripoint &origin, const std::string &type, int radius, - bool must_be_seen, bool allow_subtype_matches = false ); + bool must_be_seen, bool allow_subtype_matches = false, + bool existing_overmaps_only = false, + cata::optional om_special = cata::nullopt ); /* These 4 functions return the overmap that contains the given * overmap terrain coordinate. @@ -380,6 +411,24 @@ class overmapbuffer std::string get_description_at( const tripoint &where ); private: + /** + * Common function used by the find_closest/all/random to determine if the location is + * findable based on the specified criteria. + * @param location The location to evaluate. Uses overmap terrain coordinates. + * @param type Type of terrain to look for + * @param must_be_seen If true, the terrain must have been seen by the player to be acceptable. + * @param allow_subtype_matches If true, will allow matching on subtypes for the terrain type + * (e.g. forest will match forest_thick and forest_water). + * @param existing_overmaps_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. + */ + bool is_findable_location( const tripoint &location, const std::string &type, bool must_be_seen, + bool allow_subtype_matches = false, + bool existing_overmaps_only = false, + cata::optional om_special = cata::nullopt ); + std::unordered_map< point, std::unique_ptr< overmap > > overmaps; /** * Set of overmap coordinates of overmaps that are known @@ -404,6 +453,15 @@ class overmapbuffer */ bool check_ot_type( const std::string &otype, int x, int y, int z ); bool check_ot_subtype( const std::string &otype, int x, int y, int z ); + bool check_overmap_special_type( const overmap_special_id &id, const tripoint &loc ); + + /** + * These versions of the check_* methods will only check existing overmaps, and + * return false if the overmap doesn't exist. They do not create new overmaps. + */ + bool check_ot_type_existing( const std::string &otype, const tripoint &loc ); + bool check_ot_subtype_existing( const std::string &otype, const tripoint &loc ); + bool check_overmap_special_type_existing( const overmap_special_id &id, const tripoint &loc ); private: /** * Go thorough the monster groups of the overmap and move out-of-bounds From 295ac95ca99e2a2f135e5d0901e1c618d01f724d Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Tue, 11 Dec 2018 13:42:04 -0900 Subject: [PATCH 5/7] Add place_special methods to overmapbuffer Allows for placement of a specific special at a specified location and orientation, or simply specifying a special to be placed and deferring placement to the overmap's placement algorithm. --- src/overmap.cpp | 28 ++++++------ src/overmap.h | 20 ++++++--- src/overmapbuffer.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++ src/overmapbuffer.h | 34 +++++++++++--- 4 files changed, 158 insertions(+), 26 deletions(-) diff --git a/src/overmap.cpp b/src/overmap.cpp index b366670e16c82..e44fafc0ff67f 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -3371,27 +3371,28 @@ void overmap::place_special( const overmap_special &special, const tripoint &p, } } -std::vector overmap::get_sectors() const +om_special_sectors get_sectors( const int sector_width ) { std::vector res; - res.reserve( ( OMAPX / OMSPEC_FREQ ) * ( OMAPY / OMSPEC_FREQ ) ); - for( int x = 0; x < OMAPX; x += OMSPEC_FREQ ) { - for( int y = 0; y < OMAPY; y += OMSPEC_FREQ ) { + res.reserve( ( OMAPX / sector_width ) * ( OMAPY / sector_width ) ); + for( int x = 0; x < OMAPX; x += sector_width ) { + for( int y = 0; y < OMAPY; y += sector_width ) { res.emplace_back( x, y ); } } std::random_shuffle( res.begin(), res.end() ); - return res; + return { res, sector_width }; } bool overmap::place_special_attempt( overmap_special_batch &enabled_specials, - const point §or, const bool place_optional, const bool must_be_unexplored ) + const point §or, const int sector_width, const bool place_optional, + const bool must_be_unexplored ) { const int x = sector.x; const int y = sector.y; - const tripoint p( rng( x, x + OMSPEC_FREQ - 1 ), rng( y, y + OMSPEC_FREQ - 1 ), 0 ); + const tripoint p( rng( x, x + sector_width - 1 ), rng( y, y + sector_width - 1 ), 0 ); const city &nearest_city = get_nearest_city( p ); std::random_shuffle( enabled_specials.begin(), enabled_specials.end() ); @@ -3425,17 +3426,18 @@ bool overmap::place_special_attempt( overmap_special_batch &enabled_specials, } void overmap::place_specials_pass( overmap_special_batch &enabled_specials, - std::vector §ors, const bool place_optional, const bool must_be_unexplored ) + om_special_sectors §ors, const bool place_optional, const bool must_be_unexplored ) { // Walk over sectors in random order, to minimize "clumping". - std::random_shuffle( sectors.begin(), sectors.end() ); - for( auto it = sectors.begin(); it != sectors.end(); ) { + std::random_shuffle( sectors.sectors.begin(), sectors.sectors.end() ); + for( auto it = sectors.sectors.begin(); it != sectors.sectors.end(); ) { const size_t attempts = 10; bool placed = false; for( size_t i = 0; i < attempts; ++i ) { - if( place_special_attempt( enabled_specials, *it, place_optional, must_be_unexplored ) ) { + if( place_special_attempt( enabled_specials, *it, sectors.sector_width, place_optional, + must_be_unexplored ) ) { placed = true; - it = sectors.erase( it ); + it = sectors.sectors.erase( it ); if( enabled_specials.empty() ) { return; // Job done. Bail out. } @@ -3475,7 +3477,7 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) if( enabled_specials.empty() ) { return; } - std::vector sectors = get_sectors(); + om_special_sectors sectors = get_sectors( OMSPEC_FREQ ); // First, place the mandatory specials to ensure that all minimum instance // counts are met. diff --git a/src/overmap.h b/src/overmap.h index 83d5904a93c07..a4195a420f132 100644 --- a/src/overmap.h +++ b/src/overmap.h @@ -94,6 +94,11 @@ struct map_layer { std::vector notes; }; +struct om_special_sectors { + std::vector sectors; + int sector_width; +}; + // Wrapper around an overmap special to track progress of placing specials. struct overmap_special_placement { int instances_placed; @@ -364,11 +369,6 @@ class overmap void polish_river(); void good_river( int x, int y, int z ); - // Returns a vector of permuted coordinates of overmap sectors. - // Each sector consists of 12x12 small maps. Coordinates of the sectors are in range [0, 15], [0, 15]. - // Check OMAPX, OMAPY, and OMSPEC_FREQ to learn actual values. - std::vector get_sectors() const; - om_direction::type random_special_rotation( const overmap_special &special, const tripoint &p, bool must_be_unexplored ) const; @@ -391,7 +391,7 @@ class overmap * @param place_optional restricts attempting to place specials that have met their minimum count in the first pass. */ void place_specials_pass( overmap_special_batch &enabled_specials, - std::vector §ors, bool place_optional, const bool must_be_unexplored ); + om_special_sectors §ors, bool place_optional, const bool must_be_unexplored ); /** * Attempts to place specials within a sector. @@ -400,7 +400,7 @@ class overmap * @param place_optional restricts attempting to place specials that have met their minimum count in the first pass. */ bool place_special_attempt( overmap_special_batch &enabled_specials, - const point §or, bool place_optional, const bool must_be_unexplored ); + const point §or, const int sector_width, bool place_optional, const bool must_be_unexplored ); void place_mongroups(); void place_radios(); @@ -417,4 +417,10 @@ bool is_ot_type( const std::string &otype, const oter_id &oter ); // Matches any oter_id that contains the substring passed in, useful when oter can be a suffix, not just a prefix. bool is_ot_subtype( const char *otype, const oter_id &oter ); +/** +* Gets a collection of sectors and their width for usage in placing overmap specials. +* @param sector_width used to divide the OMAPX by OMAPY map into sectors. +*/ +om_special_sectors get_sectors( const int sector_width ); + #endif diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 56f52a1e2f67d..394bd91904410 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -1200,3 +1200,105 @@ bool overmapbuffer::is_safe( int x, int y, int z ) } return true; } + +bool overmapbuffer::place_special( const overmap_special &special, const tripoint &p, + om_direction::type dir, const bool must_be_unexplored, const bool force ) +{ + // get_om_global will transform these into overmap local coordinates, which + // we'll then use with the overmap methods. + int x = p.x; + int y = p.y; + overmap &om = get_om_global( x, y ); + const tripoint om_loc( x, y, p.z ); + + // Get the closest city that is within the overmap because + // all of the overmap generation functions only function within + // the single overmap. If future generation is hoisted up to the + // buffer to spawn overmaps, then this can also be changed accordingly. + const city c = om.get_nearest_city( om_loc ); + + bool placed = false; + // Only place this special if we can actually place it per its criteria, or we're forcing + // the placement, which is mostly a debug behavior, since a forced placement may not function + // correctly (e.g. won't check correct underlying terrain). + if( om.can_place_special( special, om_loc, dir, must_be_unexplored ) || force ) { + om.place_special( special, om_loc, dir, c, must_be_unexplored, force ); + placed = true; + } + return placed; +} + +bool overmapbuffer::place_special( const overmap_special_id special_id, const tripoint ¢er, + int radius ) +{ + // First find the requested special. If it doesn't exist, we're done here. + bool found = false; + overmap_special special; + for( const auto &elem : overmap_specials::get_all() ) { + if( elem.id == special_id ) { + special = elem; + found = true; + break; + } + } + if( !found ) { + return false; + } + + // Figure out the longest side of the special for purposes of determining our sector size + // when attempting placements. + const auto calculate_longest_side = [&special]() { + auto min_max_x = std::minmax_element( special.terrains.begin(), + special.terrains.end(), []( const overmap_special_terrain & lhs, + const overmap_special_terrain & rhs ) { + return lhs.p.x < rhs.p.x; + } ); + + auto min_max_y = std::minmax_element( special.terrains.begin(), + special.terrains.end(), []( const overmap_special_terrain & lhs, + const overmap_special_terrain & rhs ) { + return lhs.p.y < rhs.p.y; + } ); + + const int special_longest_side = std::max( min_max_x.second->p.x - min_max_x.first->p.x, + min_max_y.second->p.y - min_max_y.first->p.y ) + 1; + + // If our longest side is greater than the OMSPEC_FREQ, just use that instead. + return std::min( special_longest_side, OMSPEC_FREQ ); + }; + + const int longest_side = calculate_longest_side(); + + // Predefine our sectors to search in. + om_special_sectors sectors = get_sectors( longest_side ); + + // Get all of the overmaps within the defined radius of the center. + for( const auto &om : get_overmaps_near( omt_to_sm_copy( center ), omt_to_sm_copy( radius ) ) ) { + + // Build an overmap_special_batch for the special on this overmap. + std::vector specials; + specials.push_back( &special ); + overmap_special_batch batch( om->pos(), specials ); + + // Attempt to place the specials using our batch and sectors. We + // require they be placed in unexplored terrain right now. + om->place_specials_pass( batch, sectors, true, true ); + + // The place special pass will erase specials that have reached their + // maximum number of instances so first check if its been erased. + if( batch.empty() ) { + return true; + } + + // Hasn't been erased, so lets check placement counts. + for( const auto &special_placement : batch ) { + if( special_placement.instances_placed > 0 ) { + // It was placed, lets get outta here. + return true; + } + } + } + + // If we got this far, we've failed to make the placement. + return false; +} diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index d3dc688f35f88..f96d3de304979 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -4,6 +4,7 @@ #include "enums.h" #include "int_id.h" +#include "omdata.h" #include "overmap_types.h" #include "string_id.h" @@ -19,13 +20,7 @@ struct om_vehicle; struct oter_t; using oter_id = int_id; - -class overmap_special; -using overmap_special_id = string_id; - class overmap; -class overmap_special; -class overmap_special_batch; struct radio_tower; struct regional_settings; class vehicle; @@ -410,6 +405,33 @@ class overmapbuffer std::string get_description_at( const tripoint &where ); + /** + * Place the specified overmap special directly on the map using the provided location and rotation. + * Intended to be used when you have a special in hand, the desired location and rotation are known + * and the special should be directly placed rather than using the overmap's placement algorithm. + * @param special The overmap special to place. + * @param location The location to place the overmap special. Absolute overmap terrain coordinates. + * @param dir The direction to rotate the overmap special before placement. + * @param must_be_unexplored If true, will require that all of the terrains where the special would be + * placed are unexplored. + * @param force If true, placement will bypass the checks for valid placement. + * @returns True if the special was placed, else false. + */ + bool place_special( const overmap_special &special, const tripoint &location, + om_direction::type dir, + const bool must_be_unexplored, const bool force ); + /** + * Place the specified overmap special using the overmap's placement algorithm. Intended to be used + * when you have a special that you want placed but it should be placed similarly to as if it were + * created during overmap generation. + * @param special_id The id of overmap special to place. + * @param center Used in conjunction with radius to search the specified and adjacent overmaps for + * a valid placement location. Absolute overmap terrain coordinates. + * @param radius Used in conjunction with center. Absolute overmap terrain units. + * @returns True if the special was placed, else false. + */ + bool place_special( const overmap_special_id special_id, const tripoint ¢er, int radius ); + private: /** * Common function used by the find_closest/all/random to determine if the location is From eb1f4a988848ec539c3d60e1b67e8fb0572e29f3 Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Tue, 11 Dec 2018 12:23:09 -0900 Subject: [PATCH 6/7] Use overmapbuffer.place_special in debug special placement This brings debug placement more in line with the placement done during map generation, e.g. it will make connections, record placement into the data structure tying overmap terrains to the special that placed them, and so on. --- src/overmap_ui.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/overmap_ui.cpp b/src/overmap_ui.cpp index a6a92c51a97b7..2665d4d549296 100644 --- a/src/overmap_ui.cpp +++ b/src/overmap_ui.cpp @@ -1151,10 +1151,9 @@ tripoint display( const tripoint &orig, const draw_data_t &data = draw_data_t() overmap_buffer.ter( curs ) = uistate.place_terrain->id.id(); overmap_buffer.set_seen( curs.x, curs.y, curs.z, true ); } else { + overmap_buffer.place_special( *uistate.place_special, curs, uistate.omedit_rotation, false, true ); for( const auto &s_ter : uistate.place_special->terrains ) { const tripoint pos = curs + om_direction::rotate( s_ter.p, uistate.omedit_rotation ); - - overmap_buffer.ter( pos ) = s_ter.terrain->get_rotated( uistate.omedit_rotation ); overmap_buffer.set_seen( pos.x, pos.y, pos.z, true ); } } From 382843308c4e6c8650f7b809598091e196ea9a00 Mon Sep 17 00:00:00 2001 From: Jason Jones Date: Mon, 10 Dec 2018 15:48:53 -0900 Subject: [PATCH 7/7] Make mission_start::reveal_refugee_center overmap special aware --- src/mission_start.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/mission_start.cpp b/src/mission_start.cpp index 0d218066feb4b..32c9fae8091b6 100644 --- a/src/mission_start.cpp +++ b/src/mission_start.cpp @@ -1733,8 +1733,21 @@ void reveal_any_target( mission *miss, const std::vector &omter_ids void mission_start::reveal_refugee_center( mission *miss ) { + const overmap_special_id os_evac_center( "evac_center" ); const tripoint your_pos = g->u.global_omt_location(); - const tripoint center_pos = overmap_buffer.find_closest( your_pos, "evac_center_18", 0, false ); + + // Try and find the special. + tripoint center_pos = overmap_buffer.find_closest( your_pos, "evac_center_18", OMAPX * 5, false, + false, true, os_evac_center ); + + // We couldn't find the special, so let's try and place it. + if( center_pos == overmap::invalid_tripoint ) { + const bool placed = overmap_buffer.place_special( os_evac_center, your_pos, OMAPX * 5 ); + if( placed ) { + center_pos = overmap_buffer.find_closest( your_pos, "evac_center_18", OMAPX * 5, false, + false, true, os_evac_center ); + } + } if( center_pos == overmap::invalid_tripoint ) { add_msg( _( "You don't know where the address could be..." ) );