Skip to content

Commit

Permalink
New crouching mechanics for hiding and stealth (#29092)
Browse files Browse the repository at this point in the history
  • Loading branch information
Wokko1 authored and kevingranade committed Apr 18, 2019
1 parent 5819d39 commit 69dc10f
Show file tree
Hide file tree
Showing 14 changed files with 456 additions and 12 deletions.
138 changes: 138 additions & 0 deletions data/json/furniture.json

Large diffs are not rendered by default.

218 changes: 218 additions & 0 deletions data/json/terrain.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion data/raw/keybindings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2008,7 +2008,7 @@
},
{
"type": "keybinding",
"name": "Toggle move mode (run/walk)",
"name": "Toggle move mode (run/walk/crouch)",
"category": "DEFAULTMODE",
"id": "toggle_move",
"bindings": [ { "input_method": "keyboard", "key": "\"" } ]
Expand Down
2 changes: 1 addition & 1 deletion src/action.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ enum action_id : int {
ACTION_MOVE_DOWN,
/** Ascend a staircase */
ACTION_MOVE_UP,
/** Toggle run/walk mode */
/** Toggle run/walk/crouch mode */
ACTION_TOGGLE_MOVE,
/**@}*/

Expand Down
36 changes: 34 additions & 2 deletions src/creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,40 @@ bool Creature::sees( const Creature &critter ) const
abs( posz() - critter.posz() ) <= 1 ) ) ) {
return false;
}

if( const player *p = dynamic_cast<const player *>( &critter ) ) {
if( p->move_mode == "crouch" ) {
const int coverage = g->m.obstacle_coverage( pos(), critter.pos() );
if( coverage < 30 ) {
return sees( critter.pos(), critter.is_player() );
}
float size_modifier = 1.0;
switch( p->get_size() ) {
case MS_TINY:
size_modifier = 2.0;
break;
case MS_SMALL:
size_modifier = 1.4;
break;
case MS_MEDIUM:
break;
case MS_LARGE:
size_modifier = 0.6;
break;
case MS_HUGE:
size_modifier = 0.15;
break;
}
const int vision_modifier = 30 - 0.5 * coverage * size_modifier;
if( vision_modifier > 1 ) {
return sees( critter.pos(), critter.is_player(), vision_modifier );
}
return false;
}
}
return sees( critter.pos(), critter.is_player() );
}

