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

Make some of the UNIQUE overmap specials globally unique #49322

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/json/overmap/overmap_special/specials.json
Original file line number Diff line number Diff line change
Expand Up @@ -3386,7 +3386,7 @@
"city_distance": [ 3, -1 ],
"city_sizes": [ 4, -1 ],
"occurrences": [ 75, 100 ],
"flags": [ "UNIQUE", "SAFE_AT_WORLDGEN" ]
"flags": [ "GLOBALLY_UNIQUE", "SAFE_AT_WORLDGEN" ]
},
{
"type": "overmap_special",
Expand Down
1 change: 1 addition & 0 deletions doc/JSON_FLAGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1157,6 +1157,7 @@ These branches are also the valid entries for the categories of `dreams` in `dre
- ```SAFE_AT_WORLDGEN``` Location will not spawn overmap monster groups during worldgen (does not affect monsters spawned by mapgen).
- ```TRIFFID``` Location is related to triffids. Used to classify location.
- ```UNIQUE``` Location is unique and will only occur once per overmap. `occurrences` is overridden to define a percent chance (e.g. `"occurrences" : [75, 100]` is 75%)
- ```GLOBALLY_UNIQUE``` Location will only occur once per world. `occurrences` is overridden to define a percent chance (e.g. `"occurrences" : [75, 100]` is 75%)
- ```URBAN```
- ```WILDERNESS```

Expand Down
7 changes: 4 additions & 3 deletions doc/OVERMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,12 @@ each normal special has a very high chance of being placed at least once per ove
quirks of the code (most notably, the number of specials is only slightly more than the number of slots per
overmap, specials that failed placement don't get disqualified and can be rolled for again, and placement iterates
until all sectors are occupied). For specials that are not common enough to warrant appearing more
than once per overmap please use the "UNIQUE" flag.
than once per overmap please use the "UNIQUE" flag. For specials that should only have one instance
per world use "GLOBALLY_UNIQUE".

### Occurrences ( UNIQUE )
### Occurrences ( UNIQUE, GLOBALLY_UNIQUE )

When the special has the "UNIQUE" flag, instead of defining the minimum and maximum number placed
When the special has the "UNIQUE" or "GLOBALLY_UNIQUE" flag, instead of defining the minimum and maximum number placed
the occurrences field defines the chance of the special to be included in any one given overmap.
Before any placement rolls, all specials with this flag have to succeed in an x_in_y (first value, second
value) roll to be included in the `overmap_special_batch` for the currently generated overmap;
Expand Down
17 changes: 14 additions & 3 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,8 @@ void overmap_specials::check_consistency()
const size_t actual_count = std::accumulate( specials.get_all().begin(), specials.get_all().end(),
static_cast< size_t >( 0 ),
[]( size_t sum, const overmap_special & elem ) {
return sum + ( elem.flags.count( "UNIQUE" ) ? static_cast<size_t>( 0 ) : static_cast<size_t>( (
const bool unique = elem.flags.count( "UNIQUE" ) > 0 || elem.flags.count( "GLOBALLY_UNIQUE" ) > 0;
return sum + ( unique ? static_cast<size_t>( 0 ) : static_cast<size_t>( (
std::max( elem.occurrences.min, 0 ) ) ) ) ;
} );

Expand Down Expand Up @@ -4115,6 +4116,10 @@ bool overmap::can_place_special( const overmap_special &special, const tripoint_
if( !special.id ) {
return false;
}
if( special.flags.count( "GLOBALLY_UNIQUE" ) > 0 &&
overmap_buffer.contains_unique_special( special.id ) ) {
return false;
}

return std::all_of( special.terrains.begin(),
special.terrains.end(), [&]( const overmap_special_terrain & elem ) {
Expand Down Expand Up @@ -4150,6 +4155,9 @@ void overmap::place_special(
if( !force ) {
cata_assert( can_place_special( special, p, dir, must_be_unexplored ) );
}
if( special.flags.count( "GLOBALLY_UNIQUE" ) > 0 ) {
overmap_buffer.add_unique_special( special.id );
}

const bool blob = special.flags.count( "BLOB" ) > 0;
const bool is_safe_zone = special.flags.count( "SAFE_AT_WORLDGEN" ) > 0;
Expand Down Expand Up @@ -4335,11 +4343,14 @@ void overmap::place_specials( overmap_special_batch &enabled_specials )
continue;
}

if( iter->special_details->flags.count( "UNIQUE" ) > 0 ) {
const bool unique = iter->special_details->flags.count( "UNIQUE" ) > 0;
const bool globally_unique = iter->special_details->flags.count( "GLOBALLY_UNIQUE" ) > 0;
if( unique || globally_unique ) {
const overmap_special_id &id = iter->special_details->id;
const int min = iter->special_details->occurrences.min;
const int max = iter->special_details->occurrences.max;

if( x_in_y( min, max ) ) {
if( x_in_y( min, max ) && ( !globally_unique || !overmap_buffer.contains_unique_special( id ) ) ) {
// Min and max are overloaded to be the chance of occurrence,
// so reset instances placed to one short of max so we don't place several.
iter->instances_placed = max - 1;
Expand Down
14 changes: 14 additions & 0 deletions src/overmapbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ void overmapbuffer::clear()
{
overmaps.clear();
known_non_existing.clear();
placed_unique_specials.clear();
last_requested_overmap = nullptr;
}

Expand Down Expand Up @@ -896,6 +897,19 @@ bool overmapbuffer::check_overmap_special_type( const overmap_special_id &id,
return om_loc.om->check_overmap_special_type( id, om_loc.local );
}

void overmapbuffer::add_unique_special( const overmap_special_id &id )
{
if( contains_unique_special( id ) ) {
debugmsg( "Unique overmap special placed more than once: %s", id.str() );
}
placed_unique_specials.emplace( id );
}

bool overmapbuffer::contains_unique_special( const overmap_special_id &id ) const
{
return placed_unique_specials.find( id ) != placed_unique_specials.end();
}

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,
Expand Down
21 changes: 21 additions & 0 deletions src/overmapbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
#include <new>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "coordinates.h"
#include "enums.h"
#include "json.h"
#include "memory_fast.h"
#include "omdata.h"
#include "optional.h"
Expand Down Expand Up @@ -493,6 +495,8 @@ class overmapbuffer
mutable std::set<point_abs_om> known_non_existing;
// Cached result of previous call to overmapbuffer::get_existing
overmap mutable *last_requested_overmap;
// Set of globally unique overmap specials that have already been placed
std::unordered_set<overmap_special_id> placed_unique_specials;

/**
* Get a list of notes in the (loaded) overmaps.
Expand Down Expand Up @@ -527,6 +531,23 @@ class overmapbuffer
const tripoint_abs_omt &loc );
bool check_overmap_special_type_existing( const overmap_special_id &id,
const tripoint_abs_omt &loc );

/**
* Adds the given globally unique overmap special to the list of placed specials.
*/
void add_unique_special( const overmap_special_id &id );
/**
* Returns true if the given globally unique overmap special has already been placed.
*/
bool contains_unique_special( const overmap_special_id &id ) const;
/**
* Writes the placed unique specials as a JSON value.
*/
void serialize_placed_unique_specials( JsonOut &json ) const;
/**
* Reads placed unique specials from JSON and overwrites the global value.
*/
void deserialize_placed_unique_specials( JsonIn &jsin );
private:
/**
* Go thorough the monster groups of the overmap and move out-of-bounds
Expand Down
19 changes: 19 additions & 0 deletions src/savegame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "omdata.h"
#include "options.h"
#include "overmap.h"
#include "overmapbuffer.h"
#include "overmap_types.h"
#include "regional_settings.h"
#include "scent_map.h"
Expand Down Expand Up @@ -1255,6 +1256,8 @@ void game::unserialize_master( std::istream &fin )
jsin.read( seed );
} else if( name == "weather" ) {
weather_manager::unserialize_all( jsin );
} else if( name == "placed_unique_specials" ) {
overmap_buffer.deserialize_placed_unique_specials( jsin );
} else {
// silently ignore anything else
jsin.skip_value();
Expand Down Expand Up @@ -1297,6 +1300,8 @@ void game::serialize_master( std::ostream &fout )

json.member( "active_missions" );
mission::serialize_all( json );
json.member( "placed_unique_specials" );
overmap_buffer.serialize_placed_unique_specials( json );

json.member( "factions", *faction_manager_ptr );
json.member( "seed", seed );
Expand Down Expand Up @@ -1383,3 +1388,17 @@ void Creature_tracker::serialize( JsonOut &jsout ) const
}
jsout.end_array();
}

void overmapbuffer::serialize_placed_unique_specials( JsonOut &json ) const
{
json.write_as_array( placed_unique_specials );
}

void overmapbuffer::deserialize_placed_unique_specials( JsonIn &jsin )
{
placed_unique_specials.clear();
jsin.start_array();
while( !jsin.end_array() ) {
placed_unique_specials.emplace( jsin.get_string() );
}
}