From 75e76c92ca2897d7cf0ea008fc9a0376ac3dc9e2 Mon Sep 17 00:00:00 2001 From: CLIDragon <84266961+CLIDragon@users.noreply.github.com> Date: Thu, 29 Aug 2024 13:14:31 +1000 Subject: [PATCH] Replace pf_special with a scoped enum and add more flags The current flags do not support all functionalities required (e.g. whether a piece of terrain is sheltered or unsheltered), and lack granularity with e.g. GOESUPDOWN requiring extra checks for ramps, whether it goes up or goes down, and so forth. Additionally, using a scoped enum is good practice and will hopefully prevent accidental misuse of the enum type. Part of #75945. The additional flags aren't presently used as they are intended to be part of a new pathfinding implementation. Co-authored-by: prharvey <2677507+prharvey@users.noreply.github.com> --- src/map.cpp | 36 ++++++++----- src/map.h | 8 +-- src/pathfinding.cpp | 34 ++++++++----- src/pathfinding.h | 121 ++++++++++++++++++++++++++++++++++++-------- 4 files changed, 147 insertions(+), 52 deletions(-) diff --git a/src/map.cpp b/src/map.cpp index 3042a73a93858..c8e4ab179eda5 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -10932,7 +10932,7 @@ void map::update_pathfinding_cache( const tripoint &p ) const return; } pathfinding_cache &cache = get_pathfinding_cache( p.z ); - pf_special cur_value = PF_NORMAL; + PathfindingFlags cur_value = PathfindingFlag::Ground; const_maptile tile = maptile_at_internal( p ); @@ -10946,39 +10946,49 @@ void map::update_pathfinding_cache( const tripoint &p ) const const int cost = move_cost_internal( furniture, terrain, field, veh, part ); if( cost > 2 ) { - cur_value |= PF_SLOW; + cur_value |= PathfindingFlag::Slow; } else if( cost <= 0 ) { - cur_value |= PF_WALL; + cur_value |= PathfindingFlag::Obstacle; if( terrain.has_flag( ter_furn_flag::TFLAG_CLIMBABLE ) ) { - cur_value |= PF_CLIMBABLE; + cur_value |= PathfindingFlag::Climbable; } } if( veh != nullptr ) { - cur_value |= PF_VEHICLE; + cur_value |= PathfindingFlag::Vehicle; } for( const auto &fld : tile.get_field() ) { const field_entry &cur = fld.second; if( cur.is_dangerous() ) { - cur_value |= PF_FIELD; + cur_value |= PathfindingFlag::DangerousField; } } if( ( !tile.get_trap_t().is_benign() || !terrain.trap.obj().is_benign() ) && !here.has_vehicle_floor( p ) ) { - cur_value |= PF_TRAP; + cur_value |= PathfindingFlag::DangerousTrap; } - if( terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) || - terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) || - terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) || terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) || - terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) ) { - cur_value |= PF_UPDOWN; + if( terrain.has_flag( ter_furn_flag::TFLAG_GOES_UP ) ) { + cur_value |= PathfindingFlag::GoesUp; + } + + if( terrain.has_flag( ter_furn_flag::TFLAG_GOES_DOWN ) ) { + cur_value |= PathfindingFlag::GoesDown; + } + + if( terrain.has_flag( ter_furn_flag::TFLAG_RAMP ) || + terrain.has_flag( ter_furn_flag::TFLAG_RAMP_UP ) ) { + cur_value |= PathfindingFlag::GoesUp | PathfindingFlag::RampUp; + } + + if( terrain.has_flag( ter_furn_flag::TFLAG_RAMP_DOWN ) ) { + cur_value |= PathfindingFlag::GoesDown | PathfindingFlag::RampDown; } if( terrain.has_flag( ter_furn_flag::TFLAG_SHARP ) && !here.has_vehicle_floor( p ) ) { - cur_value |= PF_SHARP; + cur_value |= PathfindingFlag::Sharp; } cache.special[p.x][p.y] = cur_value; diff --git a/src/map.h b/src/map.h index 4ede732195a15..345fc0f084143 100644 --- a/src/map.h +++ b/src/map.h @@ -102,7 +102,7 @@ template struct weighted_int_list; struct field_proc_data; -enum pf_special : int; +class PathfindingFlags; using relic_procgen_id = string_id; @@ -814,17 +814,17 @@ class map // Includes climbing, bashing and opening doors. int cost_to_pass( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, const pathfinding_settings &settings, - pf_special p_special ) const; + PathfindingFlags p_special ) const; // Pathfinding cost helper that computes the cost of moving into |p| // from |cur| based on perceived danger. // Includes moving through traps. int cost_to_avoid( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, const pathfinding_settings &settings, - pf_special p_special ) const; + PathfindingFlags p_special ) const; // Sum of cost_to_pass and cost_to_avoid. int extra_cost( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, const pathfinding_settings &settings, - pf_special p_special ) const; + PathfindingFlags p_special ) const; public: // Vehicles: Common to 2D and 3D diff --git a/src/pathfinding.cpp b/src/pathfinding.cpp index 927b3727ae72f..51baa9a9bce9b 100644 --- a/src/pathfinding.cpp +++ b/src/pathfinding.cpp @@ -122,7 +122,7 @@ static bool vertical_move_destination( const map &m, ter_furn_flag flag, tripoin { const pathfinding_cache &pf_cache = m.get_pathfinding_cache_ref( t.z ); for( const point &p : closest_points_first( t.xy(), SEEX ) ) { - if( pf_cache.special[p.x][p.y] & PF_UPDOWN ) { + if( pf_cache.special[p.x][p.y] & ( PathfindingFlag::GoesDown | PathfindingFlag::GoesUp ) ) { const tripoint t2( p, t.z ); if( m.has_flag( flag, t2 ) ) { t = t2; @@ -195,7 +195,9 @@ std::vector map::straight_route( const tripoint_bub_ms &f, const pathfinding_cache &pf_cache = get_pathfinding_cache_ref( f.z() ); // Check all points for any special case (including just hard terrain) if( std::any_of( ret.begin(), ret.end(), [&pf_cache]( const tripoint_bub_ms & p ) { - constexpr pf_special non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP | PF_SHARP; + constexpr PathfindingFlags non_normal = PathfindingFlag::Slow | + PathfindingFlag::Obstacle | PathfindingFlag::Vehicle | PathfindingFlag::DangerousTrap | + PathfindingFlag::Sharp; return pf_cache.special[p.x()][p.y()] & non_normal; } ) ) { ret.clear(); @@ -208,9 +210,11 @@ static constexpr int PF_IMPASSABLE = -1; static constexpr int PF_IMPASSABLE_FROM_HERE = -2; int map::cost_to_pass( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, const pathfinding_settings &settings, - pf_special p_special ) const + PathfindingFlags p_special ) const { - constexpr pf_special non_normal = PF_SLOW | PF_WALL | PF_VEHICLE | PF_TRAP | PF_SHARP; + constexpr PathfindingFlags non_normal = PathfindingFlag::Slow | + PathfindingFlag::Obstacle | PathfindingFlag::Vehicle | PathfindingFlag::DangerousTrap | + PathfindingFlag::Sharp; if( !( p_special & non_normal ) ) { // Boring flat dirt - the most common case above the ground return 2; @@ -220,7 +224,7 @@ int map::cost_to_pass( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, return PF_IMPASSABLE; } - if( settings.avoid_sharp && ( p_special & PF_SHARP ) ) { + if( settings.avoid_sharp && ( p_special & PathfindingFlag::Sharp ) ) { return PF_IMPASSABLE; } @@ -288,7 +292,7 @@ int map::cost_to_pass( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, } // If we can climb it, great! - if( climb_cost > 0 && p_special & PF_CLIMBABLE ) { + if( climb_cost > 0 && p_special & PathfindingFlag::Climbable ) { return climb_cost; } @@ -328,9 +332,9 @@ int map::cost_to_pass( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, } int map::cost_to_avoid( const tripoint_bub_ms & /*cur*/, const tripoint_bub_ms &p, - const pathfinding_settings &settings, pf_special p_special ) const + const pathfinding_settings &settings, PathfindingFlags p_special ) const { - if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { + if( settings.avoid_traps && ( p_special & PathfindingFlag::DangerousTrap ) ) { const const_maptile &tile = maptile_at_internal( p ); const ter_t &terrain = tile.get_ter_t(); const trap &ter_trp = terrain.trap.obj(); @@ -342,7 +346,7 @@ int map::cost_to_avoid( const tripoint_bub_ms & /*cur*/, const tripoint_bub_ms & } } - if( settings.avoid_dangerous_fields && ( p_special & PF_FIELD ) ) { + if( settings.avoid_dangerous_fields && ( p_special & PathfindingFlag::DangerousField ) ) { // We'll walk through even known-dangerous fields if we absolutely have to. return 500; } @@ -352,7 +356,7 @@ int map::cost_to_avoid( const tripoint_bub_ms & /*cur*/, const tripoint_bub_ms & int map::extra_cost( const tripoint_bub_ms &cur, const tripoint_bub_ms &p, const pathfinding_settings &settings, - pf_special p_special ) const + PathfindingFlags p_special ) const { int pass_cost = cost_to_pass( tripoint_bub_ms( cur ), tripoint_bub_ms( p ), settings, p_special ); if( pass_cost < 0 ) { @@ -436,7 +440,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, layer.closed[parent_index] = true; const pathfinding_cache &pf_cache = get_pathfinding_cache_ref( cur.z ); - const pf_special cur_special = pf_cache.special[cur.x][cur.y]; + const PathfindingFlags cur_special = pf_cache.special[cur.x][cur.y]; // 7 3 5 // 1 . 2 @@ -464,7 +468,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, // Penalize for diagonals or the path will look "unnatural" int newg = layer.gscore[parent_index] + ( ( cur.x != p.x && cur.y != p.y ) ? 1 : 0 ); - const pf_special p_special = pf_cache.special[p.x][p.y]; + const PathfindingFlags p_special = pf_cache.special[p.x][p.y]; const int cost = extra_cost( tripoint_bub_ms( cur ), tripoint_bub_ms( p ), settings, p_special ); if( cost < 0 ) { if( cost == PF_IMPASSABLE ) { @@ -477,7 +481,7 @@ std::vector map::route( const tripoint &f, const tripoint &t, // Special case: pathfinders that avoid traps can avoid ledges by // climbing down. This can't be covered by |extra_cost| because it // can add a new point to the search. - if( settings.avoid_traps && ( p_special & PF_TRAP ) ) { + if( settings.avoid_traps && ( p_special & PathfindingFlag::DangerousTrap ) ) { const const_maptile &tile = maptile_at_internal( p ); const ter_t &terrain = tile.get_ter_t(); const trap &ter_trp = terrain.trap.obj(); @@ -505,7 +509,9 @@ std::vector map::route( const tripoint &f, const tripoint &t, pf.add_point( newg, newg + 2 * rl_dist( p, t ), cur, p ); } - if( !( cur_special & PF_UPDOWN ) || !settings.allow_climb_stairs ) { + // TODO: We should be able to go up ramps even if we can't climb stairs. + if( !( cur_special & ( PathfindingFlag::GoesUp | PathfindingFlag::GoesDown ) ) || + !settings.allow_climb_stairs ) { // The part below is only for z-level pathing continue; } diff --git a/src/pathfinding.h b/src/pathfinding.h index e62769ae1042d..666607a821a47 100644 --- a/src/pathfinding.h +++ b/src/pathfinding.h @@ -6,38 +6,117 @@ #include "game_constants.h" #include "mdarray.h" -enum pf_special : int { - PF_NORMAL = 0x00, // Plain boring tile (grass, dirt, floor etc.) - PF_SLOW = 0x01, // Tile with move cost >2 - PF_WALL = 0x02, // Unpassable ter/furn/vehicle - PF_VEHICLE = 0x04, // Any vehicle tile (passable or not) - PF_FIELD = 0x08, // Dangerous field - PF_TRAP = 0x10, // Dangerous trap - PF_UPDOWN = 0x20, // Stairs, ramp etc. - PF_CLIMBABLE = 0x40, // 0 move cost but can be climbed on examine - PF_SHARP = 0x80, // sharp items (barbed wire, etc) +// An attribute of a particular map square that is of interest in pathfinding. +// Has a maximum of 32 members. For more, the datatype underlying PathfindingFlags +// needs to be increased. +enum class PathfindingFlag : uint8_t { + Ground = 0, // Can walk on + Slow, // Move cost > 2 + Swimmable, // Can swim in + Air, // Empty air + Unsheltered, // Outside and above ground level + Obstacle, // Something stopping us, might be bashable. + Bashable, // Something bashable. + Impassable, // Impassable obstacle. + Vehicle, // Vehicle tile (passable or not) + DangerousField, // Dangerous field + DangerousTrap, // Dangerous trap (i.e. not flagged benign) + GoesUp, // Valid stairs up + GoesDown, // Valid stairs down + RampUp, // Valid ramp up + RampDown, // Valid ramp down + Climbable, // Obstacle but can be climbed on examine + Sharp, // Sharp items (barbed wire, etc) + Door, // A door (any kind) + InsideDoor, // A door that can be opened from the inside only + LockedDoor, // A locked door + Pit, // A pit you can fall into / climb out of. + DeepWater, // Deep water. + Burrowable, // Can burrow into + HardGround, // Can not dig & burrow intotiny = 1, + RestrictTiny, // Tiny cannot enter + RestrictSmall, // Small cannot enter + RestrictMedium, // Medium cannot enter + RestrictLarge, // Large cannot enter + RestrictHuge, // Huge cannot enter + Lava, // Lava terrain }; -constexpr pf_special operator | ( pf_special lhs, pf_special rhs ) +class PathfindingFlags { - return static_cast( static_cast< int >( lhs ) | static_cast< int >( rhs ) ); + public: + constexpr PathfindingFlags() = default; + + // NOLINTNEXTLINE(google-explicit-constructor) + constexpr PathfindingFlags( PathfindingFlag flag ) : flags_( uint32_t{ 1 } << static_cast + ( flag ) ) {} + + constexpr void set_union( PathfindingFlags flags ) { + flags_ |= flags.flags_; + } + constexpr void set_intersect( PathfindingFlags flags ) { + flags_ &= flags.flags_; + } + constexpr void set_clear( PathfindingFlags flags ) { + flags_ &= ~flags.flags_; + } + + constexpr void clear() { + flags_ = 0; + } + + constexpr bool is_set( PathfindingFlag flag ) const { + return flags_ & ( uint32_t{ 1 } << static_cast( flag ) ); + } + constexpr bool is_set( PathfindingFlags flags ) const { + return ( flags_ & flags.flags_ ) == flags.flags_; + } + + constexpr bool is_any_set() const { + return flags_; + } + + constexpr explicit operator bool() const { + return is_any_set(); + } + + constexpr PathfindingFlags &operator|=( PathfindingFlags flags ) { + set_union( flags ); + return *this; + } + + constexpr PathfindingFlags &operator&=( PathfindingFlags flags ) { + set_intersect( flags ); + return *this; + } + + private: + uint32_t flags_ = 0; +}; + +constexpr PathfindingFlags operator|( PathfindingFlags lhs, PathfindingFlags rhs ) +{ + return lhs |= rhs; +} + +constexpr PathfindingFlags operator&( PathfindingFlags lhs, PathfindingFlags rhs ) +{ + return lhs &= rhs; } -constexpr pf_special operator & ( pf_special lhs, pf_special rhs ) +constexpr PathfindingFlags operator|( PathfindingFlags lhs, PathfindingFlag rhs ) { - return static_cast( static_cast< int >( lhs ) & static_cast< int >( rhs ) ); + return lhs |= rhs; } -inline pf_special &operator |= ( pf_special &lhs, pf_special rhs ) +constexpr PathfindingFlags operator&( PathfindingFlags lhs, PathfindingFlag rhs ) { - lhs = static_cast( static_cast< int >( lhs ) | static_cast< int >( rhs ) ); - return lhs; + return lhs &= rhs; } -inline pf_special &operator &= ( pf_special &lhs, pf_special rhs ) +constexpr PathfindingFlags operator |( const PathfindingFlag &a, const PathfindingFlag &b ) { - lhs = static_cast( static_cast< int >( lhs ) & static_cast< int >( rhs ) ); - return lhs; + return PathfindingFlags( a ) | PathfindingFlags( b ); } struct pathfinding_cache { @@ -46,7 +125,7 @@ struct pathfinding_cache { bool dirty = false; std::unordered_set dirty_points; - cata::mdarray special; + cata::mdarray special; }; struct pathfinding_settings {