diff --git a/src/lightmap.cpp b/src/lightmap.cpp index dee86db0ce83d..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 ) ) { @@ -670,9 +704,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 +718,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 +911,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 +962,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; + 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 ); 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 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();