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 #75945. The additional flags aren't presently used as they are
intended to be part of a new pathfinding implementation.
  • Loading branch information
CLIDragon committed Aug 29, 2024
1 parent 6a1c46f commit 4a30154
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 37 deletions.
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
30 changes: 18 additions & 12 deletions src/pathfinding.cpp
Original file line number Diff line number Diff line change
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 @@ -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 4a30154

Please sign in to comment.