bool Creature::sees( const tripoint &t, bool is_player ) const
bool Creature::sees( const tripoint &t, bool is_player, int range_mod ) const
{
if( !fov_3d && posz() != t.z ) {
return false;
Expand All @@ -207,6 +236,9 @@ bool Creature::sees( const tripoint &t, bool is_player ) const
if( has_effect( effect_no_sight ) ) {
range = 1;
}
if( range_mod > 0 ) {
range = std::min( range, range_mod );
}
if( is_player ) {
// Special case monster -> player visibility, forcing it to be symmetric with player vision.
const float player_visibility_factor = g->u.visibility() / 100.0f;
Expand Down
2 changes: 1 addition & 1 deletion src/creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ class Creature
*/
/*@{*/
virtual bool sees( const Creature &critter ) const;
virtual bool sees( const tripoint &t, bool is_player = false ) const;
virtual bool sees( const tripoint &t, bool is_player = false, int range_mod = 0 ) const;
/*@}*/

/**
Expand Down
5 changes: 5 additions & 0 deletions src/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10513,6 +10513,11 @@ bool game::walk_move( const tripoint &dest_loc )
} else if( u.has_bionic( bionic_id( "bio_ankles" ) ) ) {
volume = 12;
}
if( u.move_mode == "run" ) {
volume *= 1.5;
} else if( u.move_mode == "crouch" ) {
volume /= 2;
}
sounds::sound( dest_loc, volume, sounds::sound_t::movement, _( "footsteps" ), true,
"none", "none" ); // Sound of footsteps may awaken nearby monsters
sfx::do_footstep();
Expand Down
28 changes: 28 additions & 0 deletions src/map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6059,6 +6059,34 @@ bool map::sees( const tripoint &F, const tripoint &T, const int range, int &bres
return visible;
}

int map::obstacle_coverage( const tripoint &loc1, const tripoint &loc2 ) const
{
// Can't hide if you are standing on furniture, or non-flat slowing-down terrain tile.
if( furn( loc2 ).obj().id || ( move_cost( loc2 ) > 2 && !has_flag_ter( TFLAG_FLAT, loc2 ) ) ) {
return 0;
}
const int ax = std::abs( loc1.x - loc2.x ) * 2;
const int ay = std::abs( loc1.y - loc2.y ) * 2;
int offset = std::min( ax, ay ) - ( std::max( ax, ay ) / 2 );
tripoint obstaclepos;
bresenham( loc2, loc1, offset, 0, [&obstaclepos]( const tripoint & new_point ) {
// Only adjacent tile between you and enemy is checked for cover.
obstaclepos = new_point;
return false;
} );
if( const auto obstacle_f = furn( obstaclepos ) ) {
return obstacle_f->coverage;
}
if( const auto vp = veh_at( obstaclepos ) ) {
if( vp->obstacle_at_part() ) {
return 60;
} else if( !vp->part_with_feature( VPFLAG_AISLE, true ) ) {
return 45;
}
}
return ter( obstaclepos )->coverage;
}

// This method tries a bunch of initial offsets for the line to try and find a clear one.
// Basically it does, "Find a line from any point in the source that ends up in the target square".
std::vector<tripoint> map::find_clear_path( const tripoint &source,
Expand Down
6 changes: 6 additions & 0 deletions src/map.h
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,12 @@ class map
**/
bool sees( const tripoint &F, const tripoint &T, int range, int &bresenham_slope ) const;
public:
/**
* Returns coverage of target in relation to the observer. Target is loc2, observer is loc1.
* First tile from the target is an obstacle, which has the coverage value.
* If there's no obstacle adjacent to the target - no coverage.
*/
int obstacle_coverage( const tripoint &loc1, const tripoint &loc2 ) const;
/**
* Check whether there's a direct line of sight between `F` and
* `T` with the additional movecost restraints.
Expand Down
5 changes: 4 additions & 1 deletion src/mapdata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ static const std::unordered_map<std::string, ter_bitflags> ter_bitflags_map = {
{ "NO_FLOOR", TFLAG_NO_FLOOR }, // Things should fall when placed on this tile
{ "SEEN_FROM_ABOVE", TFLAG_SEEN_FROM_ABOVE },// This should be visible if the tile above has no floor
{ "HIDE_PLACE", TFLAG_HIDE_PLACE }, // Creature on this tile can't be seen by other creature not standing on adjacent tiles
{ "BLOCK_WIND", TFLAG_BLOCK_WIND}, // This tile will partially block the wind.
{ "BLOCK_WIND", TFLAG_BLOCK_WIND }, // This tile will partially block the wind.
{ "FLAT", TFLAG_FLAT }, // This tile is flat.
{ "RAMP", TFLAG_RAMP }, // Can be used to move up a z-level
}
};
Expand Down Expand Up @@ -1073,6 +1074,7 @@ void ter_t::load( JsonObject &jo, const std::string &src )
map_data_common_t::load( jo, src );
mandatory( jo, was_loaded, "name", name_ );
mandatory( jo, was_loaded, "move_cost", movecost );
optional( jo, was_loaded, "coverage", coverage );
optional( jo, was_loaded, "max_volume", max_volume, legacy_volume_reader,
DEFAULT_MAX_VOLUME_IN_SQUARE );
optional( jo, was_loaded, "trap", trap_id_str );
Expand Down Expand Up @@ -1173,6 +1175,7 @@ void furn_t::load( JsonObject &jo, const std::string &src )
map_data_common_t::load( jo, src );
mandatory( jo, was_loaded, "name", name_ );
mandatory( jo, was_loaded, "move_cost_mod", movecost );
optional( jo, was_loaded, "coverage", coverage );
optional( jo, was_loaded, "comfort", comfort, 0 );
optional( jo, was_loaded, "floor_bedding_warmth", floor_bedding_warmth, 0 );
optional( jo, was_loaded, "bonus_fire_warmth_feet", bonus_fire_warmth_feet, 300 );
Expand Down
2 changes: 2 additions & 0 deletions src/mapdata.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ enum ter_bitflags : int {
TFLAG_RAMP,
TFLAG_HIDE_PLACE,
TFLAG_BLOCK_WIND,
TFLAG_FLAT,

NUM_TERFLAGS
};
Expand Down Expand Up @@ -220,6 +221,7 @@ struct map_data_common_t {
std::array<long, SEASONS_PER_YEAR> symbol_;

int movecost; // The amount of movement points required to pass this terrain by default.
int coverage; // The coverage percentage of a furniture piece of terrain. <30 won't cover from sight.
units::volume max_volume; // Maximal volume of items that can be stored in/on this furniture

std::string description;
Expand Down
10 changes: 7 additions & 3 deletions src/panels.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,8 @@ void draw_stealth( player &u, const catacurses::window &w )
mvwprintz( w, 0, 0, c_light_gray, _( "Speed" ) );
mvwprintz( w, 0, 7, value_color( u.get_speed() ), "%s", u.get_speed() );
mvwprintz( w, 0, 15 - to_string( u.movecounter ).length(), c_light_gray,
to_string( u.movecounter ) + ( u.move_mode == "walk" ? "W" : "R" ) );
to_string( u.movecounter ) + ( u.move_mode == "walk" ? "W" : ( u.move_mode == "crouch" ? "C" :
"R" ) ) );

if( u.is_deaf() ) {
mvwprintz( w, 0, 22, c_red, _( "DEAF" ) );
Expand Down Expand Up @@ -1205,7 +1206,9 @@ void draw_char( player &u, const catacurses::window &w )

const auto str_walk = pgettext( "movement-type", "W" );
const auto str_run = pgettext( "movement-type", "R" );
const char *move = u.move_mode == "walk" ? str_walk : str_run;
const auto str_crouch = pgettext( "movement-type", "C" );
const char *move = u.move_mode == "walk" ? str_walk : ( u.move_mode == "crouch" ? str_crouch :
str_run );
std::string movecost = std::to_string( u.movecounter ) + "(" + move + ")";
bool m_style = get_option<std::string>( "MORALE_STYLE" ) == "horizontal";
std::string smiley = morale_emotion( morale_pair.second, get_face_type( u ), m_style );
Expand Down Expand Up @@ -1435,7 +1438,8 @@ void draw_health_classic( player &u, const catacurses::window &w )
mvwprintz( w, 5, 21, u.get_speed() < 100 ? c_red : c_white,
_( "Spd " ) + to_string( u.get_speed() ) );
mvwprintz( w, 5, 26 + to_string( u.get_speed() ).length(), c_white,
to_string( u.movecounter ) + " " + ( u.move_mode == "walk" ? "W" : "R" ) );
to_string( u.movecounter ) + " " + ( u.move_mode == "walk" ? "W" : ( u.move_mode == "crouch" ? "C" :
"R" ) ) );
}

// temperature
Expand Down
12 changes: 10 additions & 2 deletions src/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1827,6 +1827,9 @@ int player::run_cost( int base_cost, bool diag ) const
// Rationale: Average running speed is 2x walking speed. (NOT sprinting)
stamina_modifier *= 2.0;
}
if( move_mode == "crouch" ) {
stamina_modifier *= 0.5;
}
movecost /= stamina_modifier;

if( diag ) {
Expand Down Expand Up @@ -2977,10 +2980,15 @@ void player::toggle_move_mode()
add_msg( _( "You start running." ) );
} else {
add_msg( m_bad, _( "You're too tired to run." ) );
move_mode = "crouch";
add_msg( _( "You start crouching." ) );
}
} else if( move_mode == "run" ) {
move_mode = "crouch";
add_msg( _( "You slow down and start crouching." ) );
} else if( move_mode == "crouch" ) {
move_mode = "walk";
add_msg( _( "You slow to a walk." ) );
add_msg( _( "You stop crouching." ) );
}
}

Expand Down Expand Up @@ -12032,7 +12040,7 @@ void player::clear_memorized_tile( const tripoint &pos )
player_map_memory.clear_memorized_tile( pos );
}

bool player::sees( const tripoint &t, bool ) const
bool player::sees( const tripoint &t, bool, int ) const
{
static const bionic_id str_bio_night( "bio_night" );
const int wanted_range = rl_dist( pos(), t );
Expand Down
2 changes: 1 addition & 1 deletion src/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,7 @@ class player : public Character
void clear_memorized_tile( const tripoint &pos );

// see Creature::sees
bool sees( const tripoint &c, bool is_player = false ) const override;
bool sees( const tripoint &c, bool is_player = false, int range_mod = 0 ) const override;
// see Creature::sees
bool sees( const Creature &critter ) const override;
/**
Expand Down

0 comments on commit 69dc10f

Please sign in to comment.