Skip to content

Commit

Permalink
Replace pf_special with a scoped enum and add more flags
Browse files Browse the repository at this point in the history
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 CleverRaven#75945. The additional flags aren't presently used as they are
intended to be part of a new pathfinding implementation.

Co-authored-by: prharvey <[email protected]>
  • Loading branch information
CLIDragon and prharvey committed Sep 2, 2024
1 parent c17fa69 commit 75e76c9
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 52 deletions.
36 changes: 23 additions & 13 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 );

Expand All @@ -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;
Expand Down
8 changes: 4 additions & 4 deletions src/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ template<typename T>
struct weighted_int_list;
struct field_proc_data;

enum pf_special : int;
class PathfindingFlags;

using relic_procgen_id = string_id<relic_procgen_data>;

Expand Down Expand Up @@ -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
Expand Down
34 changes: 20 additions & 14 deletions src/pathfinding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -195,7 +195,9 @@ std::vector<tripoint_bub_ms> 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();
Expand All @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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();
Expand All @@ -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;
}
Expand All @@ -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 ) {
Expand Down Expand Up @@ -436,7 +440,7 @@ std::vector<tripoint> 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
Expand Down Expand Up @@ -464,7 +468,7 @@ std::vector<tripoint> 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 ) {
Expand All @@ -477,7 +481,7 @@ std::vector<tripoint> 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();
Expand Down Expand Up @@ -505,7 +509,9 @@ std::vector<tripoint> 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;
}
Expand Down
121 changes: 100 additions & 21 deletions src/pathfinding.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<pf_special>( 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<uint8_t>
( 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<uint8_t>( 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<pf_special>( 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<pf_special>( 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<pf_special>( static_cast< int >( lhs ) & static_cast< int >( rhs ) );
return lhs;
return PathfindingFlags( a ) | PathfindingFlags( b );
}

struct pathfinding_cache {
Expand All @@ -46,7 +125,7 @@ struct pathfinding_cache {
bool dirty = false;
std::unordered_set<point> dirty_points;

cata::mdarray<pf_special, point_bub_ms> special;
cata::mdarray<PathfindingFlags, point_bub_ms> special;
};

struct pathfinding_settings {
Expand Down

0 comments on commit 75e76c9

Please sign in to comment.