Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find or create overmap special after overmap generation #27142

Prev Previous commit
Next Next commit
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.
  • Loading branch information
ralreegorganon committed Dec 19, 2018

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 295ac95ca99e2a2f135e5d0901e1c618d01f724d
28 changes: 15 additions & 13 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
@@ -3371,27 +3371,28 @@ void overmap::place_special( const overmap_special &special, const tripoint &p,
}
}

std::vector<point> overmap::get_sectors() const
om_special_sectors get_sectors( const int sector_width )
{
std::vector<point> 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 &sector, const bool place_optional, const bool must_be_unexplored )
const point &sector, 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<point> &sectors, const bool place_optional, const bool must_be_unexplored )
om_special_sectors &sectors, 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<point> 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.
20 changes: 13 additions & 7 deletions src/overmap.h
Original file line number Diff line number Diff line change
@@ -94,6 +94,11 @@ struct map_layer {
std::vector<om_note> notes;
};

struct om_special_sectors {
std::vector<point> 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<point> 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<point> &sectors, bool place_optional, const bool must_be_unexplored );
om_special_sectors &sectors, 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 &sector, bool place_optional, const bool must_be_unexplored );
const point &sector, 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
102 changes: 102 additions & 0 deletions src/overmapbuffer.cpp
Original file line number Diff line number Diff line change
@@ -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 &center,
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<const overmap_special *> 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;
}
34 changes: 28 additions & 6 deletions src/overmapbuffer.h
Original file line number Diff line number Diff line change
@@ -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<oter_t>;

class overmap_special;
using overmap_special_id = string_id<overmap_special>;

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 &center, int radius );

private:
/**
* Common function used by the find_closest/all/random to determine if the location is