diff --git a/data/json/overmap/overmap_special/aircraft_carrier.json b/data/json/overmap/overmap_special/aircraft_carrier.json index ecda177f8f79..daa2697d10fc 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": [ "CLASSIC", "LAKE", "MAN_MADE", "UNIQUE", "MILITARY", "ELECTRIC_GRID" ] + "flags": [ "CLASSIC", "LAKE", "MAN_MADE", "UNIQUE", "MILITARY", "ELECTRIC_GRID", "GLOBALLY_UNIQUE" ] } ] diff --git a/data/json/overmap/overmap_special/specials.json b/data/json/overmap/overmap_special/specials.json index a8a1369e0300..912837b5475d 100644 --- a/data/json/overmap/overmap_special/specials.json +++ b/data/json/overmap/overmap_special/specials.json @@ -1796,7 +1796,7 @@ "city_distance": [ 3, -1 ], "city_sizes": [ 4, -1 ], "occurrences": [ 50, 100 ], - "flags": [ "UNIQUE" ] + "flags": [ "ELECTRIC_GRID", "UNIQUE", "GLOBALLY_UNIQUE" ] }, { "type": "overmap_special", diff --git a/src/overmap.cpp b/src/overmap.cpp index 82dd30748803..80f185301f69 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -5502,6 +5502,11 @@ bool overmap::can_place_special( const overmap_special &special, const tripoint_ return false; } + if( special.has_flag( "GLOBALLY_UNIQUE" ) && + overmap_buffer.contains_unique_special( special.id ) ) { + return false; + } + const std::vector fixed_terrains = special.required_locations(); return std::all_of( fixed_terrains.begin(), fixed_terrains.end(), @@ -5539,6 +5544,10 @@ std::vector overmap::place_special( 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 grid = special.has_flag( "ELECTRIC_GRID" ); special_placement_result result = special.place( *this, p, dir ); @@ -5942,10 +5951,18 @@ void overmap::place_specials( overmap_special_batch &enabled_specials ) const float rate = is_true_center && special.has_flag( "ENDGAME" ) ? 1 : zone_ratio[current]; + const bool unique = iter.special_details->has_flag( "UNIQUE" ); + const bool globally_unique = iter.special_details->has_flag( "GLOBALLY_UNIQUE" ); + int amount_to_place; - if( special.has_flag( "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(); + int chance = roll_remainder( min * rate ); - amount_to_place = x_in_y( chance, max ) ? 1 : 0; + //FINGERS CROSSED EMOGI + amount_to_place = x_in_y( min, max ) && ( !globally_unique || + !overmap_buffer.contains_unique_special( id ) ) ? 1 : 0; } else { // Number of instances normalized to terrain ratio float real_max = std::max( static_cast( min ), max * rate ); diff --git a/src/overmapbuffer.cpp b/src/overmapbuffer.cpp index cb873b3ed5e0..564f6e527a13 100644 --- a/src/overmapbuffer.cpp +++ b/src/overmapbuffer.cpp @@ -200,6 +200,7 @@ void overmapbuffer::clear() { overmaps.clear(); known_non_existing.clear(); + placed_unique_specials.clear(); last_requested_overmap = nullptr; } @@ -939,6 +940,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 b821f1c20e77..b26635acd74f 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 "overmap_types.h" #include "point.h" @@ -515,6 +517,9 @@ class overmapbuffer // 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. * @param z only this specific z-level is search for notes. @@ -548,6 +553,24 @@ 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 1e4a849697ed..0bb2f13ceec7 100644 --- a/src/savegame.cpp +++ b/src/savegame.cpp @@ -34,6 +34,7 @@ #include "output.h" #include "overmap.h" #include "overmap_types.h" +#include "overmapbuffer.h" #include "popup.h" #include "regional_settings.h" #include "scent_map.h" @@ -1167,6 +1168,8 @@ void game::unserialize_master( std::istream &fin ) jsin.read( *faction_manager_ptr ); } else if( name == "seed" ) { jsin.read( seed ); + } else if( name == "placed_unique_specials" ) { + overmap_buffer.deserialize_placed_unique_specials( jsin ); } else if( name == "weather" ) { JsonObject w = jsin.get_object(); w.read( "lightning", get_weather().lightning_active ); @@ -1202,6 +1205,9 @@ 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 ); @@ -1283,3 +1289,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() ); + } +}