Skip to content

Commit

Permalink
Merge pull request CleverRaven#74191 from Procyonae/BetterInCityCheck
Browse files Browse the repository at this point in the history
Better in city check
  • Loading branch information
Maleclypse authored Oct 24, 2024
2 parents c9ee15e + b95fe0c commit 0f77443
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 12 deletions.
15 changes: 14 additions & 1 deletion doc/EFFECT_ON_CONDITION.md
Original file line number Diff line number Diff line change
Expand Up @@ -1248,7 +1248,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 @@ -1265,6 +1265,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 @@ -1760,10 +1760,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 @@ -659,7 +659,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 @@ -2888,15 +2888,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 @@ -4180,6 +4187,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 @@ -5825,6 +5909,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 @@ -5833,6 +5918,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 @@ -5846,6 +5932,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 @@ -5907,7 +5994,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 @@ -6329,7 +6420,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 @@ -6382,6 +6473,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 @@ -7021,7 +7113,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

0 comments on commit 0f77443

Please sign in to comment.