From f00a2f806540cc2a3dfd736c0666d22e3e48ffe4 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 23 Feb 2020 14:40:00 -0500 Subject: [PATCH 1/3] Simplify shadowcasting types There are a bunch of types which are just a std::array of pointers to two-dimensional arrays. These are all quite verbose. Introduce a template alias to simplify all those types. --- src/lightmap.cpp | 37 ++++++++++++++++++------------------- src/shadowcasting.h | 9 ++++++--- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index dee86db0ce83d..5656bfb8ce51b 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -670,9 +670,9 @@ template void cast_zlight_segment( - const std::array &output_caches, - const std::array &input_arrays, - const std::array &floor_caches, + const array_of_grids_of &output_caches, + const array_of_grids_of &input_arrays, + const array_of_grids_of &floor_caches, const tripoint &offset, int offset_distance, T numerator = 1.0f, int row = 1, float start_major = 0.0f, float end_major = 1.0f, @@ -684,9 +684,9 @@ template void cast_zlight_segment( - const std::array &output_caches, - const std::array &input_arrays, - const std::array &floor_caches, + const array_of_grids_of &output_caches, + const array_of_grids_of &input_arrays, + const array_of_grids_of &floor_caches, const tripoint &offset, const int offset_distance, const T numerator, const int row, float start_major, const float end_major, @@ -877,9 +877,9 @@ template void cast_zlight( - const std::array &output_caches, - const std::array &input_arrays, - const std::array &floor_caches, + const array_of_grids_of &output_caches, + const array_of_grids_of &input_arrays, + const array_of_grids_of &floor_caches, const tripoint &origin, const int offset_distance, const T numerator ) { // Down @@ -928,16 +928,15 @@ void cast_zlight( // I can't figure out how to make implicit instantiation work when the parameters of // the template-supplied function pointers are involved, so I'm explicitly instantiating instead. template void cast_zlight( - const std::array &output_caches, - const std::array &input_arrays, - const std::array &floor_caches, + const array_of_grids_of &output_caches, + const array_of_grids_of &input_arrays, + const array_of_grids_of &floor_caches, const tripoint &origin, int offset_distance, float numerator ); template void cast_zlight( - const std::array &output_caches, - const std::array - &input_arrays, - const std::array &floor_caches, + const array_of_grids_of &output_caches, + const array_of_grids_of &input_arrays, + const array_of_grids_of &floor_caches, const tripoint &origin, int offset_distance, fragment_cloud numerator ); template transparency_caches; - std::array seen_caches; - std::array floor_caches; + array_of_grids_of transparency_caches; + array_of_grids_of seen_caches; + array_of_grids_of floor_caches; for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) { auto &cur_cache = get_cache( z ); transparency_caches[z + OVERMAP_DEPTH] = &cur_cache.transparency_cache; diff --git a/src/shadowcasting.h b/src/shadowcasting.h index 8b542e95cfcb4..fb7779a423124 100644 --- a/src/shadowcasting.h +++ b/src/shadowcasting.h @@ -110,14 +110,17 @@ void castLightAll( Out( &output_cache )[MAPSIZE_X][MAPSIZE_Y], const point &offset, int offsetDistance = 0, T numerator = 1.0 ); +template +using array_of_grids_of = std::array; + // TODO: Generalize the floor check, allow semi-transparent floors template< typename T, T( *calc )( const T &, const T &, const int & ), bool( *check )( const T &, const T & ), T( *accumulate )( const T &, const T &, const int & ) > void cast_zlight( - const std::array &output_caches, - const std::array &input_arrays, - const std::array &floor_caches, + const array_of_grids_of &output_caches, + const array_of_grids_of &input_arrays, + const array_of_grids_of &floor_caches, const tripoint &origin, int offset_distance, T numerator ); #endif From 4c70a18f8a71e81fef9ac6e9b68a560009ceca92 Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 23 Feb 2020 15:18:51 -0500 Subject: [PATCH 2/3] Separate caches for light and vision transparency Crouching affects your ability to see past adjacent obstacles. Previously this was implemented by altering the transparency cache. However, this also stops light passing the tile, and means that crouching by a window can cause a room to fall dark. Fix by separating the transparency cache into two, one for light and one for vision. The latter is a copy of the former, except with tweaks for crouching and with the avatar's tile forced to be transparent. --- src/lightmap.cpp | 38 ++++++++++++++++++++++++++++++++++++-- src/map.cpp | 21 ++++----------------- src/map.h | 2 ++ 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/lightmap.cpp b/src/lightmap.cpp index 5656bfb8ce51b..01b5668cb8b79 100644 --- a/src/lightmap.cpp +++ b/src/lightmap.cpp @@ -143,6 +143,40 @@ bool map::build_transparency_cache( const int zlev ) return true; } +bool map::build_vision_transparency_cache( const int zlev ) +{ + auto &map_cache = get_cache( zlev ); + auto &transparency_cache = map_cache.transparency_cache; + auto &vision_transparency_cache = map_cache.vision_transparency_cache; + + memcpy( &vision_transparency_cache, &transparency_cache, sizeof( transparency_cache ) ); + + const tripoint &p = g->u.pos(); + + if( p.z != zlev ) { + return false; + } + + bool dirty = false; + + bool is_crouching = g->u.movement_mode_is( CMM_CROUCH ); + for( const tripoint &loc : points_in_radius( p, 1 ) ) { + if( loc == p ) { + // The tile player is standing on should always be visible + if( ( has_furn( p ) && !furn( p )->transparent ) || !ter( p )->transparent ) { + vision_transparency_cache[p.x][p.y] = LIGHT_TRANSPARENCY_CLEAR; + } + } else if( is_crouching && coverage( loc ) >= 30 ) { + // If we're crouching behind an obstacle, we can't see past it. + vision_transparency_cache[loc.x][loc.y] = LIGHT_TRANSPARENCY_SOLID; + map_cache.transparency_cache_dirty = true; + dirty = true; + } + } + + return dirty; +} + void map::apply_character_light( player &p ) { if( p.has_effect( effect_onfire ) ) { @@ -1102,7 +1136,7 @@ castLightAll floor_caches; for( int z = -OVERMAP_DEPTH; z <= OVERMAP_HEIGHT; z++ ) { auto &cur_cache = get_cache( z ); - transparency_caches[z + OVERMAP_DEPTH] = &cur_cache.transparency_cache; + transparency_caches[z + OVERMAP_DEPTH] = &cur_cache.vision_transparency_cache; seen_caches[z + OVERMAP_DEPTH] = &cur_cache.seen_cache; floor_caches[z + OVERMAP_DEPTH] = &cur_cache.floor_cache; std::uninitialized_fill_n( diff --git a/src/map.cpp b/src/map.cpp index 290f45f579ea7..6c3bb1e039a5d 100755 --- a/src/map.cpp +++ b/src/map.cpp @@ -7718,33 +7718,19 @@ void map::build_map_cache( const int zlev, bool skip_lightmap ) for( int z = minz; z <= maxz; z++ ) { build_outside_cache( z ); seen_cache_dirty |= build_transparency_cache( z ); + seen_cache_dirty |= build_vision_transparency_cache( zlev ); seen_cache_dirty |= build_floor_cache( z ); do_vehicle_caching( z ); } - const tripoint &p = g->u.pos(); - bool is_crouching = g->u.movement_mode_is( CMM_CROUCH ); - for( const tripoint &loc : points_in_radius( p, 1 ) ) { - if( loc == p ) { - // The tile player is standing on should always be transparent - if( ( has_furn( p ) && !furn( p ).obj().transparent ) || !ter( p ).obj().transparent ) { - get_cache( p.z ).transparency_cache[p.x][p.y] = LIGHT_TRANSPARENCY_CLEAR; - } - } else if( is_crouching && coverage( loc ) >= 30 ) { - // If we're crouching behind an obstacle, we can't see past it. - get_cache( loc.z ).transparency_cache[loc.x][loc.y] = LIGHT_TRANSPARENCY_SOLID; - get_cache( loc.z ).transparency_cache_dirty = true; - seen_cache_dirty = true; - } - } - if( seen_cache_dirty ) { skew_vision_cache.clear(); } // Initial value is illegal player position. + const tripoint &p = g->u.pos(); static tripoint player_prev_pos; if( seen_cache_dirty || player_prev_pos != p ) { - build_seen_cache( g->u.pos(), zlev ); + build_seen_cache( p, zlev ); player_prev_pos = p; } if( !skip_lightmap ) { @@ -8243,6 +8229,7 @@ level_cache::level_cache() std::fill_n( &outside_cache[0][0], map_dimensions, false ); std::fill_n( &floor_cache[0][0], map_dimensions, false ); std::fill_n( &transparency_cache[0][0], map_dimensions, 0.0f ); + std::fill_n( &vision_transparency_cache[0][0], map_dimensions, 0.0f ); std::fill_n( &seen_cache[0][0], map_dimensions, 0.0f ); std::fill_n( &camera_cache[0][0], map_dimensions, 0.0f ); std::fill_n( &visibility_cache[0][0], map_dimensions, LL_DARK ); diff --git a/src/map.h b/src/map.h index eceef058560bc..782e442c66e6c 100644 --- a/src/map.h +++ b/src/map.h @@ -170,6 +170,7 @@ struct level_cache { bool outside_cache[MAPSIZE_X][MAPSIZE_Y]; bool floor_cache[MAPSIZE_X][MAPSIZE_Y]; float transparency_cache[MAPSIZE_X][MAPSIZE_Y]; + float vision_transparency_cache[MAPSIZE_X][MAPSIZE_Y]; float seen_cache[MAPSIZE_X][MAPSIZE_Y]; float camera_cache[MAPSIZE_X][MAPSIZE_Y]; lit_level visibility_cache[MAPSIZE_X][MAPSIZE_Y]; @@ -1559,6 +1560,7 @@ class map // Builds a transparency cache and returns true if the cache was invalidated. // Used to determine if seen cache should be rebuilt. bool build_transparency_cache( int zlev ); + bool build_vision_transparency_cache( int zlev ); void build_sunlight_cache( int zlev ); public: void build_outside_cache( int zlev ); From 9528517bc7bff3b5f55e3ed1bfb2fb4a7453743b Mon Sep 17 00:00:00 2001 From: John Bytheway Date: Sun, 23 Feb 2020 13:51:08 -0500 Subject: [PATCH 3/3] Add a test for crouching vision This tests that the player cannot see past an adjacent obstacle when crouching, but light can still pass it. --- tests/vision_test.cpp | 70 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/tests/vision_test.cpp b/tests/vision_test.cpp index df21a9eee6684..96efebcca0a97 100644 --- a/tests/vision_test.cpp +++ b/tests/vision_test.cpp @@ -22,9 +22,27 @@ #include "game_constants.h" #include "point.h" +enum class vision_test_flags { + none = 0, + no_3d = 1 << 0, + crouching = 1 << 1, +}; + +static vision_test_flags operator&( vision_test_flags l, vision_test_flags r ) +{ + return static_cast( + static_cast( l ) & static_cast( r ) ); +} + +static bool operator!( vision_test_flags f ) +{ + return !static_cast( f ); +} + static void full_map_test( const std::vector &setup, const std::vector &expected_results, - const time_point &time ) + const time_point &time, + const vision_test_flags flags ) { const ter_id t_brick_wall( "t_brick_wall" ); const ter_id t_window_frame( "t_window_frame" ); @@ -40,6 +58,12 @@ static void full_map_test( const std::vector &setup, clear_map(); g->reset_light_level(); + if( !!( flags & vision_test_flags::crouching ) ) { + g->u.set_movement_mode( character_movemode::CMM_CROUCH ); + } else { + g->u.set_movement_mode( character_movemode::CMM_WALK ); + } + REQUIRE( !g->u.is_blind() ); REQUIRE( !g->u.in_sleep_state() ); REQUIRE( !g->u.has_effect( effect_narcosis ) ); @@ -224,7 +248,7 @@ struct vision_test_case { std::vector setup; std::vector expected_results; time_point time; - bool test_3d; + vision_test_flags flags; static void transpose( std::vector &v ) { if( v.empty() ) { @@ -261,7 +285,7 @@ struct vision_test_case { } void test() const { - full_map_test( setup, expected_results, time ); + full_map_test( setup, expected_results, time, flags ); } void test_all_transformations() const { @@ -286,6 +310,7 @@ struct vision_test_case { void test_all() const { // Disabling 3d tests for now since 3d sight casting is actually // different (it sees round corners more). + const bool test_3d = !( flags & vision_test_flags::no_3d ); if( test_3d ) { INFO( "using 3d casting" ); fov_3d = true; @@ -326,7 +351,7 @@ TEST_CASE( "vision_daylight", "[shadowcasting][vision]" ) "444", }, midday, - true + vision_test_flags::none }; t.test_all(); @@ -346,7 +371,7 @@ TEST_CASE( "vision_day_indoors", "[shadowcasting][vision]" ) "111", }, midday, - true + vision_test_flags::none }; t.test_all(); @@ -370,7 +395,8 @@ TEST_CASE( "vision_light_shining_in", "[shadowcasting][vision]" ) "1144444444", }, midday, - false // 3D FOV gives different results here due to it seeing round corners more + // 3D FOV gives different results here due to it seeing round corners more + vision_test_flags::no_3d }; t.test_all(); @@ -388,7 +414,7 @@ TEST_CASE( "vision_no_lights", "[shadowcasting][vision]" ) "111", }, midnight, - true + vision_test_flags::none }; t.test_all(); @@ -408,7 +434,7 @@ TEST_CASE( "vision_utility_light", "[shadowcasting][vision]" ) "444", }, midnight, - true + vision_test_flags::none }; t.test_all(); @@ -428,7 +454,7 @@ TEST_CASE( "vision_wall_obstructs_light", "[shadowcasting][vision]" ) "111", }, midnight, - true + vision_test_flags::none }; t.test_all(); @@ -452,7 +478,29 @@ TEST_CASE( "vision_wall_can_be_lit_by_player", "[shadowcasting][vision]" ) "66", }, midnight, - true + vision_test_flags::none + }; + + t.test_all(); +} + +TEST_CASE( "vision_crouching_blocks_vision_but_not_light", "[shadowcasting][vision]" ) +{ + vision_test_case t { + { + "###", + "#u#", + "#=#", + " ", + }, + { + "444", + "444", + "444", + "666", + }, + midday, + vision_test_flags::crouching }; t.test_all(); @@ -481,7 +529,7 @@ TEST_CASE( "vision_see_wall_in_moonlight", "[shadowcasting][vision]" ) }, // Want a night time full_moon - time_past_midnight( full_moon ), - true + vision_test_flags::none }; t.test_all();