Skip to content

Commit

Permalink
Add place_special methods to overmapbuffer
Browse files Browse the repository at this point in the history
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 17, 2018
1 parent 741c772 commit 2240120
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 26 deletions.
28 changes: 15 additions & 13 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() );
Expand Down Expand Up @@ -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.
}
Expand Down Expand Up @@ -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.
Expand Down
20 changes: 13 additions & 7 deletions src/overmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand All @@ -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.
Expand All @@ -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();
Expand All @@ -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
Expand Up @@ -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
Expand Up @@ -4,6 +4,7 @@

#include "enums.h"
#include "int_id.h"
#include "omdata.h"
#include "overmap_types.h"
#include "string_id.h"

Expand All @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 2240120

Please sign in to comment.