diff --git a/data/json/recipes/basecamps/recipe_groups.json b/data/json/recipes/basecamps/recipe_groups.json index 6a45004645072..a1a995f491fa2 100644 --- a/data/json/recipes/basecamps/recipe_groups.json +++ b/data/json/recipes/basecamps/recipe_groups.json @@ -111,6 +111,7 @@ }, { "id": "faction_base_bare_bones_basecamp_0", + "//": "Hardcoded reference in function 'place'(overmap.cpp). Please be mindful if changing the ID.", "description": "Faction Base Bare Bones Basecamp. Manually add bulletin board to use. No construction. Can be constructed anywhere. Access to crafting recipes using Supplies zone covered furniture and appliances.", "om_terrains": [ "ANY" ] } diff --git a/doc/OVERMAP.md b/doc/OVERMAP.md index fe06ff2f74eb4..d8c4162ac3531 100644 --- a/doc/OVERMAP.md +++ b/doc/OVERMAP.md @@ -423,7 +423,7 @@ Depending on the subtype, there are further relevant fields: "overmaps": [ { "point": [ 0, 0, 0 ], "overmap": "campground_1a_north", "locations": [ "forest_edge" ] }, { "point": [ 1, 0, 0 ], "overmap": "campground_1b_north" }, - { "point": [ 0, 1, 0 ], "overmap": "campground_2a_north" }, + { "point": [ 0, 1, 0 ], "overmap": "campground_2a_north", "camp": "isherwood_family", "camp_name": "Campground camp" }, { "point": [ 1, 1, 0 ], "overmap": "campground_2b_north" } ], "connections": [ { "point": [ 1, -1, 0 ], "terrain": "road", "connection": "local_road", "from": [ 1, 0, 0 ] } ], @@ -444,6 +444,8 @@ Depending on the subtype, there are further relevant fields: | `point` | `[ x, y, z]` of the overmap terrain within the special. | | `overmap` | Id of the `overmap_terrain` to place at the location. If ommited no overmap_terrain is placed but the point will still be checked for valid locations when deciding if placement is valid. | | `locations` | List of `overmap_location` ids that this overmap terrain may be placed on. Overrides the specials overall `locations` field. | +| `camp` | Will make a NPC-owned camp spawn here when given a value. The entered value is the ID of the faction that owns this camp. | +| `camp_name` | Name that will be displayed on the overmap for the camp. | ### Connections diff --git a/src/basecamp.cpp b/src/basecamp.cpp index 46f9e55169b58..8d1b067f72cdb 100644 --- a/src/basecamp.cpp +++ b/src/basecamp.cpp @@ -187,9 +187,12 @@ void basecamp::add_expansion( const std::string &bldg, const tripoint_abs_omt &n update_resources( bldg ); } -void basecamp::define_camp( const tripoint_abs_omt &p, const std::string_view camp_type ) +void basecamp::define_camp( const tripoint_abs_omt &p, const std::string_view camp_type, + bool player_founded ) { - query_new_name( true ); + if( player_founded ) { + query_new_name( true ); + } omt_pos = p; const oter_id &omt_ref = overmap_buffer.ter( omt_pos ); // purging the regions guarantees all entries will start with faction_base_ @@ -205,9 +208,11 @@ void basecamp::define_camp( const tripoint_abs_omt &p, const std::string_view ca e.pos = omt_pos; expansions[base_camps::base_dir] = e; const std::string direction = oter_get_rotation_string( omt_ref ); - const oter_id bcid( direction.empty() ? "faction_base_camp_0" : "faction_base_camp_new_0" + - direction ); - overmap_buffer.ter_set( omt_pos, bcid ); + if( player_founded ) { + const oter_id bcid( direction.empty() ? "faction_base_camp_0" : "faction_base_camp_new_0" + + direction ); + overmap_buffer.ter_set( omt_pos, bcid ); + } update_provides( base_camps::faction_encode_abs( e, 0 ), expansions[base_camps::base_dir] ); } else { @@ -808,14 +813,8 @@ void basecamp::unload_camp_map() void basecamp::set_owner( faction_id new_owner ) { - for( const std::pair fac : g->faction_manager_ptr->all() ) { - if( fac.first == new_owner ) { - owner = new_owner; - return; - } - } - //Fallthrough, id must be invalid - debugmsg( "Could not find matching faction for new owner's faction_id!" ); + // Absolutely no safety checks, factions don't exist until you've encountered them but we sometimes set the owner before that + owner = new_owner; } faction_id basecamp::get_owner() diff --git a/src/basecamp.h b/src/basecamp.h index 8584101fbc429..7c1a638f3e818 100644 --- a/src/basecamp.h +++ b/src/basecamp.h @@ -207,7 +207,8 @@ class basecamp void add_expansion( const std::string &terrain, const tripoint_abs_omt &new_pos ); void add_expansion( const std::string &bldg, const tripoint_abs_omt &new_pos, const point &dir ); - void define_camp( const tripoint_abs_omt &p, std::string_view camp_type ); + void define_camp( const tripoint_abs_omt &p, std::string_view camp_type, + bool player_founded = true ); std::string expansion_tab( const point &dir ) const; // check whether the point is the part of camp diff --git a/src/map.cpp b/src/map.cpp index 4803ed2411103..4a6c7ccf7a491 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -6754,12 +6754,15 @@ basecamp map::hoist_submap_camp( const tripoint &p ) return pcamp ? *pcamp : basecamp(); } -void map::add_camp( const tripoint_abs_omt &omt_pos, const std::string &name ) +void map::add_camp( const tripoint_abs_omt &omt_pos, const std::string &name, bool need_validate ) { basecamp temp_camp = basecamp( name, omt_pos ); overmap_buffer.add_camp( temp_camp ); get_player_character().camps.insert( omt_pos ); - g->validate_camps(); + // Mapgen-spawned camps never need or want to validate, since they'll always be on newly generated OMTs + if( need_validate ) { + g->validate_camps(); + } } void map::update_submaps_with_active_items() diff --git a/src/map.h b/src/map.h index e72bf4317550c..36f752acd32a6 100644 --- a/src/map.h +++ b/src/map.h @@ -1712,7 +1712,7 @@ class map computer *add_computer( const tripoint &p, const std::string &name, int security ); // Camps - void add_camp( const tripoint_abs_omt &omt_pos, const std::string &name ); + void add_camp( const tripoint_abs_omt &omt_pos, const std::string &name, bool need_validate = true ); void remove_submap_camp( const tripoint & ); basecamp hoist_submap_camp( const tripoint &p ); bool point_within_camp( const tripoint &point_check ) const; diff --git a/src/omdata.h b/src/omdata.h index 5fde4ee375a00..93edb935c8ccc 100644 --- a/src/omdata.h +++ b/src/omdata.h @@ -499,6 +499,8 @@ struct overmap_special_terrain : overmap_special_locations { const std::set & ); oter_str_id terrain; std::set flags; + std::optional camp_owner; + std::string camp_name; void deserialize( const JsonObject &om ); }; diff --git a/src/overmap.cpp b/src/overmap.cpp index 70d02cae1bb82..cdfe5dd82dd78 100644 --- a/src/overmap.cpp +++ b/src/overmap.cpp @@ -1102,6 +1102,8 @@ void overmap_special_terrain::deserialize( const JsonObject &om ) { om.read( "point", p ); om.read( "overmap", terrain ); + om.read( "camp", camp_owner ); + om.read( "camp_name", camp_name ); om.read( "flags", flags ); om.read( "locations", locations ); } @@ -1312,6 +1314,18 @@ struct fixed_overmap_special_data : overmap_special_data { points.insert( pos ); } + if( elem.camp_owner.has_value() ) { + if( !elem.camp_owner.value().is_valid() ) { + debugmsg( "In %s, camp at %s has invalid owner %s", context, pos.to_string(), + elem.camp_owner.value().c_str() ); + } + if( elem.camp_name.empty() ) { + debugmsg( "In %s, camp was defined but missing a camp_name.", context ); + } + } else if( !elem.camp_name.empty() ) { + debugmsg( "In %s, camp_name defined but no owner. Invalid name is discarded.", context ); + } + if( elem.locations.empty() ) { debugmsg( "In %s, no location is defined for point %s or the " "overall special.", context, pos.to_string() ); @@ -1418,6 +1432,21 @@ struct fixed_overmap_special_data : overmap_special_data { result.omts_used.push_back( location ); const oter_id tid = elem.terrain->get_rotated( dir ); om.ter_set( location, tid ); + if( elem.camp_owner.has_value() ) { + // This always results in z=0, but pos() doesn't return z-level information... + tripoint_abs_omt camp_loc = {project_combine( om.pos(), location.xy() ), 0}; + get_map().add_camp( camp_loc, "faction_camp", false ); + std::optional bcp = overmap_buffer.find_camp( camp_loc.xy() ); + if( !bcp ) { + debugmsg( "Camp placement during special generation failed at %s", camp_loc.to_string() ); + } else { + basecamp *temp_camp = *bcp; + temp_camp->set_owner( elem.camp_owner.value() ); + temp_camp->set_name( elem.camp_name ); + // FIXME? Camp types are raw strings! Not ideal. + temp_camp->define_camp( camp_loc, "faction_base_bare_bones_basecamp_0", false ); + } + } if( blob ) { for( int x = -2; x <= 2; x++ ) { for( int y = -2; y <= 2; y++ ) {