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

Better in city check #74191

Merged
merged 12 commits into from
Oct 24, 2024
15 changes: 14 additions & 1 deletion doc/EFFECT_ON_CONDITION.md
Original file line number Diff line number Diff line change
Expand Up @@ -1227,7 +1227,7 @@ Runs a query, allowing you to pick specific tile around. When picked, stores coo

### `map_in_city`
- type: location string or [variable object](#variable-object)
- return true if the location is in a city
- return true if the location is in the bounds of a city at or above z-1

#### Valid talkers:

Expand All @@ -1244,6 +1244,19 @@ Check the location is in a city.
},
```

Each time the avatar enters an OMT display a message as to whether or not they're in a city.
```
{
"type": "effect_on_condition",
"id": "EOC_TEST_IS_IN_CITY",
"eoc_type": "EVENT",
"required_event": "avatar_enters_omt",
"condition": { "map_in_city": { "mutator": "u_loc_relative", "target": "(0,0,0)" } },
"effect": [ { "u_message": "You are in a city OMT.", "type": "good" } ],
"false_effect": [ { "u_message": "You are NOT in a city OMT.", "type": "bad" } ]
},
```

### `player_see_u`, `player_see_npc`
- type: simple string
- return true if player can see alpha or beta talker
Expand Down
13 changes: 9 additions & 4 deletions src/condition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1741,10 +1741,15 @@ conditional_t::func f_map_in_city( const JsonObject &jo, std::string_view member
{
str_or_var target = get_str_or_var( jo.get_member( member ), member, true );
return [target]( dialogue const & d ) {
tripoint_abs_ms target_pos = tripoint_abs_ms( tripoint::from_string( target.evaluate( d ) ) );
city_reference c = overmap_buffer.closest_city( project_to<coords::sm>( target_pos ) );
c.distance = rl_dist( c.abs_sm_pos, project_to<coords::sm>( target_pos ) );
return c && c.get_distance_from_bounds() <= 0;
tripoint_abs_omt target_pos = project_to<coords::omt>( tripoint_abs_ms( tripoint::from_string(
target.evaluate( d ) ) ) );

// TODO: Remove this in favour of a seperate condition for location z-level that can be used in conjunction with this map_in_city as needed
if( target_pos.z() < -1 ) {
return false;
}

return overmap_buffer.is_in_city( target_pos );
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/omdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ class overmap_special
/** @returns true if this special requires a city */
bool requires_city() const;
/** @returns whether the special at specified tripoint can belong to the specified city. */
bool can_belong_to_city( const tripoint_om_omt &p, const city &cit ) const;
bool can_belong_to_city( const tripoint_om_omt &p, const city &cit, const overmap &omap ) const;
const cata::flat_set<std::string> &get_flags() const {
return flags_;
}
Expand Down
102 changes: 97 additions & 5 deletions src/overmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2884,15 +2884,22 @@ bool overmap_special::requires_city() const
constraints_.city_distance.max < std::max( OMAPX, OMAPY );
}

bool overmap_special::can_belong_to_city( const tripoint_om_omt &p, const city &cit ) const
bool overmap_special::can_belong_to_city( const tripoint_om_omt &p, const city &cit,
const overmap &omap ) const
{
if( !requires_city() ) {
return true;
}
if( !cit || !constraints_.city_size.contains( cit.size ) ) {
return false;
}
return constraints_.city_distance.contains( cit.get_distance_from( p ) - ( cit.size ) );
if( constraints_.city_distance.max > std::max( OMAPX, OMAPY ) ) {
// Only care that we're more than min away from a city
return !omap.distance_to_city( p, constraints_.city_distance.min );
}
const std::optional<int> dist = omap.distance_to_city( p, constraints_.city_distance.max );
// Found a city within max and it's greater than min away
return !!dist && constraints_.city_distance.min < *dist;
}

bool overmap_special::has_flag( const std::string &flag ) const
Expand Down Expand Up @@ -4176,6 +4183,83 @@ void overmap::clear_connections_out()
connections_out.clear();
}

bool overmap::is_in_city( const tripoint_om_omt &p ) const
{
if( !city_tiles.empty() ) {
return city_tiles.find( p.xy() ) != city_tiles.end();
} else {
// Legacy handling
return distance_to_city( p ) == 0;
}

}

std::optional<int> overmap::distance_to_city( const tripoint_om_omt &p,
int max_dist_to_check ) const
{
if( !city_tiles.empty() ) {
for( int i = 0; i <= max_dist_to_check; i++ ) {
for( const tripoint_om_omt &tile : closest_points_first( p, i, i ) ) {
if( is_in_city( tile ) ) {
return i;
}
}
}
} else {
// Legacy handling
const city &nearest_city = get_nearest_city( p );
if( !!nearest_city ) {
// 0 if within city
return std::max( 0, nearest_city.get_distance_from( p ) - nearest_city.size );
}
}
return {};
}

void overmap::flood_fill_city_tiles()
{
std::unordered_set<point_om_omt> visited;
// simplifies bounds checking
const half_open_rectangle<point_om_omt> omap_bounds( point_om_omt( 0, 0 ), point_om_omt( OMAPX,
OMAPY ) );

// Look through every point on the overmap
for( int y = 0; y < OMAPY; y++ ) {
for( int x = 0; x < OMAPX; x++ ) {
point_om_omt checked( x, y );
// If we already looked at it in a previous flood-fill, ignore it
if( visited.find( checked ) != visited.end() ) {
continue;
}
// Is the area connected to this point enclosed by city_tiles?
bool enclosed = true;
// Predicate for flood-fill. Also detects if any point flood-filled to borders the edge
// of the overmap and is thus not enclosed
const auto is_unchecked = [&enclosed, &visited, &omap_bounds, this]( const point_om_omt & pt ) {
if( city_tiles.find( pt ) != visited.end() ) {
return false;
}
// We hit the edge of the overmap! We're free!
if( !omap_bounds.contains( pt ) ) {
enclosed = false;
return false;
}
return true;
};
// All the points connected to this point that aren't part of a city
std::vector<point_om_omt> area = ff::point_flood_fill_4_connected( checked, visited, is_unchecked );
if( !enclosed ) {
continue;
}
// They are enclosed, and so should be considered part of the city.
city_tiles.reserve( city_tiles.size() + area.size() );
for( const point_om_omt &pt : area ) {
city_tiles.insert( pt );
}
}
}
}

static std::map<std::string, std::string> oter_id_migrations;
static std::map<oter_type_str_id, std::pair<translation, faction_id>> camp_migration_map;

Expand Down Expand Up @@ -5821,6 +5905,7 @@ void overmap::place_cities()
if( ter( p ) == settings->default_oter[OVERMAP_DEPTH] ) {
placement_attempts = 0;
ter_set( p, oter_road_nesw ); // every city starts with an intersection
city_tiles.insert( c );
tmp.pos = p.xy();
tmp.size = size;
}
Expand All @@ -5829,6 +5914,7 @@ void overmap::place_cities()
tmp = random_entry( cities_to_place );
p = tripoint_om_omt( tmp.pos, 0 );
ter_set( tripoint_om_omt( tmp.pos, 0 ), oter_road_nesw );
city_tiles.insert( tmp.pos );
}
if( placement_attempts == 0 ) {
cities.push_back( tmp );
Expand All @@ -5842,6 +5928,7 @@ void overmap::place_cities()
} while( ( cur_dir = om_direction::turn_right( cur_dir ) ) != start_dir );
}
}
flood_fill_city_tiles();
}

overmap_special_id overmap::pick_random_building_to_place( int town_dist, int town_size,
Expand Down Expand Up @@ -5903,7 +5990,11 @@ void overmap::place_building( const tripoint_om_omt &p, om_direction::type dir,
const overmap_special_id building_tid = pick_random_building_to_place( town_dist, town.size,
placed_unique_buildings );
if( can_place_special( *building_tid, building_pos, building_dir, false ) ) {
place_special( *building_tid, building_pos, building_dir, town, false, false );
std::vector<tripoint_om_omt> used_tripoints = place_special( *building_tid, building_pos,
building_dir, town, false, false );
for( const tripoint_om_omt &p : used_tripoints ) {
city_tiles.insert( p.xy() );
}
if( building_tid->has_flag( "CITY_UNIQUE" ) ) {
placed_unique_buildings.emplace( building_tid );
}
Expand Down Expand Up @@ -6325,7 +6416,7 @@ static pf::directed_path<point_om_omt> straight_path( const point_om_omt &source
}

pf::directed_path<point_om_omt> overmap::lay_out_street( const overmap_connection &connection,
const point_om_omt &source, om_direction::type dir, size_t len ) const
const point_om_omt &source, om_direction::type dir, size_t len )
{
const tripoint_om_omt from( source, 0 );
// See if we need to make another one "step" further.
Expand Down Expand Up @@ -6378,6 +6469,7 @@ pf::directed_path<point_om_omt> overmap::lay_out_street( const overmap_connectio
break;
}

city_tiles.insert( pos.xy() );
++actual_len;

if( actual_len > 1 && connection.has( ter_id ) ) {
Expand Down Expand Up @@ -7017,7 +7109,7 @@ bool overmap::place_special_attempt(
continue;
}
// City check is the fastest => it goes first.
if( !special.can_belong_to_city( p, nearest_city ) ) {
if( !special.can_belong_to_city( p, nearest_city, *this ) ) {
continue;
}
// See if we can actually place the special there.
Expand Down
11 changes: 10 additions & 1 deletion src/overmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,16 @@ class overmap
void clear_connections_out();
void place_special_forced( const overmap_special_id &special_id, const tripoint_om_omt &p,
om_direction::type dir );
// Whether the tripoint's point is true in city_tiles
bool is_in_city( const tripoint_om_omt &p ) const;
// Returns the distance to the nearest city_tile within max_dist_to_check or std::nullopt if there isn't one
std::optional<int> distance_to_city( const tripoint_om_omt &p,
int max_dist_to_check = OMAPX ) const;
private:
// Any point that is part of or surrounded by a city
std::unordered_set<point_om_omt> city_tiles;
// Fill in any gaps in city_tiles that don't connect to the map edge
void flood_fill_city_tiles();
std::multimap<tripoint_om_sm, mongroup> zg; // NOLINT(cata-serialize)
public:
/** Unit test enablers to check if a given mongroup is present. */
Expand Down Expand Up @@ -507,7 +516,7 @@ class overmap
const point_om_omt &dest, int z, bool must_be_unexplored ) const;
pf::directed_path<point_om_omt> lay_out_street(
const overmap_connection &connection, const point_om_omt &source,
om_direction::type dir, size_t len ) const;
om_direction::type dir, size_t len );
public:
void build_connection(
const overmap_connection &connection, const pf::directed_path<point_om_omt> &path, int z,
Expand Down
9 changes: 9 additions & 0 deletions src/overmapbuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1682,6 +1682,15 @@ bool overmapbuffer::is_safe( const tripoint_abs_omt &p )
return true;
}

bool overmapbuffer::is_in_city( const tripoint_abs_omt &p )
{
point_abs_om overmap_pos;
tripoint_om_omt potential_city_tile;
std::tie( overmap_pos, potential_city_tile ) = project_remain<coords::om>( p );
overmap &target_overmap = get( overmap_pos );
return target_overmap.is_in_city( potential_city_tile );
}

std::optional<std::vector<tripoint_abs_omt>> overmapbuffer::place_special(
const overmap_special &special, const tripoint_abs_omt &origin, om_direction::type dir,
const bool must_be_unexplored, const bool force )
Expand Down
4 changes: 4 additions & 0 deletions src/overmapbuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,10 @@ class overmapbuffer
* If there are any, it's not safe.
*/
bool is_safe( const tripoint_abs_omt &p );
/**
* Check if the tripoint is part of or surrounded by a city, ignoring z-level
*/
bool is_in_city( const tripoint_abs_omt &p );

/**
* Move the tracking mark of the given vehicle.
Expand Down
5 changes: 5 additions & 0 deletions src/savegame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,8 @@ void overmap::unserialize( const JsonObject &jsobj )
}
cities.push_back( new_city );
}
} else if( name == "city_tiles" ) {
om_member.read( city_tiles );
} else if( name == "connections_out" ) {
om_member.read( connections_out );
} else if( name == "roads_out" ) {
Expand Down Expand Up @@ -1251,6 +1253,9 @@ void overmap::serialize( std::ostream &fout ) const
json.end_array();
fout << std::endl;

json.member( "city_tiles", city_tiles );
fout << std::endl;

json.member( "connections_out", connections_out );
fout << std::endl;

Expand Down
Loading