From 17ed66ecf320dfd79fdbaca8e11d49bd43cbe816 Mon Sep 17 00:00:00 2001 From: eltank <8000047+eltank@users.noreply.github.com> Date: Tue, 15 Jun 2021 21:59:57 -0700 Subject: [PATCH 1/2] Keep track of placed GLOBALLY_UNIQUE specials --- doc/JSON_FLAGS.md | 1 + doc/OVERMAP.md | 7 ++++--- src/overmap.cpp | 23 ++++++++++++++++------- src/overmapbuffer.cpp | 14 ++++++++++++++ src/overmapbuffer.h | 21 +++++++++++++++++++++ src/savegame.cpp | 19 +++++++++++++++++++ 6 files changed, 75 insertions(+), 10 deletions(-) diff --git a/doc/JSON_FLAGS.md b/doc/JSON_FLAGS.md index db062fa6af5be..dee031a8713b2 100644 --- a/doc/JSON_FLAGS.md +++ b/doc/JSON_FLAGS.md @@ -1169,6 +1169,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``` - ```MAN_MADE``` - For location, created by human. For Innawood mod purposes diff --git a/doc/OVERMAP.md b/doc/OVERMAP.md index 4c7600de28a96..c581f77b2f90b 100644 --- a/doc/OVERMAP.md +++ b/doc/OVERMAP.md @@ -343,11 +343,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; diff --git a/src/overmap.cpp b/src/overmap.cpp index 868d2ca571b75..e95b0195ece5c 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -614,9 +614,9 @@ 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 ) { - size_t min_occur = - static_cast( std::max( elem.get_constraints().occurrences.min, 0 ) ); - return sum + ( elem.has_flag( "UNIQUE" ) ? static_cast( 0 ) : min_occur ); + size_t min_occur = static_cast( std::max( elem.get_constraints().occurrences.min, 0 ) ); + const bool unique = elem.has_flag( "UNIQUE" ) || elem.has_flag( "GLOBALLY_UNIQUE" ); + return sum + ( unique ? 0 : min_occur ); } ); if( actual_count > max_count ) { @@ -5950,6 +5950,10 @@ bool overmap::can_place_special( const overmap_special &special, const tripoint_ if( !special.id ) { return false; } + if( special.has_flag( "GLOBALLY_UNIQUE" ) && + overmap_buffer.contains_unique_special( special.id ) ) { + return false; + } if( special.has_eoc() ) { dialogue d( get_talker_for( get_avatar() ), nullptr ); @@ -5994,6 +5998,9 @@ std::vector overmap::place_special( if( !force ) { cata_assert( can_place_special( special, p, dir, must_be_unexplored ) ); } + if( special.has_flag( "GLOBALLY_UNIQUE" ) ) { + overmap_buffer.add_unique_special( special.id ); + } const bool is_safe_zone = special.has_flag( "SAFE_AT_WORLDGEN" ); @@ -6142,13 +6149,15 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) continue; } - if( iter->special_details->has_flag( "UNIQUE" ) ) { - const overmap_special_placement_constraints &constraints = - iter->special_details->get_constraints(); + const bool unique = iter->special_details->has_flag( "UNIQUE" ); + const bool globally_unique = iter->special_details->has_flag( "GLOBALLY_UNIQUE" ); + if( unique || globally_unique ) { + const overmap_special_id &id = iter->special_details->id; + const overmap_special_placement_constraints &constraints = iter->special_details->get_constraints(); const int min = constraints.occurrences.min; const int max = constraints.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; diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index 9fe02e68ac542..9f72e86b89983 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -255,6 +255,7 @@ void overmapbuffer::clear() { overmaps.clear(); known_non_existing.clear(); + placed_unique_specials.clear(); last_requested_overmap = nullptr; } @@ -1086,6 +1087,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, diff --git a/src/overmapbuffer.h b/src/overmapbuffer.h index c4db0703d0820..2dd51e9ee8310 100644 --- a/src/overmapbuffer.h +++ b/src/overmapbuffer.h @@ -9,11 +9,13 @@ #include #include #include +#include #include #include #include "coordinates.h" #include "enums.h" +#include "json.h" #include "memory_fast.h" #include "omdata.h" #include "optional.h" @@ -535,6 +537,8 @@ class overmapbuffer mutable std::set 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 placed_unique_specials; /** * Get a list of notes in the (loaded) overmaps. @@ -569,6 +573,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 diff --git a/src/savegame.cpp b/src/savegame.cpp index aa975bd9cf6de..c3ae6f39bb1bf 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -31,6 +31,7 @@ #include "omdata.h" #include "options.h" #include "overmap.h" +#include "overmapbuffer.h" #include "overmap_types.h" #include "path_info.h" #include "regional_settings.h" @@ -1450,6 +1451,8 @@ void game::unserialize_master( std::istream &fin ) weather_manager::unserialize_all( jsin ); } else if( name == "timed_events" ) { timed_event_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(); @@ -1535,6 +1538,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( "timed_events" ); timed_event_manager::serialize_all( json ); @@ -1659,3 +1664,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() ); + } +} From 207ac3aec0d670edb1ec9b3ea688ac7c5457ce0c Mon Sep 17 00:00:00 2001 From: anothersimulacrum Date: Sat, 16 Jul 2022 20:06:12 +0000 Subject: [PATCH 2/2] Make large military locations globally unique Unlike faction bases, these locations aren't 'special' - they just add location variety, and so the issue of making the player spawn location special, and the chance of missing them if you go the wrong direction are much less relevant. They _should_ be rare, so if you miss them, it's not a problem. Making the spawn location special is not great, but I think is acceptable for this. --- data/json/overmap/overmap_special/aircraft_carrier.json | 2 +- data/json/overmap/overmap_special/specials.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/json/overmap/overmap_special/aircraft_carrier.json b/data/json/overmap/overmap_special/aircraft_carrier.json index db579163fc584..15e1169a8b994 100644 --- a/data/json/overmap/overmap_special/aircraft_carrier.json +++ b/data/json/overmap/overmap_special/aircraft_carrier.json @@ -201,6 +201,6 @@ "city_distance": [ -1, 1000 ], "locations": [ "lake_surface" ], "occurrences": [ 75, 100 ], - "flags": [ "LAKE", "MAN_MADE", "UNIQUE", "MILITARY" ] + "flags": [ "LAKE", "MAN_MADE", "GLOBALLY_UNIQUE", "MILITARY" ] } ] diff --git a/data/json/overmap/overmap_special/specials.json b/data/json/overmap/overmap_special/specials.json index a94c1fc304f73..d38076a0c6dab 100644 --- a/data/json/overmap/overmap_special/specials.json +++ b/data/json/overmap/overmap_special/specials.json @@ -7574,7 +7574,7 @@ "city_sizes": [ 4, -1 ], "occurrences": [ 45, 100 ], "//": "Inflated chance, in effect ~10%", - "flags": [ "MILITARY", "UNIQUE", "MAN_MADE" ] + "flags": [ "MILITARY", "GLOBALLY_UNIQUE", "MAN_MADE" ] }, { "type": "overmap_special",